Dockerコンテナの停止コマンドと手順をわかりやすく紹介

はい、承知いたしました。Dockerコンテナの停止コマンドと手順について、約5000語の詳細な記事を作成します。


Dockerコンテナの停止コマンドと手順を徹底解説:安全かつ確実にコンテナを管理する方法

1. はじめに:Dockerコンテナのライフサイクルと「停止」の重要性

現代のソフトウェア開発と運用において、Dockerコンテナは不可欠な技術となっています。アプリケーションとその依存関係をパッケージ化し、どこでも一貫して実行できるコンテナは、開発、テスト、デプロイメントの効率を劇的に向上させました。コンテナは、起動(Run)、一時停止(Pause)、再開(Unpause)、停止(Stop)、削除(Remove)といった明確なライフサイクルを持っています。

このライフサイクルの中で、「停止(Stop)」は非常に重要な状態遷移です。コンテナを停止するという行為は、単にアプリケーションの実行を終えるだけでなく、関連するリソース(CPU、メモリ、ネットワークなど)を解放し、システムの状態を管理するために必要不可欠な操作です。デプロイメントの更新、システムメンテナンス、リソースの最適化、あるいは問題が発生したコンテナの隔離と再起動など、様々な状況でコンテナの停止が必要となります。

しかし、コンテナを適切に停止するためには、単に停止コマンドを実行するだけでは不十分な場合があります。アプリケーションがデータの永続化を行っている場合、未処理のリクエストがある場合、あるいはシャットダウン時に特定のクリーンアップ処理が必要な場合など、状況に応じた停止方法を選択し、その裏側で何が起こっているのかを理解しておくことが安全かつ確実な運用には不可欠です。

この記事では、Dockerコンテナの「停止」に焦点を当て、その目的、仕組み、そして主要な停止コマンドである docker stopdocker kill を中心に、様々な停止手順を徹底的に解説します。さらに、コンテナ停止の裏側で発生するシグナルハンドリングのメカニズム、Docker ComposeやDocker Swarmといったオーケストレーションツールでの停止方法、そしてコンテナが停止しない場合のトラブルシューティングや、安全な運用のためベストプラクティスについても詳しく説明します。この記事を通じて、Dockerコンテナの停止に関する深い理解を得て、日々のコンテナ管理に役立てていただければ幸いです。

2. Dockerコンテナの停止とは?シャットダウンシグナルとグレースフルシャットダウン

Dockerコンテナを「停止」するという操作は、一般的にコンテナ内で実行されている主要プロセス(PID 1)に停止を促すシグナルを送信し、そのプロセスが正常に終了するのを待つプロセスです。これは、オペレーティングシステムにおいてプロセスを終了させる際に行われる一般的な方法に基づいています。

シャットダウンシグナル(Shutdown Signal)

Unix系のOSでは、プロセス間通信やプロセス制御のために「シグナル」という仕組みが使われます。プロセスに対して特定のシグナルを送ることで、そのプロセスに何かアクションを起こさせるよう通知できます。コンテナの停止において重要なのは、主に以下の2つのシグナルです。

  • SIGTERM (Terminate Signal): プロセスに対して「終了してほしい」という終了要求を伝えるシグナルです。このシグナルを受け取ったプロセスは、通常、現在行っている処理を適切に終了させ(例えば、開いているファイルを閉じたり、ネットワーク接続を閉じたり、未処理のキューを処理したり)、リソースを解放してから自律的に終了することが期待されます。これを「グレースフルシャットダウン(Graceful Shutdown)」と呼びます。docker stop コマンドは、デフォルトでこの SIGTERM をコンテナ内のPID 1プロセスに送信します。
  • SIGKILL (Kill Signal): プロセスに対して「即座に終了せよ」という強制終了要求を伝えるシグナルです。このシグナルはOSカーネルによって直接処理され、プロセスは現在どのような状態であっても直ちに強制終了されます。プロセスはこのシグナルを無視したり、捕捉したり、遅延させたりすることはできません。データの一貫性が損なわれたり、リソースが適切に解放されなかったりする可能性があるため、これは「非グレースフルシャットダウン」または「強制停止」と呼ばれます。docker kill コマンドは、デフォルトでこの SIGKILL をコンテナ内のPID 1プロセスに送信します。

グレースフルシャットダウン(Graceful Shutdown)

グレースフルシャットダウンは、アプリケーションが停止要求を受け取った際に、実行中のタスクを完了させ、状態を保存し、リソースを解放するなど、終了に必要な後処理を安全に行うプロセスです。例えば:

  • Webサーバーであれば、新しい接続の受け付けを停止し、現在処理中のリクエストが完了するのを待つ。
  • データベースであれば、保留中のトランザクションをコミットまたはロールバックし、ファイルを同期する。
  • メッセージキューのコンシューマーであれば、現在処理中のメッセージを完了させ、キューからの読み取りを停止する。

グレースフルシャットダウンを適切に行うことで、データの損失を防ぎ、システムの状態を整合性の取れた状態に保つことができます。これは、アプリケーションが SIGTERM のような終了要求シグナルを捕捉(ハンドリング)し、それに応じたシャットダウンロジックを実行するように設計されている必要があります。

コンテナ停止コマンドは、これらのシグナルを利用してコンテナ内のプロセスに終了を促します。docker stop はまずグレースフルシャットダウンを試み、指定された時間内に終了しない場合に強制停止に移行します。一方、docker kill は最初から強制停止を行います。これらの違いを理解し、状況に応じて使い分けることが重要です。

3. Dockerコンテナを停止する主要コマンド

Dockerコンテナを停止するための主要なコマンドは docker stopdocker kill です。それぞれのコマンドの機能、使い方、そして使い分けについて詳しく見ていきましょう。

3.1. docker stop コマンド:正常終了のための停止

docker stop コマンドは、コンテナを正常に、すなわちグレースフルに停止させることを目的としたコマンドです。デフォルトでは、コンテナ内のPID 1プロセスに SIGTERM シグナルを送信し、アプリケーションが自身をクリーンアップして終了するための時間を与えます。

3.1.1. 基本的な使い方

docker stop コマンドの最も基本的な使い方は、停止したいコンテナのIDまたは名前を指定することです。

bash
docker stop [OPTIONS] CONTAINER [CONTAINER...]

例:my-web-server という名前のコンテナを停止する

bash
docker stop my-web-server

コンテナIDの一部または全部を指定することもできます。コンテナIDは docker ps コマンドで確認できます。

“`bash

例:コンテナIDが a3f1b2c4d5e6… のコンテナを停止

docker stop a3f1b2c4d5e6

短縮形でも可

docker stop a3f1b2
“`

コンテナIDは通常、前方一致でユニークであれば短縮形で指定できます。

3.1.2. 複数のコンテナを停止する

複数のコンテナを一度に停止したい場合は、コンテナIDまたは名前をスペース区切りで複数指定します。

bash
docker stop container1 container2 container3

3.1.3. タイムアウトオプション (-t, --time) の詳細

docker stop コマンドの重要なオプションに、タイムアウトを設定するための -t または --time オプションがあります。このオプションは、Dockerデーモンが SIGTERM を送信してから、コンテナが実際に終了するのを待つ最大時間を秒単位で指定します。

