Docker コンテナが即座に終了する問題のトラブルシューティング

DockerBeginner
オンラインで実践に進む

はじめに

この実践的な実験(Lab)では、Docker の一般的な問題である、起動直後に終了してしまうコンテナを特定し、解決する方法を学びます。この問題は、初心者の方々を混乱させることが多く、設定エラーからアプリケーションの問題まで、さまざまな理由で発生する可能性があります。

この実験を完了することで、Docker コンテナのライフサイクルを理解し、コンテナが即座に終了する原因を診断し、デバッグ技術を習得し、コンテナを確実に実行するためのベストプラクティスを実装できるようになります。これらのスキルは、開発環境または本番環境で Docker を使用するすべての人にとって不可欠です。

Docker コンテナの基本を理解する

Docker コンテナの基礎を探求し、コンテナの管理に使用される基本的なコマンドに慣れることから始めましょう。

Docker コンテナとは?

Docker コンテナは、アプリケーションを実行するために必要なすべてを含む、軽量でスタンドアロンで実行可能なソフトウェアパッケージです。

  • コード
  • ランタイム(Runtime)
  • システムツール
  • ライブラリ
  • 設定

コンテナは、ホストシステムや他のコンテナから分離して実行され、さまざまな環境間で一貫性を提供します。

システムでの Docker の探索

まず、Docker がシステムに正しくインストールされ、実行されていることを確認しましょう。

docker --version

次のような出力が表示されるはずです。

Docker version 20.10.21, build baeda1f

次に、現在実行中のコンテナがあるかどうかを確認しましょう。

docker ps

このコマンドは、実行中のコンテナを一覧表示します。まだ何も開始していないため、列ヘッダーのみが表示されるはずです。

CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

停止したコンテナを含むすべてのコンテナを表示するには、次のようにします。

docker ps -a

コンテナのライフサイクルを理解する

Docker コンテナのライフサイクルは、いくつかの状態から構成されます。

  1. Created(作成済み): コンテナは作成されましたが、まだ開始されていません。
  2. Running(実行中): コンテナは定義されたプロセスを実行しています。
  3. Paused(一時停止): コンテナのプロセスは一時的に中断されています。
  4. Stopped(停止): コンテナは終了または停止しました。
  5. Deleted(削除済み): コンテナはシステムから削除されました。

簡単なコンテナを実行し、そのライフサイクルを観察してみましょう。

docker run hello-world

次のような出力が表示されるはずです。

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.
...

このコンテナは、メッセージを表示した後、すぐに実行を終了したことに注意してください。これは、メッセージを表示して終了するように設計されているため、hello-world コンテナにとっては実際には正常な動作です。

すべてのコンテナのリストでコンテナを確認します。

docker ps -a

hello-world コンテナが「Exited(終了)」ステータスでリストに表示されるはずです。

CONTAINER ID   IMAGE         COMMAND    CREATED          STATUS                      PORTS     NAMES
a1b2c3d4e5f6   hello-world   "/hello"   30 seconds ago   Exited (0) 30 seconds ago             peaceful_hopper

終了コード (0) は、コンテナがエラーなしで正常に終了したことを示しています。

コンテナ管理のための主要な Docker コマンド

この実験全体で使用するいくつかの重要な Docker コマンドを以下に示します。

  • docker run [OPTIONS] IMAGE [COMMAND]: コンテナを作成して開始します。
  • docker ps: 実行中のコンテナを一覧表示します。
  • docker ps -a: すべてのコンテナ(停止したものを含む)を一覧表示します。
  • docker logs [CONTAINER_ID]: コンテナのログを表示します。
  • docker inspect [CONTAINER_ID]: 詳細なコンテナ情報を取得します。
  • docker exec -it [CONTAINER_ID] [COMMAND]: 実行中のコンテナでコマンドを実行します。
  • docker stop [CONTAINER_ID]: 実行中のコンテナを停止します。
  • docker rm [CONTAINER_ID]: コンテナを削除します。

Docker コンテナの基本とそのライフサイクルを理解したところで、予期せず終了するコンテナのトラブルシューティングに進みます。

コンテナ終了の問題を特定する

このステップでは、すぐに終了する Docker コンテナを作成し、問題の診断方法を学びます。

