為各種框架 build image

今天將會應用之前 build image 的技巧,來為以下框架的 hello world 寫 Dockerfile。

  1. Phoenix
  2. Amber
  3. Rocket
  4. Lapis

這幾個框架在台灣應該都很冷門,主要是想展示:只要有 image 就有辦法初始化程式;只要有程式和 Dockerfile,讀者就可以建得出一樣的環境與 server,這正是 Docker 實現 IaC 特性的方便之處。

以下示範四種框架的方法都大同小異,初始化程式是使用了應用 Container 裡提到的借用指令技巧來做;寫 Dockerfile 則是與 Laravel 建 image 做法一樣,一步一步寫出來。以下直接把結果放上來,但會說明哪個地方撞牆很久。

Phoenix

Phoenix 使用 Elixir 語言撰寫,參考官網的 hello world 建置與執行指令如下:

mix archive.install hex phx_new 1.5.5
mix phx.new --no-ecto --no-webpack --app ironman phoenix

# 啟動 server
mix phx.server

mix 是 Elixir 內建的套件管理工具。

從這裡可以看得出,它框架初始化的做法與 Laravel 或 Node 類似:透過套件管理工具下載框架提供的工具,再用這個工具來初始化程式。因此可以直接使用 Elixir image,進入 container 直接執行上面兩個指令,即可產生 Phoenix 的初始化程式碼在 host 裡。

docker run --rm -it -v $PWD:/source -w /source elixir:1.10-alpine sh

Dockerfile

FROM elixir:1.10-alpine

WORKDIR /usr/app/src

# 準備套件工具的設定
RUN set -xe && \
mix local.hex --force && \
mix local.rebar --force

# 安裝套件
COPY mix.* ./
RUN mix deps.get

# 原始碼與編譯
COPY . .
RUN mix compile

# 啟動 server 的指令
CMD ["mix", "phx.server"]

最後是 run image:

docker build -t=phoenix .
docker run --rm -it -p 4000:4000 phoenix

其他框架因為只是名稱和 port 不同,所以就不重覆此段範例了。

小結

Phoenix 是編譯語言,所以會比 Laravel 多了編譯的過程。另外啟用 hexrebar 套件在建 image 過程就有明顯的提示訊息,所以都很容易解決。

Amber

Amber 使用 Crystal 撰寫。先找到如何產生初始化程式的指令:

amber new --minimal amber

這裡會發現它跟 Phoenix 不一樣,有自己專屬的指令 amber 在處理初始化。而這個指令需要編譯,且需要 Crystal 的環境,有點麻煩。幸好 Amber 官方已經建好 Amber image 可以直接使用了。

docker run --rm -it -v $PWD:/source -w /source amberframework/amber:0.35.0 bash

Dockerfile

產生出來的程式裡面就有 Dockerfile 了,真的很貼心:

FROM amberframework/amber:0.35.0

WORKDIR /app

# shards 是 Crystal 的套件管理工具
COPY shard.* /app/
RUN shards install

COPY . /app

RUN rm -rf /app/node_modules

# Amber 起本機測試服務,並開啟動態編譯功能
CMD amber watch

小結

Amber 因為有提供 Dockerfile,所以就相較單純了點,只有特別去了解 shards 指令是套件管理工具(之前的版本是 crystal deps),以及了解 amber 指令有什麼功能。

Rocket

Rocket 使用 Rust 撰寫。Phoenix、Amber 與 Lapis 都有提供框架專屬的整合指令,但 Rocket 沒有,但 Rust 內建的套件管理工具 Cargo 有基本專案初始化指令,可以從這裡開始建立專案:

# 使用 cargo 建立專案
cargo new rocket --bin

再來 Cargo.tomlmain.rs 檔,照著官方教學輸入內容,最後執行即可啟動開發用的 server:

cargo run

主要都是用 Cargo,所以可以直接拿 Rust image 來執行指令:

docker run --rm -it -v $PWD:/source -w /source rust:1.46 bash

Dockerfile

FROM rust:1.46

WORKDIR /usr/src/app

ENV USER=dummy

# Rocket 官方教學有提到要開 nightly
# 會在這裡執行 cargo init,這是 cargo build 一個 issue
RUN rustup default nightly && cargo init --bin --name dummy .

COPY Cargo.* ./

# 下載並編譯依賴
RUN cargo build

COPY . .

# 編譯主程式
RUN cargo build

CMD ["cargo", "run"]

小結

這個框架遇到了兩個麻煩事,第一個是昨天提的 Alpine 問題,總算遇到了,在 cargo build 編譯的時候出錯,改成 Debian 就正常了。

另一個則是,原本想跟其他框架一樣,把依賴 COPY Cargo.* ./ 跟複製檔案 COPY . . 切成兩個階段,但 cargo build 會需要存在一個 src/main.rs 檔,才能正常執行。一個很笨,但很有效的方法:cargo init 一個 dummy 專案即可。

Lapis

Lapis 使用 Lua 撰寫。Docker 官方並沒有出 Lua image,因此有手癢寫了 Lua image

Lapis 參考官方文件,建置與執行的指令如下:

lapis new

# 使用 MoonScript 需要編譯
moonc *.moon

lapis server

Lapis 官方也沒出 image,有寫了一個 Lapis image,可以直接拿來用:

Lapis 的 Dockerfile 比較複雜,有興趣可以看 Dockerfile

docker run --rm -it -v $PWD:/source -w /source mileschou/lapis:alpine sh

Dockerfile

FROM mileschou/lapis:alpine

WORKDIR /usr/src/app

COPY . .

RUN moonc *.moon

CMD ["lapis", "server"]

小結

Lapis 是最麻煩的環境,以前已經搞定了,所以這邊的 Dockerfile 就能寫得很簡單。

今日自我回顧

今天的程式碼有放上 GitHub,有興趣可以去下載回來 build 看看。

雖然開頭有提到,今天要示範 Docker 的 IaC 特性,但額外想提的觀念是:身為開發人員,想要使用既有的 Docker image 或是撰寫 Dockerfile,不只是要學好 Docker 而已,對程式工具、執行流程以及相關錯誤訊息都要有清楚的認知,才能真正有效發揮出 Docker 的優點。