“`bash
docker stop -t 30 my-web-server

または

docker stop –time 30 my-web-server
“`

上記の例では、my-web-server コンテナに SIGTERM を送信した後、30秒間コンテナが自律的に終了するのを待ちます。

3.1.3.1. タイムアウトの仕組みと目的

docker stop が実行されると、Dockerデーモンは以下の手順でコンテナの停止を試みます。

  1. コンテナ内のPID 1プロセス(多くの場合、DockerfileのENTRYPOINTまたはCMDで指定されたコマンド)に SIGTERM シグナルを送信します。
  2. 指定されたタイムアウト時間(デフォルトは10秒)だけ、コンテナが正常に終了するのを待ちます。
  3. タイムアウト時間内にコンテナが自律的に終了した場合、停止処理は成功します。
  4. タイムアウト時間内にコンテナが終了しなかった場合、Dockerデーモンは最終手段として、コンテナ内のPID 1プロセスに SIGKILL シグナルを送信し、強制的にコンテナを終了させます。

このタイムアウトの仕組みは、グレースフルシャットダウンを試みるための時間を与えつつ、アプリケーションがシグナルを無視したり、シャットダウン処理でハングしたりしていつまでも終了しない状況を防ぐためにあります。アプリケーションが SIGTERM を適切にハンドリングしてタイムアウト時間内に終了すれば、安全な停止が実現できます。タイムアウトした場合の SIGKILL は、予期せぬ状態での終了を引き起こす可能性があるため、可能な限り避けたい状況です。

3.1.3.2. デフォルト値と設定方法

docker stop のデフォルトのタイムアウト時間は 10秒 です。これは、多くのアプリケーションにとってグレースフルシャットダウンを完了させるのに十分な時間であることが多いという経験則に基づいています。

しかし、アプリケーションの種類や処理内容によっては、10秒ではグレースフルシャットダウンが完了しない場合があります。例えば、大量のデータを処理中のバッチジョブや、多数のアクティブな接続を持つ高負荷なサーバーなどは、終了に10秒以上かかることがあります。このような場合は、-t オプションを使用してタイムアウト値を増やす必要があります。

“`bash

グレースフルシャットダウンに60秒与える

docker stop -t 60 my-batch-processor
“`

Dockerホストのデーモン設定ファイル(通常 /etc/docker/daemon.json)で、すべてのコンテナに対するデフォルトの停止タイムアウト値を設定することも可能です。

json
{
"shutdown-timeout": 30
}

この設定はDockerデーモン全体に影響するため、個別のコンテナで特定のタイムアウトが必要な場合は、docker stop -t オプションを使う方が柔軟です。

3.1.3.3. 適切なタイムアウト値の決定

適切なタイムアウト値を決定するには、コンテナ内で実行されているアプリケーションがグレースフルシャットダウンを完了させるのに必要な最大時間を把握する必要があります。これは、アプリケーションの設計、実行中のタスク、リソースの負荷などによって異なります。

  • アプリケーションのテスト: 実際にアプリケーションを停止させ、終了にかかる時間を計測するのが最も確実な方法です。開発段階でシャットダウン処理のテストを行い、必要な時間を把握しておくことが望ましいです。
  • アプリケーションログの確認: シャットダウン処理中にアプリケーションが出力するログを確認することで、どのステップに時間がかかっているかを特定できる場合があります。
  • リソース使用率の監視: 停止処理中のCPUやメモリ、ネットワークI/Oなどのリソース使用率を監視することで、シャットダウンがブロックされている原因の手がかりが得られる場合があります。

タイムアウト値を必要以上に長く設定すると、停止処理全体にかかる時間が増加し、デプロイメントや再起動などの運用タスクに影響します。逆に、短すぎるとグレースフルシャットダウンが完了する前に SIGKILL が送信され、データ損失やリソースリークのリスクが高まります。したがって、適切なバランスを見つけることが重要です。

3.1.4. docker stop が送信するシグナル (SIGTERM)

前述の通り、docker stop はデフォルトで SIGTERM シグナルを送信します。この挙動は、stop コマンド自体で変更することはできません。ただし、Dockerfileの STOPSIGNAL 命令を使用することで、コンテナに対して docker stop がデフォルトで送信するシグナルを変更することは可能です。

例:Dockerfile 内で SIGINT を停止シグナルとして指定する

dockerfile
FROM ubuntu
STOPSIGNAL SIGINT
CMD ["/app/my_server"]

このDockerfileでビルドされたイメージから起動されたコンテナに対して docker stop を実行すると、SIGINT が送信されます。これは、アプリケーションがデフォルトの SIGTERM ではなく特定のシグナルでグレースフルシャットダウンをトリガーするように設計されている場合に便利です。しかし、特別な理由がない限りは、デフォルトの SIGTERM を使用し、アプリケーションがそれを適切にハンドリングするように実装するのが一般的です。

3.1.5. コマンド実行例

まず、テスト用のコンテナを起動します。ここでは、簡単なWebサーバーとしてNginxコンテナを使用します。

“`bash
docker run -d –name my-nginx nginx

稼働中のコンテナを確認

docker ps
“`

text
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a3f1b2c4d5e6 nginx "/docker-entrypoint.…" 10 seconds ago Up 9 seconds 80/tcp my-nginx

my-nginx コンテナを停止します。

bash
docker stop my-nginx

コンテナが停止したことを確認します。

bash
docker ps

text
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

稼働中のコンテナは表示されなくなりました。停止したコンテナも含めて表示するには -a オプションを使用します。

bash
docker ps -a

text
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a3f1b2c4d5e6 nginx "/docker-entrypoint.…" 2 minutes ago Exited (0) 8 seconds ago my-nginx

STATUSExited (0) となっており、正常に停止したことがわかります。(終了コード0は一般的に正常終了を意味します)。

タイムアウトを指定して停止する例:

“`bash

一度コンテナを再起動

docker start my-nginx

10秒のタイムアウトを指定して停止

docker stop -t 10 my-nginx
“`

もしコンテナがシグナルを無視するか、停止に時間がかかる処理を行っている場合、指定したタイムアウト後に強制終了されます。それをシミュレートするのは少し複雑ですが、概念としては上記の手順となります。

3.2. docker kill コマンド:強制終了のための停止

docker kill コマンドは、コンテナを即座に強制終了させるためのコマンドです。これは、docker stop がタイムアウトした場合や、アプリケーションがシグナルに応答しない場合など、他の方法でコンテナを停止できない場合の最終手段として使用されることが多いです。

3.2.1. docker stop との違い:SIGKILL の利用

docker kill コマンドのデフォルトの動作は、コンテナ内のPID 1プロセスに SIGKILL シグナルを送信することです。SIGKILL はOSカーネルによって直接処理されるシグナルであり、アプリケーションはこれを捕捉したり無視したりすることができません。そのため、docker kill はほぼ確実にコンテナを終了させることができます。

しかし、SIGKILL による強制終了は、アプリケーションがグレースフルシャットダウンを行う機会を奪います。これにより、未保存のデータの損失、ディスク上のファイルやネットワーク接続の不整合、リソースリークなどが発生する可能性があります。したがって、docker kill の使用は慎重に行うべきです。

