「さっきまであったのに消えてしまう」コンテナ
docker run を実行すると、ターミナルにコンテナIDが表示され、一見すべてが正常に見えます。しかし、数秒後にはサービスにアクセスできなくなります。docker ps を実行してもリストは空のまま。最終的に docker ps -a を実行してようやく真実が判明します。コンテナは生成された瞬間に終了していたのです。
# ターミナルでの表示結果
CONTAINER ID IMAGE COMMAND STATUS NAMES
7a1b2c3d4e5f my-app:latest "docker-entrypoint.s…" Exited (0) 5 seconds ago my-app-container
Dockerコンテナは、メインプロセス(PID 1)が実行されている間だけ存続します。そのプロセスがタスクを完了するか、問題が発生してクラッシュすると、コンテナは停止します。これはバグではなく、Dockerの設計通りの動作です。ここでは、最も一般的な原因の診断方法と修正方法について説明します。
ステップ 1:ログから状況を把握する
むやみにコードを書き換えないでください。コンテナが「Exited(終了)」状態であっても、通常DockerはSTDOUT(標準出力)とSTDERR(標準エラー出力)のストリームを保持しています。まずは、出力の最後の20〜50行を確認することから始めましょう。
docker logs --tail 50 <container_id_or_name>
スタックトレース、「Permission denied(アクセス拒否)」、設定ファイルの不足といった具体的な手がかりを探します。ログが完全に空で Exited (0) と表示されている場合は、コンテナが短いタスク(ls や短いスクリプトなど)を実行し、他にやることがなくなったために終了した可能性があります。
ステップ 2:インタラクティブなコンテナを維持する
ubuntu、alpine、debian などのベースイメージは、シェルを提供するために構築されています。これらをインタラクティブなターミナルなしで実行すると、シェルは入力がないと判断し、リソースを節約するために即座に終了します。
単発実行時のクイックフィックス
-it フラグを使用します。これにより、Dockerに対して標準入力を開き続け、擬似ターミナル(pseudo-TTY)を割り当てるよう指示します:
docker run -it ubuntu /bin/bash
Docker Composeでの解決策
開発環境の構築にComposeを使用している場合は、サービス定義に以下の2行を追加します。これは -it の動作を模倣し、コンテナがセッションを閉じるのを防ぎます。
services:
app:
image: ubuntu
tty: true
stdin_open: true
「無限ループ」のトリック
後で exec で中に入るために、コンテナをバックグラウンドで起動したままにしておきたい場合があります。その場合は、デフォルトのコマンドを終了しないプロセスで上書きします:
services:
app:
image: alpine
command: tail -f /dev/null
ステップ 3:終了コード 1(アプリのクラッシュ)のトラブルシューティング
Exited (1) ステータスは、アプリケーションが実行を試みたものの、致命的なエラーが発生したことを意味します。約90%のケースで、原因は次の3つのいずれかです:
- 環境変数の不足: アプリが
DATABASE_URLを期待しているが、environmentブロックで定義されていない。 - ボリュームの権限: ローカルフォルダーを
/var/log/appにマウントしたが、コンテナユーザーがホストマシンへの書き込み権限を持っていない。 - 不適切な WORKDIR:
CMDがルートディレクトリでindex.jsを探しているが、WORKDIRが/usr/src/appに設定されている。
プロのヒント: 常にパスを確認してください。エントリポイントスクリプトを使用する場合は、イメージをビルドする前に、正しい実行権限が付与されていることを確認します:
# Dockerfile内
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
ステップ 4:WindowsからLinuxへの改行コードの罠
Windowsで開発し、Linux上でコンテナを実行する場合、スクリプトがCRLFの改行コードで保存されている可能性があります。Linuxはこれを認識しません。その結果、ファイルが明らかに存在していても、紛らわしい「file not found」エラーが発生することがよくあります。
これは、IDEでLF改行コードを使用するように強制するか、ビルドプロセスに dos2unix を追加することで解決できます:
# Dockerfileで改行コードを処理する堅牢な方法
RUN apt-get update && apt-get install -y dos2unix
RUN dos2unix /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
ステップ 5:JSON形式(Exec形式)を使用する
CMD の書き方は重要です。コマンドが /bin/sh -c でラップされてしまうため、シェル形式(CMD node index.js)は避けてください。これを使用すると、アプリケーションが SIGTERM などの Unix シグナルを受信できなくなります。
# 推奨:Exec形式
CMD ["node", "index.js"]
Exec形式を使用すると、アプリケーションが確実に PID 1 になります。これにより、正常なシャットダウンが可能になり、エラー発生後に「ゾンビ」プロセスが残るのを防ぐことができます。
修正の確認方法
変更を適用した後、コマンドがエラーを出さなかったからといって、正常に動作していると思い込まないでください。30秒後に稼働時間(uptime)を確認しましょう。docker ps を使用して、ステータスが Up 30 seconds(またはそれ以上)と表示されていることを確認します。ヘルスチェックを定義している場合は、docker inspect --format='{{json .State.Health.Status}}' <container_id> を実行して確認してください。「healthy」というステータスが表示されれば、プロセスが実際にその役割を果たしているという究極の証明になります。