すぐに終了するコンテナの実行

まず、Ubuntu コンテナを実行してみましょう。

docker run ubuntu

興味深いことに気づくでしょう。コマンドはすぐに完了し、プロンプトに戻ります。Ubuntu コンテナはどこにあるのでしょうか?確認してみましょう。

docker ps

実行中のコンテナはありません。次に、停止したものを含むすべてのコンテナを確認します。

docker ps -a

次のような出力が表示されます。

CONTAINER ID   IMAGE         COMMAND       CREATED          STATUS                      PORTS     NAMES
f7d9e7f6543d   ubuntu        "/bin/bash"   10 seconds ago   Exited (0) 10 seconds ago             focused_galileo
a1b2c3d4e5f6   hello-world   "/hello"      10 minutes ago   Exited (0) 10 minutes ago             peaceful_hopper

Ubuntu コンテナは起動し、ステータスコード 0 で即座に終了しました。これはエラーなしで終了したことを示しています。これは期待される動作ですが、初心者には混乱を招く可能性があります。

コンテナが終了する理由を理解する

コンテナは、特定のコマンドまたはプロセスを実行するように設計されています。そのプロセスが完了または終了すると、コンテナは停止します。これは、Docker の設計の基本的な原則です。

Ubuntu コンテナがすぐに終了した理由は次のとおりです。

  1. Ubuntu イメージのデフォルトコマンドは /bin/bash です。
  2. -it フラグ(インタラクティブ、ターミナル)なしで実行すると、bash シェルへの入力がありません。
  3. 入力がなく、実行する特定のコマンドがない場合、bash はすぐに終了します。
  4. メインプロセス(bash)が終了すると、コンテナは停止します。

コンテナのログと情報の表示

終了した Ubuntu コンテナのログを調べて、何が起こったのかを理解しましょう。まず、docker ps -a の出力からコンテナ ID を見つけます。次に、次のようにします。

docker logs CONTAINER_ID

CONTAINER_ID を実際のコンテナ ID に置き換えます。コンテナが終了前にログを生成しなかったため、出力が表示されない可能性があります。

コンテナに関する詳細な情報については、次のようにします。

docker inspect CONTAINER_ID

これにより、コンテナの設定と状態情報を含む大きな JSON オブジェクトが表示されます。

終了コードに注目してみましょう。

docker inspect CONTAINER_ID --format='{{.State.ExitCode}}'

次のように表示されるはずです。

0

これにより、コンテナがエラーではなく、正常に終了したことが確認されます。

コンテナを実行し続ける

Ubuntu コンテナを実行し続けるには、次のいずれかを行う必要があります。

  1. インタラクティブセッションを提供する、または
  2. デフォルトのコマンドを長時間実行されるプロセスで上書きする

インタラクティブなアプローチを試してみましょう。

docker run -it ubuntu

これで、bash プロンプトでコンテナ内に入ります。

root@3a4b5c6d7e8f:/#

この bash セッションがアクティブである限り、コンテナは実行されたままになります。コンテナを終了するには、exit と入力するか、Ctrl+D を押します。

exit

または、すぐに完了しないコマンドを提供することで、コンテナを実行し続けることができます。

docker run -d ubuntu sleep 300

これにより、Ubuntu コンテナが実行され、sleep 300 コマンドが実行されます。これにより、コンテナは 300 秒間(5 分間)実行されたままになります。

コンテナが実行されていることを確認します。

docker ps

実行中の状態でコンテナが表示されるはずです。

CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS          PORTS     NAMES
9a8b7c6d5e4f   ubuntu    "sleep 300"   10 seconds ago   Up 10 seconds             hopeful_hopper

すぐに終了するコンテナを診断する場合は、次の重要な点を覚えておいてください。

  1. コンテナは、メインプロセスが完了すると終了します。
  2. ログと終了コードを確認して、停止した理由を理解します。
  3. コンテナを実行し続ける必要がある場合は、メインプロセスが終了しないことを確認します。

よくあるコンテナ終了の問題のトラブルシューティング

コンテナがすぐに終了する理由を理解したので、予期しないコンテナ終了の一般的な原因と、そのトラブルシューティング方法を探ってみましょう。

