はじめに
この実践的な実験(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 コンテナのライフサイクルは、いくつかの状態から構成されます。
- Created(作成済み): コンテナは作成されましたが、まだ開始されていません。
- Running(実行中): コンテナは定義されたプロセスを実行しています。
- Paused(一時停止): コンテナのプロセスは一時的に中断されています。
- Stopped(停止): コンテナは終了または停止しました。
- 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 コンテナがすぐに終了した理由は次のとおりです。
- Ubuntu イメージのデフォルトコマンドは
/bin/bashです。 -itフラグ(インタラクティブ、ターミナル)なしで実行すると、bash シェルへの入力がありません。- 入力がなく、実行する特定のコマンドがない場合、bash はすぐに終了します。
- メインプロセス(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 コンテナを実行し続けるには、次のいずれかを行う必要があります。
- インタラクティブセッションを提供する、または
- デフォルトのコマンドを長時間実行されるプロセスで上書きする
インタラクティブなアプローチを試してみましょう。
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
すぐに終了するコンテナを診断する場合は、次の重要な点を覚えておいてください。
- コンテナは、メインプロセスが完了すると終了します。
- ログと終了コードを確認して、停止した理由を理解します。
- コンテナを実行し続ける必要がある場合は、メインプロセスが終了しないことを確認します。
よくあるコンテナ終了の問題のトラブルシューティング
コンテナがすぐに終了する理由を理解したので、予期しないコンテナ終了の一般的な原因と、そのトラブルシューティング方法を探ってみましょう。
問題のあるコンテナの作成
コンテナが予期せず終了する簡単な状況を作成してみましょう。まず、テストファイル用のディレクトリを作成します。
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 スクリプトが提供されていない環境変数を期待していたため、コンテナは終了しました。
問題の診断
コンテナが予期せず終了した場合は、次のトラブルシューティング手順に従ってください。
- エラーかどうかを判断するために、終了コードを確認します。
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
- コンテナのログを調べます。
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...
コンテナはまだ終了しますが、今回はスクリプトが最後に到達し、正常に終了するためです。コンテナを実行し続ける場合は、無限ループまたは長時間実行されるプロセスを含めるようにスクリプトを変更する必要があります。
コンテナ終了の一般的な原因
コンテナが予期せず終了する可能性のある一般的な理由は次のとおりです。
- 不足している環境変数: 先ほど説明したとおり
- 依存関係の欠如: 欠落しているライブラリまたはシステムパッケージ
- 接続の失敗: データベースまたは他のサービスに到達できない
- 権限の問題: ファイルまたはリソースへのアクセス権限が不十分
- リソースの制限: コンテナがメモリまたは CPU を使い果たす
- アプリケーションのクラッシュ: アプリケーションコードのバグ
トラブルシューティングの手法
これらの問題ごとに、効果的なトラブルシューティングアプローチを次に示します。
- ログの確認: 常に
docker logsで最初にコンテナログを確認します。 - エントリポイントのオーバーライド:
docker run --entrypoint /bin/sh -it my-imageを使用してコンテナに入り、調査します。 - デバッグステートメントの追加: より多くのログを追加するようにアプリケーションを変更します。
- リソース使用量の確認:
docker statsを使用して、コンテナのリソース使用量を監視します。 - 環境の検査:
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 コンテナの信頼性が大幅に向上しました。
- デフォルト値を使用した正常なエラー処理
- 監視のためのコンテナヘルスチェック
- 自動回復のための適切な再起動ポリシー
- リソース枯渇を防ぐためのリソース制限
- より簡単なトラブルシューティングのための適切なロギング構成
これらの手法は、障害から回復し、問題が発生した場合に優れた可観測性を提供する、回復力のあるコンテナを作成するのに役立ちます。
まとめ
この実験では、Docker コンテナの終了に関する問題をトラブルシューティングして解決するための重要なスキルを学びました。
- Docker コンテナのライフサイクルと、メインプロセスが完了したときにコンテナが終了する理由の理解
- ログと終了コードを通じて、コンテナが即座に終了する一般的な原因の特定
- コンテナ化されたアプリケーションでの堅牢なエラー処理の実装
- アプリケーションの状態を監視するためのコンテナヘルスチェックの追加
- 障害からの自動回復のための再起動ポリシーの設定
- コンテナのクラッシュを防ぐための適切なリソース制限の設定
- より簡単なデバッグのための適切なロギング戦略の実装
これらのスキルは、信頼性の高い Docker コンテナデプロイメントの基盤を形成します。実際のシナリオで Docker を使い続けるにつれて、これらのトラブルシューティング手法は、安定した回復力のあるコンテナ化されたアプリケーションを維持するために非常に役立つことがわかるでしょう。
コンテナが即座に終了する最も一般的な原因は次のとおりです。
- メインプロセスがタスクを完了する(設計による)
- 環境変数または構成の欠落
- アプリケーションエラーまたは例外
- リソースの制約または接続の問題
この実験で取り上げた診断手法とベストプラクティスを適用することで、これらの問題を迅速に特定して解決し、Docker コンテナがどのような環境でも確実に実行されるようにすることができます。



