Systemd vs. Docker:是敵是友?誰來管理誰?

在上一篇文章《解鎖 systemd:現代 Linux 開發者不可不知的服務管理利器》中,探討了如何利用 systemd 來優雅地管理應用程式。一個非常核心且重要的問題:「既然有了 Docker 和 Docker Compose,為什麼還需要 systemd 呢? 如果用了 Docker,還需要 systemd 嗎?

讓我們來深入剖析這兩者之間的關係,會發現,它們並非互相取代的競爭對手,而是一對相輔相成的黃金搭檔。


理解各自的職責:分層的藝術

要理解 systemdDocker 的關係,最關鍵的一點是:它們解決的是不同層面的問題

用一個生動的比喻來理解:

  • Linux 作業系統:就像一棟辦公大樓,提供了基礎設施(水、電、空間)。
  • systemd:是這棟大樓的總務與保全系統。它的職責是確保大樓的基礎服務正常運轉,比如確保電力系統 (network.target) 在上班前就已啟動,並在伺服器(大樓)重啟時,自動化地讓各個辦公室(服務)恢復運作。它關心的是「程序是否在作業系統層級上運行」。
  • Docker:則像是一個個**「拎包入住」的模組化辦公室**。每個辦公室(Container)都自帶了所有必需的辦公用品、裝潢和特定環境(應用程式的函式庫、依賴、配置),與隔壁辦公室完全隔離。Docker 關心的是「應用程式能否在一個隔離且一致的環境中運行」。
  • Docker Compose:是部門規劃師。他拿著一張藍圖 (docker-compose.yml),規劃:「這個部門需要三個辦公室,一個是前端,一個是後端,一個是資料庫,而且它們之間可以用內線電話(虛擬網路)溝通。」

從這個比喻中,可以清楚地了解:systemd 負責管理整棟大樓的運轉,而 Docker 則負責管理每個辦公室內部的環境

為什麼不直接用 Docker 就好?

可能會想說:「應用程式和它的所有依賴都在 Docker Compose 裡定義好了,只要 docker compose up -d 就能跑起來,為什麼還需要 systemd 這個『大樓管理員』呢?」

原因有二:

1. 誰來管理 Docker 自己?

Docker Engine (即 dockerd 服務) 本身就是一個需要在作業系統上運行的背景程序。當伺服器開機時,是什麼東西自動啟動了 dockerd 服務,讓你可以使用 docker 指令?

答案正是 systemd

可以隨時在 Linux 伺服器上執行這個指令來驗證:

sudo systemctl status docker.service

會看到 docker.service 正在被 systemd 管理著。systemd 確保了 Docker 這個「模組化辦公室的供應商」在伺服器啟動時就已經準備就緒。

2. 追求的是終極可靠性

DockerDocker Compose 提供了優秀的內部管理機制。例如,可以在 docker-compose.yml 中為服務設定 restart: always

# docker-compose.yml
services:
  my-app:
    image: my-app-image
    restart: always # 當容器停止時,Docker 會自動重啟它

這在大部分情況下都很好用。但是,如果發生了更嚴重的問題,例如 Docker Engine (dockerd) 本身崩潰了或被意外停止了呢?

這時,所有由它管理的容器(無論 restart 策略是什麼)都會停止運行,而且無法自行恢復。就像辦公室的電力總開關被關了,單個辦公室內的備用電源是沒用的。

這時,就需要更高層級的管理者——systemd——登場了。

systemd 來管理 Docker Compose

這就是最佳實踐的誕生:systemd 來啟動、停止和監控我們的 docker-compose 應用

這樣做,我們就建立了一個強大的、多層次的可靠性保障:

  • 容器層級:Docker 負責在容器內部保持應用程式健康。
  • 主機層級systemd 負責確保整個 Docker Compose 應用(作為一個整體單元)在主機上始終運行。

實戰:為 Docker Compose 建立一個 systemd 服務

假設 docker-compose.yml 放在 /home/user/my_docker_app/ 目錄下。可以像這樣為它建立一個 systemd 服務檔:

# /etc/systemd/system/my-docker-compose-app.service

[Unit]
Description=My Docker Compose Application
# 要求 docker.service 必須先啟動
Requires=docker.service
# 在 docker.service 啟動之後再啟動本服務
After=docker.service

[Service]
# 使用你的使用者
User=myuser
# Docker Compose 檔案所在的目錄
WorkingDirectory=/home/myuser/my_docker_app

# 啟動指令:在背景中啟動 compose
# 注意:我們不再使用 -d 參數,因為 systemd 會處理背景化
ExecStart=/usr/local/bin/docker-compose up
# 停止指令
ExecStop=/usr/local/bin/docker-compose down

# 讓 systemd 自動重啟我們的服務
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target

注意docker-compose 的路徑可能是 /usr/bin/docker-compose/usr/local/bin/docker-compose,請用 which docker-compose 確認。

這樣做的好處是什麼?

  1. 開機自啟:執行 sudo systemctl enable my-docker-compose-app.service,整個應用集群就會在伺服器啟動時自動運行。
  2. 終極守護:如果 Docker Engine 崩潰後重啟,systemd 會因為依賴關係(RequiresAfter)自動地重新啟動 my-docker-compose-app 服務。
  3. 統一管理介面:可以像管理普通系統服務一樣管理 Docker 應用,使用 systemctl start, systemctl stop, systemctl status
  4. 集中日誌docker-compose 的所有輸出(包含所有容器的 log)都會被 journald 捕獲。只需執行 sudo journalctl -u my-docker-compose-app.service -f 就可以查看應用集群的所有日誌,極其方便!

結論

  • 要管理一個獨立的、非容器化的程式嗎?
    👉 直接使用 systemd,如上一篇文章所示。

  • 要打包一個含有多個組件、複雜依賴的應用程式,並確保它在任何地方都能一致地運行嗎?
    👉 使用 DockerDocker Compose 來進行封裝。

  • 希望將這個打包好的 Docker 應用,在生產環境的 Linux 伺服器上進行可靠、穩定、可維護的部署嗎?
    👉 將 Docker Compose 作為一個整體,用 systemd 來管理它。

systemdDocker 它們各自填補了現代化應用部署體系中不可或缺的一環。systemd 是穩固的地基,Docker Compose 是精美的建築。只有當地基穩固時,上面的建築才能屹立不倒。