3.2.2. docker kill を使うべきシナリオ

docker kill は、以下のようなシナリオで検討されます。

  • docker stop を実行してもコンテナがタイムアウトし、停止しない場合。
  • コンテナ内のアプリケーションが応答せず、正常な手段では終了させられない場合。
  • リソースを緊急に解放する必要がある場合(ただし、これは危険を伴います)。
  • 開発やデバッグ目的で、状態の保存を気にせずコンテナを素早く終了させたい場合。

3.2.3. 基本的な使い方

docker kill コマンドの基本的な使い方は docker stop と同様に、コンテナのIDまたは名前を指定します。

bash
docker kill [OPTIONS] CONTAINER [CONTAINER...]

例:my-web-server コンテナを強制停止する

bash
docker kill my-web-server

複数のコンテナを指定することも可能です。

bash
docker kill container1 container2

3.2.4. 特定のシグナルを送信する (-s, --signal) オプション

docker kill コマンドは、デフォルトでは SIGKILL を送信しますが、-s または --signal オプションを使用することで、任意のシグナルをコンテナ内のPID 1プロセスに送信することができます。

“`bash
docker kill -s SIGHUP my-process-container

または

docker kill –signal SIGHUP my-process-container
“`

この例では、my-process-containerSIGHUP (Hang Up Signal) を送信しています。一部のアプリケーションは SIGHUP を受け取ると設定ファイルを再読み込みするなどの動作を行うように設計されています。-s オプションは、このように特定の目的でコンテナにシグナルを送りたい場合に利用できます。

-s オプションで指定できるシグナルは、OSで定義されている標準的なシグナル名またはシグナル番号です。シグナル名は大文字で指定します(例: SIGINT, SIGUSR1)。

3.2.4.1. 主要なシグナルとその意味

コンテナ管理に関連する、-s オプションで指定されうる主要なシグナルには以下のようなものがあります。

  • SIGTERM (15): 正常終了要求。docker stop のデフォルト。
  • SIGKILL (9): 強制終了。捕捉・無視不可。docker kill のデフォルト。
  • SIGHUP (1): 端末の切断。サーバープロセスでは設定ファイルの再読み込みなどに使われることがある。
  • SIGINT (2): 割り込みシグナル。Ctrl+Cでプログラムを終了させる際などに使われる。
  • SIGQUIT (3): 終了シグナル。コアダンプを伴う終了などに使われることがある。
  • SIGUSR1 (10), SIGUSR2 (12): ユーザー定義シグナル。アプリケーションが独自の目的で利用する。

-s オプションを使用することで、コンテナ内のアプリケーションの特定の動作(例えば、統計情報のダンプやログレベルの変更など)をトリガーするためにシグナルを利用することも技術的には可能です。ただし、これはアプリケーションがそのシグナルを捕捉し、特定のハンドリングロジックを実装していることが前提です。

3.2.5. コマンド実行例

再びNginxコンテナを使用します。

“`bash

コンテナ起動

docker run -d –name my-nginx-to-kill nginx

稼働中のコンテナを確認

docker ps
“`

text
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b8c7d6a5f4e3 nginx "/docker-entrypoint.…" 5 seconds ago Up 4 seconds 80/tcp my-nginx-to-kill

my-nginx-to-kill コンテナを強制停止します。

bash
docker kill my-nginx-to-kill

コンテナが即座に停止したことを確認します。

bash
docker ps

text
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

停止したコンテナを確認します。

bash
docker ps -a

text
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b8c7d6a5f4e3 nginx "/docker-entrypoint.…" 45 seconds ago Exited (137) 6 seconds ago my-nginx-to-kill

STATUSExited (137) となっています。終了コード137は、OSシグナル9 (SIGKILL) によってプロセスが終了させられたことを意味します(128 + 9 = 137)。これは、docker kill のデフォルト動作である SIGKILL による強制終了が成功したことを示しています。

特定のシグナルを送る例:

“`bash

シグナルをハンドリングするようなテスト用コンテナが必要

例えば、SIGINTを受け取ったら終了する簡単なPythonスクリプト

save this as app.py

import signal

import sys

import time

def signal_handler(sig, frame):

print(f’Received signal {sig}. Shutting down gracefully…’)

sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)

print(“App started. Waiting for signal…”)

try:

while True:

time.sleep(1)

except KeyboardInterrupt:

print(“KeyboardInterrupt received, exiting.”)

sys.exit(0)

Dockerfile

FROM python:3.9-slim

COPY app.py /app/app.py

CMD [“python”, “/app/app.py”]

イメージをビルドしてコンテナ起動 (ここでは my-signal-app という名前でビルド済みとする)

docker run -d –name my-signal-app my-signal-app

SIGINT を送信して停止させる

docker kill -s SIGINT my-signal-app

ログを確認

docker logs my-signal-app
“`

ログには Received signal 2. Shutting down gracefully... のようなメッセージが表示され、アプリケーションが SIGINT を捕捉して終了処理を行ったことが確認できるでしょう。

3.3. docker rm コマンド:コンテナの削除 (停止とは異なるが関連)

コンテナのライフサイクルにおいて、停止の次に位置するのが「削除(Remove)」です。docker rm コマンドは、停止したコンテナをシステムから完全に削除し、そのコンテナに関連付けられていた書き込み可能なレイヤーやメタデータを解放します。

3.3.1. 停止と削除の違い

  • 停止 (docker stop/docker kill): コンテナ内のプロセスを終了させますが、コンテナ自体の状態(ファイルシステム、設定、メタデータなど)はディスク上に保持されます。停止したコンテナは docker ps -a で確認でき、docker start で再開することができます。
  • 削除 (docker rm): 停止したコンテナのディスク上の情報を完全に削除します。削除されたコンテナは復旧できません。

3.3.2. 停止したコンテナを削除する手順

コンテナを削除するには、まずそのコンテナが停止している必要があります。稼働中のコンテナを docker rm しようとすると、エラーになります。

例:稼働中のNginxコンテナを削除しようとする

bash
docker run -d --name my-nginx-to-remove nginx
docker ps

text
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c9d8e7b6a5f4 nginx "/docker-entrypoint.…" 5 seconds ago Up 4 seconds 80/tcp my-nginx-to-remove

稼働中のコンテナを削除しようとする

bash
docker rm my-nginx-to-remove

text
Error response from daemon: You cannot remove a running container c9d8e7b6a5f4.... Stop the container before attempting removal or use -f

上記のように、稼働中のため削除できないというエラーが表示されます。

正しくは、まず docker stop で停止させてから docker rm を実行します。

bash
docker stop my-nginx-to-remove
docker rm my-nginx-to-remove

これでコンテナはシステムから削除されます。docker ps -a で確認しても表示されなくなります。

3.3.3. 強制削除オプション (-f, --force) の注意点

docker rm コマンドには -f または --force オプションがあります。このオプションを使用すると、稼働中のコンテナを強制的に停止させてから削除します。

“`bash

再びコンテナを起動

docker run -d –name my-nginx-to-force-remove nginx
docker ps
“`

