1. 程式人生 > 程式設計 >[Docker實踐系列-02]利用DockerCompose編排完整的PHP開發環境

[Docker實踐系列-02]利用DockerCompose編排完整的PHP開發環境

Docker 重要性不用多做解釋,我們天天吵著雲原生,最重要的一門技術,你是否掌握了呢?從今天,一起來Docker吧!

1. 目標

1、利用 docker-compose 編排一個PHP開發環境;
2、PHP 常見擴充套件安裝;
3、Nginx php-fpm Mysql Redis等容器互聯;
4、PHP 程式能夠操作 Mysql Redis
2、蒐集相關日誌便於排查;


2. 工具

1、作業系統:Ubuntu
2、編輯器:VSCode
3、Docker
4、Docker Compose 官方檔案
5、Compose 中文手冊
6、如果Docker映象拉取慢,可以嘗試這個 www.daocloud.io/mirror


3. 步驟

3.1 檢查軟體

確保 Docker 和 Docker Compose 已安裝好
比如

docker -v Docker version 19.03.1,build 74b1e89

docker-compose -v docker-compose version 1.17.1,build 6d101fb

3.2 建立 docker-compose.yml 檔案

在你的資料夾中新增這個檔案,這是我們編排的模板檔案,格式如下

version: '3'
services:
  php-workspace:
    image: php:7.3-fpm-alpine
  nginx:
    image: nginx:alpine
複製程式碼

注意點:
1、模板中定義的 version 欄位宣告瞭模板的格式和支援宣告欄位,3 是目前比較推薦也用的比較多的的版本
2、注意 key 的層級和空格,我們可以使用命令 docker-compose config 命令檢查格式是否正確
3、使用 VSCode方便的一點是可以看到可以程式碼可以根據層級摺疊

image.png

4、alpine是一種小型輕量的Linux,我們使用基於 alpine 的映象是因為做出的映象體積比較小,關於後期容器構建的優化,這個是另外一個話題,這裡不展開闡述。

3.3 “讓子彈飛起來”

docker-compose up --build 構建映象並啟動容器

這時能看到有兩個已經在執行的容器和映象

image.png

3.4 讓 Nginx 工作起來

剛剛啟動的容器可以使用 docker-compose down 來關閉專案,還會自動刪除容器。
現在,我們要指定埠讓 Nginx 能處理請求,可以參考 Compose 中文手冊的模板檔案的語法。

image.png

3.4.1 增加 ports 引數

指定宿主機埠和容器埠的對映關係,如下;

version: '3'
services:
  php-workspace:
    image: php:7.3-fpm-alpine
  nginx:
    image: nginx:alpine
    ports:
      - "8080:8080"
複製程式碼

3.4.2 執行 docker-compose up

去瀏覽器請求 localhost:8080

image.png

3.5 PHP檔案交給 PHP-FPM 處理

image.png


其實選中容器右鍵能管理容器,Attach Shell 就能快速進入容器,我們進入容器就能看到內部情況,比如,nginx 的www目錄和配置目錄,php-fpm 的預設配置等等。

3.5.1 增加 nginx 的站點配置

新增檔案 nginx/conf.d/site.conf檔案,內容如下,下面的思路也可以參考上篇《利用Docker搭建PHP開發環境》做法。

image.png

