我先講結論:Docker Compose 的 depends_on
只能控制容器的執行順序,開完一個容器就會開下一個,不會等到容器正常執行才開下一個容器。導致單純使用 depends_on
時,就好像蒙面走鋼索。假設 B 容器相依於 A 容器提供的服務,要是 B 容器在 A 容器還沒執行正常時就開始,B 容器就無法正確初始化。
前幾天,我要用 Docker Compose 裝 Bookstack,使用 LinuxServer.io 的 image。如果使用範例的 docker-compose.yml
,搭配他們家的 MariaDB image 可以正常運作,但是換成官方的 MariaDB image 卻無法正常運作。
看 log 有 connection refused 還以為是網路問題。花了一些時間才發現原因在於 MariaDB container 因為 bug 造成初始化時間很長。Bookstack container 在進行 php artisan migrate
時,資料庫尚未初始化完成,所以產生錯誤。
發現同樣是 MariaDB image,有些初始化時間長到造成錯誤,有些卻不會。所以做了一個實驗看看各個 MariaDB image 初始化需要的時間(以第一則到最後一則 log 的時間計算):
- mariadb:4 分 09 秒
- mariadb/server:3 分 54 秒
- bitnami/mariadb:10 秒
- linuxserver/mariadb:12 秒
- demyx/mariadb:看不出來,應該也是很短
雖然在這邊只要用初始化時間短的 image 就沒問題,不過在其他狀況下,如何確保容器正常執行後,再開與它相依的容器呢?
- 放在不同的
docker-compose.yml
,分別啓動 - 用
healthcheck
搭配depends_on
version: "2.1"
services: bookstack: image: linuxserver/bookstack container_name: bookstack environment: - PUID=1000 - PGID=1000 - DB_HOST=bookstack_db - DB_USER=bookstack - DB_PASS=yourdbpass - DB_DATABASE=bookstackapp restart: unless-stopped depends_on: bookstack_db: condition: service_healthy bookstack_db: image: mariadb container_name: bookstack_db environment: - MYSQL_ROOT_PASSWORD=yourdbpass - MYSQL_DATABASE=bookstackapp - MYSQL_USER=bookstack - MYSQL_PASSWORD=yourdbpass healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 30s timeout: 10s retries: 10 restart: unless-stopped
- 用
wait-for-it
之類的工具
覺得第一種方法比較省事,有需要再考慮第二種跟第三種。
好,講結論。使用 Docker Compose 時,如果有 docker container 要在另一個 docker container 完成初始化、正常執行後才啓動的話,不能只依賴 depends_on
選項,必須要用其他方式才能確保這些服務都正常運作。