把command line 的程式打包成Docker Image

平時我們用別人的Docker image,都以網絡服務為主,也就是,他們的image通常以常駐,打開某個網絡端口(UDP/TCP Port)提供服務。

但像nodejs, java等,其實是一些command line程式。它們其實可以一次性地執行後,回傳結果。

這次我們就以一個mdbook程式為例子,介紹打包的流程。

第一件事,在Docker image中,安裝或編譯binary

# Dockerfile
FROM ubuntu:22.04

RUN apt-get update && apt-get install cargo -y && cargo install mdbook

ENV PATH="${PATH}:/root/.cargo/bin"

VOLUME /opt/mdbookdata
WORKDIR /opt/mdbookdata
ENTRYPOINT ["mdbook"]

打包成image,給他一個名字,例如mdbook:beta

sudo docker build -t mdbook:beta ./

在Dockerfile中,我選擇使用 ENTRYPOINTexec 格式,因為我需要後期在 docker run 動態加入參數。

例如,要把參數 --version 傳入container,讓mdbook就可以看到該參數。

sudo docker run --rm -it -v $(pwd):/opt/mdbookdata mdbook:beta --version

但在docker image中,它是使用root來執行。如果你直接執行build,那麼它的輸出資料夾 book/ 的擁有人就會變成了root。你要用指定 --user $(id -u):$(id -g) 來指定執行者。

sudo docker run --user $(id -u):$(id -g) --rm -it -v $(pwd):/opt/mdbookdata mdbook:beta build

還有,如果不想每次都打這麼長的指令,你可以在 ~/.bashrc 中做別名(alias)。

# edit ~/.bashrc
alias mdbook='sudo docker run --user $(id -u):$(id -g) --rm -it -v $(pwd):/opt/mdbookdata mdbook:beta $1'

這樣就可以簡化執行指令。

# let .bashrc take effect without logout login
source ~/.bashrc
# run command through alias
mdbook --version
# it support multiple arguments
mdbook build --dest-dir ./dest