text
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d0c9b8a7f6e5 nginx "/docker-entrypoint.…" 5 seconds ago Up 4 seconds 80/tcp my-nginx-to-force-remove

-f オプションを付けて削除する

bash
docker rm -f my-nginx-to-force-remove

このコマンドは、内部的にまず docker stop (タイムアウトあり) か docker kill に近い動作でコンテナを停止させ、その後に削除を実行します。実質的には docker stopdocker rm を続けて実行するのと似ていますが、アプリケーションがグレースフルシャットダウンを完了できるかは、内部でどのように停止が試みられるか(例えば、タイムアウト時間など)に依存します。強制停止と同様に、データの損失などのリスクを伴う可能性があるため、本番環境での利用は慎重に行うべきです。

緊急時や開発環境での手早いクリーンアップには便利ですが、プロダクションでは docker stop で正常終了を確認してから docker rm するのが推奨される手順です。

4. 停止対象のコンテナを特定する方法

コンテナを停止するには、そのコンテナを正確に特定する必要があります。Dockerでは、コンテナを識別する方法がいくつかあります。

4.1. docker ps コマンドで稼働中のコンテナを確認する

最も一般的な方法は、docker ps コマンドを使用して現在稼働中のコンテナの一覧を表示することです。

bash
docker ps

出力例:

text
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a3f1b2c4d5e6 nginx "/docker-entrypoint.…" 5 minutes ago Up 5 minutes 80/tcp my-nginx
b8c7d6a5f4e3 ubuntu "bash" 7 minutes ago Up 7 minutes my-bash-container

この一覧から、停止したいコンテナの CONTAINER ID または NAMES を確認できます。

4.2. コンテナID、コンテナ名による指定

コンテナを一意に識別するために、Dockerは各コンテナに長い一意なID(例: a3f1b2c4d5e6...)と、デフォルトで生成されるかユーザーが指定した名前(例: my-nginx, my-bash-container)を割り当てます。

停止コマンドでは、これらの識別子を使って対象コンテナを指定します。

  • コンテナID: 長いID全体、または一意に特定できる範囲の短いID(短縮形)を使用できます。例えば、IDが a3f1b2c4d5e67890... のコンテナは、a3f1b2c4d5e6a3f1b2 といった短縮形で指定できます。ただし、同じPrefixを持つ別のコンテナが同時に存在する場合は、より長いPrefixが必要になります。
  • コンテナ名: --name オプションでユーザーが指定した名前、またはDockerが自動生成した名前を使用できます。名前はDockerホスト上で一意である必要があります。

名前で指定するのが、多くの場合最も分かりやすい方法です。

bash
docker stop my-nginx
docker kill my-bash-container

4.3. 複数のコンテナを指定する方法

前述の通り、スペース区切りで複数のコンテナIDや名前を並べて指定することで、複数のコンテナを一度に停止できます。

bash
docker stop container1_name container2_id container3_id_short

4.4. 停止状態のコンテナを確認する (docker ps -a)

docker ps はデフォルトで稼働中のコンテナのみを表示しますが、-a または --all オプションを付けると、停止中(Exited)や作成済み(Created)の状態も含め、すべてのコンテナが表示されます。

bash
docker ps -a

text
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a3f1b2c4d5e6 nginx "/docker-entrypoint.…" 1 hour ago Exited (0) 5 minutes ago my-nginx
b8c7d6a5f4e3 ubuntu "bash" 1 hour ago Exited (137) 10 minutes ago my-bash-container
c9d8e7b6a5f4 ubuntu "sleep infinity" 2 hours ago Up 2 hours running-container

このリストから、以前に停止したコンテナのIDや名前を確認し、必要に応じて docker rm で削除したり、docker start で再起動したりすることができます。

5. コンテナ停止の裏側:シグナルハンドリングとPID 1

Dockerコンテナが停止する際に内部で何が起きているかを理解することは、アプリケーションを正しく設計し、停止時の問題をトラブルシューティングする上で非常に重要です。中心的な概念は「シグナルハンドリング」と「コンテナ内のPID 1プロセス」です。

5.1. OSシグナルとは

シグナルは、Unix/Linuxシステムにおけるプロセス間の非同期通信のメカニズムです。カーネルや他のプロセスから特定のイベント発生を通知するために使われます。シグナルを受け取ったプロセスは、あらかじめ登録しておいた「シグナルハンドラ」と呼ばれる関数を実行して、そのシグナルに応じた処理を行うことができます。多くのシグナルは捕捉して処理できますが、SIGKILLSIGSTOP など一部のシグナルは捕捉・無視・ブロックすることができません。

5.2. コンテナにおけるPID 1 プロセス

一般的なLinuxシステムでは、最初のプロセスであるinitプロセス(PID 1)がシステム全体のプロセス管理を担当し、他のプロセスの親となります。親プロセスが終了した子プロセス(ゾンビプロセス)を回収するなどの重要な役割を担います。

Dockerコンテナ内では、デフォルトではDockerfileの ENTRYPOINT または CMD で指定されたコマンドが、コンテナ内のPID 1として起動されます。これは、コンテナが「単一の主要プロセスを実行する」という設計思想に基づいています。

この「PID 1問題」と呼ばれる特有の状況が、シグナルハンドリングにおいて重要になります。通常のLinux環境では、シェルなどで起動された子プロセスは、親プロセス(シェルなど)がシグナルを捕捉し、そのシグナルを子プロセスに転送することがあります。しかし、コンテナ内のPID 1プロセスは、従来のinitシステムのようなシグナル転送の役割を持ちません。PID 1プロセスは、OSから直接シグナルを受け取りますが、受け取ったシグナルをその子プロセス(もしあれば)に自動的に転送しないという特性があります。

もしコンテナ内のPID 1プロセスが、自身が受け取った SIGTERM などのシグナルを捕捉・ハンドリングせず、かつ子プロセスに転送もしない場合、子プロセスはシグナルの存在に気づかず、グレースフルシャットダウンを行う機会を得られません。タイムアウト時間後にはPID 1が SIGKILL で強制終了され、その子プロセスも道連れに強制終了されることになります。

この問題を回避するため、以下のいずれかの対応が必要になります。

  1. アプリケーション自身がPID 1として起動され、かつ必要なシグナル(特に SIGTERM)を適切にハンドリングするように設計する。
  2. コンテナのPID 1として、シグナル転送機能を持つ軽量なinitプロセス(dumb-init, tini など)を起動し、そのinitプロセスが本来のアプリケーションプロセスを子として起動するようにする。 Dockerでは、docker run--init オプションや、Composeファイルの init: true オプションで tini を有効にできます。

docker run --init の例:

bash
docker run --init -d --name my-app-with-init my-app-image

このコマンドで起動された my-app-with-init コンテナでは、tini がPID 1として起動され、my-app-image のエントリーポイント/CMDを子プロセスとして起動します。Dockerデーモンから送信されたシグナルはまず tini が受け取り、それをアプリケーションプロセスに転送してくれます。これにより、アプリケーションがシグナルをハンドリングできるようになります。

