はじめに
この実験では、Docker Compose サービス内でワンオフタスクを実行するためにdocker compose runコマンドを効果的に使用する方法を学びます。これは、フルサービスのスタックを起動せずに管理コマンドを実行したり、デバッグを行ったり、特定の操作を実行するための強力なテクニックです。
以下の様々なシナリオを探索します:
- デフォルトのサービスコマンドの上書き
- インタラクション用にサービスポートを有効化
- 手動でのポートマッピング
- リンクされたサービスを起動せずにコマンドを実行
- 実行後のコンテナ自動削除
この実験を完了する頃には、様々なワンオフタスクに対してdocker compose runを活用できるようになるでしょう。
サービスコマンドを上書きしてワンオフコマンドを実行
このステップでは、Docker イメージや Dockerfile で指定されたデフォルトコマンドを上書きして、Docker コンテナ内でワンオフコマンドを実行する方法を学びます。これは、メインサービスを起動せずにコンテナ環境内で管理タスクを実行したり、デバッグを行ったり、特定のスクリプトを実行する場合に便利です。
まず、このデモンストレーションで使用するシンプルな Docker イメージをプルしましょう。ubuntuイメージを使用します。
docker pull ubuntu:latest
イメージがプルされ、ダウンロードされていることを示す出力が表示されるはずです。
Using default tag: latest
latest: Pulling from library/ubuntu
...
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest
次に、ubuntuイメージをベースにしたコンテナでワンオフコマンドを実行しましょう。docker runコマンドにイメージ名と実行したいコマンドを指定します。例えば、コンテナ内のルートディレクトリの内容をリスト表示するls -l /コマンドを実行してみます。
docker run ubuntu ls -l /
このコマンドはubuntuイメージから新しいコンテナを作成し、その中でls -l /コマンドを実行してから終了します。ルートディレクトリの内容を示す次のような出力が表示されるはずです:
total 68
drwxr-xr-x 2 root root 4096 Oct 26 00:00 bin
drwxr-xr-x 2 root root 4096 Oct 26 00:00 boot
drwxr-xr-x 5 root root 360 Nov 1 00:00 dev
drwxr-xr-x 19 root root 4096 Nov 1 00:00 etc
drwxr-xr-x 2 root root 4096 Oct 26 00:00 home
drwxr-xr-x 7 root root 4096 Oct 26 00:00 lib
drwxr-xr-x 2 root root 4096 Oct 26 00:00 lib64
drwxr-xr-x 2 root root 4096 Oct 26 00:00 media
drwxr-xr-x 2 root root 4096 Oct 26 00:00 mnt
drwxr-xr-x 2 root root 4096 Oct 26 00:00 opt
drwxr-xr-x 2 root root 4096 Oct 04 14:00 proc
drwx------ 2 root root 4096 Oct 26 00:00 root
drwxr-xr-x 2 root root 4096 Oct 26 00:00 run
drwxr-xr-x 2 root root 4096 Oct 26 00:00 sbin
drwxr-xr-x 2 root root 4096 Oct 26 00:00 srv
drwxr-xr-x 2 root root 4096 Oct 26 00:00 sys
drwxrwxrwt 2 root root 4096 Oct 26 00:00 tmp
drwxr-xr-x 11 root root 4096 Oct 26 00:00 usr
drwxr-xr-x 12 root root 4096 Oct 26 00:00 var
この場合、ubuntuイメージのデフォルトコマンドは通常/bin/bashのようなシェルです。イメージ名の後にls -l /を指定することで、Docker にデフォルトコマンドではなくこの特定のコマンドを実行するように指示しています。
別のコマンド、例えばコンテナ内の現在の作業ディレクトリを表示するpwdを試してみましょう。
docker run ubuntu pwd
出力は/となり、ルートディレクトリがデフォルトの作業ディレクトリであることを示します。
/
これは、コンテナにインタラクティブに入ったり、イメージのデフォルトコマンドを変更したりすることなく、簡単に任意のコマンドを実行できることを示しています。
サービスポートを有効化してコマンドを実行
このステップでは、コンテナ内のサービスが公開するように設定されたポートを有効にしたまま、Docker コンテナ内でコマンドを実行する方法を探ります。これは、デバッグや管理のために一時的なコマンドを実行したいが、メインサービスにはコンテナ外部からアクセス可能な状態を維持したい場合に便利です。
このデモンストレーションでは、シンプルな Web サーバーイメージを使用します。人気のある Web サーバーであるnginxイメージをプルしましょう。
docker pull nginx:latest
イメージがプルされていることを示す出力が表示されるはずです。
Using default tag: latest
latest: Pulling from library/nginx
...
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
デフォルトのnginxイメージは、コンテナ内のポート 80 でリッスンするように設定されています。このポートをホストマシンからアクセス可能にするには、docker runコマンドで-pフラグを使用してホストポートをコンテナポートにマッピングする必要があります。ホストポート 8080 をコンテナポート 80 にマッピングしましょう。
ここで、デフォルトの Nginx サービスを実行する代わりに、ポートマッピングを有効にしたままecho "Hello from the container"のようなシンプルなコマンドを実行してみます。
docker run -p 8080:80 nginx echo "Hello from the container"
このコマンドで Nginx サーバーが起動し、"Hello from the container"が表示されると期待するかもしれません。しかし、docker runでイメージ名の後にコマンドを指定すると、そのコマンドがデフォルトコマンドを置き換えます。したがって、この場合コンテナはechoコマンドを実行して終了します。ポートマッピングを指定したにもかかわらず、Nginx サーバーは起動しません。
出力は単純に次のようになります:
Hello from the container
Web ブラウザやcurlでhttp://localhost:8080にアクセスしようとすると、Nginx サーバーが実行されていないため接続が拒否されます。
curl http://localhost:8080
出力はおそらく次のようになります:
curl: (7) Failed to connect to localhost port 8080 after ... connection refused
これは重要なポイントを示しています:docker run <image> <command>でデフォルトコマンドを上書きすると、コンテナは指定されたコマンドのみを実行し、イメージが実行するように設計されたサービスは起動しません。したがって、ポートマッピングは設定されていても、そのポートでリッスンするサービスが実行されていないため有効になりません。
サービスが実行中でそのポートが有効な状態でコマンドを実行するには、通常サービスをバックグラウンドで起動してからコマンドを実行します。しかし、docker runコマンドは単一のコマンドを実行して終了するように設計されています。実行中のサービスと並行してコマンドを実行する効果を得るには、通常サービスを既に実行しているコンテナに対してdocker execを使用します。docker execについては後のステップで探求します。
このステップで重要なのは、docker runにコマンドを指定するとデフォルトのエントリーポイント/コマンドが上書きされ、ポートマッピングが指定されていてもデフォルトで設定されたサービスは起動しないことを理解することです。
手動ポートマッピングでコマンドを実行
このステップでは、docker runを使用したポートマッピングについてさらに探求し、ホストとコンテナのポートを明示的に指定する方法に焦点を当てます。前のステップではデフォルトコマンドを上書きするとサービスが実行されないことを示しましたが、サービスにアクセス可能にしたい場合に手動ポートマッピングを理解することは重要です。
引き続きnginxイメージを使用します。念のため説明すると、nginxイメージはコンテナ内でポート 80 を公開します。これをホストマシンからアクセス可能にするには、-pフラグの後にホストポート:コンテナポートを指定します。
nginxコンテナを実行し、ホストポート 8081 をコンテナポート 80 にマッピングしましょう。今回はコマンドを上書きしないので、デフォルトの Nginx サービスが起動します。また、バックグラウンドで実行するためにデタッチモード (-d) で実行します。
docker run -d -p 8081:80 nginx
コンテナ ID を示す長い文字列が表示され、コンテナがデタッチモードで起動したことがわかります。
<container_id>
コンテナが実行中でポートがマッピングされたので、curlまたは Web ブラウザを使用してホストマシンから Nginx のウェルカムページにアクセスできます。
curl http://localhost:8081
デフォルトの Nginx ウェルカムページの HTML コンテンツが表示されるはずです。これにより、Nginx サーバーがコンテナ内で実行されており、ホストマシンからポート 8081 経由でアクセス可能であることが確認できます。
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</body>
</html>
これは、手動ポートマッピングによって、コンテナ内の特定のポートで実行されているサービスにアクセスするために使用するホストポートを制御できることを示しています。これはホストマシンでのポート競合を避け、コンテナ化されたサービスをアクセス可能にするために不可欠です。
実行中のコンテナを停止するには、docker stopコマンドの後にコンテナ ID または名前を指定します。コンテナ ID はdocker psを実行して確認できます。
docker ps
これにより、実行中のコンテナのリストが表示されます。先ほど起動したnginxコンテナのコンテナ ID を見つけてください。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
<container_id> nginx "nginx -g 'daemon off" About a minute ago Up About a minute 0.0.0.0:8081->80/tcp <container_name>
次に、コンテナ ID を使用してコンテナを停止します。<container_id>は実際の出力の ID に置き換えてください。
docker stop <container_id>
コンテナ ID が再度表示され、コンテナが停止したことが確認できます。
<container_id>
コンテナを停止した後で再度http://localhost:8081にアクセスしようとすると、接続が拒否されます。
リンクされたサービスを起動せずにコマンドを実行
このステップでは、マルチコンテナアプリケーションの一部である Docker コンテナで、他のリンクされたサービスを起動せずにコマンドを実行する方法を学びます。これは、データベースマイグレーションやセットアップスクリプト、デバッグコマンドを実行する際に、アプリケーションスタック全体を起動する必要がない場合に特に便利です。
Docker Compose はマルチコンテナアプリケーションを管理する標準ツールであり、特定のサービスでワンオフコマンドを実行する機能を備えていますが、ここでは基本的な Docker の概念を説明します。この環境には Docker Compose がプリインストールされていないため、docker runコマンドとネットワーク機能に焦点を当てます。
Web アプリケーションとデータベースの 2 つのコンテナからなるシンプルなシナリオを想定します。Web アプリケーションには汎用のubuntuイメージを、データベースにはpostgresイメージを使用します。
まず、postgresイメージをプルします:
docker pull postgres:latest
イメージがプルされていることを示す出力が表示されます。
Using default tag: latest
latest: Pulling from library/postgres
...
Status: Downloaded newer image for postgres:latest
docker.io/library/postgres:latest
次に、コンテナが名前で相互に通信できるように Docker ネットワークを作成します。
docker network create my-app-network
ネットワーク ID が表示されます。
<network_id>
続いて、postgresコンテナを実行し、作成したネットワークに接続します。PostgreSQL ユーザーのパスワードも設定します。
docker run -d --network my-app-network --name my-database -e POSTGRES_PASSWORD=mypassword postgres
コンテナ ID が表示され、データベースコンテナがバックグラウンドで実行されていることがわかります。
<container_id>
ここで、「Web アプリケーション」コンテナがデータベースマイグレーションスクリプトなど、データベースとやり取りするコマンドを実行する必要があると想定します。通常、Docker Compose を使用している場合、Web サービスでコマンドを実行すると、Docker Compose がネットワーク設定とリンクを処理します。
docker runのみを使用する場合、Web アプリケーションコンテナを実行してmy-databaseに接続しようとすると、通常は同じネットワーク上にある必要があります。
同じネットワークに接続されたubuntuコンテナでコマンドを実行し、データベースとやり取りする可能性のあるコマンドをシミュレートします。データベースコンテナを名前 (my-database) で ping してみます。
docker run --network my-app-network ubuntu ping -c 4 my-database
このコマンドは以下の処理を行います:
ubuntuイメージから新しいコンテナを作成my-app-networkに接続- コンテナ内で
ping -c 4 my-databaseコマンドを実行
ubuntuコンテナはmy-databaseコンテナと同じネットワーク上にあるため、名前my-databaseをデータベースコンテナの IP アドレスに解決して ping を実行できます。
ping のリクエストとレスポンスを示す出力が表示されます:
PING my-database (172.18.0.2) 56(84) bytes of data.
64 bytes from my-database.my-app-network (172.18.0.2): icmp_seq=1 ttl=64 time=0.050 ms
64 bytes from my-database.my-app-network (172.18.0.2): icmp_seq=2 ttl=64 time=0.054 ms
64 bytes from my-database.my-app-network (172.18.0.2): icmp_seq=3 ttl=64 time=0.054 ms
64 bytes from my-database.my-app-network (172.18.0.2): icmp_seq=4 ttl=64 time=0.054 ms
--- my-database ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3060ms
rtt min/avg/max/mdev = 0.050/0.053/0.054/0.001 ms
これは、コンテナでワンオフコマンドを実行し、同じネットワーク上の他のコンテナとやり取りできることを示しています(この場合、ubuntuコンテナには典型的な「サービス」がありません)。重要なのは、コマンドを実行するコンテナを、やり取りが必要なサービスと同じネットワークに接続することです。
最後に、実行中のデータベースコンテナとネットワークをクリーンアップします。
docker stop my-database
my-database
docker rm my-database
my-database
docker network rm my-app-network
my-app-network
コマンド実行後にコンテナを自動削除
このステップでは、Docker コンテナでコマンドを実行し、完了後に自動的にコンテナを削除する方法を学びます。これは、実行後にコンテナを保持する必要がないワンオフタスク、スクリプト、ジョブに非常に便利です。コンテナを自動削除することで、システムをクリーンに保ち、停止したコンテナが蓄積するのを防ぎます。
このデモンストレーションでは再びubuntuイメージを使用します。メッセージを表示して終了するようなシンプルなコマンドを実行します。コマンド完了後にコンテナを自動削除するには、docker runコマンドに--rmフラグを指定します。
--rmフラグを付けてコンテナを実行し、echo "This container will be removed automatically"コマンドを実行してみましょう。
docker run --rm ubuntu echo "This container will be removed automatically"
このコマンドは以下の処理を行います:
ubuntuイメージから新しいコンテナを作成- コンテナ内で
echo "This container will be removed automatically"コマンドを実行 echoコマンドが終了すると、コンテナが自動的に削除される
echoコマンドの出力が表示されます:
This container will be removed automatically
コマンド終了後、コンテナは停止され削除されます。コンテナが削除されたことを確認するには、停止したコンテナも含めてすべてのコンテナをリスト表示するdocker ps -aコマンドを使用します。
docker ps -a
先ほど実行したコンテナがリストに表示されていないはずです。--rmフラグを付けずにコマンドを実行した場合、コンテナは"Exited"ステータスで出力に表示されます。
別の例を試してみましょう。sleepを使用して数秒間待機してから終了するコマンドを、再び--rmフラグを付けて実行します。
docker run --rm ubuntu sh -c "echo 'Starting sleep...'; sleep 5; echo 'Sleep finished.'"
このコマンドはsh -cを使用して、5 秒間スリープする前後にメッセージを表示するシンプルなスクリプトを実行します。
最初のメッセージがすぐに表示されます:
Starting sleep...
約 5 秒後、2 番目のメッセージが表示されます:
Sleep finished.
スクリプトが終了すると、コンテナは自動的に削除されます。再びdocker ps -aで確認できます。
docker ps -a
sleep コマンドを実行したコンテナはコンテナリストに表示されていないはずです。
--rmフラグの使用は、特定のタスクを実行して終了するように設計されたコンテナにとって良いプラクティスです。これにより、ディスクスペースの管理が容易になり、コンテナリストをクリーンに保つことができます。
まとめ
この実験では、Docker Compose サービス内でワンオフタスクを実行するためにdocker compose runコマンドを使用する方法を学びました。まず、サービスの設定で定義されたデフォルトコマンドを上書きする方法を理解し、管理やデバッグ目的で任意のコマンドを実行できるようにしました。
次に、これらのワンオフタスクのネットワーク接続を管理する方法を探求しました。具体的には、サービスポートを有効にしたり、手動でポートマッピングを行ったりしました。最後に、リンクされたサービスを起動せずにコマンドを実行する方法と、コマンド完了後にコンテナを自動削除してクリーンな環境を維持する方法を学びました。



