近幾年,容器技術迅猛發展,併在各大企業得到了廣泛應用。它標準化了應用程式的執行環 境,從而簡化了程式的部署流程,同時也促進了 PaaS(Platform as a Service)系統的發展,LAIN 即為其中之一。作為一種革命性的新技術,Docker 具有種種優點,比如一處編譯、處處執行,易於編排和輕量等等;但是,由於發展時間較短, 在生產環境中使用時,Docker 在一些環節出現了效能問題,另外,Docker 容器與虛擬機器之 間也有一些微妙的區別,在生產環境中使用時需要給予特別的註意。下麵分享一下 LAIN 生 產叢集在執行過程中遇到的一些問題和解決方案。
為了方便管理,我們把 Jenkins 搬進了容器。但是,Jenkins 容器經常停止響應, 需要定期重啟。為什麼在虛擬機器上可以正常執行的程式到了容器裡就出現問題了呢? 透過 ps aux 可以發現,Jenkins 容器裡有大量的僵屍行程,即圖 1 中的 [git-remote-http] 行程。
行程表是有限的系統資源,當僵屍行程佔用了大量的行程表空間時,就會導致無法啟動新的 行程。那為什麼容器裡會產生大量的僵屍行程呢?因為在類 UNIX 系統中,PID 為 1 的 行程是特殊的:它負責收割僵屍行程。在虛擬機器中,PID 為 1 的行程通常是 Systemd、 Sys Vinit 或者 upstart,它們都能自動收割僵屍行程;在容器裡,情況有很大的不同: PID 為 1的行程常常是使用者自己寫的程式,很可能不會收割僵屍行程,比如上面的 jenkins-master,這時候就會造成僵屍行程的累積,最終導致容器內無法再啟動新的行程。
log-driver | 日誌收集速度 |
syslog | 14.9 MB/s |
json-file | 37.9 MB/s |
可以看到,這個速度並不理想,當容器的標準輸出較多時,Docker Daemon 不能及時處理, 就會影響這個容器的正常執行;同時,Docker Daemon 對標準輸出的處理會阻塞其他操作, 比如 docker ps 和 docker stop 等命令也會卡死。這時,我們只能重啟 Docker Daemon,同時禁止容器的標準輸出。
上述 2 個問題嚴重地影響了應用的正常執行,是否有辦法可以解決呢?即,我們希望找到 一個工具,既可以收割僵屍行程,又可以把標準輸出重定向到檔案並自動 rotate。 表 2 是我們找到的一些工具。
行程管理工具 | 僵屍行程 | 重定向標準輸出並自動 rotate |
S6 | 收割 | 支援 |
tini | 收割 | 不支援 |
systemd | 收割 | 支援,但為二進位制 |
phusion-baseimages | 收割 | 不支援 |
daemontools | 不收割 | 支援 |
supervisor | 不收割 | 支援 |
表 2:行程管理工具的比較
從上表可以看出,S6 是最滿足我們需求的解決方案。而且,它體積很小,只有 904 KB,啟 動時間不超過 100 ms,執行時佔用的 CPU 和記憶體可以忽略不計。
S6[1] 包含 s6-svscan、s6-supervise 和 s6-log 等元件。這些元件遵循 UNIX 設計哲學,相互獨立,功能正交,透過適當組合可 以實現強大的功能。那具體怎樣組合呢?S6 的作者把類 UNIX 系統的執行時可以分為 3 個 階段(Bercot,n.d.),如圖 2 所示。 而 s6-overlay[2] 實現了此方案,下麵參考 s6-overlay 說明如何在容器中使用 S6 管理行程。
階段 1
s6-svscan -t0 /var/run/s6/services
為了適應 LAIN 的需求,我們還將 Dockerfile 裡的 CMD 寫入了 /etc/services.d/app/run。整個流程如圖 3 所示。
階段 2
在階段 2,s6-overlay 首先把 /etc/services.d 裡的檔案複製到 s6-svscan 的執行時 目錄 /var/run/s6/services,然後透過 s6-svscanctl -a /var/run/s6/services 觸 發 s6-svcan 對此目錄的檢索;s6-svcan 發現 /var/run/s6/services/app/run 和 /var/run/s6/services/app/log/run 後,會呼叫 s6-supervise 分別啟動這個兩個服 務,如圖 4 所示。
首先,容器執行過程中,s6-svcan 會收割僵屍行程,解決了我們的第一個問題。
其次,s6-log 將 CMD 的標準輸出重定向到 /lain/logs/default/current,而不是傳送到 Docker Daemon,這樣就避免了 Docker Daemon 收集日誌的效能瓶頸, 表 3 是我們的測試結果。可以看到,在 日誌檔案 test.log 體積較小的時候,s6-log 收集日誌的速度可以達到 200 MB/s 左 右,幾乎與直接寫入檔案的速度相當;當 test.log 的體積逐漸增大時,因為會觸發越來 越頻繁的日誌輪轉,所以 s6-log 的速度逐漸降低,逐漸趨近於 90 MB/s 左右。 s6-log 只有在日誌檔案達到 256 MB 時才會觸發輪轉,而這種情況在實際的生成環境中 並不會頻繁發生,因此 s6-log 的實際日誌收集速度應該在 100 MB/s 以上,足以滿足我 們的需求,解決了我們的第二個問題。
test.log 體積 | 直接用檔案收集日誌的速度 | 日誌收集速度 |
80 MB | 94.5 MB/s | 209 MB/s |
160 MB | 91.4 MB/s | 212 MB/s |
320 MB | 90.5 MB/s | 103 MB/s |
640 MB | 234 MB/s | 99.6 MB/s |
1280 MB | 225 MB/s | 95.7 MB/s |
2560 MB | 212 MB/s | 91.8 MB/s |
表 3:s6-log 的日誌收集速度
階段 3
s6-supervise 還會監聽 SIGTERM 訊號,當 s6-supervise 收到此訊號後進入階段 3: 執行 /var/run/s6/services/app/finish,也就是 s6-svscanctl -t /var/run/s6/services,從而讓整個容器優雅地退出,這比 Docker 預設的等待 10 秒後 強制殺死行程的行為友好很多。
綜上,在 Docker 中使用 S6 可以獲得以下好處:
-
自動收割僵屍行程
-
在保留自動 rotate 功能的同時提高日誌收集的速度
-
適當處理 SIGTERM 訊號,最佳化 docker stop 的使用者體驗
參考文獻:
-
Bercot, Laurent. n.d. “How to Run S6-Svscan as Process 1.” http://skarnet.org/software/s6/s6-svscan-1.html.
相關連結:
-
http://www.skarnet.org/software/s6/
-
https://github.com/just-containers/s6-overlay
基於Kubernetes的容器雲平臺實踐培訓
本次培訓包含:Kubernetes核心概念;Kubernetes叢集的安裝配置、運維管理、架構規劃;Kubernetes元件、監控、網路;針對於Kubernetes API介面的二次開發;DevOps基本理念;Docker的企業級應用與運維等,點選識別下方二維碼加微信好友瞭解具體培訓內容。