Docker Linux 映像檔 容器

正確撰寫Dockerfile 製作最好用容器映像檔

2017-10-16
容器映像檔是容器技術堆疊時最基本的元件,本文將示範如何依照不同發行版本的基礎映像檔,加入所需的套件和功能,逐步一層層堆疊成特定用途的容器映像檔。然後介紹Dockerfile,並提供撰寫時所需注意的事項。

此外,在前面Dockerfile範例中,先定義ENTRYPOINT ["nginx"],再來是CMD ["-g", "daemon off;"],此CMD就變成參數形式,成為可替換ENTRYPOINT ["nginx"]後面參數的折衷方式,例如執行「docker run -d -p 80:80 philipz/nginx -t」會檢查nginx.conf。

以上是撰寫Dockerfile指令的簡單說明,其餘指令可參考表1,建議嘗試各個指令,多加練習便可輕易上手。

表1 Dockerfile指令說明

Dockerfile最佳實踐

了解如何撰寫Dockerfile之後,仍有些技巧必須知道,以便凸顯使用容器的好處,官方文件(https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/)亦有列出Dockerfile要點,以下是筆者個人的撰寫心得:

1. 選用較小的基礎映像檔

官方推薦使用Alpine(https://alpinelinux.org/)發行版作為基礎映像檔,除了是基於最小Linux發行版BusyBox所建置,同時還具備套件管理工具apk,可有效縮小映像檔的容量大小,且不會失去原本需要的系統功能,為此Docker公司還招募Apline發行版的開發人員Natanael Copa,協助以Alpine製作的各種應用系統的容器映像檔,例如nginx:alpine等,標籤為alpine的映像檔版本。

2. 容器是即用即拋

正如之前提過,容器用法就像是基礎架構即程式碼中「不可變伺服器」的方式,隨著程式改版就換掉整個容器映像檔,加快系統變更疊代速度,並利用標籤(Tag)來做版控。

資訊人員可能都曾聽過「寵物與牛(Pets vs Cattle)」的比喻,傳統系統維運是把電腦當寵物在保護,而進入虛擬化後,虛擬機就像牛一樣,過時就刪除淘汰,而容器更像是「寵物雞」,隨時打掉重練,比牛更靈活。

3. 善用.dockerignore

如同其名稱,借鏡Git的.gitignore,透過.dockerignore在建置過程中排除一些不必要目錄或檔案進入映像檔,加快建置速度及效能,例如.git、node_module或*.class,不需要將開發階段的函式庫或暫存檔放入容器內。

4. 縮減資料層數目

Dockerfile一行指令便是一個資料層(Layer),建議在維護性和資料層數取得平衡,先將相依函式庫安裝好,再放入程式。若函式庫與放置程式在同一行,便無法使用快取機制加速建置,每次變動程式「docker build .」都會重新安裝函式庫,導致建置映像檔時間過長。撰寫順序會因為程式語言而有所不同,可視自身開發流程來調整。

5. 結合多行命令和參數

尤其是RUN指令,在安裝套件時,可能因為長度過長,造成可讀性不佳,建議可以使用反斜線(\)跳脫字元,Windows PowerShell則是使用反引號(`),切分成多行方便閱讀,如下範例,而且多個命令,亦可利用Shell形式,以&&合併多個命令,減少資料層數量:


6. 優先使用COPY

前面已談過ADD和COPY的差異,當無法確定使用ADD或COPY時,建議使用COPY。

7. 採用CMD保持彈性

了解CMD和ENTRYPOINT差異後,通常會建議使用CMD,讓映像檔啟動容器具備更多種方式,例如當容器有錯誤情況,可以用終端互動模式啟動,進入容器中檢查設定檔或配置,若是以ENTRYPOINT寫死執行命令或程式,就只能用唯一的方式啟動容器,除非很有把握,不然仍建議只用CMD指令。

8. ONBUILD沿用指令步驟

在程式語言的映像檔中,通常可以看到標籤為XXX-onbuild,如Node(https://hub.docker.com//node)、Ruby(https://hub.docker.com//ruby)等,就以node:6.11-onbbuild映像檔為例。「ONBUILD COPY package.json /usr/src/app/」和「ONBUILD RUN npm install && npm cache clean --force」表示之後使用node:6.11-onbbuild作為基礎的Dockerfile皆會執行「COPY package.json」,並且「RUN npm install」,這正好符合Node.js開發步驟,先定義package.json,再透過npm安裝相依套件,因此利用ONBUILD指令,可強制讓之後的延伸映像檔重複必要步驟,讓Dockerfile更簡潔,只要使用「FROM node:6.11-onbbuild」,就可以建置出Node.js應用程式映像檔。

9. 以USER指令指定容器啟動身分

通常映像檔若無指定使用者身分,會以root角色啟動,這會造成安全上的隱憂,因此nginx可以加上「USER wwwdata」,限制容器是以www-data角色啟動,避免因網頁伺服器漏洞,取得root權限,進而影響到容器,甚至是Docker Engine的運作。再者,就拿官方postgres資料庫映像檔(https://hub.docker.com/_/postgres/)來說,其中啟動的腳本檔,有一行「exec gosu postgres」是透過gosu工具(https://github.com/tianon/gos)來限定User ID、Group ID,以免因root權限漏洞造成資料刪除消失。

10. 使用Docker Security Benchmark掃描

官方有提供安全掃描工具(https://github.com/docker/docker-bench-security),檢查容器映像檔並給予建議,將建置好的映像檔,使用此工具掃描後,再回頭修改Dockerfile,能讓容器運作更穩定。

結語

在上述Dockerfile指令和最佳實踐文章中,可以感受到,撰寫Dockerfile並不是只有單一方式和一成不變的規則,還可將不同階段的映像檔寫在單一Dockerfile(https://docs.docker.com/engine/userguide/engimage/multistage-build/)建置出多個開發階段所需的映像檔,更多的技巧是取決於容器內運作的程式。

在理解了單一容器映像檔製作之後,下一篇將要解說部署多容器系統架構工具時,Docker Compose所需要的YAML定義檔docker-compose.yml撰寫技巧。

<本文作者:鄭淳尹,Docker/Moby.Taipei社群共同發起人,曾任宏碁eDC維運工程師,玉山銀行資訊處專員,現為臺北榮民總醫院資訊工程師,系統維護及開發設計超過15年。開源技術愛好者,陸續在COSCUP開源人年會、Container Summit研討會、台灣微軟開發者大會、群益期貨和永豐金證券等分享資訊技術,並在多間大學資工系擔任Docker容器技術講師。現任微軟MVP,並翻譯審閱多本容器技術書籍。>


追蹤我們Featrue us

本站使用cookie及相關技術分析來改善使用者體驗。瞭解更多

我知道了!