5.3. SIGTERMSIGKILL の処理メカニズム

  • docker stop (SIGTERM -> タイムアウト -> SIGKILL):

    1. DockerデーモンがコンテナのPID 1に SIGTERM を送信します。
    2. PID 1プロセスが SIGTERM を捕捉していれば、登録されたシグナルハンドラが実行され、グレースフルシャットダウン処理を開始します。
    3. アプリケーションはシャットダウン処理を完了し、自律的に終了します。
    4. DockerデーモンはPID 1プロセスが終了したことを検知し、停止処理を完了します。
    5. もしPID 1が SIGTERM を捕捉しない、またはグレースフルシャットダウン処理に時間がかかり、指定されたタイムアウト時間(デフォルト10秒)が経過した場合、Dockerデーモンは次のステップに進みます。
    6. DockerデーモンはコンテナのPID 1に SIGKILL を送信します。
    7. OSカーネルがPID 1プロセスを即座に強制終了します。
    8. PID 1プロセスの終了に伴い、その子プロセスもすべて強制終了されます。
    9. DockerデーモンはPID 1が終了したことを検知し、停止処理を完了します。
  • docker kill (デフォルト SIGKILL):

    1. DockerデーモンがコンテナのPID 1に SIGKILL を送信します。
    2. OSカーネルがPID 1プロセスを即座に強制終了します(シグナルハンドラは実行されません)。
    3. PID 1プロセスの終了に伴い、その子プロセスもすべて強制終了されます。
    4. DockerデーモンはPID 1が終了したことを検知し、停止処理を完了します。

-s オプションで別のシグナルを指定した場合は、その指定されたシグナルがPID 1に送信されます。アプリケーションがそのシグナルをハンドリングしていれば、対応する処理が行われます。そうでなければ、デフォルトのシグナルハンドラ(多くの場合はプロセス終了)が実行されるか、シグナルが無視されるかのいずれかになります。

5.4. グレースフルシャットダウンの実装(シグナルハンドラ)

アプリケーション開発者は、コンテナ化を前提とする場合、アプリケーションが SIGTERM を受け取った際に適切に終了するためのグレースフルシャットダウンロジックを実装する必要があります。これは、使用しているプログラミング言語やフレームワークに依存します。

多くのモダンなフレームワークやライブラリは、シャットダウンフックやシグナルハンドリングの仕組みを提供しています。

例(概念的なPythonコード):

“`python
import signal
import sys
import time
import threading

未処理のタスクをシミュレート

pending_tasks = []
is_shutting_down = False

def graceful_shutdown(sig, frame):
“””SIGTERMなどの終了シグナルを受け取った際のハンドラ”””
global is_shutting_down
if is_shutting_down:
print(“Shutdown already in progress. Forcing exit.”)
sys.exit(1) # 二度目のシグナルは強制終了に近い動作
is_shutting_down = True
print(f”Received signal {sig}. Initiating graceful shutdown…”)

# 新しいタスクの受け付けを停止 (Webサーバーなら新しいリクエストを受け付けない)
stop_accepting_new_tasks()

# 現在処理中のタスクやキューに残っているタスクを完了させる
print(f"Waiting for {len(pending_tasks)} pending tasks to complete...")
while pending_tasks:
    # タスク処理ロジック (例: pending_tasks.pop().process())
    time.sleep(0.1) # ダミー処理
    print(f"Remaining tasks: {len(pending_tasks)}")
    if not pending_tasks:
         print("All pending tasks completed.")
         break
    # タイムアウト管理なども考慮に入れる必要がある (Docker stopのタイムアウトとは別にアプリ側でもタイムアウトを持つ)
    # if shutdown_timeout_reached:
    #     print("Shutdown timeout reached. Exiting with pending tasks.")
    #     break

# リソースの解放 (データベース接続、ファイルハンドルなど)
print("Releasing resources...")
release_resources()

print("Graceful shutdown complete. Exiting.")
sys.exit(0) # 正常終了コードで終了

def stop_accepting_new_tasks():
print(“Stopped accepting new tasks.”)

def release_resources():
print(“Resources released.”)

SIGTERM (デフォルトの停止シグナル) のハンドラを登録

signal.signal(signal.SIGTERM, graceful_shutdown)

SIGINT (Ctrl+Cなど) のハンドラも登録しておくと便利

signal.signal(signal.SIGINT, graceful_shutdown)

— メインアプリケーションロジック —

print(“Application started. Waiting for tasks or shutdown signal…”)

ダミータスクを追加

pending_tasks = [1, 2, 3, 4, 5]

アプリケーションが終了しないように待機

try:
while not is_shutting_down:
time.sleep(1)
# シャットダウンモードに入ったら、メインループも終了処理を待つか、別のロジックに移行する
# ここではシグナルハンドラがsys.exit()を呼ぶので、このループは終了する
except SystemExit:
print(“Main loop exiting gracefully.”)
except KeyboardInterrupt:
print(“KeyboardInterrupt received in main loop, exiting.”) # SIGINTがハンドリングされなかった場合など
sys.exit(0)

print(“Application finished.”)
“`

このようなシグナルハンドラを実装することで、docker stop コマンドに対してアプリケーションが適切に応答し、安全に終了できるようになります。

5.5. アプリケーションでのシグナル無視と問題点

もしコンテナ内のPID 1プロセスが SIGTERM を完全に無視するように実装されているか、シグナルを捕捉する仕組みがそもそもない場合(例:シグナルハンドリングを考慮せずに書かれた単純なスクリプトや古いアプリケーション)、docker stop が送信する SIGTERM は効果がありません。

この場合、docker stop コマンドは指定されたタイムアウト時間いっぱい待ち続け、最終的に SIGKILL を送信して強制終了します。これは以下のような問題を引き起こします。

  • 停止に時間がかかる: グレースフルシャットダウンのタイムアウト時間だけ無駄に待つことになるため、コンテナ停止までの時間が長くなります。
  • 状態の不整合: 強制終了により、アプリケーションが処理中のデータが失われたり、ファイルやデータベースの状態が不整合になったりするリスクがあります。
  • リソースリーク: 開いたままのファイル、ネットワーク接続、メモリなどが適切に解放されず、システム全体のリソース不足につながる可能性があります。
  • 予期せぬエラー: アプリケーションが次回起動された際に、前回予期せず終了したことによるリカバリ処理が必要になったり、起動に失敗したりすることがあります。

これらの理由から、特に本番環境で稼働させる重要なアプリケーションのコンテナにおいては、シグナルハンドリングを適切に実装し、docker stop によるグレースフルシャットダウンに対応させることが強く推奨されます。前述の --init オプションや、dumb-init/tini を使うことも有効な手段です。

6. 様々なシナリオでのコンテナ停止

ここまでは単一のコンテナに対する基本的な停止コマンドを見てきましたが、実際の運用では複数のコンテナをまとめて扱ったり、Docker ComposeやDocker Swarmといったオーケストレーションツールを使ったりすることが一般的です。それぞれのシナリオでの停止方法を解説します。

6.1. すべてのコンテナを一括停止/削除する

開発環境などで、すべての稼働中コンテナを一度に停止したい場合があります。これは、docker ps コマンドとコマンド置換を組み合わせて実現できます。

まず、稼働中のコンテナのIDのみを取得します。

bash
docker ps -q

