很多年前「建構一個與 Ubuntu 相容的小型 Docker 映像」這篇文章中,山姆鍋提出為什麼偏好小型的容器映像檔,如今這個偏好依然存在。最近一時興起使用 Docker multi-stage 重新弄了一個 Basebox
容器,並以 Deno 作為示範。
新版 Basebox 特色
新版 Basebox ⎘ 同樣基於 Ubuntu 系統,但只包含基本作業系統目錄結構、重要的動態連結程式庫,以及一個 nologin
執行檔; 去掉了 busybox
以及 bash
的支援。如果直接執行 sampot/basebox
會得到下列結果:
$ docker run -it --rm sampot/basebox
nologin: Attempted login by UNKNOWN on /dev/pts/0
This account is currently not available.
failed to resize tty, using default size
Basebox
設計作為其它容器的基礎,本身沒提供什麼直接用途。Basebox
適合用在容器只有封裝一個執行檔,但執行檔還需要系統存在一些必要的動態連結程式庫(.so)的情境。
使用 Basebox 作為 Deno 容器的基礎
Deno ⎘ 是一個安全的 JavaScript 與 TypeScript 的執行環境,為 Node.js 之父 Ryan Dahl 所帶領開發。 雖然 Deno 的執行只需要一個執行檔,但還需要系統存在一些必要的動態連結程式庫(.so),剛好適合拿來作為 Basebox
的範例。
找出 Deno 需要的動態連結檔
使用 ubuntu:18.04
容器,下載安裝 deno
後,使用 ldd
指令找出 deno
相依的動態程式庫有哪些。底下是一個執行範例:
$ ldd /tmp/deno
linux-vdso.so.1 (0x00007ffdac595000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f363a275000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f363a06d000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f3639e4e000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f3639c36000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3639845000)
/lib64/ld-linux-x86-64.so.2 (0x00007f363ca1f000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f36394a7000)
如果程式需要不存在 Basebox
容器中的動態連結程式庫(共用物件檔),則需要從 ubuntu:18.04
容器額外複製到目標容器映像中。Basebox
剛好已經包含 Deno 所需的檔案,所以不需要額外複製動作。底下範例假設還需要 libgcc_s.so.1
檔,Dockerfile 中使用下列指令複製:
COPY --from=builder /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib
共用的動態程式庫都放置在 /lib 目錄中。
驗證 Deno 可以正常執行
底下是使用 sampot/deno
容器執行 welcome.ts
範例的結果:
$ docker run -it --rm sampot/deno run https://deno.land/std/examples/welcome.ts
Download https://deno.land/std/examples/welcome.ts
Warning Implicitly using master branch https://deno.land/std/examples/welcome.ts
Compile https://deno.land/std/examples/welcome.ts
Welcome to Deno 🦕
sampot/deno
容器的相關程式碼可從 GitHub ⎘ 取得,使用上的問題可以發 issue。
小結
這篇文章的出發點純粹只是因為技術上覺得有趣,除非對於容器映像檔大小以及容器安全性非常執著,不然應該不會有人自找麻煩吧!在 Kubernetes 還沒成為容器調度系統的事實標準前,在 Docker 容器內同時執行多個行程(process)有其合理用途,只要確保一個容器只負責一件事情,主行程之外都只提供輔助性的功能(e.g. log 收集)。由於 Kubernetes Pod 允許多個容器共享網路以及本地檔案系統空間,在一個容器執行多個行程的需求應該會越來越少才對。
正面看待,容器內除了應用元件外,不包含任何其它執行檔可以進一步減少被攻擊面積,至於值不值得做到這個程度,就是見仁見智的問題了。