Systemd與Docker的愛恨情仇
DevConf.cz 是一個由紅帽贊助的開發者大會,今年在捷克共和國布爾諾舉行。會議上有很多不同的演講,但 最大的主題是容器 。大部分演講是實踐性質的,要么是展示如何使用各種容器技術的教程,比如 Kubernetes 和 Atomic.app ,要么是對新產品的介紹,比如 Cockpit 。
不過,紅帽容器工程主管Dan Walsh所做的演講無疑是容器主題中最有趣的。他展示的是Linux容器世界核心沖突之一:systemd與Docker守護進程。這絕非是一個新問題;在Ubuntu采用systemd,以及CoreOS引入 圍繞systemd構建的容器系統 Rocket時,這個問題就出現了。
Systemd vs. Docker
“這是Lennart Poettering,”Walsh一邊展示一張照片一邊說。“這是Solomon Hykes”,他展示了另一張照片。“兩個人都不愿意妥協。而我則介于他們之間。”
由于Walsh曾受命讓systemd支持Docker,他詳述了兩個系統間代碼、個人及運營沖突的歷史。從很多方面來說,這也是紅帽與Docker公司之間補丁沖突的歷史。Poettering是systemd的主要作者,并在紅帽工作,而Hykes則是Docker公司的創始人和CTO。
依據Walsh的演講,沖突的根源在于Docker守護進程被設計用來接管很多systemd也為Linux完成的功能。包括初始化、服務激活、安全及日志。“從很多方面講,Docker想成為systemd,”他聲稱,“它夢想成為systemd。”
他闡述的第一個沖突在于服務初始化與重啟。在systemd模式中,這些都是由systemd控制的;在Docker的世界中,則全由Docker守護進程控制。比如說,可以在systemd單元文件中以“docker run”語句來定義服務,以便以容器方式運行它們,也可以在Docker守護進程中將服務定義成“自動重啟”容器。兩種方式都行得通,但混合起來就不行。 Docker文檔 推薦使用Docker自動重啟,除非是在混合容器化服務與非容器化服務時;這種情況下它推薦使用systemd或Upstart。
不過,當以容器方式運行的服務依賴于其他容器化服務時,問題就出現了。對于常規服務,systemd有一個名為 sd_notify 的功能,可以在服務準備就緒時傳遞消息,然后就可以啟動那些依賴于它們的服務。但是,Docker采用的是C/S架構。客戶端為每個用戶會話調用docker run及其他命令,但容器的啟動和管理是在Docker守護進程中進行的(相當于“服務端”)。客戶端無法發送sd_notify狀態消息,因為它不做容器服務的實際管理,也不清楚服務的就緒時間,守護進程也無法發送這類消息,因為它不是通過systemd的單元文件調用的。因此,Walsh的團隊嘗試了一種曲折的變通方式來啟用sd_notify:
- systemd請求來自Docker客戶端的sd_notify
- 該客戶端發送一條sd_notify消息給Docker守護進程
- 守護進程設置一個容器來完成sd_notify
- 守護進程從該容器獲取一條sd_notify消息
- 守護進程發送一條sd_notify消息給該客戶端
- 該客戶端發送一條sd_notify消息通知systemd Docker容器已就緒
用于啟用這個錯綜復雜系統的補丁未被Docker項目接納,對此Walsh毫不意外。sd_notify確實可以用于Docker守護進程自身,因此systemd可以依賴于守護進程的運行。但是還是無法為單個容器化服務執行sd_notify,因此Docker項目依然 沒有可靠的辦法來管理容器化服務依賴的啟動順序 。
Systemd有一項功能叫“ 套接字激活(socket activation) ”,可以在收到針對某個特定網絡套接字的請求時自動啟動服務。這使得服務器可以支持那些“偶爾需要”的服務,而無須始終運行。Docker守護進程自身曾經支持過套接字激活,不過由于與Docker的自動重啟相沖突而將其禁用了。
Walsh的團隊對單個容器的套接字激活更感興趣。其好處是可以消除“始終運行”容器的額外開銷。但是,開發人員發現他們不得不采用與sd_notify變通方式類似的方法,差別在于傳遞的是一個套接字,而不僅僅是一條消息。他們甚至沒有嘗試實現。
Linux的 cgroup 可用來定義每個服務的系統資源額度,比如CPU、內存及I/O限制。Systemd允許在初始化文件中定義cgroup限制,這樣你就可以定義服務啟動時的資源策略。但是,使用Docker時,這又與其C/S模式相沖突。Systemd的cgroup設置只對客戶端產生作用;不對容器實際運行所在的守護進程產生作用。相反的,每個容器都繼承了Docker守護進程的cgroup設置。不過,用戶可以通過docker run命令 參數 來傳遞cgroup限制,這雖然可行,但未能與系統的整體管理策略整合。
Walsh能聯系得上的唯一成功之處是日志。Docker的日志也無法與systemd的 journald 協作。容器的日志輸出都是在每個容器本地,一旦刪除容器,所有日志將被自動清除。從安全審計的角度來看,這是一大敗筆。Docker 1.9開始支持--log-driver=journald開關,將日志記錄到journald中。不過,Docker容器默認不使用journald,因此每次都要傳遞這個開關。
容器內部的systemd
Walsh還想在Fedora、紅帽企業Linux(RHEL)及CentOS容器基礎鏡像中啟用systemd,部分是因為很多軟件包需要systemctl功能以便正確安裝。他一開始嘗試的是使用“ fakesystemd ”來代替systemctl,該服務用于滿足軟件包的systemctl需要,沒有其余的功能。結果這會造成問題,他很快就放棄了,不過還是遲了一步,未能阻止它在RHEL 7.0中發布。
在RHEL 7.1中,該團隊添加了“systemd-container”,這是systemd的一個大幅度刪減版本。這依然會對那些軟件中需要完整systemd的用戶造成問題,Poettering要求容器團隊進行修改。在RHEL 7.2中,容器具有了真正的systemd,減少了需要安裝的依賴,因此尺寸更小。Walsh的團隊正著手進一步縮減這些依賴。
根據Walsh所說,容器中沒有systemd最大的問題是它“退回到了使用初始化腳本之前。”每個鏡像作者都在容器內創建自己的瘋狂的啟動腳本,而不是使用軟件包作者精心制作的啟動腳本。他演示了在具有systemd的容器內,服務初始化是何嘗的簡單,創建一個運行Apache httpd服務器的容器,其Dockerfile只有三行:
FROM fedoraRUN yum -y install httpd; yum clean all; systemctl enable httpd;
CMD [ "/sbin/init" ]</pre>
不過,要在Docker中使用systemd,有一個主要障礙:運行具有systemd的容器要求運行時要帶有--privileged標記,這讓它變得不安全。這是因為Docker守護進程要求容器運行的“服務”應用程序其PID永遠是1。在具有systemd的容器中,其PID是1,應用程序則具有其他的PID,這會造成Docker認為容器失敗并將其停止。
Poettering說PID 1具有特殊要求。其中之一是殺死那些被它們的調用會話所遺棄的“僵尸”進程。對Docker來說,這是一個真正的問題,因為應用程序以PID 1運行,卻不處理僵尸進程。比如,運行Oracle數據庫的容器可能在退出時會遺留幾千個僵尸進程。另一個要求是寫入syslog,除非配置容器寫入日志到journald,否則將進入/dev/null。
Walsh嘗試了幾種辦法以便systemd在非特權容器中工作,并提交了四個不同的拉取請求( 7685 、 10994 、 13525 及 13526 )給Docker項目。所有請求都被Docker維護人員拒絕了。當Docker貢獻者之一Jessie Frazelle帶著印有“我對systemd相關拉取請求說不(I say no to systemd specific PRs)”的名牌來到 DockerCon.EU 2015 大會時,有關這些修改的爭論達到了頂峰。
![]()
Systemd與容器的未來
紅帽容器團隊還大量參與了開放容器項目 runC工具 的開發。該項目是開放容器組織(OCI)的實踐性產出,OCI是 由Linux基金會于2015成立 的非營利組織,目的是為容器API設置行業標準。OCI還維護著 libcontainer ,這是Docker用于啟動容器的類庫。按照Walsh所述,Docker最終需要采納runC作為其技術棧的一部分,以便在其他平臺上運行,尤其是Windows。
使用來自runC的成果,紅帽人員已經創建了一組名為“ oci-hooks ”的補丁,為Docker添加了大量systemd支持功能。它使用一個“鉤子”,可以在容器啟動之后應用程序運行之前觸發指定目錄下的所有可執行文件。使用這種方法執行的東西中間是 RegisterMachine鉤子 ,它會通知宿主機上systemd的machinectl容器正在運行。用戶可以使用machinectl命令看到所有Docker容器,以及runC容器:
# machinectlMACHINE CLASS SERVICE
9a65036e4a6dc769d0e40fa80871f95a container docker
fd493b71a79c2b7913be54a1c9c77f1c container runc
2 machines listed.</pre>
這些鉤子還允許在非特權容器里運行systemd。這個拉取請求( 17021 )同樣被Docker項目拒絕了。盡管如此,它還是包含在了紅帽發行的Docker包中。因此,Docker與systemd未來的一部分可能會涉及Docker的派生。
Walsh同時指出,cgroup、sd_notify以及套接字激活對runC都是開箱即用的。這是因為runC不使用Docker的C/S模型;僅僅是個可執行文件。他看不到Docker公司與紅帽之間未來在systemd問題上修復的突破口。Walsh預測紅帽可能會更多地轉向runC,并遠離Docker守護進程。按他的話來說,Docker正忙于“ containerd ”,這是一個新的systemd替代品,將取代初始化系統的功能。
但是,鑒于自Docker項目啟動以來短時間內Linux容器生態系統的快速變化,幾乎不可能預測systemd、Docker及runC今后一年的關系將如何。毫無疑問的是,將會有更多的變化和沖突報道。
原文鏈接: Systemd vs. Docker (翻譯: 梁曉勇 )
來自: http://dockone.io/article/1093