【Docker】Docker compose ~ depends_on ~

◾️はじめに

Docker compose の depends_on に関しては
知っていて使っていたが、これに関連したトラブルがあり
depends_onに関する知識の上積み、切り分け手法、解決方法など
ためになることが多かったので、メモ。

目次

【0】今回学べたこと
 1)切り分け手法
 2)depends_on / condition
 3)docker compose up --build
【1】depends_on
 1)depends_onの注意点
【2】トラブル:depends_onを指定したサービスが起動しない
 1)調査手法
 2)原因
 3)解決案を行う上での注意点
 4)解決案1:Healthチェックしてから起動させるBashを組む
 5)解決案2:depends_on / conditionを使う

【0】今回学べたこと

1)切り分け手法

Command Explanations
docker ps -a --all , -a: 全てのコンテナを表示(デフォルトは実行中のコンテナだけ表示)
docker compose logs [SERVICE]/docker logs [SERVICE] サービスのログ表示(-fだとリアルタイム表示)
docker compose build [SERVICE] サービスのビルド
* 以下の関連記事にも更新しておく

Docker ~ トラブルシュート方法 ~
https://dk521123.hatenablog.com/entry/2023/12/12/034018

2)depends_on / condition

depends_on:
  db:
    # ★注目
    condition: service_started

https://qiita.com/waniwaninowani/items/d43c4b8cea690a50581f

Value of condition Explanations
service_started 指定したサービスが起動したら起動
service_healthy 指定したサービスが起動し、更にhealthcheck が通れば起動
service_completed_successfully 依存サービスを開始する前に、依存関係が正常に終了していることを指定

https://docs.docker.jp/compose/compose-file/

3)docker compose up --build

# build → run で実行
docker compose up --build

# 注意点:docker compose up -d だけだと、
# Docker image構築まで行わないのでハマった

【1】depends_on

* サービス間の依存関係を指定し、コンテナ起動順序を制御
* e.g. DB -> Backend -> Frontend の順番で起動

1)depends_onの注意点

* depends_on のみだと、起動順序だけでヘルスチェックは行わない

【2】トラブル:depends_onを指定したサービスが起動しない

 以下のdocker-compose.yamlで、
docker compose up -d し全てAll greenだったのだが、
backendが起動できておらず、全然docker psに表示されない。

docker-compose.yaml (一部抜粋)
https://github.com/dk521123/job-info-extractor/blob/develop/docker-compose.yml

services:
  db:
    image: postgres:15
    container_name: postgres_db
    volumes:
      - volume_postgres:/var/lib/postgresql/data
    ports:
      - 5432:5432
    env_file:
      - .env
  backend:
    build: ./backend
    container_name: backend_app
    ports:
      - 8000:8000
    env_file:
      - .env
    depends_on:
      - db
volumes:
  volume_postgres:
    name: v_postgres

1)調査手法

[1] docker ps -a を実行し、全てのコンテナ状況を表示してみる

# --all , -a: 全てのコンテナを表示(デフォルトは実行中のコンテナだけ表示)
docker ps -a

...
276d899005a9 job-info-extractor-backend "uvicorn app.main:ap…" 22 seconds ago Exited (1) 16 seconds ago backend
# Exited (1) でどっかで落ちていることが分かった

[2] docker compose logs [SERVICE]で落ちている箇所を特定

# docker logs [SERVICE]
# docker compose logs [SERVICE]
docker compose logs backend

...
sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) connection to server at "db" (172.20.0.2), port 5432 failed: Connection refused Is the server running on that host and accepting TCP/IP connections? (Background on this error at: https://sqlalche.me/e/20/e3q8)
# => バックエンドが DB(PostgreSQL) に接続できずに落ちている

[3] docker compose build [SERVICE]で個別で実行できるか試す

# docker compose build [SERVICE]
docker compose build backend

# 再度、別途、backendのみ起動させると、問題なく起動する。
# (ってことは、DB接続情報に誤りがあるっと言ったことはなさそう)

2)原因

depends_on のみだと、起動順序だけでヘルスチェックは行なっておらず
BackendでDB接続しようとした際に、DBのサービスは起動しているが、
まだPostgreSQLサービス自体は起動していなかったため、エラーになり
立ち上がらないままになっていた。

3)解決案を行う上での注意点

* 解決案1・2どちらで行うにしても、「docker compose up --build」する
 => そうしないと変更が反映されない可能性があるので。そこでめちゃくちゃハマった

4)解決案1:Healthチェックしてから起動させるBashを組む

backend/Dockerfile

FROM python:3.13-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
    tesseract-ocr \
    tesseract-ocr-jpn \
    fonts-noto-cjk \
    poppler-utils \
    build-essential \
    gcc \
    libgl1 \
    # ★注目
    postgresql-client \
    && apt-get clean && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY . /app

RUN mkdir -p /app/uploads

RUN pip install --no-cache-dir -r requirements.txt

# ★注目
COPY ./entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

backend/entrypoint.sh

#!/bin/sh
set -e

: "${DB_HOST:=db}"
: "${DB_PORT:=5432}"
: "${POSTGRES_USER:=postgres}"
: "${WAIT_RETRIES:=60}"
: "${WAIT_SLEEP:=1}"

i=0
echo "Waiting for Postgres at ${DB_HOST}:${DB_PORT}..."
while [ $i -lt "$WAIT_RETRIES" ]; do
  # pg_isready でHealthチェックしてから起動させる
  #. => https://www.postgresql.jp/docs/9.4/app-pg-isready.html
  if pg_isready -h "$DB_HOST" -p "$DB_PORT" -U "$POSTGRES_USER" >/dev/null 2>&1; then
    echo "Postgres is ready"
    break
  fi
  i=$((i+1))
  echo "Postgres unavailable - sleeping ${WAIT_SLEEP}s ($i/$WAIT_RETRIES)"
  sleep "$WAIT_SLEEP"
done

if [ $i -ge "$WAIT_RETRIES" ]; then
  echo "Timed out waiting for Postgres" >&2
  exit 1
fi

exec uvicorn app.main:app --host 0.0.0.0 --port 8000

5)解決案2:depends_on / conditionを使う

docker-compose.yaml

services:
  db:
    image: postgres:15
    container_name: postgres_db
    volumes:
      - volume_postgres:/var/lib/postgresql/data
    ports:
      - 5432:5432
    env_file:
      - .env
    # ★注目
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      timeout: 3s
      retries: 5

  backend:
    build: ./backend
    container_name: backend_app
    ports:
      - 8000:8000
    env_file:
      - .env
    depends_on:
      db:
        # ★注目
        condition: service_healthy

関連記事

Docker compose ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2022/05/21/222910
Docker compose ~ 基本編 ~
https://dk521123.hatenablog.com/entry/2020/04/11/000000
Docker ~ トラブルシュート方法 ~
https://dk521123.hatenablog.com/entry/2023/12/12/034018
Docker によるトラブルシューティング
https://dk521123.hatenablog.com/entry/2017/09/24/162257