學無止盡

Docker 學習的道路真的是學無止盡,以下列出三十天裡沒有提到的東西,有興趣的讀者可以用關鍵字查詢。

最後,來個小測驗考考大家是不是對 Docker 更了解:

本測試有參考 Best practices for writing Dockerfiles

  1. Docker image 與 container 之間的關係,下列哪種描述「錯誤」?
    • (A) Container 必須基於 image 產生;但相對地,image 無法藉由 container 產生。
    • (B) 同個 image ID 內容是不可變的(immutable),而 container 內容是可變的(mutable)。
    • (C) 若使用某個 image 運行一個 container,則想移除 image 前,必須先移除 container 才能移除 image。
    • (D) 上述 ABC 都正確。
    • (E) 上述 ABC 都「錯誤」。
  2. 考慮部署「線上環境」,VM 與 container 的選擇與比較,下列哪種描述「錯誤」?
    • (A) Container 啟動速度比 VM 快。
    • (B) Container 記憶體使用量比 VM 小。
    • (C) 若過去是使用 VM,想改用 Container 就必須使用與 VM 一樣的作業系統建置,才能確保系統運作正常。
    • (D) 上述 ABC 都正確。
    • (E) 上述 ABC 都「錯誤」。
  3. 在符合 Docker 官方的最佳實踐(Docker best practices)與 12 Factor 原則的前提下,有一個 load balance 後面接多個 container 部署在「線上環境」,平常好好的,今天發現某一個 container 程式運作異常(如:即將 OOM),但後端服務(backing service)沒異狀。此時最「不」應該採取下列哪種做法?
    • (A) 啟動一個新的 container 接到 LB,然後中斷有問題的 container 流量。
    • (B) 透過 rolling update 機制,將該 container 砍掉重練。
    • (C) 嘗試進入有問題的 container,利用工具 debug。
    • (D) 上述 ABC 做法都符合。
    • (E) 上述 ABC 做法都「不」符合。
  4. 下列都是兩個 container 組合的架構,皆可正常運行。以單一 container 作為一個 SaaS 來看,哪一種「比較不」符合 Docker 官方的最佳實踐(Docker best practices)?
    • (A) Nginx container + Apache with PHP-FPM(FastCGI)container。
    • (B) Apache container + Tomcat container。
    • (C) Nginx container + Node container。
    • (D) 以上 ABC 皆符合。
    • (E) 以上 ABC 皆「不」符合。
  5. 考慮部署「線上環境」,下列建置 Docker image 與運行 container 的描述,何種做法「不」符合 Docker 官方的最佳實踐(Docker best practices)?
    • (A) 持續整合(Continuous Integration)有提到要經常做驗證,因此部署上線後的第一件事是在 container 上先跑單元測試,再啟動應用程式。
    • (B) 利用 Docker VOLUME 參數設定,可以直接進上線用的 container,到共用 volume 目錄裡更新程式。
    • (C) 使用 ARG 參數,在建置 image 階段過程才能機敏資訊加入 image 裡,如此一來,機敏資訊就可以不用放原始碼裡了。
    • (D) 上述 ABC 做法都符合。
    • (E) 上述 ABC 做法都「不」符合。

Image 與 Container 之間的關係

第一題的答案是 (A)

A 描述錯誤:docker run 可以基於 image 產生 container,所以第一句話是正確的;相對地,docker commit 可以藉由 container 產生 image,所以第二句話是錯誤的。

B 描述正確:docker run <IMAGE ID> 指令中, 為 SHA256 digest,同個 digest 的內容無法改變。而 container 內容則是可變的,比方說進入 Nginx container 新增 HTML 檔,即可立刻使用瀏覽器看到內容。

C 描述正確:Image 上 run 一個 container 時,可以使用 docker rmi -f <IMAGE ID> 強制把 image 移除。雖然無法再使用該 Digest 再次啟動 container,但實際上 image 內容依然是存在的,因為重新 pull image 會發現 Already exists 的關鍵字。實際測試指令如下:

$ docker run php:7.4 php -v
Unable to find image 'php:7.4' locally
7.4: Pulling from library/php
...
$ docker rmi -f php:7.4
Untagged: php:7.4
Untagged: php@sha256:...
Deleted: sha256:...
$ docker pull php:7.4
7.4: Pulling from library/php
xxxxxxxxxxxx: Already exists
xxxxxxxxxxxx: Already exists
...