-q または --quiet オプションは、コンテナID(またはその他の指定した情報)のみをシンプルに出力します。

この出力結果を docker stop コマンドの引数として渡します。

bash
docker stop $(docker ps -q)

このコマンドは、docker ps -q の出力(稼働中の全コンテナIDリスト)を docker stop コマンドの引数として渡すことで、すべての稼働中コンテナの停止を試みます。

同様に、すべてのコンテナ(停止中のものも含む)を削除したい場合は、docker ps -a -q を使用します。

bash
docker rm $(docker ps -a -q)

注意: このコマンドは、停止中のコンテナも含め、ローカルのDockerデーモンが管理するすべてのコンテナを削除します。実行する前に、本当に削除して良いコンテナだけが表示されているか docker ps -a で確認することを強く推奨します。

6.2. 特定の条件に一致するコンテナを選択的に停止する

特定のラベルが付いたコンテナ、特定のイメージから作成されたコンテナなど、条件に一致するコンテナのみを停止したい場合があります。これは docker ps--filter オプションとコマンド置換を組み合わせて行います。

例:ラベル env=dev が付いているすべての稼働中コンテナを停止する

bash
docker stop $(docker ps -q --filter "label=env=dev")

例:my-app-image イメージから作成されたすべての稼働中コンテナを停止する

bash
docker stop $(docker ps -q --filter "ancestor=my-app-image")

--filter オプションで指定できる条件は多岐にわたります(name, label, before, since, status など)。詳しくは docker ps --help や Dockerの公式ドキュメントを参照してください。

6.3. Docker Compose で管理されているコンテナの停止

Docker Composeは、複数のコンテナで構成されるアプリケーションを定義し、まとめて管理するためのツールです。Composeで起動されたコンテナを停止するには、専用のコマンドを使用します。

Docker Composeプロジェクトのディレクトリに移動し、docker-compose.yml ファイルが存在する場所で以下のコマンドを実行します。

注意: Docker Compose V2 (CLIが docker compose) と V1 (CLIが docker-compose) ではコマンド名が異なります。V2が推奨です。以下はV2のコマンド (docker compose) で説明します。V1の場合は docker-compose に読み替えてください。

6.3.1. docker compose down

docker compose down コマンドは、Composeファイルで定義されたサービスに関連付けられたコンテナ、ネットワーク、およびボリュームを停止し、削除します。これは、環境全体をクリーンアップする際によく使われます。

bash
docker compose down [OPTIONS]

オプション例:

  • -v, --volumes: 匿名ボリュームを含め、ボリュームも削除します。永続化したいデータを含む名前付きボリュームはデフォルトでは削除されませんが、匿名ボリュームは削除されるため注意が必要です。
  • --rmi type: イメージを削除します。typeall (すべて) または local (タグ付けされていないもののみ) を指定できます。
  • --timeout seconds: コンテナ停止時のタイムアウトをデフォルトの10秒から変更します。これは docker stop -t と同様の目的です。

docker compose down は、内部的にまず各サービスコンテナに対して docker stop に相当する処理(シグナル送信、タイムアウト待ち)を実行し、その後 docker rm に相当する処理を行います。

6.3.2. docker compose stop

docker compose stop コマンドは、Composeファイルで定義されたサービスに関連付けられたコンテナを停止しますが、削除はしません。コンテナの状態は維持されるため、後で docker compose start で再開できます。

bash
docker compose stop [OPTIONS] [SERVICE...]

特定のサービスのみを停止したい場合は、サービス名を指定します。

bash
docker compose stop web db

オプション例:

  • -t, --timeout seconds: コンテナ停止時のタイムアウトを指定します。

docker compose stop は、個別の docker stop コマンドを各コンテナに対して実行するのに似ていますが、Composeファイルで定義されたサービス間の依存関係や、後述する stop_grace_period の設定などが考慮されます。

6.3.3. docker compose kill

docker compose kill コマンドは、Composeファイルで定義されたサービスに関連付けられたコンテナを強制終了します。これは、docker kill コマンドに相当します。

bash
docker compose kill [OPTIONS] [SERVICE...]

特定のサービスのみを強制終了したい場合は、サービス名を指定します。

bash
docker compose kill worker

オプション例:

  • -s, --signal signal: 送信するシグナルを指定します。デフォルトは SIGKILL です。

docker compose killdocker compose stop で停止できない場合の最終手段として利用されます。

6.3.4. stop_grace_period の設定

Docker Composeでは、Composeファイル内で各サービスごとに stop_grace_period を設定することで、そのサービスコンテナに対する docker compose stopdocker compose down 実行時のグレースフルシャットダウン待機時間をサービス単位でカスタマイズできます。

“`yaml
version: ‘3.8’
services:
web:
image: my-web-app
ports:
– “80:80”
stop_grace_period: 30s # このサービスは停止時に30秒待つ

worker:
image: my-worker
stop_grace_period: 1m # このサービスは停止時に1分待つ
# …
“`

この設定は、個々のサービスが必要とするシャットダウン時間にばらつきがある場合に非常に便利です。単位はs(秒)、m(分)、h(時間)などで指定します。指定がない場合のデフォルトはDockerデーモンのデフォルト停止タイムアウト(通常10秒)が使用されます。

6.3.5. サービス間の停止順序

Docker Composeは、デフォルトではコンテナを並列に停止させます。しかし、アプリケーションによっては、データベースを停止する前にアプリケーションサーバーを停止する必要があるなど、特定の停止順序が必要な場合があります。Composeファイルには depends_on というサービス間の依存関係を定義するフィールドがありますが、これは起動順序を指定するためのものであり、停止順序には影響しません

厳密な停止順序を制御したい場合は、以下のいずれかの方法を検討する必要があります。

  • 手動でサービスごとに docker compose stop <service_name> を依存関係の下流から順番に実行する。
  • カスタムスクリプトを作成し、依存関係を考慮した停止ロジックを実装する。
  • オーケストレーションツール(Kubernetesなど)に移行し、PodDisruptionBudgetやterminationGracePeriodSeconds、PreStopフックなどの機能を利用する。

多くの場合、アプリケーションが SIGTERM を適切にハンドリングし、依存サービスが利用できなくなった状況でも安全に終了できるような設計になっていれば、並列停止でも問題ありません。しかし、依存関係が複雑なシステムでは注意が必要です。

6.4. Docker Swarm で管理されているサービスの停止

Docker Swarmは、複数のDockerホストをクラスタ化し、サービスとしてコンテナを管理するためのオーケストレーションツールです。Swarmモードで管理されているコンテナ(タスクと呼ばれます)を停止するには、サービスを操作するコマンドを使用します。

6.4.1. サービスのスケーリングによる停止

Swarmサービスに含まれるコンテナ(タスク)の数を減らすことで、タスクを停止させることができます。これは最も一般的な方法です。

bash
docker service scale SERVICE=REPLICAS

例:my-web-service サービスのタスク数を0に減らす

bash
docker service scale my-web-service=0

このコマンドを実行すると、Swarmマネージャーは現在実行中のタスク数を指定された数(この場合は0)に減らすようにスケジューリングを行います。既存のタスクは、デフォルトの停止シグナルとタイムアウト(サービス定義やDockerデーモン設定に依存)を使って停止処理が実行されます。最終的にタスク数が指定した数になるように、不要になったタスクのコンテナが停止・削除されます。

