撰寫 docker-compose.yaml 經常會遇到要使用 depends_on 的情況,確保某些特定的服務可以先啟動,例如 Web Application 經常要 depends_on 資料庫(database)服務。

但是 depends_on 指確保該 container 狀態會進到 running ,而非確保 container 內的服務會完整啟動結束。

However, for startup Compose does not wait until a container is “ready” (whatever that means for your particular application) - only until it’s running.

以 Web Application 與資料庫服務為例,在這種情況下,就會遇到資料庫服務的 container 已經進到 running ,但其實資料庫的程序(process)還沒完全啟動,進而導致 Web Application 服務連不到資料庫而失敗。

遇到這問題,建議可以使用 wait-for-it.sh 來解決!

wait-for-it.sh

wait-for-it.sh 是用 bash 所寫成的 script, 專門用以確定指定的 Host 與 Port 能夠連線,因此可以避免 depends_on 可能會遇到的問題。

當然,如果你所使用的 docker image 有內建的 sleep 指令可以使用的話,其實也可以簡單使用 sleep 等待個幾秒就好,但不見得所有的 docker image 內都有 sleep ,譬如 golang 的 image 就沒有 sleep 指令能夠使用,所以使用 wait-for-it.sh 會讓事情簡單許多。

wait-for-it.sh 可以在 vishnubob/wait-for-it 下載,並不需要額外安裝什麼軟體,唯一的要求只有 bash 。

最簡單的使用方法如下:

$ chmod +x ./wait-for-it.sh
$ ./wait-for-it.sh 127.0.0.1:9999 -t 5 -- echo "server is up"

上述的指令會負責確認本機(127.0.0.1)的 9999 通訊埠可以連線,如果超過 5 秒都無法連線就直接結束執行,如果能夠正常連線則直接列印 “server is up” 字串。

docker-compose.yaml 與 wait-for-it.sh 應用範例

接下來以實際範例解說如何將 wait-for-it.sh 應用至 docker-compose.yaml 中。

以下 docker-compose.yaml 中的 web 需要 db 先啟動完成:

version: "3"
services:
  web:
    container_name: "web"
    build:
      context: .
      dockerfile: Dockerfile
    depends_on:
      - db
    ports:
      - "5000:5000"
  db:
    image: "postgres:10"
    restart: always
    container_name: "db"
    ports:
      - "5432:5432"

那麼就必須在 web 的 Dockerfile 中加上 wait-for-it.sh ,讓 web 等待 db 能夠連線後再執行真正的服務,例如以下的 golang Dockerfile 最後 CMD 的部分:

FROM golang:1.12.12-buster
WORKDIR /go/app
COPY . .
RUN go build -o webapp main.go
EXPOSE 5000
CMD ["./wait-for-it.sh", "db:5432", "--", "./webapp"]

最後,除了 bash 寫成的 wait-for-it.sh 之外,如果是習慣使用 sh 的人,則可以使用 wait-for

以上, Happy Coding!

References

https://docs.docker.com/compose/startup-order/