徒手用 Docker 構建本身的 PHP 開發環境

1. 前言

1.1 爲何要用 Docker ?

是否有這樣的場景,你搞了一個項目,在本地開發時須要搭建環境,放到線上時也須要搭建環境,到公司想暗戳戳玩一下要搭建環境,不搭還不行,由於你的環境依賴還挺多。這個時候若是有了Docker,只須要在機器上裝個Docker,放上寫好的Dickerfile,一行命令就自動完成這個事,方便又高效,豈不是很爽?php

1.2 準備

接下來,本文介紹如何搭建一個PHP的開發環境,將用 zPhal-dockerfiles 作爲例子,這是我爲個人博客系統準備的一套Dockerfile。html

如今不論是windows,mac仍是linux,docker均可以很好支持,包括Windows系統,在win10系統下Docker for Windows 其實仍是挺不錯的,就是比較吃內存。mysql

經過Docker命令行,咱們能夠作不少事情,拉取鏡像,運行容器,容器內執行命令等,可是如今,咱們要用更加簡單粗暴的方式,編寫好dockerfiles文件,而後經過docker-compose管理好這些文件,簡化操做流程。linux

什麼是Dockerfile?
Dockerfile是由一系列命令和參數構成的腳本,這些命令應用於拉取的基礎鏡像並最終建立一個新的鏡像,經過Dockerfile咱們能夠建立一個你須要的鏡像,裏面是包含了你要安裝的軟件,至關因而提早定製好要安裝的拓展,執行的命令等,而後一鍵執行,極大地簡化操做流程。

按照本文來搭建環境,你須要:nginx

  • 首先了解一下Docker以及Docker的一些基本操做,還有docker-compose是什麼。
  • 而後須要安裝 Docker 和 docker-compose,我將使用 docker-compose 來管理個人 dockerfiles。

注意,編寫 dockerfile 是活的,不是死的,每一個人寫出來的 dockerfile 都會不同,取決於你的需求。git

Docker的官方文檔很是清楚,雖然是英文,可是基本上什麼都有,有問題上文檔翻是很是明智的:Docker Documentationgithub

2. 開始編寫

接下來都是以 zPhal-dockerfiles 爲例子,完整的能夠點連接進去看,下面的只是片斷。web

2.1 預覽

首先,咱們來看一下,我建立的這個dockerfile項目,我大概分紅了下面的目錄(固然這個是本身定的,並非要求這麼去排版你的文件):redis

zPhal-dockerfiles
    app/
        index.php
        phpinfo.php
    data/
        .gitignore
    files/
        mysql/
            conf.d/
                mysql-file.cnf
            Dockerfile
        nginx/
            conf.d/
                default.conf
                zphal.conf
            Dockerfile
            nginx.conf
        php/
            pkg/
                .gitignore
            Dockerfile
            php.ini
            php-dev.ini
            php-fpm.conf
        redis/
            Dockerfile
        docker-compose.yml
    logs/
    .gitgnore
    README.md

在這個項目裏,我用到PHP,MySQL,Nginx,Redis;以及Composer,Phalcon拓展等。sql

總的來講,咱們作這件事有三個流程:編寫好各個軟件的dockerfile;編寫好配置文件;經過docker-compose處理全部的dockerfile,包括將配置配置文件扔進去 dockerfile 文件將構建的鏡像中。

2.2 編寫 Dockerfile 文件

2.2.1 PHP

下面是PHP的Dockerfile:

FROM php:7.2-fpm
MAINTAINER goozp "gzp@goozp.com"

