使用Systemd執行Docker容器

weixin_33858249發表於2017-09-26
本文講的是使用Systemd執行Docker容器【編者的話】現今Docker的每個主要發行版都在轉移到Systemd上,使用Systemd執行Docker更有便於主機系統初始化以及程式管理等。但Systemd在監控容器上有一弊端,它不監控容器,而監控的是客戶端,導致了若客戶端與容器脫離聯絡後無論容器是否執行正常Systemd均會將該容器停掉等問題。但可以通過Systemd-Docker來解決。

你可以通過Shell指令碼或者Docker Compose(目前它還不能用於生產環境)來執行Docker容器,但在一些使用者場景下,你也可以使用主機系統初始化/程式管理。現在看起來,好像主流的Linux發行版本都準備使用Systemd,所以接下來我將深入介紹下Docker和Systemd。

如果你的一個非容器化的服務依賴於容器,那這個時候使用Systemd就可能是最好的解決方案。更有趣的是,純容器應用的開發者也注意到Systemd的價值。最後提醒下大家,CoreOS就建立在Systemd和Docker之上。

『官方的Docker文件之使用Systemd』中,你可以看到官方推薦手動建立Docker容器,並只在service檔案中使用docker start以及docker stop。我不贊同這個建議,因為這使得主機之間的遷移配置或使用一個新容器重啟服務更加困難----如果service檔案包含了所有的依賴元件會更好。這是通過CoreOS的方法,我將在這部落格展示一個案例。

我們以一個基於『Systemd』的Docker化的Redis為例來進行說明。例子中我將使用CentOS 7,我想其它的發行版也應該類似。你幾乎需要以下所有的service file:
[Unit]
Description=Redis Container
After=Docker.service
Requires=Docker.service
[Service]
TimeoutStartSec=0
Restart=always
ExecStartPre=-/usr/bin/Docker stop redis
ExecStartPre=-/usr/bin/Docker rm redis
ExecStartPre=/usr/bin/Docker pull redis
ExecStart=/usr/bin/Docker run --rm --name %n redis
[Install]
WantedBy=multi-user.target

這裡需要注意的事情有:
  • 很明顯,容器依賴於Docker的執行,因此Requires 這行。After這行也需要注意避免競爭條件。
  • 在我們啟動容器之前,我們首先停掉並移除任何存在相同名稱的容器,然後拉取最新的版本的映象。以”-”開頭的意味著如果命令執行失敗,系統不會終止。
  • 這意味著我們的容器將每次都從頭開始。如果你希望儲存資料,你需要使用volumes或volume 容器,亦或改變code(如果存在),重啟舊的容器。
  • 我們使用TimeoutStartSec=0實現永不超時,例如Docker pull可能需要一段時間。

如果你儲存這檔案到/etc/Systemd/system/Docker.redis.service並且執行systemctl start Docker.redis,Systemd將啟動Redis 容器(記住,如果它需要拉取redis映象這可能需要花一些時間)。那麼我們就可以手動訪問它,或另設一個依賴於它的服務。例如,如果我們有一個是執行在一個容器並且依賴於redis服務的應用foo,我們就可以使用以下服務檔案:
[Unit]
Description=Foo Service
After=Docker.service
Requires=Docker.service
After=Docker.redis.service
Requires=Docker.redis.service
[Service]
TimeoutStartSec=0
Restart=always
ExecStartPre=-/usr/bin/Docker stop foo
ExecStartPre=-/usr/bin/Docker rm foo
ExecStartPre=/usr/bin/Docker pull foo
ExecStart=/usr/bin/Docker run --name foo \
--link Docker.redis.ervice:redis --rm\
foo
[Install]
WantedBy=multi-user.target

如果redis容器發生故障,Systemd將自動重啟redis service和依賴foo的服務。

這配置工作花費大部分時間,這有一個主要問題。Systemd不會監控自身的容器,它監控的是客戶端。無論出於何種原因如果客戶端與容器脫離(比如網路問題),Systemd將停掉這個容器,即使它可能正常執行。相反,如果容器掛了但客戶端仍正常執行,Systemd不會做任何操作。我們真正需要監控的是容器,而不是客戶端。正好這有一個解決方法---Systemd-Docker。

System-Docker的工作原理是當它啟動時通過包裝Docker命令和移動容器程式到Systemd服務單元的cgroup上。我們的redis例項看起來是這樣的:
[Unit]
Description=Redis
After=Docker.service
Requires=Docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=/usr/bin/Docker pull redis
ExecStart=/usr/local/bin/Systemd-Docker --cgroups name=Systemd run --rm --name %n redis
Restart=always
RestartSec=10s
Type=notify
NotifyAccess=all
[Install]
WantedBy=multi-user.target

這是簡單的,但有一個問題是,Systemd-Docker命令隱瞞事實,那就是具有相同名字的停止了的容器將被刪除。我不得不新增--cgroups name=system 引數到存在這一問題的CentOS7和cgroups(其他發行版應該不需要這個引數)。如果你想嘗試Systemd-Docker,注意,你當前必須從頭構建,由於在我寫本博文時,“go get”功能是不完整的提供給了Docker的一個依賴。

總之,如果你想使用Systemd,顯然你需要在用之前思考得全面點。如果你只是使用一些工具不需要執行時超可靠的,這篇博文的的配置規則就可以了。否則你需要使用Systemd-Docker或解決監控容器的其他方法。

[1].在這種情況下執行,我們可以參照一個不需要修改或第三方工具的快速解決方案,請參閱下面問題的更多資訊: 
• https://github.com/Docker/Docker/issues/7245 
• https://github.com/Docker/Docker/pull/10427 
• https://github.com/ibuildthecl ... es/25

原文連結:Running Docker Containers with Systemd(翻譯:陳藝華)

原文釋出時間為:2015-04-21 
本文作者:CDocer 
本文來自雲棲社群合作伙伴DockerOne,瞭解相關資訊可以關注DockerOne。
原文標題:使用Systemd執行Docker容器

相關文章