コンテナが停止コマンドを無視する場合
終了を拒否するコンテナほど厄介なものはありません。docker stopを実行し、デフォルトの10秒間待機しても、クリーンに終了せずターミナルがハングしてしまいます。その後、Dockerは「cannot stop container(コンテナを停止できません)」というエラーを返します。docker killやdocker-compose downのような標準的なコマンドが、これらのゾンビプロセスに対して突然無力に感じられることがあります。コンテナはゴースト状態に陥っています。完全に実行されているわけではありませんが、死んでもいないのです。
具体的なエラーメッセージ
ターミナルまたはDockerログに、次のような特定の出力が表示されるはずです。
Error response from daemon: cannot stop container my-container: tried to kill container, but did not receive an exit event
なぜコンテナが動かなくなるのか
DockerはPID 1にSIGTERMシグナルを送信することでコンテナを停止させます。プロセスが10秒以内に終了しない場合、DockerはSIGKILLにエスカレーションします。このエラーは、SIGKILLさえ失敗するか、Dockerデーモンがプロセスの状態を見失ったときに発生します。これには通常、いくつかの技術的なボトルネックが関係しています。
- シグナルの不一致: アプリケーションがPID 1として実行されていますが、Linuxシグナルをキャッチしたり転送したりするようにプログラムされていません。
- 割り込み不能スリープ(D状態): ハングしたNFSマウントや故障したSSDでの90秒のタイムアウト待ちなど、プロセスがI/O待ちで立ち往生しています。
- セキュリティロック: AppArmorやSELinuxのプロファイルが、デーモンによる最終的なKillシグナルの送信をブロックしています。
- カーネルの同期ずれ: Linuxカーネルと
containerdが、その特定のプロセスに関する共有状態を失っています。
ステップバイステップの修正方法
方法 1: コマンドラインによる強制終了
まだ複雑なシステム変更に飛び込まないでください。デーモンがまだプロセスに到達できるかどうかを確認するために、まずは最も強力なシグナルを試してみましょう。
docker kill --signal=SIGKILL my-container
ターミナルが同じ「exit event」エラーを返した場合は、Dockerデーモンが制御を失っています。Dockerをバイパスして、ホストOSと直接やり取りする必要があります。
方法 2: ホストPIDを介してプロセスを終了させる
DockerコンテナはLinuxホスト上の単なる隔離されたプロセスであるため、標準的なLinuxツールを使用して終了させることができます。まず、ホストの視点から見た実際のプロセスID(PID)を見つける必要があります。
- ホストPIDを抽出する:
docker inspect --format '{{.State.Pid}}' my-container
- 数値(例:4502)が表示されたら、手動で強制終了します。
```
sudo kill -9 4502
PIDが0として返される場合、Dockerはプロセスが消滅したと考えていますが、コンテナのメタデータがまだ「Running(実行中)」状態のまま動かなくなっていることを意味します。
方法 3: Containerd Shimをクリアする
Dockerはコンテナのライフサイクルを管理するためにcontainerd-shimを使用します。メインプロセスが終了しても、このShimが残り続け、デーモンを混乱させることがあります。
- コンテナに関連付けられているShimプロセスを見つけます。
ps aux | grep containerd-shim | grep my-container
- PIDを特定して削除します。
```
sudo kill -9 <shim_pid>
方法 4: 「D」状態のプロセスを特定する
kill -9を実行してもプロセスが終了しない場合、それは**割り込み不能スリープ(D状態)**にある可能性があります。これらのプロセスはハードウェアを待機しており、すべてのシグナルを無視します。psのプロセスステータス列を確認することで、これらを見つけることができます。
ps -eo pid,stat,comm | grep " D "
コンテナがここに表示される場合は、マウントを確認してください。ハングしたNFS共有や、iowaitが100%のディスクが通常、原因です。プロセスが消える前に、ハードウェアまたはネットワークのハングを修正する必要があります。多くの場合、D状態のプロセスをクリアするにはホストの再起動しか方法がありません。
方法 5: Dockerエンジンの再起動
containerdの内部状態が破損した場合、サービスのリフレッシュによってゴーストイベントが解消されることがよくあります。
sudo systemctl restart docker
警告: daemon.jsonで"live-restore": trueを構成していない限り、これにより実行中のすべてのコンテナが停止します。
コンテナのスタックを防ぐ方法
1. 適切なInitプロセスを使用する
ほとんどのアプリはPID 1として動作するように構築されていません。システムのinitのようにシグナルを処理しません。--initフラグを使用して、シグナル転送を正しく処理する軽量なinitバイナリであるtiniでアプリをラップします。
docker run --init my-image
2. グレースフルシャットダウンのロジックを追加する
アプリケーションがSIGTERMをリッスンしていることを確認してください。Node.jsを使用している場合、アプリは次のようになります。
process.on('SIGTERM', () => {
server.close(() => {
console.log('Shutting down gracefully...');
process.exit(0);
});
});
3. 停止タイムアウトの調整
データベースがバッファのフラッシュに20秒を必要とする場合、Dockerのデフォルトの10秒設定では強制終了が発生してしまいます。コンテナがクリーンに終了するための時間を増やしてください。
docker stop --time=30 my-container
最終確認
コンテナがメモリから解放されたことを確認します。
docker ps -a | grep my-container
ステータスがExitedであるか、コンテナが消えていれば、修正は成功です。これで、docker rm my-containerを使用して残骸を安全に削除し、新しく開始できます。