# 設置時區
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 更新安裝依賴包和PHP核心拓展
RUN apt-get update && apt-get install -y \
        git \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd \
        && docker-php-ext-install zip \
        && docker-php-ext-install pdo_mysql \
        && docker-php-ext-install opcache \
        && docker-php-ext-install mysqli \
        && rm -r /var/lib/apt/lists/*

# 將預先下載好的拓展包從宿主機拷貝進去
COPY ./pkg/redis.tgz /home/redis.tgz
COPY ./pkg/cphalcon.tar.gz /home/cphalcon.tar.gz

# 安裝 PECL 拓展,這裏咱們安裝的是Redis
RUN pecl install /home/redis.tgz && echo "extension=redis.so" > /usr/local/etc/php/conf.d/redis.ini

# 安裝第三方拓展,這裏是 Phalcon 拓展
RUN cd /home \
    && tar -zxvf cphalcon.tar.gz \
    && mv cphalcon-* phalcon \
    && cd phalcon/build \
    && ./install \
    && echo "extension=phalcon.so" > /usr/local/etc/php/conf.d/phalcon.ini

# 安裝 Composer
ENV COMPOSER_HOME /root/composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
ENV PATH $COMPOSER_HOME/vendor/bin:$PATH

RUN rm -f /home/redis.tgz \
        rm -f /home/cphalcon.tar.gz 

WORKDIR /data

# Write Permission
RUN usermod -u 1000 www-data

第一行定義了基礎鏡像,這裏咱們用了PHP7.2的fpm版本,這裏第二行定義了一個維護者。

接下來定義了時區,在每個dockerfile都定義了這一句,主要是爲了使全部的容器的時間都與宿主機同步,其實咱們能夠在docker-composer.yml文件中這麼定義:

services:
  php-fpm:
    volumes:
      - /etc/localtime:/etc/localtime:ro

可是在非linux系統,好比Windows中運行時,咱們不能取到/etc/localtime,爲了更大兼容全部平臺,我把時間同步寫到dockerfile中。

接下來安裝一些拓展,其實安裝拓展的過程相似於咱們徒手在linux中安裝PHP拓展,值得一提的是composer。我將Composer直接安裝在了php-fpm的鏡像中,其實官方也提供了Composer的鏡像,拉取composer鏡像執行也能夠達到目的,由於咱們使用composer只是爲了執行composer命令來管理咱們的包,若是composer單獨是一個容器的話,咱們在不用時,還能夠將容器關掉;可是在這裏,我直接將composer裝進php-fpm鏡像中,主要是個人項目安裝了一些PHP拓展,在編寫composer.json文件時,我定義了extension的依賴,這樣composer執行時會檢查環境是否安裝了這些依賴,全部若是我直接用composer鏡像的話,還須要把我用的拓展安裝到鏡像裏,就麻煩多了,因此我直接在php鏡像中就把這個事作了,其實沒什麼區別,取決於你怎麼用。

2.2.2 Nginx

下面是 Nginx 的 dockerfile:

FROM nginx:1.12

# set timezome
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

這個就簡單多了,我只設置了一個時間。由於我不須要安裝其它的東西,能夠直接使用官方的鏡像。

固然,咱們須要修改配置文件,只要事先寫好配置文件就行,最後在 docker-compose.yml 文件中,將配置文件扔進去,這個下面會講,包括PHP的配置文件,MySQL的配置文件,都是同樣的。

2.2.3 MySQL

下面是 MySQL 的 dockerfile:

FROM mysql:5.7

# set timezome
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

MySQL也沒有什麼特別之處,直接使用官方的鏡像。

2.2.4 Redis

下面是 Redis 的,也直接使用官方鏡像:

FROM redis:3.2

# set timezome
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

2.3 編寫配置文件

如何處理配置文件呢,我將配置文件進行歸類,PHP的配置文件放在PHP目錄下,Nginx的配置放在Nginx目錄下,至於要不要再新建一個子文件夾就看狀況了,好比conf.d文件夾。

下面以Nginx配置文件爲例,首先nginx目錄是這樣的:

nginx/
    conf.d/
        default.conf
        zphal.conf
    Dockerfile
    nginx.conf

除了nginx.conf外,還有一個子文件夾conf.d用來存放全部的域名配置文件,在linux下搭建過php環境的應該都比較熟悉。這些配置文件就是咱們到時候要傳進去容器中的文件,咱們並不會在宿主機使用這些文件。

因此須要注意的最重要一點就是,配置文件中出現的路徑是容器內環境的路徑,而不是宿主機的路徑,每個容器內都有一個運行環境,都是一臺微型小系統,這些路徑都是容器內的路徑。咱們能夠經過掛載與容器內通信來同步文件,在命令行啓動容器也須要掛載文件路徑,而如今掛載這一步咱們也用docker-compose來解決。

下面是一個配置文件示例:

server {
    listen   80 default;
    index index.html index.htm;
    server_name localhost docker;

    root /data/www;
    index index.php index.html index.htm;
    location / {
        try_files $uri $uri/ /index.html;
    }

    location ~ \.php {
        include fastcgi_params;
        fastcgi_pass   php-fpm:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  /data/www/$fastcgi_script_name;
    }
}

root /data/www中,/data/www路徑,是到時候nginx容器的路徑,而不是當前在操做的宿主機的路徑,因此到時候咱們要掛載web程序放的位置到這個路徑。

2.4 編寫 docker-compose.yml

在php,nginx等目錄的同級,咱們建立一個 docker-compose.yml,咱們在執行 docker-compose 相關命令時,會自動找到這個文件,並根據裏面的內容來執行。

接上面 nginx 的例子,咱們先談掛載,由於這是最重要的一步。在 docker-compose.yml 中,nginx 的部分:

nginx:
    build: ./nginx
    depends_on:
      - php-fpm
    links:
      - php-fpm:php-fpm
    volumes:
      - ../app:/data/www:rw
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ../logs/nginx:/var/log/nginx
    ports:
      - "80:80"
      - "8080:8080"
      - "443:443"
    restart: always
    command: nginx -g 'daemon off;'

有一個 volumes 參數,這裏就是咱們要掛載的目錄的相關配置,第一條咱們將../app掛載到/data/www之中,也是咱們配置文件中定義的默認監聽的root,而app目錄是咱們宿主機中的一個目錄,經過這樣掛載咱們能夠直接將咱們的項目文件放到app中,docker會幫你傳輸到容器內的/data/www目錄下。

其它的參數:

  • build 定義了你的 dockerfile 在哪裏,若是沒有寫 dockerfile 能夠不用 build,能夠用 images參數定義官方鏡像,好比image:mysql:5.7
  • depends_on表示將依賴其它鏡像,好比nginx依賴php-fpm,沒有它我nginx無法玩;
  • links定義鏈接,好比要鏈接到php-fpm容器,就是php-fpm:php-fpm,後面是別名;
  • ports表示端口映射,80:80表示將80端口映射到宿主機的80端口
  • restart重啓,restart: always表示將自動重啓
  • command是自動執行的命令
  • ......

參數不少,更多的能夠參考官方文檔。

下面是一個完整的 docker-compose.yml 文件:

version: '3.2'
services:
  php-fpm:
    build: ./php/
    ports:
      - "9000:9000"
    links:
      - mysql-db:mysql-db
      - redis-db:redis-db
    volumes:
      - ../app:/data/www:rw
      - ./php/php-dev.ini:/usr/local/etc/php/php.ini:ro
      - ./php/php-fpm.conf:/usr/local/etc/php-fpm.conf:ro
      - ../logs/php-fpm:/var/log/php-fpm:rw
    restart: always
    command: php-fpm

  nginx:
    build: ./nginx
    depends_on:
      - php-fpm
    links:
      - php-fpm:php-fpm
    volumes:
      - ../app:/data/www:rw
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ../logs/nginx:/var/log/nginx
    ports:
      - "80:80"
      - "8080:8080"
      - "443:443"
    restart: always
    command: nginx -g 'daemon off;'

  mysql-db:
      build: ./mysql
      ports:
        - "3306:3306"
      volumes:
        - ../data/mysql:/var/lib/mysql:rw
        - ../logs/mysql:/var/lib/mysql-logs:rw
        - ./mysql/conf.d:/etc/mysql/conf.d:ro
      environment:
        MYSQL_ROOT_PASSWORD: 123456
        MYSQL_DATABASE: zphaldb
        MYSQL_USER: zphal
        MYSQL_PASSWORD: zphal123
      restart: always
      command: "--character-set-server=utf8"

  redis-db:
      build: ./redis
      ports:
        - "6379:6379"
      volumes:
        - ../data/redis:/data
      restart: always

3. 使用

這一套編寫下來,咱們怎麼用呢?

3.1 使用搭建好的環境

  1. 首先,進入項目dockerfiles的目錄下,這裏是files目錄:

    cd zPhal-dockerfiles/files
    
    wget https://pecl.php.net/get/redis-3.1.6.tgz -O php/pkg/redis.tgz
    wget https://codeload.github.com/phalcon/cphalcon/tar.gz/v3.3.1 -O php/pkg/cphalcon.tar.gz

    而後下載咱們會用到的PHP拓展包。

  2. 執行命令:

    docker-compose up

    docker會自動經過編寫好的docker-compose.yml內容構建鏡像,而且啓動容器。
    若是沒問題,下次啓動時能夠以守護模式啓用,全部容器將後臺運行:

    docker-compose up -d
  3. 關閉容器
    能夠這樣關閉容器並刪除服務:

    docker-compose down

使用 docker-compose 基本上就這麼簡單,用 stop,start 等這些命令來操縱容器服務。而更多的工做是在於編寫 dockerfile 和 docker-compose.yml 文件。

3.2 使用 Composer

當咱們要使用composer時怎麼作呢? 咱們已經在php-fpm裏安裝了composer。

用 docker-compose 進行操做:

docker-compose run --rm -w /data/www/zPhal php-fpm composer update

-w /data/www/zPhal爲在php-fpm的工做區域,zPhal項目也是掛載在裏面,全部咱們能夠直接在容器裏運行composer。

或者進入宿主機app目錄下用docker命令:

cd zPhal-dockerfiles/app

docker run -it --rm -v `pwd`:/data/www/ -w /data/www/zPhal files_php-fpm composer update

4. 注意事項

  • 注意掛載路徑
  • 構建失敗時,注意容器內是否報錯
  • 加速鏡像。若是過程下載鏡像很慢,可使用國內的加速鏡像服務,好比阿里雲,Daocloud。

本文首發在個人博客:徒手用 Docker 構建本身的 PHP 開發環境

相關文章
相關標籤/搜索