如何 10 步 Docker 化一個應用?

  • 2019 年 10 月 25 日
  • 筆記

本文將講解如何將應用 Docker 化的一些很實用的技巧和準則,推薦一讀。

一、選擇基礎鏡像

每種對應技術幾乎都有自己的基礎鏡像,例如:

  • https://hub.docker.com/_/java/
  • https://hub.docker.com/_/python/
  • https://hub.docker.com/_/nginx/

如果不能直接使用這些鏡像,我們就需要從基礎作業系統鏡像開始安裝所有的依賴。

網上大多數教程使用的都是以 Ubuntu(例如:Ubuntu:16.04 )作為基礎鏡像,這並不是一個問題,但是我建議優先考慮 Alpine 鏡像:

  • https://hub.docker.com/_/alpine/

Alpine 是一個非常小的基礎鏡像(它的容量大約只有 5MB)。

註:在基於 Alpine 的鏡像中你無法使用 apt-get 命令。不過你不必擔心,因為 Alpine 系統有自己的軟體包倉庫和包管理工具 apk。關於 apk 的具體使用你可以詳細參考:「Alpine Linux配置使用技巧」一文。

二、安裝必要軟體包

這個步驟通常比較瑣碎,有一些容易忽略的細節:

  • apt-get updateapt-get install 命令應該寫在一行(如果使用 Alpine 則對應的是 apk 命令)。這不是一個常見的做法,但是在 Dockerfile 中應該要這麼做。否則 apt-get update 命令產出的臨時層可能會被快取,導致構建時沒有更新包資訊。(具體可參見此文)。
  • 確認是否只安裝了實際需要的軟體(特別是在生產環境中運行這個容器)。

註:我見過有人在他們的鏡像中安裝了 vim 和其他開發工具。如果這是必要的,應該針對構建、調試和開發環境創建不同的 Dockerfile。這不僅僅關係到鏡像大小,還涉及到安全性、可維護性等等。

三、添加自定義文件

一些優化 Dockerfile 的小提示:

  • 理解 COPY 和 ADD 指令的區別,具體可參考此文。
  • 儘可能遵照文件系統慣例來存放文件。例如:針對解釋型應用程式(如:Python),使用 /usr/src 目錄。
  • 檢查添加文件的屬性。如果需要可執行許可權,沒有必要在鏡像上新建一個層( 通過 RUN chmod +x … 指令來增加許可權)。你只需要在程式碼倉庫的源文件上修正這些屬性即可,即使開發平台是 Windows,也可以參照此文給文件增加可執行許可權。

四、定義容器運行時的用戶許可權

  • 容器中的進程默認情況下是以 root 許可權運行的。
  • 如果容器中的應用程式需要使用特定的用戶或組(/etc/passwd 或 /etc/group)來運行時,可以在容器啟動時使用 docker run 命令的--user 參數來指定其固定的 UID 或 GID。
  • 儘可能避免容器中的進程以 root 許可權運行。

註:現在不少熱門應用程式鏡像都需要用特定的用戶 ID 來運行(例如:Elastic Search 需要 uid:gid = 1000:1000),請盡量不要在寫出這樣的鏡像。更多關於容器內運行應用程式的許可權說明可參考此文。

五、定義暴露的埠

不要為了暴露特權埠(例如:80)而將容器以 root 許可權運行。如果有這樣的需求,可以讓容器暴露一個非特權埠(例如:8080),然後在啟動時進行埠映射。

註:低於 1024 的 TCP / IP 埠號就是特權埠,因為不允許普通用戶在這些埠上運行服務。

六、定義入口點(entrypoint)

  • 普通方式:直接運行可執行文件。
  • 更好的方式:創建一個 docker-entrypoint.sh 腳本,這樣可以通過環境變數來配置容器的入口點。這也是一個非常普遍的做法,可參考下面這些例子:elasticsearch 的 docker-entrypoint.sh 文件 和 postgres 的 docker-entrypoint.sh 文件。

七、定義一種配置方式

每個應用程式都需要參數化,你基本上可以遵循以下兩個原則:

  • 使用應用程式特定的配置文件:該方式需要通過文檔來說明配置文件的格式、欄位、放置位置等等(當運行環境比較複雜,例如:應用程式跨越不同的技術,則不太合適)。
  • 使用作業系統環境變數:簡單而有效。這也是 12-factors 推薦的方式。

註:使用環境變數方式並不意味著您需要丟棄配置文件並重構應用程式的配置機制,你只需要通過 envsubst 命令來替換配置文件模板中的值就可以了(這個流程一般需要在 docker-entrypoint.sh 文件中完成,因為這需要在容器進程運行前完成)。例如:在 Nginx 配置中使用環境變數,具體方法可參考此文。

這種方式可以將應用程式的配置文件封裝在容器內部。

八、外部化數據

關於數據存儲有一條黃金法則:絕對不要將任何持久化數據保存到容器內。

容器的文件系統本身是被設計成臨時和短暫的。因此任何由應用程式生成的內容、數據文件和處理結果都應該保存到掛載的卷或者作業系統綁定掛載點上(既:將宿主機作業系統的目錄掛載到容器中)。

如果將數據保存到綁定掛載點,對於要綁定到容器的宿主機上的目錄,你需要注意以下幾點:

  • 在宿主機作業系統上創建非特權用戶和組。
  • 所有需要綁定目錄的所有者都是該用戶。
  • 根據使用場景給授權(僅針對這個特定的用戶和組,其他用戶無權訪問)。
  • 容器也以該用戶運行。
  • 容器可以完全控制這些目錄。

九、確保處理好日誌

如果這是一個新的應用程式,並且希望它能夠堅持 Docker 約定,就不應該將日誌寫入任何文件。應用程式應該使用標準輸出和標準錯誤輸出日誌,這和之前推薦使用環境變數傳遞參數一樣,這也是 12-factors 之一,具體可以參見這裡。

Docker 會自動捕捉應用程式的標準輸出,並可以通過 docker logs 命令查看。有關於 docker logs 的具體使用你可以參考這裡。

但是在一些實際場景下你可能會遇到問題,例如:運行一個簡單的 Nginx 容器,至少會有兩種不同的日誌文件:

  • HTTP 訪問日誌(Access Logs)
  • 錯誤日誌(Error Logs)

對於這種按照特定結構輸出日誌的應用,就不太適合將它們的日誌輸出到標準輸出。這種情況下,你需要按持久化的方式處理這些日誌,並確保這些日誌文件的能正常的輪轉。

十、輪轉日誌

如果應用程式將日誌寫到文件,或者會無限追加內容到文件,就需要關注這些文件的輪轉(rotation),這對於防止伺服器空間耗盡非常有用的。

如果使用綁定掛載,我們可以依靠宿主機的一些工具來實現文件輪轉功能。例如:logrotate,關於 logrotate 的使用你可以參考示例一、示例二。

註:本文在 「如何 Docker 化任意一個應用」的基礎上整理和修改,原文地址:http://t.cn/ReT0AyJ 。