Skip to content

建構精簡版的 Deno 容器

Published: 5 分鐘

很多年前「建構一個與 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 允許多個容器共享網路以及本地檔案系統空間,在一個容器執行多個行程的需求應該會越來越少才對。

正面看待,容器內除了應用元件外,不包含任何其它執行檔可以進一步減少被攻擊面積,至於值不值得做到這個程度,就是見仁見智的問題了。

郭信義 (Sam Kuo)

奔騰網路科技技術長,專長分散式系統、Web 應用與雲端服務架構、設計、開發、部署與維運。工作之餘,喜歡關注自由軟體的發展與應用,偶爾寫一下部落格文章。