問題のあるコンテナの作成

コンテナが予期せず終了する簡単な状況を作成してみましょう。まず、テストファイル用のディレクトリを作成します。

mkdir -p ~/project/docker-exit-test
cd ~/project/docker-exit-test

次に、エラーのある簡単な Python スクリプトを作成します。

nano app.py

次のコードを追加します。

import os

## 必要な環境変数を読み込もうとします
database_url = os.environ['DATABASE_URL']

print(f"Connecting to database: {database_url}")
print("Application running...")

## アプリケーションコードの残りの部分はここに入ります

ファイルを保存して終了します(Ctrl+O、Enter、Ctrl+X を押します)。

次に、このアプリケーションでイメージをビルドするための Dockerfile を作成します。

nano Dockerfile

次の内容を追加します。

FROM python:3.9-slim

WORKDIR /app

COPY app.py .

CMD ["python", "app.py"]

ファイルを保存して終了します。

Docker イメージをビルドします。

docker build -t exit-test-app .

イメージが正常にビルドされたことを示す出力が表示されるはずです。

Successfully built a1b2c3d4e5f6
Successfully tagged exit-test-app:latest

次に、コンテナを実行します。

docker run exit-test-app

コンテナがエラーで即座に終了するのがわかるはずです。

Traceback (most recent call last):
  File "/app/app.py", line 4, in <module>
    database_url = os.environ['DATABASE_URL']
  File "/usr/local/lib/python3.9/os.py", line 679, in __getitem__
    raise KeyError(key) from None
KeyError: 'DATABASE_URL'

Python スクリプトが提供されていない環境変数を期待していたため、コンテナは終了しました。

問題の診断

コンテナが予期せず終了した場合は、次のトラブルシューティング手順に従ってください。

  1. エラーかどうかを判断するために、終了コードを確認します。
docker ps -a

コンテナを探し、終了コードをメモします。エラーを示す非ゼロであるはずです。

CONTAINER ID   IMAGE           COMMAND            CREATED          STATUS                     PORTS     NAMES
b1c2d3e4f5g6   exit-test-app   "python app.py"   20 seconds ago   Exited (1) 19 seconds ago           vigilant_galileo
  1. コンテナのログを調べます。
docker logs $(docker ps -a -q --filter ancestor=exit-test-app --latest)

これにより、イメージから作成された最新のコンテナからログが取得されます。

エラーメッセージは問題を明確に示しています。Python スクリプトは、存在しない DATABASE_URL という名前の環境変数にアクセスしようとしています。

問題の修正

次に、不足している環境変数を提供して問題を修正しましょう。

docker run -e DATABASE_URL=postgresql://user:password@db:5432/mydatabase exit-test-app

これで、コンテナが正常に実行されるのがわかるはずです。

Connecting to database: postgresql://user:password@db:5432/mydatabase
Application running...

コンテナはまだ終了しますが、今回はスクリプトが最後に到達し、正常に終了するためです。コンテナを実行し続ける場合は、無限ループまたは長時間実行されるプロセスを含めるようにスクリプトを変更する必要があります。

コンテナ終了の一般的な原因

コンテナが予期せず終了する可能性のある一般的な理由は次のとおりです。

  1. 不足している環境変数: 先ほど説明したとおり
  2. 依存関係の欠如: 欠落しているライブラリまたはシステムパッケージ
  3. 接続の失敗: データベースまたは他のサービスに到達できない
  4. 権限の問題: ファイルまたはリソースへのアクセス権限が不十分
  5. リソースの制限: コンテナがメモリまたは CPU を使い果たす
  6. アプリケーションのクラッシュ: アプリケーションコードのバグ

トラブルシューティングの手法

これらの問題ごとに、効果的なトラブルシューティングアプローチを次に示します。

  1. ログの確認: 常に docker logs で最初にコンテナログを確認します。
  2. エントリポイントのオーバーライド: docker run --entrypoint /bin/sh -it my-image を使用してコンテナに入り、調査します。
  3. デバッグステートメントの追加: より多くのログを追加するようにアプリケーションを変更します。
  4. リソース使用量の確認: docker stats を使用して、コンテナのリソース使用量を監視します。
  5. 環境の検査: docker exec -it CONTAINER_ID env を実行して、環境変数を検証します。

