深入淺出CoreOS(三):Systemd和Fleet
來自: http://dockone.io/article/1055
【編者的話】Systemd并不是CoreOS特有的服務。本質上說Systemd是沒有依附于任何一個Linux發行版的獨立項目,但是很多行版Linux都在青睞Systemd管理服務的優勢,所以CoreOS選擇了它。Fleet是管理coreos和部署app的工具。有了fleet,你就可以把整個coreos集群當做一臺節點來處理。讓我們來一起學習一下關于此方面的內容。
這是深入淺出CoreOS系列文章中的第三篇,也是最后一篇。
在上一篇文章中,我們學習了cloud-config配置文件,在代理模式下運行etcd,以及關于etcd集群的一些常規設置。這篇文章中,我們將探討Systemd、Unit文件、Fleet和fleetctl。
Systemd概述
Systemd是一個在CoreOS的用于初始化系統的工具。它提供了許多強大的功能,例如啟動服務、停止服務、監控和重啟進程等。在CoreOS中,Systemd既可以用來管理Docker容器的生命周期,又可以管理不同系統的啟動任務。
要深入學習Systemd,必須要深入查看一系列關于Systemd的博客。不過,在此我們只討論需要CoreOS上的Docker容器上操作所需要的一些Systemd知識。
更多關于Systemd的內容,可以參考 官方文檔 。
Unit文件
Systemd為每一個守護進程記錄一個初始化結構文件,我們稱之為一個Unit。Systemd系統取代了傳統系統為每一個守護進程初始化一次腳本的做法。記錄有很多不同種類的Unit文件,但是我們只會關注可以運行Docker容器的服務類型Unit文件。
下面列表,是按照順序一個服務Unit(service unit)生命周期內的過程,以及作用:
- ExecStartPre: ExecStart之前運行的命令(指定在啟動執行 ExecStart 的命令前的準備工作,可以有多個,如前面第二個例子中所示,所有命令會按照文件中書寫的順序依次被執行。編者注)
- ExecStart:運行這個單元最主要的命令(這個參數是幾乎每個 .service 文件都會有的,指定服務啟動的主要命令,在每個配置文件中只能使用一次。編者注)
- ExecStartPost: ExecStart 運行完成后要執行的命令(指定在啟動執行 ExecStart 的命令后的收尾工作,也可以有多個。編者注)
- ExecReload:當使用 systemctl重新加載服務所需執行的命令
- ExecStop: 通過執行systemctl 確認單元服務失敗或者停止時執行的命令
- ExecStopPost: ExecStop 執行完成之后所執行的命令(指定在 ExecStop 命令執行后的收尾工作,也可以有多個。編者注)
- RestartSec:在重啟服務之前系統休眠時間(很有效的防止失敗服務少于100ms重啟一次)(如果服務需要被重啟,這個參數的值為服務被重啟前的等待秒數。編者注)
下面我們演示創建一個Hello_world.service的簡單Unit文件:
[Unit]Description=HelloWorldApp
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker rm busybox1
ExecStartPre=/usr/bin/docker pull busybox
ExecStart=/usr/bin/docker run --rm --name busybox1 busybox /bin/sh -c "while true; do echo Hello World; sleep 1; done"
ExecStop=/usr/bin/docker stop busybox1
[Install]
WantedBy=multi-user.target</pre>
讓我們仔細來看一下剛才創建這個Unit文件:
- Description: 可以在Systemd日志展示(可以通過journalctl和Systemdctl來檢查日志文件)
- After=docker.service 和 Requires=docker.service 意思是只有docker.service啟動后才可以被激活。當然也可以定義自己的動作.你也可以使用多個After來做限制,多個after使用空格分開即可
- =- 是Systemd忽略錯誤的一個語法.在這種情況下,docker會發揮一個非0的退出碼,如果想停止一個不存在的容器,這對我們來說并不是一個錯誤。
- ExecStartPre=-/usr/bin/docker rm busybox1 如果 busybox1鏡像存在,則刪除它。 如果鏡像不存在,則腳本繼續往下運行。
- ExecStartPre=/usr/bin/docker pull busybox 將從注冊中心獲取最新的busybox
- ExecStart= 當單元開始啟動,這里允許你執行想運行的命令。但是不要用-d來啟動Docker容器,因為這樣講組織docker作為一個子進程被啟動. Systemd會認為進程已經退出,相應的單元也會停止。
- ExecStop=/usr/bin/docker stop busybox1 busybox1容器將會被停止.
- WantedBy=multi-user.target" 當啟動 multi-user.target時,Systemd將會將獲取單元.
如果要啟動一個新的Unit,必須使用Systemd創建symlink然后啟動文件:
$ sudo systemctl enable /etc/Systemd/system/hello.service$ sudo systemctl start hello.service</pre>
要驗證Unit已經啟動,我們可以檢查正在運行的容器列表:
Docker ps然后,通過journalctl命令輸出Unit,如下:
$ journalctl -f -u hello.service-- Logs begin at Fri 2014-02-07 00:05:55 UTC. --
Feb 11 17:46:26 localhost docker[23470]: Hello World
Feb 11 17:46:27 localhost docker[23470]: Hello World
Feb 11 17:46:28 localhost docker[23470]: Hello World
[...]</pre>
-u指的是unit,-f指的是一起,可以按CTRL+C隨時退出。Systemd服務Unit(services unit)只能運行在一臺機器上。它只能用于單任務的場景,比如下載文件或者重啟機器等。
更多關于Systemd units的知識, 請參考文檔 。
FLEET 概述
Fleet運行在Systemd上層,在集群層次控制Systemd,負責創建分布式Systemd:
![]()
要想把服務發布進集群,就必須提交合規范的Systemd unit。
FLEETCTL
Fleetctl是官方推薦的控制fleet unit工具,它可以在本地或者遠程CoreOS集群使用。Fleetctl命令類似于systemctl命令,必須在命令前面加上sudo命令在配合使用。Fleetctl命令可以直接在集群上運行。或者如果你喜歡從外部主機執行fleetctl(即你的工作站),可以通過SSH把系統環境變量作為參數傳遞給集群機器,像這樣:
$ export FLEETCTL_TUNNEL=remote_machines_ip讓我們看一下你可以執行的任務,列出所需的命令。
開始單元服務:
$ fleetctl start hello_world.service檢查單元服務狀態:
$ fleetctl status hello_world.service停止服務:
$ fleetctl stop hello_world.service查看服務文件:
$ fleetctl cat hello_world.service提交服務:
$ fleetctl submit hello_world.service查看日志:
?$ fleetctl journal hello_world.service</pre>
SSH訪問服務
$ fleetctl ssh hello_world.service服務列表:
$ fleetctl list-units列舉集群機器:
$ fleetctl list-machinesUnit 文件
Fleet主要是依賴Unit文件交互的。對于Systemd Unit,它定義了想要做什么以及fleet應該如何去做,在集群中Fleet安排一到多臺機器,Fleet安排一個有效的Unit文件。在[X-Fleet]部分留意fleet的一些特殊部分,可以用來替換Systemd單元的[install]部分。其余Systemd部分保持與fleet相同。
我們一起看一下特殊的部分配置:
- MachineID: 規定每一個機器的唯一字符串編號
- MachineOf: 對那些有特定單元的,限制的機器才可以有
- MachineMetadata: 限制那些有特定元數據的機器
- Conflicts: 防止一個單元使用其他單元名字glob-matching合并其它Unit
- Global: 在所有集群機器上規定這個Unit
上面所有的都在單元文件的[X-Fleet]部分。除非單元文件上MachineMetadata提供配置Global=true,否則我們認為這個文件是無效的。如果你想在所有集群或者MachineMetadata指定值得機器上規定相同的單元,全局單元文件就非常有用了。
下面我們來看一下上述提到的Fleet Unit中使用的hello_world.service文件:
[Unit]Description=HelloWorldApp
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker rm busybox1
ExecStartPre=/usr/bin/docker pull busybox
ExecStart=/usr/bin/docker run --rm --name busybox1 busybox /bin/sh -c "while true; do echo Hello World; sleep 1; done"
ExecStop=/usr/bin/docker stop busybox1
[X-Fleet]
MachineMetadata=group=hw_webservers
Conflicts=hello_world@*</pre> 例子中的hello_world@
是做什么的?
好的,讓我們來說說 hello_world@1.servic e and hello_world@2.servic e兩個完全相同的文件。
在集群上啟動:
$ fleetctl start hello_world@*這兩個Fleet文件都部署在 MachineMetadata=group=hw_webservers配置的服務器,配置Conflicts=hello_world@意圖是防止一個Unit使用了global-matching與Unit文件沖突。
讓我們看另一個hello_world.service 單元的例子。
規定集群中所有的機器的這個單元:
[X-Fleet]Global=true</pre>
規定機器上有some_other_unit.service單元:
[X-Fleet]MachineOf=some_other_unit.service</pre>
規定單元機器的編號為:562999f8
[X-Fleet]MachineID="562999f8"</pre>
獲取機器ID:
$ fleetctl list-machines寡欲fleet 單元更多的只是, 參考官方文檔 。
集群中運行一個容器
你要做的就是提供一個嚴格格式的【install】部分Unit文件,如下:
[Unit]Description=hello_docker
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker rm busybox1
ExecStartPre=/usr/bin/docker pull busybox
ExecStart=/usr/bin/docker run --rm --name busybox1 busybox /bin/sh -c "while true; do echo Hello Docker; sleep 1; done"
ExecStop=/usr/bin/docker stop busybox1</pre>
注意:如果你是手動運行docker命令,請確保不要在命令行中復制運行命令在分離模式中開啟一個容器,這種做法將會使會造成容器運行幾秒鐘后自動退出。
總結
這篇文章中:
- 為Systemd寫入Unit文件
- 如何用Fleet創建分布式Systemd
- 為Fleet寫入Unit文件
請不要忘了系列的上兩篇文章,在第一篇我們講述了CoreOS的基礎原理 ,自動升級,發布渠道,集群發現等。在第二篇文章中,我們熟悉了cloud-config,etcd等。
下一個系列中,主要學習Docker。
原文鏈接: CoreOS Overview, Part Three (翻譯:張亞龍)
</div>