相信有那麼一天,我們將可以像畢凱艦長一樣用嘴巴叫所有主機做事!


Cheng Wei Chen



利用 Docker 建構 Nginx + php-fpm 5.2 + mysql

Docker 的熱潮不用說明,大家都已經耳熟能詳,最近也有越來越多的 Production 案例出現,前一陣子更已推出 1.6 版,我個人的評估認為現在是一個滿好的時間點,可以由資訊收集、觀望,轉變進入測試及實用。

於是決定就用 php 常見的一個問題來當作我個人第一個 Docker 實作的題目, 那就是「如何建立一個舊版本的 php 工作環境」



評估環境需求

首先評估舊專案環境需求,發現舊專案只能運行在 php 5.2 的環境,在伺服器上要安裝舊版本 php 其實已經有好幾種解法,但既然要改用 Docker 來實現,於是思考的方向便偏向希望能將 php 5.2 獨立運行在一個 container 之中, 於是環境需求便規劃為 Nginx + php-fpm 5.2 + mysql。


安裝 Docker

如何安裝 Docker 這基本動作已經有太多教學文件可以參考,其實直接看官網文件就已足夠。


取得 image

正式實作的第一步驟當然是先搜尋有沒有前人已經建立好的 Docker repository 可以直接運用。

基本上只要在 Docker hub 搜尋 php、phpfpm、php 5.2...或其他相關的關鍵字,會跑出來的 repository 多到不可數,稍微評比之後,選用 helder/php-5.2 作為我的 php image。

會選擇它有幾個原因:

  1. 作者有提供原始的 Dockerfile,假如不滿意,還可以自己微調再重新 build。
  2. 這是 php-fpm,可以單獨運行為一個 service。
  3. 被下載次數有破百,代表還算有些人氣,通常有人氣的東西比較不會有問題。(非絕對,使用前還是要自行判斷。)

有了 php,接著還需要 mysql 及 Nginx,一樣的做法直接上 Docker hub 搜尋,這次就直接選用官方建立的 mysql 及 Nginx repository。


Docker Run !!

準備來實際運行,先將專案原始碼放在 /works/www 之下,接著依序啟動 container。

docker run --restart=always --name mysql -v /etc/localtime:/etc/localtime:ro -p 3306:3306 -v /works/db:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=rootroot -e MYSQL_DATABASE=test_project -e MYSQL_USER=project -e MYSQL_PASSWORD=12345678 -d mysql/mysql-server:5.5

因為後面的 php-fpm 要與它 link,因此第一個要啟動的 container 是 mysql,幾個參數簡單說明一下:

  • --restart=always 設定 container 會自動啟動。
  • --name mysql 給 container 一個固定的名稱。
  • -v /etc/localtime:/etc/localtime:ro 有前人教導的進階招式,讓 container 與 host 的時間一致。
  • -p 3306:3306 將 host 的 port 3306 對應給 container 的 3306。
  • -v /works/db:/var/lib/mysql 根據 repository 上的使用說明,如此讓 container 的 DB 資料,直接保存於 host 上。
  • -e MYSQL_ROOT_PASSWORD=rootroot -e MYSQL_DATABASE=test_project -e MYSQL_USER=project -e MYSQL_PASSWORD=12345678
    -e 都是設定 ENV 變數,分別是設定 root 密碼、建立資料庫、建立新使用者、設定使用者密碼。

再來啟動 php-fpm 5.2。

docker run --restart=always --name php-fpm_5.2 -v /etc/localtime:/etc/localtime:ro -v /works/www:/works/www --link mysql:db -d helder/php-5.2

續上,這裡比較不同的參數是 --link mysql:db 如此一來 mysql 及 php-fpm 兩個 container 之間會建立連結,若是透過 host 的 iptables 去觀察,就會發現類似下圖的設定。


最後啟動 Nginx。

docker run --restart=always --name nginx -v /etc/localtime:/etc/localtime:ro -p 80:80 -e NGINX_SITE_ROOT=/works/www -v /works/www:/works/www -v /works/nginx/log:/var/log/nginx/ --link php-fpm_5.2:phpfpm -d nginx

續上,參數差不多,但這次要 --link php-fpm_5.2:phpfpm ,讓 Nginx 可以與 php-fpm 連接。


Error ?

按著上面的步驟實作,乍看之下沒有太大的問題,但是實際上舊專案卻無法正常運作, 問題發生在 php 程式運行時會無法連上 mysql。

查看 php 程式碼,發現舊專案的 db 連線路徑設定為 localhost,但實際上 mysql 是運行於另一個 container,有著不同的 ip 位置, 雖然可以手動透過 docker inspect mysql 查看正確的 ip 位置,但當每次 container restart 時,ip 位置就會變動一次,總不可能每次都手動查詢,再手動變更 php 原始碼。

事實上,當 container 透過 --link 彼此連接之後,會自動將連接的資訊放入 ENV 之中,因此若登入 container 之中透過 env 指令,即可看到類似下圖的內容,確實 mysql container 的 ip 位置早已寫入在 ENV。


但很可惜的是在此 php-fpm container 中,卻無法用 $_ENV 抓到上圖的 mysql ip 位置,因此不得已只得手動做一些處理,

基本上處理的方式很簡單,就是建立一個新的 shell script,當每次 container 啟動時,就會去抓取 ENV,並將 mysql container 的 ip 寫入 php-fpm.conf,接著才啟動 php-fpm。如此一來即可在 php 程式碼中,透過 $_ENV 找到 mysql container 的 ip 位置,並設定為 db 連線路徑。

我微調後的 php-fpm 5.2 也有放上 Docker hub,可以直接取得。

除了 mysql 連線問題之外,Nginx 與 php-fpm 之間也存在同樣的連線問題,在另外就是針對不同的專案,需要設定不同的 nginx site config,所以同樣的又需要手動對 Nginx 做一些處理,讓 container 每次啟動時,都會將 php-fpm container 的 ip 設定於 config。

除此之外,我還加了一個 ENV 參數,讓我可以設定 Nginx site config 中的 root /path/to/folder;

同樣微調後的 nginx 也已放上 Docker hub,可以直接取得。

於是最後的指令如下:

  1. docker run --restart=always --name mysql -v /etc/localtime:/etc/localtime:ro -p 3306:3306 -v /works/db:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=rootroot -e MYSQL_DATABASE=test_project -e MYSQL_USER=project -e MYSQL_PASSWORD=12345678 -d mysql/mysql-server:5.5
  2. docker run --restart=always --name php-fpm_5.2 -v /etc/localtime:/etc/localtime:ro -v /works/www:/works/www --link mysql:db -d chengweisdocker/docker-phpfpm-5.2
  3. docker run --restart=always --name nginx -v /etc/localtime:/etc/localtime:ro -p 80:80 -e NGINX_SITE_ROOT=/works/www -v /works/www:/works/www -v /works/nginx/log:/var/log/nginx/ --link php-fpm_5.2:phpfpm -d chengweisdocker/docker-nginx:phpfpm

其實在 Nginx 的 container 還有許多可以調整的地方,像是更高度客製化的 site config 或者透過 shell script 來建立更自動化更方便使用的版本,其實在 Docker hub 就已經有很多值得參考的範例。

越是深入認識 Docker 就越能理解它的方便性及魅力所在,還有好多可以深入研究的用法,總覺得可以研究的東西太多了,可以先給我時光屋嗎?




沒有留言:

張貼留言

不歡迎留言打廣告,所以有進行留言管理,敬請見諒。