イメージでエントリポイントのオーバーライド手法を試してみましょう。

docker run --entrypoint /bin/sh -it exit-test-app

これで、シェルでコンテナ内に入ります。環境を調べることができます。

ls -la
cat app.py
echo $DATABASE_URL

DATABASE_URL が設定されていないことがわかります。完了したら、コンテナを終了します。

exit

コンテナが終了する理由を理解し、これらのトラブルシューティング手法を適用することで、ほとんどのコンテナ終了の問題を迅速に診断して解決できます。

コンテナの信頼性のためのベストプラクティスの実装

コンテナの終了のトラブルシューティング方法を理解したので、コンテナをより信頼性が高く堅牢にするためのベストプラクティスを実装しましょう。エラー処理、ヘルスチェック、およびロギングを適切に実行して、サンプルアプリケーションを強化します。

1. エラー処理の改善

Python アプリケーションを変更して、不足している環境変数を適切に処理するようにしましょう。

cd ~/project/docker-exit-test
nano app.py

内容を以下のように更新します。

import os
import time
import sys

## デフォルト値を持つ環境変数を取得します
database_url = os.environ.get('DATABASE_URL', 'sqlite:///default.db')

print(f"Connecting to database: {database_url}")

## 長時間実行されるプロセスをシミュレートします
try:
    print("Application running... Press Ctrl+C to exit")
    counter = 0
    while True:
        counter += 1
        print(f"Application heartbeat: {counter}")
        time.sleep(10)
except KeyboardInterrupt:
    print("Application shutting down gracefully...")
    sys.exit(0)

ファイルを保存して終了します。

この改善されたバージョンは次のとおりです。

  • 例外を発生させる代わりに、デフォルト値を持つ os.environ.get() を使用します。
  • コンテナをアクティブに保つために、長時間実行されるループを実装します。
  • 終了時に正常なシャットダウンを処理します。

イメージを再構築しましょう。

docker build -t exit-test-app:v2 .

改善されたコンテナを実行します。

docker run -d --name improved-app exit-test-app:v2

コンテナが実行されていることを確認します。

docker ps

コンテナが実行されているのがわかるはずです。

CONTAINER ID   IMAGE              COMMAND            CREATED          STATUS          PORTS     NAMES
c1d2e3f4g5h6   exit-test-app:v2   "python app.py"   10 seconds ago   Up 10 seconds             improved-app

ログを表示して、動作していることを確認します。

docker logs improved-app

次のような出力が表示されるはずです。

Connecting to database: sqlite:///default.db
Application running... Press Ctrl+C to exit
Application heartbeat: 1
Application heartbeat: 2

2. Dockerfile でのヘルスチェックの実装

ヘルスチェックにより、Docker はコンテナのヘルス状態を監視できます。ヘルスチェックを含めるように Dockerfile を更新しましょう。

nano Dockerfile

以下のように更新します。

FROM python:3.9-slim

WORKDIR /app

## ヘルスチェックのために curl をインストールします
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

COPY app.py .

## ヘルスチェックを追加します
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

## ヘルスチェックエンドポイントのポートを公開します
EXPOSE 8080

CMD ["python", "app.py"]

次に、アプリケーションにヘルスチェックエンドポイントを追加する必要があります。

nano app.py

内容を以下のように置き換えます。

import os
import time
import sys
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler

## デフォルト値を持つ環境変数を取得します
database_url = os.environ.get('DATABASE_URL', 'sqlite:///default.db')

## ヘルスチェック用のシンプルな HTTP サーバー
class HealthRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/health':
            self.send_response(200)
            self.send_header('Content-type', 'text/plain')
            self.end_headers()
            self.wfile.write(b'OK')
        else:
            self.send_response(404)
            self.end_headers()

def run_health_server():
    server = HTTPServer(('0.0.0.0', 8080), HealthRequestHandler)
    print("Starting health check server on port 8080")
    server.serve_forever()

## ヘルスチェックサーバーを別のスレッドで起動します
health_thread = threading.Thread(target=run_health_server, daemon=True)
health_thread.start()

print(f"Connecting to database: {database_url}")

