Laravel 是目前 PHP 很流行的框架,今天以看到 Laravel 的預設歡迎頁為目標,建置 Laravel image。
首先一開始,要先把 Laravel 主程式先準備好。參考 Installing Laravel 文件,安裝 PHP 7.3 與 Composer,然後執行下面指令即可把 Laravel 程式安裝至 blog
目錄。
composer create-project --prefer-dist laravel/laravel blog
接著進 blog 目錄啟動 server:
cd blog
php artisan serve
完成後,看到 Laravel 的預設歡迎頁,程式碼就準備完成了。
先定義 Docker 要下什麼指令,會達到跟官方執行 php artisan serve
一樣的結果:
docker run --rm -it -p 8000:8000 laravel
轉換成 docker-compose.yml
如下:
version: "3.8"
services:
laravel:
image: laravel
stdin_open: true
tty: true
其中 -p 8000:8000
是配合預設開 8000 port;image 取名為 laravel
。
撰寫 Dockerfile
的過程中,會不斷重複啟動與移除 container 測試,筆者會用下面 Makefile
來簡化指令,示範和說明也比較清楚一點:
#!/usr/bin/make -f
IMAGE := laravel
VERSION := latest
.PHONY: all build rebuild shell run
# ------------------------------------------------------------------------------
all: build
# 建置 image
build:
docker build -t=$(IMAGE):$(VERSION) .
# 不使用 cache 建置 image
rebuild:
docker build -t=$(IMAGE):$(VERSION) --no-cache .
# 執行並使用 shell 進入 container
shell:
docker run --rm -it -p 8000:8000 $(IMAGE):$(VERSION) bash
# 執行 container
run:
docker run --rm -it -p 8000:8000 $(IMAGE):$(VERSION)
筆者認為寫 Dockerfile
跟 TDD 一樣有三循環如下:
docker build
並驗證是否正確撰寫 Dockerfile
的第一手,是先寫一個可以驗證成功的 Dockerfile
,後續就可以走上面的三循環。
昨天範例有提到 FROM
也是一個步驟。只要 image 在 DockerHub 能下載得到,Dockerfile
就能 build 成功,我們可以寫一個只有 FROM
的 Dockerfile
。Laravel 官網的 Server Requirements 要求 PHP >= 7.3,因此我們使用 php:7.3
image:
FROM php:7.3
接著執行 make build
與 make shell
試看看:
make build
make shell
建置完成,並在 commit 上 tag laravel
,同時進去 shell 確認 PHP 版本正確。
註:目前
laravel
tag 與php:7.3
tag 在同個 commit 上。
預設的路徑是根目錄 /
,是個一不小心就會刪錯檔案的位置,可以換到一個比較安全的目錄,比方說 /source
:
WORKDIR /source
WORKDIR
可以設定預設工作目錄。它同時是 docker build
過程與 docker run
的工作目錄,跟 -w
選項的意義相同。接著把 Laravel 原始碼複製進 container 裡,這裡使用 COPY
指令:
COPY . .
COPY
是把本機的檔案複製到 container 裡,使用方法為 COPY [hostPath] [containerPath]
要注意這裡有個雷,單一檔案複製沒有問題,但目錄複製就得小心。它的行為跟 Linux 常見的 cp
不大一樣。
cp -r somedir /some/path
以 cp
指令來說,上面指令執行完會多一個目錄 /some/path/somedir
。
COPY somedir /container/path
COPY somedir/* /container/path
以 COPY
指令來說,上面兩個指令是等價的。原本預期會多一個 /container/path/somedir
目錄,實際上是 somedir
目錄裡所有東西全複製到 /container/path
下。
解決方法是改成下面這個指令:
COPY somedir /container/path/somedir
Docker Compose 裡有提到一個設定是 command
,它定義了 container 啟動預設會執行的指令。Dockerfile
也有一樣用法的指令--CMD
。而啟動 server 指令一開始 Laravel 建好的時候已經知道了,php artisan serve
。
套用在 CMD
指令上的用法會有兩種如下:
# exec 模式,官方推薦
CMD ["php", "artisan", "serve"]
# shell 模式
CMD php artisan serve
未來會再解釋這兩個模式的差異,先用官方推薦來試試。
實際執行的時候會發現不能正常連到 server?原因很簡單,在說明 Port forwarding 的時候,曾用了下面這張圖:
當時提到每個 container 有屬於自己的 port,因為每個 container 都是獨立的個體,包括 host 也是一個獨立的個體。
再回頭看 Laravel 啟動 server 的資訊,它綁定了 127.0.0.1:8000
在 container 上。
Starting Laravel development server: http://127.0.0.1:8000
不能連線的原因其實非常單純,host 與 container 要視為兩台不一樣的機器,因為 container 僅綁定本機--也就是只有進 container 使用 curl http://127.0.0.1:8000
可以連線,而 host 連 container 會被視為外部連線,因此會連線失敗。
解決方法很單純,把綁定 IP 調整即可,下例是以 0.0.0.0
全部開放為例:
CMD ["php", "artisan", "serve", "--host", "0.0.0.0"]
建置服務成功!測試也完成了,恭喜大家成功用 Dockerfile
建置 Laravel image 成功!
今天 Dockerfile
最後長相如下:
FROM php:7.3
WORKDIR /source
COPY . .
CMD ["php", "artisan", "serve", "--host", "0.0.0.0"]
目前這個內容有很多缺陷,接下來會開始做 Dockerfile
最佳化,會一步步讓讀者知道更多 image 與 container 相關的技巧。
請問"設定路徑與原始碼",是要把"WORKDIR /source"和其他指令寫入原本的Dockerfile檔案裏? 用不用把執行中的make shell 指令停止? 最後是怎樣去執行dockerfile 裏的指令,應該用什麼command去執行?
當執行完成後是否會有以下的文字出現?
PS D:\docker\phpsource> make shell
docker run --rm -it -p 8000:8000 laravel:latest bash
root@4c90c339c0bd:/D:dockerphpsourcesource#
dockerfile內容:
FROM php:7.3
Thank you
make shell 當初設計的用意是,把寫好的 image 拿來用 bash / sh 跑看看。
然後就能在裡面執行 php -m 或其他相關除錯的指令。
它並不是用來執行 Dockerfile 的 CMD 用的。
如果我輸入了curl http://127.0.0.1:8000 ,都不能夠連線。請問是否我做錯了某一步驟?
原來我是沒有將Dockerfile, makefile, docker-compose.yml檔案放在laravel blog資料夾裏執行。所以,就出現錯誤和不能夠連線問題。
下面應該是成功執行後畫面
針對 Makefile 的部分
我也寫了一篇分享文
請 Miles 大大指教
https://blog.goodjack.tw/2023/01/use-makefile-to-manage-workflows-for-web-projects.html