轉載:http://www.csdn.net/article/2015-01-08/2823477docker
摘要:CoreOS是採用了高度精簡的系統內核及外圍定製的操做系統。ThoughtWorks的軟件工程師林帆將帶來「漫步雲端:CoreOS實踐指南」系列文章,介紹CoreOS精華和推薦的實踐方法。本文爲基礎第三篇:系統服務管家Systemd。windows
【編者按】做爲一個操做系統,CoreOS 採用了高度精簡的系統內核及外圍定製,將許多本來須要複雜人工操做或者第三方軟件支持的功能在操做系統級別進行了實現,同時剔除了其餘對於服務器系統非核心的軟件,好比GUI和包管理器。來自ThoughtWorks的軟件工程師林帆將帶來「漫步雲端:CoreOS實踐指南」系列文章,帶你們瞭解CoreOS的精華和推薦的實踐方法。本文爲基礎第三篇:系統服務管家Systemd。緩存
做者簡介:服務器
林帆,生在80後尾巴的IT攻城獅,ThoughtWorks成都辦公室CloudOps小組成員,平時喜歡在業餘時間研究DevOps相關的應用,目前在備考AWS認證和推廣Docker相關技術。網絡
在系列教程的第一篇裏咱們已經提到了Systemd,它主要的設計目標是克服傳統Linux主流啓動程序SysVinit 固有的缺點,提升系統的啓動速度。相比同類的 SysVinit 競爭者,例如Ubuntu 的 upstart,Systemd 的設計更加前衛,簡單來講,它的設計思路借鑑了Mac系統的啓動程序Launchd。事實上Systemd的做用遠不只是啓動系統,它還接管了系統服務的啓動、結束、狀態查詢和日誌歸檔等職責,並支持定時任務和經過特定事件(如插入特定USB設備)和特定端口數據觸發的任務。在CoreOS的世界裏,推薦的作法是使用Systemd來管理全部用戶服務,包括運行在應用容器(如Docker)中的服務。併發
值得指出的是,Systemd並非CoreOS特有的服務。本質上說Systemd是沒有依附於任何一個Linux發行版的獨立項目,因爲Systemd的做者Lennart Poettering 就任於紅帽,整個項目實際由RedHat公司主導。雖然RedHat Linux直到2014年中旬才用上Systemd,但RedHat旗下的Fedora早在2011年時就已經引進了Systemd做爲其啓動管理程序了。ssh
在開始使用Systemd以前,先了解一下Systemd有哪些特別之處。 工具
當SysVinit 程序初始化系統的時,會將全部可能用到的後臺服務進程所有運行起來。然而用戶須要等待系統將全部服務都啓動完成以後,纔可以登陸。這種作法會帶來兩個問題:系統的啓動時間過長和系統資源的浪費。性能
Systemd 提供了服務按需啓動的能力,使得特定的服務只有在被真正請求的時候才啓動。特別是具體硬件相關的服務,好比藍牙服務僅在藍牙適配器被插入時才須要運行,打印服務僅在打印機鏈接或程序要打印時才須要運行,甚至sshd服務也只須要在用戶使用ssh鏈接到服務器時才須要啓動。這種能力是創建在對Systemd對DBus總線或特定Socket端口監聽的特性上的,這種設計相比於傳統啓動程序具備顛覆性的進步。 ui
在SysVinit的時代,將每一個服務項目編號的方式依次執行啓動腳本。後來Ubuntu的Upstart解決了沒有直接依賴的啓動項之間的並行啓動。而Systemd經過Socket緩存、DBus緩存和創建臨時掛載點等方法進一步解決了啓動進程之間的依賴,作到了全部系統服務併發啓動,這一設計一樣是Systemd獨具特點的創意。固然,對於用戶自定義的服務,Systemd容許配置其啓動依賴項目,從而確保服務按必要的順序運行,稍後會詳細描述具體的使用方法。
Systemd啓動模型與其它啓動模型的對比
Cgroup的全稱是controller group,是將任意進程進行分組化管理的Linux內核功能,最初由Google的工程師提出,從Linux內核版本2.6.24正式啓用。拿Android來講,它的應用程序隔離就是使用的這種技術。而很長一段時間裏,在更廣闊的服務器領域,一直並無一種主流的服務管理程序可以充分利用這種早已在手機端帶來普遍好處的特性。
而Systemd正是Cgroup方面的行家,它的出現正好彌補了這個領域的缺漏。經過Cgroup,Systemd不只實現了服務之間的訪問隔離,還可以限制特定應用程序對系統資源訪問配額(好比CPU的用量、內存的量),以及精確的管理服務的生命週期。在這篇文章的後面部分會講述相關操做具體的作法。
先介紹兩個概念,Unit和Target。
Unit是Systemd管理服務的基本單元,能夠認爲每一個服務就是一個Unit,並使用一個Unit文件定義。Unit文件中須要包含相應服務的描述、屬性以及須要運行的命令。在CoreOS中服務運行的命令一般是一系列的容器操做,而將具體的服務進程封裝在容器中。
Target是Systemd中用於指定服務啓動組的方式(至關於SysVinit中的「運行級別」,若是不清楚這個概念也沒有關係,搜索「Linux運行級別」能夠查到不少相關文章)。每次系統啓動的時候都會運行與當前系統相同級別Target關聯的全部服務,若是服務不須要跟隨系統自動啓動,則徹底能夠忽略這個Target的內容。一般來講咱們大多數的Linux用戶平時使用的都是「多用戶模式」這個級別,對應的Target值爲「multi-user.target」。
只說不作假把式,如今咱們來用Systemd建立一個簡單的系統服務。
在這個系列的上一節內容裏,咱們建立了一個由3個CoreOS虛擬機節點組成的集羣,在這節中,咱們只須要使用到其中的任意一個,好比coreo-01節點。首先使用ssh鏈接進入這個節點(這種方法適用於Linux/Mac用戶,對於Windows用戶需使用Putty客戶端, 具體參考)。
vagrant ssh core-01
登陸成功後提示符變成 「core@core-01 ~ $」 ,祝賀你又向CoreOS邁出了重要一步,接下來就能夠開始在CoreOS裏面玩耍了。
Systemd約定,服務的Unit文件需放置在 /etc/systemd/system 或 /usr/lib/systemd/system 目錄中,但因爲在CoreOS的後一個目錄是隻讀分區(整個/usr目錄掛載的都是隻讀的系統分區),所以咱們一般會將用戶定義的Unit服務文件放在在/etc/systemd/system目錄中。進入這個目錄,新建一個叫「hello.service」的文件,內容入下。
[Unit] Description=Hello World After=docker.service Requires=docker.service [Service] TimeoutStartSec=0 ExecStartPre=-/usr/bin/docker kill busybox1 ExecStartPre=-/usr/bin/docker rm busybox1 ExecStartPre=/usr/bin/docker pull busybox ExecStart=/usr/bin/docker run --name busybox1 busybox /bin/sh -c "while true; do echo Hello World; sleep 1; done" ExecStop=」/usr/bin/docker kill busybox1」 [Install] WantedBy=multi-user.target
在這個Unit文件裏,咱們首先爲這個服務提供了一行簡短的描述,而後指明它須要依賴docker的服務,而且要在docker服務運行之後才能運行。整個Unit文件是用的ini文件風格的分組配置格式,最開始的這段配置被放在了Unit組裏面。在接下來的Service組中,使用ExecStart和ExecStop屬性分別指定了服務運行時和結束時須要執行的命令。最後在Install組的配置中,咱們指定了服務所屬的Target爲multi-user.target。
這裏須要注意兩個地方,首先ExecStart屬性只能包含一條主要命令,而在這個屬性的先後能夠分別使用ExecStartPre和ExecStartPost指定更多的輔助命令,ExecStop同理。有些輔助命令會加上一個減號,表示忽略這些命令的出錯(由於有些「輔助」命令原本就不必定成功,好比嘗試清空一個文件,但文件可能不存在)。其次TimeoutStartSec=0這行的目的是將Systemd的服務啓動超時檢查關閉,對於docker應用這樣作是必須的,由於docker在運行時可能會須要下載或更新鏡像文件,使得服務啓動時間變得很長,這樣能夠防止Systemd認爲服務啓動失敗而將進程誤殺。有了Unit文件,如今就能夠啓動Hello World服務了,在控制檯輸入如下命令:
sudo systemctl start hello.service
Tip:這個名字末尾的 .service 後綴是能夠省略的,由於systemctl默認的後綴就是 .service。關於Unit文件後綴的含義,會在後續進階篇的文章裏詳細說明。
Systemd會自動找到 /usr/lib/systemd/system 目錄中的 hello.service 文件,並啓動其中定義的服務。若是以前建立的Unit文件是放在其餘目錄下的,這裏須要使用文件的完整路徑。首次運行的時候須要等待一段時間,由於docker須要從網絡上下載須要的鏡像。啓動完成後能夠經過「systemctl list-units」命令查看服務是否已經在運行(這個命令接受一個可選參數做爲服務名的過濾條件,若是不帶任何參數則輸出全部服務)。
core@core-01 ~ $ sudo systemctl list-units hello* UNIT LOAD ACTIVE SUB DESCRIPTION hello.service loaded active running Hello World
咱們還能夠經過「systemctl enable」命令來將服務指定爲在系統啓動時自動啓動。
sudo systemctl enable hello.service
此時就用到了以前定義的Target組,實際上enable操做只是建立了一個鏈接文件到指定的Target組的目錄下面。經過下面命令能夠證明。
core@core-01 ~ $ ls -l /etc/systemd/system/multi-user.target.wants/hello.service /etc/systemd/system/multi-user.target.wants/hello.service -> /etc/systemd/system/hello.service系統啓動時,會自動運行其所在Target級別相應的目錄裏全部連接的服務。
至此,咱們的第一個服務已經在後臺哈皮的玩耍了,但是說好的「echo Hello World」呢?咱們從頭至尾都沒有見到服務的任何輸出啊。
其實咱們啓動的服務已經在後臺默默的輸出「Hello World」了。
Systemd經過其標準日誌服務Journald將其管理的全部後臺進程打印到到std:out(即控制檯)的輸出重定向到了日誌文件。日誌文件是二進制格式的,所以必須使用特定的工具才能查看。Journald提供了配套的程序Journalctl用於處理日誌內容。Journalctl的使用很是簡單,默認不帶任何參數的時候會輸出系統和全部後臺進程的混合日誌,經常使用的參數有--dmesg用於查看內核輸出的日誌,--system用於查看系統輸出的日誌,--unit加上Unit的名字來指定輸出特定Unit的日誌,例如如下命令。
journalctl --unit hello.service其餘還有一些比較實用的參數,好比使用 --follow 實時跟蹤日誌輸出,使用 --since 和 --until 指定顯示的日誌時間區間等,能夠經過 journalctl --help 命令得到完整的參數說明。
當一個新的Unit文件被放入 /etc/systemd/system/ 或 /usr/lib/systemd/system/ 目錄時,它是不會自動被Systemd識別到的。例如在 hello.service 文件剛剛建立好時,若是咱們讓Systemd列出全部的Unit。
sudo systemctl list-units
此時在輸出的內容中是找不到hello.service這個Unit的。直到咱們經過 systemctl 的 start 或 enable 命令將這個Unit登記到Systemd的服務列表中,這個過程就是Unit的激活。
在服務被激活前,Unit僅僅是以Unit 文件的形式存在,Systemd提供 list-unit-files 命令查看全部的Unit 文件。
sudo systemctl list-unit-files
這個命令一樣接受一個可選的參數做爲Unit名稱的匹配條件,不帶任何參數時會輸出全部Systemd找到的(也就是在那兩個目錄)Unit文件。
PS:順便回答一個常常被問到的問題,這個命令的輸出的第一列是Unit文件名,第二列是相應的Unit是否開機啓動,它的值能夠是enable、disable或static,這裏的static是神馬意思呢?其實它是指對應的 Unit 文件中沒有定義[Install]區域,所以沒法配置爲開機啓動服務。啓動、結束、強制終止和從新啓動,沒啥可說的,分別對應如下幾個命令。
sudo systemctl start <Unit名稱> sudo systemctl stop <Unit名稱> sudo systemctl kill <Unit名稱> sudo systemctl restart <Unit名稱>
這裏存在一個陷阱,直到目前版本的Systemd(v215)和Docker(v1.4.0)中,當Unit的主要命令是經過docker容器託管的時候,systemctl的kill命令會沒法正確的殺掉服務進程,而必須使用 kill -s SIGKILL 才能正常的工做,緣由見筆者在「 不完美的CoreOS」中的分析。
服務的開機自動啓動的啓用和取消,分別對應下面兩個。
sudo systemctl enable <Unit名稱> sudo systemctl disable <Unit名稱>
這兩部分是 Systemd 當中比較Tricky的地方。
首先,若是咱們修改了一個放在 /etc/systemd/system/ 的文件,好比將輸出的「Hello World」改爲了「Bye World」,當執行 systemctl restart 之後,從新啓動的服務輸出的將依然是「Hello World」。這是由於當Unit文件被激活時,Systemd會將其中的內容記入到本身的緩存當中,所以爲了獲得更新後的內容,咱們須要告訴Systemd從新讀取全部的Unit文件。
sudo systemctl daemon-reload
再次重啓Unit,能夠看到更新就會生效了。
其次是Unit文件的移除,直接刪除Unit文件後因爲緩存的做用,Systemd仍然能夠繼續使用這個Unit,即便經過daemon-reload更新緩存,在list-units中會看見這個Unit只是被標爲了not-found,依舊陰魂不散。
core@core-01 ~ $ sudo systemctl list-units hello* UNIT LOAD ACTIVE SUB DESCRIPTION hello.service not-found failed failed hello.service
此時,咱們須要明確的告訴Systemd,移除這些已經被標記爲丟失的Unit文件。
sudo systemctl reset-failed如今這個Unit才真正的從Systemd的記錄中被抹去了。
Systemd 做爲默認系統啓動和服務管理器不但具有優秀的並行化處理能力,也提供了更好的系統進程追蹤管理能力,加之按需啓動等特色,結合 Docker 的快速啓動,在 CoreOS 集羣中大規模部署 Docker 容器與使用其餘操做系統相比在性能上的優點更加明顯。
實際上,Systemd的能力遠遠不止這些,在這篇文章僅僅介紹了它在服務管理方面的運用。在下一篇裏咱們將繼續探索CoreOS是如何經過Fleet服務將這些功能擴展到大規模集羣的。(做者/林帆 審校/周小璐)