寫 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
三循環的流程,來發現缺少的工具,以及安裝需要的工具