iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 12
1
DevOps

30 天與鯨魚先生做好朋友系列 第 12

以 Laravel 為例,來 build image 吧!

Laravel 是目前 PHP 很流行的框架,今天以看到 Laravel 的預設歡迎頁為目標,建置 Laravel image。

初始化 Laravel

首先一開始,要先把 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 的第一手

筆者認為寫 DockerfileTDD 一樣有三循環如下:

  1. 新增 Dockerfile 指令
  2. 執行 docker build 並驗證是否正確
  3. 最佳化 Dockerfile

撰寫 Dockerfile 的第一手,是先寫一個可以驗證成功的 Dockerfile,後續就可以走上面的三循環。

昨天範例有提到 FROM 也是一個步驟。只要 image 在 DockerHub 能下載得到,Dockerfile 就能 build 成功,我們可以寫一個只有 FROMDockerfile。Laravel 官網的 Server Requirements 要求 PHP >= 7.3,因此我們使用 php:7.3 image:

FROM php:7.3

接著執行 make buildmake 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

設定啟動 server 指令

Docker Compose 裡有提到一個設定是 command,它定義了 container 啟動預設會執行的指令。Dockerfile 也有一樣用法的指令--CMD。而啟動 server 指令一開始 Laravel 建好的時候已經知道了,php artisan serve

套用在 CMD 指令上的用法會有兩種如下:

# exec 模式,官方推薦
CMD ["php", "artisan", "serve"]
# shell 模式
CMD php artisan serve

未來會再解釋這兩個模式的差異,先用官方推薦來試試:

咦?奇怪不能用?原因很簡單,但筆者特別提一下,在說明 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 相關的技巧。

  • 了解 build image 的流程
  • 演練實際程式 build image

上一篇
了解 Docker build 指令
下一篇
最佳化 Dockerfile - 調整 build context
系列文
30 天與鯨魚先生做好朋友30

尚未有邦友留言

立即登入留言