docker『Timeout Reached』の原因と対処法

docker『Timeout Reached』の原因と対処法

「Timeout Reached」は、docker やコンテナ内の処理が一定時間内に完了せず、クライアントやヘルスチェック・外部サービス側で時間切れになった状態を示す。レジストリへの接続、イメージの pull・ビルド、コンテナ内アプリの応答、コンテナ間通信、CI/CD からの操作など、タイムアウトの発生場所が多岐にわたるため、「どこで」「何を待っていて」時間切れになったのかを特定することが重要になる。

症状と発生条件を整理する

Timeout Reached とみなせる代表的なケースは次のようなもの。

・docker pull が一定時間で失敗し、レジストリアクセスでタイムアウト。

・docker build 中の apt / npm / pip / git clone がタイムアウト。

・docker run / docker compose up 後に、アプリにアクセスしても応答が返らずクライアント側がタイムアウト。

・healthcheck(Docker / Compose / Kubernetes)がタイムアウトし、コンテナが unhealthy 扱いになる。

・CI/CD から docker daemon への API 呼び出しがタイムアウト。

多くは「ネットワーク(DNS/プロキシ/FW)」か「アプリの処理遅延/ハング」が原因となり、稀に「リソース不足(CPU/IO)」や「タイムアウト値の設定ミス」も絡む。

ネットワーク・DNS が原因のタイムアウトを切り分ける

まずは単純なネットワーク障害やDNS問題を疑う。

# ホスト側からレジストリや外部サイトへの疎通を確認
ping -c 3 registry-1.docker.io || echo "ping NG"

# DNS 解決ができるか
nslookup registry-1.docker.io || dig registry-1.docker.io

# HTTPS ポート(443)に到達できるか
nc -vz registry-1.docker.io 443

・ここで疎通が取れない場合、docker 以前にネットワーク構成・DNS・プロキシ・FWを見直す必要がある。

・企業ネットワークやVPN配下では、HTTP/HTTPSの outbound が制限されていることも多い。

レジストリへの接続タイムアウト(docker pull / push)

docker pull / push 中の Timeout Reached は、レジストリへの接続が遅い・切れる・ブロックされている可能性が高い。

# 詳細ログを有効にして pull する例
DOCKER_CLI_HINTS=false docker -D pull nginx:1.25

# プロキシ環境での設定例(Linux)
export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080
export NO_PROXY=localhost,127.0.0.1,registry.local
docker pull registry.local/myorg/app:1.0

・レジストリが社内(オンプレ/クラウド)にある場合、セキュリティグループやFWでホスト→レジストリ間の TCP:443/5000 などが開いているか確認する。

・時間帯による輻輳や帯域制限で pull が極端に遅くなりタイムアウトすることもあるため、ミラーやキャッシュレジストリの導入も選択肢になる。

コンテナ内アプリの応答遅延・ハングによるタイムアウト

コンテナ自体は起動しているが、アプリが重くて応答しない/ハングしていると、クライアント側のHTTPタイムアウトや healthcheck のタイムアウトが発生する。

# コンテナログでアプリの状況を確認
docker logs <container> --tail=200

# コンテナ内から自身のHTTPエンドポイントを叩いてみる
docker exec -it <container> sh -lc "curl -v http://127.0.0.1:8080/health || echo 'ng'"

Dockerfile や Compose で healthcheck を設定している場合、その timeout / interval / retries が短すぎると、起動の遅いアプリがすぐに unhealthy 扱いになる。

# docker-compose.yml の healthcheck 例(タイムアウト緩和)
services:
  app:
    image: myorg/app:1.0
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 5
      start_period: 40s

・start_period を長めに取り、初期化に時間がかかるアプリを待つようにすると、無用な再起動を避けられる。

docker build 中のタイムアウト(apt / npm / pip / git 等)

イメージビルド中に apt-get や npm install、git clone がタイムアウトする場合、ビルドコンテキスト内から外部ネットワークへ出られていない、あるいはミラーが遅い。

# Dockerfile 内での一時デバッグ:curl/wgetで疎通確認
RUN apk add --no-cache curl && \
    curl -v https://example.com/health || (echo "network NG" && exit 1)

プロキシ環境では、ビルド時にも環境変数を渡す必要がある。

# docker build にプロキシを渡す例
docker build \
  --build-arg HTTP_PROXY=http://proxy.example.com:8080 \
  --build-arg HTTPS_PROXY=http://proxy.example.com:8080 \
  -t myorg/app:build .

# Dockerfile
ARG HTTP_PROXY
ARG HTTPS_PROXY
ENV HTTP_PROXY=${HTTP_PROXY} HTTPS_PROXY=${HTTPS_PROXY}

・タイムアウトするミラーを使っている場合は、公式ミラー / 地域の近いミラーに切り替える。

docker client / compose / API のタイムアウト

CI/CD などから docker daemon に対して API を呼び出している場合、ビルドや pull に時間がかかりすぎるとクライアント側のタイムアウトに達することがある。

例えば GitLab Runner / GitHub Actions / Jenkins などでは、ジョブやステップごとに timeout を持つ。

# 例: GitHub Actions での timeout-minutes 設定
jobs:
  build:
    timeout-minutes: 30
    steps:
      - name: Build image
        run: docker build -t myorg/app:${GITHUB_SHA} .

・CIのタイムアウト値を現実的な範囲に伸ばしつつ、ビルドキャッシュや multi-stage build でビルド時間自体も短縮しておくと安定する。