server {
    listen 8080;
    root /usr/share/nginx/html;
    index index.php index.html;
    server_name localhost;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    location ~ \.php$ {
        root /var/www/html; # 這裡指向的php容器的專案根目錄
        fastcgi_pass php-workspace:9000; #php-workspace是模板檔案中的php-fpm服務名
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}
複製程式碼

3.5.2 修改 docker-compose.yml 檔案

讓 nginx 和 php-fpm 服務基於Dockerfile 構建

version: '3'
services:
  php-workspace:
    build: ./php # 這裡php指我們的建立的目錄
  nginx:
    build: ./nginx # 這裡nginx指我們的建立的目錄
    ports:
      - "8080:8080"
複製程式碼

3.5.3 新增 nginx/Dockerfile 檔案

FROM nginx:alpine

COPY conf.d /etc/nginx/conf.d
複製程式碼

3.5.4 新增 php/Dockerfile 檔案

FROM php:7.3-fpm-alpine

複製程式碼

3.5.5 增加code資料夾作為php專案,新增index.php檔案

<?php

phpinfo();

複製程式碼

3.5.6 重新修改 docker-compose.yml 檔案

讓專案資料夾以掛載的形式,讓php-fpm容器可以直接讀取宿主機檔案目錄,這樣我們可以很方便的修改程式碼除錯

version: '3'
services:
  php-workspace:
    build: ./php
    volumes: # 這裡指定資料卷,可以指定多個, 中劃線代表值為陣列的一個成員
      - ./code:/var/www/html
  nginx:
    build: ./nginx #這裡nginx指我們的建立的目錄
    ports:
      - "8080:8080"
複製程式碼

3.5.7 走一下

重啟之前,再確定下程式碼檔案大概位置

image.png

docker-compose up --build 重新啟動專案,我們看到在前臺控制器的一些日誌

image.png


兩個容器都構建好了,php也啟動了。如果我們現在請求 localhost:8080,還能看到nginx的請求日誌
image.png

3.6 連線Mysql

用法我們依然參考了映象的介紹 >> hub.docker.com/_/mysql

3.6.1 新增 db 的服務

version: '3'
services:
  php-workspace:
    build: ./php
    volumes:
      - ./code:/var/www/html
  nginx:
    build: ./nginx
    ports:
      - "8080:8080"
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment: # 設定環境變數,只給定名稱的變數會自動獲取執行 Compose 主機上對應變數的值
      MYSQL_ROOT_PASSWORD: root
複製程式碼

3.6.2 重新構建

docker-compose up --build

image.png


VSCode 容器管理中右鍵連線shell中進入容器執行 mysql -u root -p,確認密碼 root,確定我們的資料庫可用
image.png

3.6.3 安裝 pdo mysql

在操作之前我們確定下pdo是不支援mysql驅動的

image.png

修改 php 的 Dockerfile 檔案如下
RUN 的第一條命令是修改了 alpine 系統的軟體源,方便後續安裝會更快點

FROM php:7.3-fpm-alpine

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories \ 
    && docker-php-ext-install pdo_mysql
複製程式碼


重新構建專案 docker-compose up --build

image.png

3.6.4 修改 index.php 檔案,程式碼如下

<?php

$dsn = 'mysql:host=db;port=3306'; // host=db,這裡的db其實是mysql的服務名
$user = 'root';
$password = 'root';

try {
    $dbh = new PDO($dsn,$user,$password);
    print_r($dbh);
} catch (PDOException $e) {
    echo 'Connection failed: '.$e->getMessage();
}

複製程式碼

3.6.5 測試 Mysql

image.png

3.7 連線 Redis

3.7.1 容器安裝PHP擴充套件的兩種方式

在Docker Hub 中 php映象檔案中說的很清楚

  • 第一種是PHP核心的擴充套件可以通過 docker-php-ext-install ext_name 安裝
  • 第二種是PECL
  • 第三種通過原始碼包編譯

Redis的擴充套件,我們用的是第二種。

3.7.2 修改 docker-compose.yml 檔案,增加redis服務

version: '3'
services:
  php-workspace:
    build: ./php
    volumes:
      - ./code:/var/www/html
  nginx:
    build: ./nginx
    ports:
      - "8080:8080"
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
  redis:
    image: redis:5.0.5-alpine
複製程式碼

3.7.3 重新構建

docker-compose up --build
構建後,我們同樣可以進入容器,使用 redis-cli 命令確定redis是否可用

image.png

3.7.4 修改 php Dockerfile

因為pecl在alpine容器中安裝擴充套件會少一些東西,所以我們先安裝了一個phpize_deps,用後刪除即可,保持映象瘦小

FROM php:7.3-fpm-alpine

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories \ 
    && docker-php-ext-install pdo_mysql \
    && apk add ${PHPIZE_DEPS} \
    && pecl install redis-4.0.1 \
    && docker-php-ext-enable redis \
    && apk del ${PHPIZE_DEPS}

複製程式碼

重新構建,我們修改index.php 程式碼 phpinfo();能看到 redis擴充套件已開啟

image.png

3.7.5 連線 Redis

修改 index.php 檔案內容如下測試

<?php

$redis = new Redis();
$redis->pconnect('redis',6379);

$key = 'first';
$redis->incr($key);

echo '頁面瀏覽了:'.$redis->get($key); die;
複製程式碼


image.png

3.8 日誌蒐集

我們回顧一下 nginx/conf.d/site.conf 的配置

server {
    listen 8080;
    root /usr/share/nginx/html;
    index index.php index.html;
    server_name localhost;
    # 我們已經把nginx相關日誌寫到這裡了,如何放到宿主機呢?
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    # ... more something
}
複製程式碼


docker日誌採集固然有很多方案,但是暫時不考慮叢集管理工具的日誌蒐集方案,這裡以資料掛載的形式,把 nginx 請求日誌落地到宿主機。

3.8.1 修改 docker-compose.yml 檔案如下

version: '3'
services:
  php-workspace:
    build: ./php
    volumes:
      - ./code:/var/www/html
  nginx:
    build: ./nginx
    volumes: # 新增這兩行
      - ./log/nginx:/var/log/nginx/
    ports:
      - "8080:8080"
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
  redis:
    image: redis:5.0.5-alpine
複製程式碼

3.8.2 重啟專案

docker-compose up 執行後,就可以看到我們的資料夾多了兩個目錄,還有兩個檔案

image.png

3.9 優化

depends_on

可以指定依賴項,解決容器啟動先後問題,比如一個應用如果依賴資料庫,要讓資料庫容器先啟動可以設定

depends_on:
      - db
      - redis
複製程式碼

環境變數

當然其實還有其他很多有用的引數設定,比如感敏資料讀取,環境變數,配置host等等,後續都可以在開發中嘗試使用。


4. 總結

以上思路可以解決本地開發中的很多問題了,如果專案中有使用 memcached 或者其他應用,做法都可以參考Docker Hub中的映象檔案說明中引入。
除此之外,在Laravel 開發中之前早就有一個開源專案叫做 Laradock 來搭建Docker化的PHP應用,不僅如此,還有很多中介軟體都可以利用 docker-compose 啟動,可以參看 >> laradock.io/ ,但是裡邊有很多很多東西,因為要滿足大部分人的擴充套件性,加了很多環境變數,if else 判斷,讓原本的 Dockerfile 更為複雜,增加了很多邏輯。但是思路基本上是相同的。
另外還有幾點要點出:
1、docker-compose.yml 定義的一個專案會以所在的資料夾名為專案名。
2、一個專案有一個預設網路,專案中的服務彼此可以互通,所以我們剛剛在 site.conf 轉發php請求可以直接用 php-workspace:9000 服務名來轉發,連線 資料庫也是可以用 db  redis 等他們的服務名來連線。

image.png


5. 完整程式碼

github.com/baiyutang/d…


6. 遺留問題

1、指定資料庫掛載的資料卷,比如Mysql,我們可以把其他環境的資料庫直接以檔案的形式拷貝,然後直接利用。這樣比我們手動匯入sql會更快遷移資料。

2、Mysql 映象如何瘦身,現在Mysql映象檔案最大,如圖有445M

image.png


7. 下篇預告

其實PHP的Docker環境已經有一個開源專案 Laradock,使用方便 易於擴充套件,但是最大的問題是 感覺臃腫 有時候安裝特別慢,特別是要考慮太多擴充套件外掛等等製作出來映象特別大。所以下一篇的思路可能會圍繞如何優化今天做的這個 demo-docker-compose,或者做 Laradock 本地化工作?
其他兩個思路:
1、直接進入到 k8s 課題
2、Docker化後如何做CI/CD


8. 擴充套件