これはサービスの停止に最も推奨される方法であり、Swarmの管理下で安全にタスクを終了させることができます。

6.4.2. 個別タスクの停止(一時的)

Swarmサービスはタスク数を維持しようとするため、個別のタスクコンテナを docker container stopdocker container kill で直接停止させても、Swarmマネージャーはそれを検知し、新しいタスクを起動して指定されたタスク数を満たそうとします。

“`bash

実行中のタスクコンテナIDを確認

docker service ps my-web-service
“`

text
ID NAME MODE REPLICAS IMAGE PORTS PUBLISHED PORTS NODE DESIRED STATE CURRENT STATE ERROR PORTS
abcdef1234 my-web-service.1 replicated 1/1 my-web-image *:80->80/tcp worker1 Running Running 10 minutes ago

上記の例で my-web-service.1 のタスクコンテナID (abcdef1234...) を使って直接停止を試みる:

bash
docker container stop abcdef1234

このコマンドを実行すると、コンテナは一時的に停止しますが、Swarmは my-web-service のDesired Stateが Running (レプリカ数1) であることを検知し、新しいコンテナを同じノードまたは別のノードで自動的に起動します。したがって、これは一時的なデバッグ目的以外にはサービスの停止方法として推奨されません。サービスの停止は、docker service scale を使うべきです。

6.4.3. サービスの削除

サービス自体をSwarmクラスタから完全に削除するには docker service rm コマンドを使用します。このコマンドは、サービスに含まれるすべてのタスクを停止・削除し、サービス定義をSwarmから解除します。

bash
docker service rm my-web-service

このコマンドは、サービスを永続的に削除したい場合に実行します。

Swarmサービスの停止においては、Composeと同様に、サービス定義で stop_grace_period を設定することが可能です。これにより、タスクコンテナが停止される際のグレースフルシャットダウン時間を制御できます。

yaml
version: '3.8'
services:
my-web-service:
image: my-web-image
deploy:
replicas: 3
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
stop_grace_period: 45s # Swarmサービスとしての停止グレースピリオド

この設定はComposeの stop_grace_period と同じ概念で、docker service scale などによってタスクが終了される際に適用されます。

7. Dockerコンテナ停止に関するトラブルシューティング

Dockerコンテナが期待通りに停止しない、docker stop がタイムアウトしてしまう、といった問題は運用中によく発生します。ここでは、その原因と診断、対処法について解説します。

7.1. docker stop がタイムアウトする、または停止しない場合

docker stop コマンドが指定されたタイムアウト時間を経過しても完了せず、最終的に強制終了(終了コード137など)となる場合、最も一般的な原因は以下のいずれかです。

  1. アプリケーションが SIGTERM シグナルを適切にハンドリングしていない、または完全に無視している。
  2. アプリケーションが SIGTERM をハンドリングしているが、グレースフルシャットダウン処理が長すぎる、またはハングしている。 (例:データベース接続が切断されて再接続を試み続けている、外部サービスへのリクエストがタイムアウトせずブロックされている、大量のキュー処理が終わらない)
  3. コンテナ内のPID 1以外のプロセスがシグナルを捕捉しており、PID 1にシグナルが適切に転送されていない。 (--init オプションや tini/dumb-init を使用していない場合に発生しやすい)
  4. Dockerデーモン自体の問題や、基盤となるOSレベルでのプロセス管理の問題。
  5. コンテナが極端な高負荷状態にあり、シグナルを受け取ったり終了処理を実行したりするリソース(CPU時間など)が不足している。

7.2. 問題の診断手順 (docker logs, docker inspect, docker top)

コンテナが停止しない原因を特定するには、以下のツールやコマンドが役立ちます。

  • docker logs <container_id>:

    • コンテナの標準出力/標準エラー出力を確認します。アプリケーションがシグナルを受け取った際のログメッセージ(例:「Shutting down…」)が出力されているか、シャットダウン処理中にエラーや警告が出ていないかなどを確認します。
    • タイムアウトした場合は、タイムアウト直前にアプリケーションが何をしていたかの手がかりが得られる場合があります。
    • アプリケーションが全くシグナルに応答した形跡がない場合は、シグナルハンドリングが実装されていないか、シグナルがコンテナ内のPID 1に到達していない可能性があります。
  • docker inspect <container_id>:

    • コンテナの詳細な設定情報や現在の状態を確認します。特に以下の情報を確認します。
      • State.Status: コンテナの状態 (running, exited など)。停止処理中はまだ running と表示されることがあります。
      • State.ExitCode: コンテナが終了した場合の終了コード。137であれば SIGKILL による強制終了の可能性が高いです。0であれば正常終了です。
      • Config.StopSignal: コンテナが docker stop で受け取るように設定されているシグナル(デフォルトは SIGTERM)。Dockerfileの STOPSIGNAL で変更されている場合があります。
      • Config.Cmd, Config.Entrypoint: コンテナのPID 1として何が実行されているか。これがシェルスクリプトなどの場合は、「PID 1問題」の可能性があります。
      • HostConfig.Init: --init オプションが有効になっているか。有効になっていない場合は、シグナル転送が適切に行われない可能性があります。
      • HostConfig.StopTimeout: このコンテナに設定されている停止タイムアウト値(秒)。
  • docker top <container_id>:

    • コンテナ内で実行されているOSプロセスの一覧を表示します。
    • PID 1として意図したプロセスが実行されているか確認します。もし dumb-inittini がPID 1であれば、アプリケーションプロセスはその子として表示されます。
    • どのプロセスがリソース(CPUなど)を大量に消費しているか確認します。シャットダウン処理がハングしているプロセスが見つかるかもしれません。
    • ゾンビプロセス(defunct と表示される)が存在しないか確認します。PID 1がゾンビプロセスを回収しない場合に発生し、システムリソースを消費する可能性があります。これは --init オプションなどで改善できます。

7.3. SIGKILL を使う必要がある状況とその影響

上記の診断を行っても原因が特定できない、あるいは原因が特定できてもアプリケーション側での即時対応が困難な場合など、どうしてもコンテナを停止させる必要がある場合は、最終手段として docker kill (または docker stop のタイムアウトによる SIGKILL) を使用することになります。

bash
docker kill <container_id>

SIGKILL はコンテナをほぼ確実に終了させますが、前述の通り、データの損失、リソースリーク、状態の不整合などのリスクが伴います。特に本番環境でデータベースや重要なステートフルアプリケーションに対して SIGKILL を実行する場合は、影響範囲を十分に理解し、必要なリカバリ手順(データの復旧、整合性チェックなど)を事前に準備しておく必要があります。

可能であれば、問題の原因を根本的に解決し、docker stop によるグレースフルシャットダウンが成功するようにアプリケーションやコンテナ構成(Dockerfile、--init オプションなど)を修正することが長期的な運用においては望ましいです。

7.4. Dockerデーモン自体の問題