了解 image 與 container 的關係後,才能進一步了解 Docker 是如何建置、如何加速建置、啟動 container 等。

VM 與 Container 的比較與選擇

第二題的答案是:(C)

此題目要考慮下列兩件事:

  1. 「考慮部署線上環境」,因此沒有環境一致的問題,只要考慮 VM 或 container 放到線上的選擇或比較即可。
  2. 因情境是「考慮部署」與「VM 與 container 的選擇與比較」,因此是在討論 guest 而不是 host。

A 與 B 很明顯正確,container 因為少了 OS 層,當然啟動速度與記憶體使用量,都相較 VM 來的有優勢。

C 假設過去 VM 習慣使用 Ubuntu 或是任一種 OS,現在 container 是否必須要用相同的 OS 才能正常運行(VM 跟 Container 也可以交換反過來看)?答案當然是「非必要」的,如官方 Redis image 就提供了 Debian 和 Alpine 等選擇,即使作業系統不一樣,只要把程式或設定做好,系統一樣能正常運作。

Container 用起來跟 VM 很類似,但要記得它們本質是不同的。而不管 VM 或 container 都得經過調校才能發揮最大的優勢。

Container 是一次性的(disposable)

第三題的答案是:(C)

A 與 B 其實是差不多的,差別是 B 會把 container 砍掉。

C 很明顯是不能做的。進去安裝 Vim 或相關除錯工具明顯錯誤就不提了,如果是為了看 log,參考 12 Factor Logs,它應該能在其他地方查得到,而不是進 container 查;如果是進去調整參數等行為,則會不符合 12 Factor Build, release, run 嚴格分離的原則。

現在 container 都是交由 Kubernetes 等 container orchestration 系統負責這些維運任務,但開發者也有必要了解這類問題的處理方法,才有辦法寫出符合 container 架構的程式。

一次性的特色可以參考 Create ephemeral containers

Container 盡可能只做一件事

第四題的答案是:(A)

參考 Decouple applications,Docker 官方是這麼說的:

Limiting each container to one process is a good rule of thumb, but it is not a hard and fast rule.

上面五種 container,唯獨 Apache with PHP-FPM(FastCGI)container 是比較不符合的,因為它必須啟動做兩件不同任務的 process。可以注意到在 DockerHub 找到類似的 image 的 CMD,都必須要使用 Supervisor 或類似的工具來管理 process。

順帶一提,官方也沒有提供 Apache + PHP-FPM 或 Nginx + PHP-FPM 的 image,而是提供只有 PHP-FPM 的 image,或是 Apache + PHP-CGI 的 image。

這概念與 SOLID 裡 SRP 的優點一樣:只做一件事,架構不會複雜,除錯會更容易。

Container 應該怎麼應用?

第四題的答案是:(E)

A 的問題:部署上線後居然可以跑單元測試?這代表測試程式碼和單元測試套件也一併被部署上線,這不符合 Don’t install unnecessary packages

正確的做法:在建置 image 前或是 Use multi-stage builds 在一開始的階段做單元測試,最終僅部署做完單元測試後的程式碼。

相對地,部署上線後,可以做冒煙測試(smoke testing)。

B 的問題:VOLUME 官方不建議用來放程式,而是放額外要保存的資料,如 MySQL 的 Dockerfile 就有設定 VOLUME /var/lib/mysql 是存放資料庫檔案。

正確的做法:程式上線應該透過建置新的 image,跑新的 container 來取代舊的。

可以試著反過來思考,為何我們會覺得 Docker 上跑 MySQL / Redis / Apache 很快速方便?因為它們把程式都包在 image 裡了。

C 的問題:「把機敏資訊加入 image 裡」這句話的意思就是,只要有辦法下載 image 就能得到機敏資訊。當然可以限制下載 image 的權限,但還是有另一個問題是難以做到環境同步,因為線上環境跟測試環境會是不同的 image--因為機敏資訊設定通常是不一樣的。

正確的做法:使用 ENV 環境參數(參考 12 Factor Config),把機敏資訊存在環境。

寫在最後

Docker 真的是學無止盡,雖然跟他相處了很久,但要寫的時候,還是得東查西查。

希望這次的鐵人賽能幫助到大家,感謝收看。