寫 Dockerfile 並不困難,但好用的 Dockerfile 就需要利用許多技巧,加上不斷嘗試,才有辦法寫出來。
接下來會以昨天的 Dockerfile 為例,說明要如何寫出好的 Dockerfile。
FROM php:7.3
WORKDIR /source
COPY . .
CMD ["php", "artisan", "serve", "--host", "0.0.0.0"]
一開始執行 docker build 指令時,Docker 會將建置目錄裡的檔案複製一份到 Docker daemon 裡,接著才會開始執行 build image。
Sending build context to Docker daemon 42.95MB
Build context 指的是複製到 Docker daemon 的檔案。
昨天完成的 Dockerfile 存在一個很明確的問題:建置過程從 build context 傳入了太多非必要的檔案(指 COPY . . 無腦複製法),這會有下面的問題:
今天針對 build context 來看看該如何調整 Dockerfile。
在 build image 的過程,如果遇到了 COPY 指令時,會從 build context 複製進 container。(其他需要用到 host 檔案的指令也是相同概念)
build context 的某些檔案可能會跟 build image 的流程或結果毫無關係,如 .git 目錄或 .vagrant 目錄。若不做任何處理,一來啟動 docker build 會花時間在複製檔案到 Docker daemon;二來在使用懶人複製法 COPY . . 也會把這些用不到的檔案複製進 container 佔用空間,可說是百害無一利。
Docker 定義了 .dockerignore 檔案,可以直接在裡面定義不進 build context 的檔案,範例如下:
.git
.vagrant
.git 或 .vagrant 跟 build 出 Laravel image 的過程或結果無關,所以全部排除是沒有問題的。有些檔案則是結果需要,但過程會不希望從 host 複製進去的,比方說 vendor 目錄。
舉個例子,當 host 是 PHP 7.3,image 是 PHP 7.2 的時候,就很有可能會出問題。如 host 的 PHP 7.3 可能會安裝 PHPUnit 9,但在 image PHP 7.2 是不能使用的。
這時也可以利用 .dockerignore 來排除 vendor 目錄,達到加速 build image 的目的:
.git
.vagrant
vendor
同樣的概念可以應用在 node_modules 或類似的目錄上。
因 vendor 裡有程式運作必要的檔案,而剛剛只提到了要排除 vendor,並沒有提到如何把 vendor 生出來,因此才會出現找不到檔案的錯誤。
vendor 在 host 可能會因為開發者環境不同,而安裝到不一樣的套件。但依昨天 Dockerfile 第一手的測試結果可以知道,container 的環境是 PHP 7.3,因此我們在 container 裡執行,就能確保安裝的環境是 PHP 7.3。
Dockerfile 執行指令使用 RUN 指令,而 Composer 安裝套件指令為 composer install,再改一次 Dockerfile 檔如下:
FROM php:7.3
WORKDIR /source
COPY . .
RUN composer install
CMD ["php", "artisan", "serve", "--host", "0.0.0.0"]
這裡發現還是有問題,關鍵訊息為 /bin/sh: 1: composer: not found,意思是 composer 這個指令在 container 裡面找不到。
即然找不到指令,先把指令安裝好再執行 composer install 總沒問題了吧?
FROM php:7.3
WORKDIR /source
COPY . .
# 安裝 Composer 指令
RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
RUN composer install
CMD ["php", "artisan", "serve", "--host", "0.0.0.0"]
composer install 看起來可以執行,但執行過程有問題,關鍵訊息有兩個:
從訊息上看起來,第一個訊息是因為 zip ext 和 unzip 指令找不到,所以 Composer 採用了替代方案,使用 git 指令,但也沒有安裝,所以出現第二個訊息。
接下來有三個選擇:
因為第一個選擇會需要提到更多 PHP 的細節,因此筆者採取第二個選擇。安裝指令視平台,會用到 apt、yum、apk 等指令,PHP 7.3 是 Debian 系統,所以會用 apt 指令。
範例是進 shell 先嘗試安裝指令。一開始無法安裝,但只要 update 套件資訊後就可以安裝了。最終的 Dockerfile 如下:
FROM php:7.3
WORKDIR /source
COPY . .
RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
# 安裝 unzip 指令
RUN apt update && apt install unzip
RUN composer install
CMD ["php", "artisan", "serve", "--host", "0.0.0.0"]
此 Dockerfile 筆者嘗試可以建置完成,並使用 make run 開瀏覽器可以看到網頁。
今天有多加了幾個 Dockerfile 指令,目的是為了減少 build context,這讓 build image 的效率加快非常多。
另外更重要的,為了讓 composer install 指令能在 container 裡正常執行,筆者花了非常多功夫在說明如何發現、以及安裝工具。當要 build 客製化的 image 時,常常會遇到這類問題,建議讀者可以多了解並嘗試這些過程,這對未來解決問題會很有幫助。
.dockerignore 將不必要的 build context 排除Dockerfile 三循環的流程,來發現缺少的工具,以及安裝需要的工具