コンテナ間通信・外部サービスへの接続でのタイムアウト

アプリが別コンテナ(DB, API, キャッシュ等)や外部サービスに接続するとき、接続先が遅い/到達不可でアプリ側の接続タイムアウトに達するパターン。

# コンテナ内からターゲットへの疎通とポートを確認
docker exec -it app sh -lc "
  ping -c1 db || echo 'ping NG';
  nc -vz db 5432 || echo 'db port NG';
"

# 接続先を環境変数で管理する例
services:
  app:
    environment:
      - DB_HOST=db
      - DB_PORT=5432

・DNS名やポート番号が間違っている場合も、「いつまで待っても繋がらない」→タイムアウトとして現れる。

・アプリ側で接続タイムアウトを適切に設定し、失敗時にはリトライ回数や間隔を調整することも重要。

リソース不足(CPU/IO)による処理遅延がタイムアウトを誘発する

ネットワークではなく、ホストやコンテナのリソース不足で処理が極端に遅くなり、その結果タイムアウトに達するパターンもある。

# リソース状況を確認
docker stats --no-stream
iostat -xz 1 5 || vmstat 1 5

# コンテナに付与しているCPU/メモリ制限
docker inspect <container> \
  --format '{{json .HostConfig.NanoCpus}} {{json .HostConfig.Memory}}'

docker-compose.yml や Kubernetes で CPU / メモリ制限を厳しく設定しすぎていると、GCや重いクエリが処理しきれずレスポンスが遅くなり、HTTPやDB接続のタイムアウトに発展する。

タイムアウト値を調整するときの指針

タイムアウト値そのものを伸ばすのは最終手段であり、まずは根本原因(遅さ・到達不可)を改善することが前提になる。とはいえ、現実には適切な値に調整することも必要。

・HTTPクライアント:接続タイムアウト(connectTimeout)と読み込みタイムアウト(readTimeout)を別々に設定。

・healthcheck:起動時間を見積もり、start_period や timeout / retries のバランスを取る。

・CIジョブ:平均ビルド時間+余裕(2〜3倍程度)を timeout とし、それ以上かかった場合は明確に異常と扱う。

// Node.js axios の例
const axiosInstance = axios.create({
  timeout: 10000, // 10秒
});

// Go http.Client の例
client := &http.Client{
  Timeout: 10 * time.Second,
}

デバッグ用ワンライナーで原因を可視化する

タイムアウトが起きている場所を絞り込むための、簡単なワンライナーをいくつか置いておく。

# 1) コンテナ内から外部に出られるか
docker exec -it <container> sh -lc "
  echo '--- ping google ---';
  ping -c1 8.8.8.8 || echo 'ping NG';
  echo '--- DNS ---';
  nslookup google.com || echo 'dns NG';
"

# 2) レジストリへのHTTP(S)疎通
curl -v https://registry-1.docker.io/v2/ || echo 'registry NG'

# 3) アプリの内部ヘルスエンドポイント
docker exec -it <container> sh -lc "curl -v http://localhost:8080/health || echo 'health NG'"

・これらが成功するかどうかで、「ネットワークか」「アプリか」「FWか」の切り分けがしやすくなる。

NG→OK 早見表(よくあるパターンと修正例)

# 1) docker pull が毎回タイムアウト
# NG: プロキシ/Firewall設定なしで企業ネットワークから直接pull
docker pull nginx:latest

# OK: プロキシやNO_PROXYを正しく設定
export HTTPS_PROXY=http://proxy:8080
export NO_PROXY=localhost,127.0.0.1,registry.local
docker pull registry.local/myorg/app:1.0

# 2) healthcheck が短すぎて起動が間に合わない
# NG:
healthcheck:
  test: ["CMD","curl","-f","http://localhost:8080/health"]
  interval: 5s
  timeout: 2s
  retries: 2

# OK:
healthcheck:
  test: ["CMD-SHELL","curl -f http://localhost:8080/health || exit 1"]
  interval: 30s
  timeout: 5s
  retries: 5
  start_period: 40s

# 3) docker build 中に apt がタイムアウト
# NG:
RUN apt-get update && apt-get install -y build-essential

# OK: 近いミラーを使う・リトライを入れる・プロキシ設定を渡す
RUN sed -i 's|archive.ubuntu.com|jp.archive.ubuntu.com|g' /etc/apt/sources.list && \
    apt-get update && \
    apt-get install -y build-essential

# 4) コンテナ間通信でDNS名ミス→タイムアウト
# NG:
DATABASE_URL=postgres://user:pass@localhost:5432/app  # コンテナ内でlocalhost

# OK:
DATABASE_URL=postgres://user:pass@db:5432/app        # サービス名を指定

チェックリスト(上から順に潰す)


1) タイムアウトが発生しているのは「どの操作」か(pull/build/appアクセス/healthcheck/CIなど)を特定したか
2) ホスト/コンテナからのネットワーク疎通・DNS・プロキシ設定を確認したか
3) レジストリや外部サービスのFW/セキュリティグループが適切に開いているか
4) コンテナ内アプリが重過ぎたりハングしていないか(ログ・CPU・メモリ・スタックを確認したか)
5) build 時の apt/npm/pip/git が正しいミラー・URLを向いているか
6) コンテナ間通信で、正しいホスト名とポートを使っているか(localhostを誤用していないか)
7) CPU/メモリ/IO制限が厳しすぎて処理が遅延していないか
8) 各種タイムアウト値(healthcheck, HTTP client, CIジョブなど)が現実的な値に設定されているか