## メインアプリケーションループ
try:
    print("Application running... Press Ctrl+C to exit")
    counter = 0
    while True:
        counter += 1
        print(f"Application heartbeat: {counter}")
        time.sleep(10)
except KeyboardInterrupt:
    print("Application shutting down gracefully...")
    sys.exit(0)

ファイルを保存して終了します。

ヘルスチェックを使用してイメージを再構築します。

docker build -t exit-test-app:v3 .

新しいバージョンでコンテナを実行します。

docker run -d --name healthcheck-app -p 8080:8080 exit-test-app:v3

約 30 秒後、ヘルスステータスを確認します。

docker inspect --format='{{.State.Health.Status}}' healthcheck-app

次のように表示されるはずです。

healthy

ヘルスエンドポイントを直接テストすることもできます。

curl http://localhost:8080/health

これにより、OK が返されるはずです。

3. Docker の再起動ポリシーの使用

Docker は、コンテナが終了またはエラーが発生した場合に、コンテナを自動的に再起動するための再起動ポリシーを提供します。

docker run -d --restart=on-failure:5 --name restart-app exit-test-app:v3

このポリシーは、ゼロ以外のコードで終了した場合、コンテナを最大 5 回再起動します。

利用可能な再起動ポリシー:

  • no: 再起動しない (デフォルト)
  • always: 終了ステータスに関係なく常に再起動
  • unless-stopped: 手動で停止しない限り常に再起動
  • on-failure[:max-retries]: ゼロ以外の終了でのみ再起動

4. リソース制限の設定

リソース枯渇によるコンテナのクラッシュを防ぐために、適切なリソース制限を設定します。

docker run -d --name resource-limited-app \
  --memory=256m \
  --cpus=0.5 \
  exit-test-app:v3

これにより、コンテナは 256MB のメモリと CPU コアの半分に制限されます。

5. 適切なロギング構成

より良いデバッグのために、構造化されたログを出力するようにアプリケーションを構成します。

docker run -d --name logging-app \
  --log-driver=json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  exit-test-app:v3

これにより、ログローテーション(最大 3 つのファイル、それぞれ 10MB)を使用して、JSON ログドライバーを使用するようにコンテナが構成されます。

ベストプラクティスの概要

これらのベストプラクティスを実装することにより、Docker コンテナの信頼性が大幅に向上しました。

  1. デフォルト値を使用した正常なエラー処理
  2. 監視のためのコンテナヘルスチェック
  3. 自動回復のための適切な再起動ポリシー
  4. リソース枯渇を防ぐためのリソース制限
  5. より簡単なトラブルシューティングのための適切なロギング構成

これらの手法は、障害から回復し、問題が発生した場合に優れた可観測性を提供する、回復力のあるコンテナを作成するのに役立ちます。

まとめ

この実験では、Docker コンテナの終了に関する問題をトラブルシューティングして解決するための重要なスキルを学びました。

  • Docker コンテナのライフサイクルと、メインプロセスが完了したときにコンテナが終了する理由の理解
  • ログと終了コードを通じて、コンテナが即座に終了する一般的な原因の特定
  • コンテナ化されたアプリケーションでの堅牢なエラー処理の実装
  • アプリケーションの状態を監視するためのコンテナヘルスチェックの追加
  • 障害からの自動回復のための再起動ポリシーの設定
  • コンテナのクラッシュを防ぐための適切なリソース制限の設定
  • より簡単なデバッグのための適切なロギング戦略の実装

これらのスキルは、信頼性の高い Docker コンテナデプロイメントの基盤を形成します。実際のシナリオで Docker を使い続けるにつれて、これらのトラブルシューティング手法は、安定した回復力のあるコンテナ化されたアプリケーションを維持するために非常に役立つことがわかるでしょう。

コンテナが即座に終了する最も一般的な原因は次のとおりです。

  1. メインプロセスがタスクを完了する(設計による)
  2. 環境変数または構成の欠落
  3. アプリケーションエラーまたは例外
  4. リソースの制約または接続の問題

この実験で取り上げた診断手法とベストプラクティスを適用することで、これらの問題を迅速に特定して解決し、Docker コンテナがどのような環境でも確実に実行されるようにすることができます。