稀に、コンテナ側の問題ではなく、Dockerデーモン自体が不安定になったり、応答しなくなったりして、コンテナの停止コマンドを受け付けなくなる場合があります。

  • docker stopdocker kill コマンド自体が応答しない、またはエラーを返す。
  • docker ps コマンドが異常に遅い、またはエラーを返す。

このような場合は、Dockerデーモンまたはその基盤となるOSに問題がある可能性があります。Dockerデーモンのログ(システムログ journalctl -u docker.service などで確認)を確認したり、必要に応じてDockerデーモンを再起動したり(ただし、これにより稼働中の全コンテナが停止する可能性があるため注意が必要です)、OSレベルの診断(リソース使用率、ディスクI/Oなど)を行ったりする必要があります。Dockerデーモンの再起動が必要な場合は、計画的に実施し、影響を受けるすべてのコンテナの停止・再起動手順を準備しておくことが重要です。

8. Dockerコンテナ停止におけるベストプラクティス

安全で効率的なDockerコンテナ運用のためには、停止コマンドの適切な使い分けや、アプリケーション設計段階からの考慮が必要です。

8.1. stopkill の使い分けの原則

  • 基本的に docker stop を使用する: コンテナを停止する際の第一選択肢は常に docker stop であるべきです。これにより、アプリケーションにグレースフルシャットダウンの機会が与えられ、安全な停止が実現できます。
  • docker kill は最終手段と位置付ける: docker stop が機能しない場合や、緊急時以外は docker kill の使用を避けるべきです。SIGKILL による強制終了はデータの整合性やリソースの状態に悪影響を与える可能性があります。
  • タイムアウト値を適切に設定する: アプリケーションが必要とするグレースフルシャットダウン時間を考慮して、docker stop -t オプションやDocker Compose/Swarmの stop_grace_period を設定します。長すぎず、短すぎない値を見つけることが重要です。

8.2. アプリケーションのグレースフルシャットダウン実装の推奨

コンテナ内で実行されるアプリケーションは、SIGTERM などの終了要求シグナルを適切にハンドリングするように設計・実装されるべきです。

  • シグナルハンドラ内で、未処理のタスクを完了させる、開いているリソースを解放する、状態を保存するといったクリーンアップ処理を実行します。
  • シグナルを受け取ってから終了するまでの時間に上限(アプリケーション内部のタイムアウト)を設けることも検討します。これにより、グレースフルシャットダウン処理自体がハングするのを防ぎ、Docker側のタイムアウトよりも早く、制御された形で終了を試みることができます。
  • アプリケーションが外部の子プロセスを起動する場合は、シグナルがそれらの子プロセスにも適切に転送されるように、コンテナのPID 1として dumb-inittini を使用することを検討します (docker run --init または Compose/Swarm設定)。

8.3. 適切なタイムアウト値の選定

グレースフルシャットダウンのタイムアウト値は、アプリケーションの特性と負荷に基づいて決定します。

  • 開発/ステージング環境でシャットダウンにかかる時間を実際に計測します。
  • ピーク時の負荷を考慮して、十分なマージンを持たせます。
  • ただし、デプロイメント時のローリングアップデートなどでは、古いバージョンのコンテナが停止してから新しいバージョンが起動するまでの時間に関わるため、停止時間が長すぎるとデプロイメント全体が遅くなります。運用上の要件も考慮してバランスを取ります。

8.4. 本番環境での計画的な停止作業

本番環境で稼働中の重要なコンテナを停止する場合は、計画的に実施することが不可欠です。

  • 影響範囲の確認: 停止するコンテナが依存している他のサービスや、そのコンテナに依存しているサービスがないかを確認します。
  • 事前の告知: 可能であれば、サービス利用者にメンテナンスや停止について事前に告知します。
  • 手順の確認: 停止、必要に応じた削除、そして新しいコンテナの起動(デプロイメントの場合)の手順を確認し、リハーサルを行います。
  • 監視体制: 停止中および停止後のシステム状態を監視し、問題が発生した際に迅速に対応できる体制を整えます。
  • 自動化: デプロイメントツールやCI/CDパイプラインの一部としてコンテナの停止・開始・削除を自動化することで、ヒューマンエラーを防ぎ、再現性のある運用を実現します。

8.5. コンテナ停止後のデータボリュームの扱い

コンテナを停止しても、デフォルトではそのコンテナにマウントされていたボリューム(名前付きボリュームやバインドマウント)は保持されます。コンテナを削除(docker rm)しても、特に指定しない限り名前付きボリュームは削除されません。

  • 停止だけならボリュームは無事: docker stopdocker kill でコンテナを停止しても、データボリュームの内容は失われません。
  • コンテナ削除とボリューム: docker rm <container_id> ではボリュームは削除されません。ボリュームを削除するには docker volume rm <volume_name> が別途必要です。
  • コンテナ削除と同時にボリュームを削除: docker rm -v <container_id> とすることで、そのコンテナに関連付けられていた匿名ボリューム(Dockerfileの VOLUME 命令で名前を指定せずに作成されたボリューム)を同時に削除できます。名前付きボリュームは -v オプションを付けても削除されません。
  • Docker Compose downとボリューム: docker compose down -v は、サービスに定義された匿名ボリュームおよび名前付きボリュームをすべて削除します。永続化したいデータを含む名前付きボリュームを誤って削除しないよう注意が必要です。永続化データ用のボリュームは、Composeファイルで別途定義し、管理することを推奨します。

コンテナの停止や削除が、アプリケーションのデータにどのような影響を与えるかを事前に理解し、データの永続化戦略(ボリュームの使い方)と停止・削除手順を整合させることが重要です。

9. まとめ:安全なDocker運用に向けて

この記事では、Dockerコンテナの停止コマンドとその手順について、詳細な説明を加えて解説しました。

  • コンテナ停止の目的は、アプリケーションを終了させ、リソースを解放することです。
  • docker stopSIGTERM を使ったグレースフルシャットダウンを試み、タイムアウト後に SIGKILL に移行します。
  • docker kill はデフォルトで SIGKILL を使い、即座に強制終了します。
  • グレースフルシャットダウンのためには、アプリケーションが SIGTERM を適切にハンドリングし、コンテナ内のPID 1プロセスにシグナルが到達することが重要です (--init オプションが有効)。
  • Docker ComposeやDocker Swarmでは、それぞれ docker compose down/stop/kill, docker service scale/rm といった専用コマンドを使用し、オーケストレーションツールがコンテナ停止を管理します。stop_grace_period 設定でシャットダウン時間を制御できます。
  • コンテナが停止しない場合は、ログ、inspect、topコマンドを使って診断し、必要に応じて docker kill による強制終了を検討しますが、これは最終手段です。
  • 安全な運用のためには、docker stop を優先し、アプリケーションにグレースフルシャットダウンを実装し、適切なタイムアウトを設定し、計画的に停止作業を行うことが重要です。

Dockerコンテナの停止は、コンテナのライフサイクル管理において非常に基本的な操作ですが、その裏側にはシグナル処理やPID 1の問題、アプリケーションの設計など、考慮すべき様々な側面があります。これらの点を理解し、適切なコマンドと手順を選択することで、より安全で安定したDocker運用を実現できるでしょう。この記事が、日々のコンテナ管理の一助となれば幸いです。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール