Dockerコンテナにどうやって入る?`docker exec`を徹底解説

Dockerコンテナにどうやって入る?docker execを徹底解説

はじめに

Dockerは、アプリケーションとその依存関係をコンテナと呼ばれる軽量でポータブルな単位にパッケージ化するためのデファクトスタンダードとなりました。開発環境の構築、マイクロサービスのデプロイ、CI/CDパイプラインなど、様々な場面で活用されています。

Dockerコンテナの大きな特徴の一つは、ホストシステムから分離された独立した実行環境であることです。これにより、環境依存性の問題を解消し、どこでも同じようにアプリケーションを実行できるようになります。

しかし、コンテナは分離されているがゆえに、内部で何が起こっているのかを確認したり、一時的に設定を変更したり、デバッグ作業を行ったりする必要が生じることがあります。まるで、物理的なサーバーや仮想マシンにSSHでログインして操作するような感覚で、コンテナ内部に「入りたい」と感じる場面があるでしょう。

Dockerには、このような要望に応えるための強力なコマンドが用意されています。それが、本記事の主題である docker exec コマンドです。

docker execコマンドを使えば、実行中のコンテナ内で新しいプロセスを起動し、そのプロセスを通じてコンテナ内部のファイルシステムを操作したり、コマンドを実行したりすることができます。これは、開発段階でのデバッグ、運用中の問題調査、あるいは一時的な管理作業など、多岐にわたるシーンで不可欠な機能です。

本記事では、この docker exec コマンドについて、その基本的な使い方から、詳細なオプション、様々な実践的な利用例、さらには使う上での注意点やベストプラクティスまで、約5000語をかけて徹底的に解説します。この記事を読み終える頃には、あなたは docker exec を自在に操り、Dockerコンテナとのより深いインタラクションを実現できるようになっているでしょう。

それでは、さっそく docker exec の世界に飛び込んでいきましょう。

Dockerコンテナへの「入り方」の概念

物理的なサーバーや仮想マシンでは、通常SSHプロトコルを使ってリモートログインし、シェルを起動して対話的に操作を行います。この感覚に慣れていると、「Dockerコンテナにもログインする」という表現を使ってしまいがちです。

しかし、Dockerコンテナへのアクセスは、SSHログインとは概念が少し異なります。Dockerコンテナは、特定のアプリケーションやサービスを実行するために設計されており、そのライフサイクルは比較的短く、「使い捨て」が基本原則とされています。コンテナが起動したら、定義されたメインプロセス(例えばWebサーバーやデータベースプロセス)が実行され、そのプロセスが終了すればコンテナも停止するのが基本的な挙動です。

従来のSSHサーバーのように、コンテナ起動時からバックグラウンドでSSHデーモンを起動し、いつでもログインできる状態にしておくのは、Dockerの哲学にはそぐわない場合が多いです。これは、コンテナをシンプルに保ち、必要最低限のプロセスだけを実行させるという考え方からです。SSHデーモンを起動することは、コンテナの起動時間やリソース消費を増やし、また攻撃対象となりうるサービスを余分に増やすことにも繋がります。

そこで登場するのが docker exec です。docker exec は、実行中のコンテナの中に、後から、任意のコマンドを実行するための新しいプロセスを起動します。SSHのようにコンテナの起動時から存在するプロセスにログインするのではなく、必要になったときに一時的にプロセスを作り出すイメージです。

コンテナ内部で作業する主な目的は、以下のようなものが考えられます。

  • デバッグ: アプリケーションが期待通りに動作しない原因を探る。ファイルシステムの状態、ログファイルの内容、実行中のプロセス、ネットワーク設定などを確認する。
  • 設定確認: コンテナ内の設定ファイルや環境変数が正しく読み込まれているかを確認する。
  • 一時的な管理操作: データベースのダンプ、キャッシュのクリア、特定のスクリプトの一回限りの実行などを行う。

これらの作業は、コンテナの恒久的な状態を変更するためではなく、あくまで一時的な調査や操作のために行われるべきです。Dockerのベストプラクティスでは、コンテナは可能な限り不変(Immutable)であるべきだと考えられています。つまり、コンテナが一度起動したら、その内部状態を外部から変更するのではなく、状態を変更したい場合は新しい設定やコードを含む新しいイメージを作成し、そのイメージから新しいコンテナを起動するというアプローチが推奨されます。

docker exec はこの不変性の原則に反する操作(コンテナ内部の状態を「その場で」変更する)を可能にしてしまう側面もあります。そのため、docker exec を使う際は、その操作が一時的なデバッグや確認のためのものであることを強く意識し、永続的な変更はDockerイメージの再構築やコンテナの再作成によって行うように心がけることが重要です。

このように、docker exec は「コンテナにログインする」というよりは「実行中のコンテナ内で一時的にコマンドを実行する」ためのツールであると理解するのが正確です。

docker execコマンドの基本

docker execコマンドの最も基本的な使い方は、実行中のコンテナ内で指定したコマンドを実行することです。

コマンドの基本構文は以下のようになります。

bash
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

  • [OPTIONS]: docker exec コマンド自体のオプションを指定します(後述します)。
  • CONTAINER: コマンドを実行したいコンテナを指定します。コンテナ名またはコンテナIDを使用できます。
  • COMMAND: コンテナ内部で実行したいコマンドを指定します。
  • [ARG...]: COMMAND に渡す引数を指定します。

最も一般的な使用例: シェルを起動して対話的に操作する

前述の通り、docker exec の典型的なユースケースは、コンテナ内部でインタラクティブなシェルを起動し、対話的に操作を行うことです。これは、物理マシンや仮想マシンにSSHでログインしてシェルを使う感覚に最も近いです。

例えば、my-web-app という名前で実行中のコンテナがあるとします。そのコンテナ内で Bash シェルを起動して操作したい場合、以下のコマンドを実行します。

bash
docker exec -it my-web-app /bin/bash

または、コンテナに /bin/bash が含まれていない場合は /bin/sh を使うことも多いです。

bash
docker exec -it my-web-app /bin/sh

このコマンドの重要な部分は -it オプションです。これは -i-t という二つのオプションを組み合わせたものです。

  • -i, --interactive: 標準入力を開いたままにします。これにより、シェルのプロンプトが表示された後、キーボードから入力を受け付けることができるようになります。このオプションがないと、コマンドは実行されてすぐに終了してしまい、対話的な操作ができません。
  • -t, --tty: 擬似端末 (pseudo-TTY) を割り当てます。これにより、コマンドの出力が整形され、シェルが適切に動作するようになります。例えば、色付きの出力やカーソル移動などが正しく機能するために必要です。このオプションがないと、シェルが「非対話的」なモードで起動されたと判断し、プロンプトが表示されなかったり、コマンドの出力が見づらくなったりします。

したがって、対話的なシェル操作を行うためには、-i-t の両方が必要です。この二つを組み合わせた -it は、docker exec で最も頻繁に使用されるオプションと言えるでしょう。

このコマンドを実行すると、ホストマシンのターミナルが、指定したコンテナ内部で実行されているシェルのセッションに切り替わります。まるでコンテナ内部に「入った」かのように、ls, cd, cat, ps などのコマンドを実行して、コンテナ内のファイルシステムやプロセスなどを操作・確認できます。

シェルセッションを終了するには、通常通り exit コマンドを入力するか、Ctrl+D を押します。シェルが終了すると、docker exec によって起動されたプロセスも終了し、ホストマシンのターミナルに戻ります。

重要な注意点: docker exec で起動されたシェルプロセスは、コンテナのメインプロセスとは独立しています。シェルプロセスが終了しても、コンテナのメインプロセス(そしてコンテナ自体)は引き続き実行されます。これは、docker attach との大きな違いです(後述します)。

docker execのオプション詳解

docker exec コマンドには、様々な挙動を制御するためのオプションが用意されています。ここでは、特によく使われる、あるいは知っておくべきオプションについて詳しく解説します。

-d, --detach: バックグラウンドでコマンドを実行

このオプションを指定すると、docker exec で実行するコマンドをバックグラウンドで実行させることができます。コマンドの標準出力や標準エラー出力は、デフォルトではホスト側のターミナルには表示されません。

bash
docker exec -d my-container touch /app/timestamp.txt

この例では、my-container/app/ ディレクトリに timestamp.txt という空のファイルを作成するコマンドをバックグラウンドで実行します。コマンドの実行結果(成功したかどうかなど)はすぐにフィードバックされず、コマンド自体はコンテナ内部で独立して実行されます。

バックグラウンド実行は、コンテナ内部で時間のかかる処理や、常に実行しておきたいスクリプトなどを一時的に起動したい場合に便利です。ただし、コンテナのメインプロセスが終了すると、docker exec -d で起動したプロセスも通常は終了します。

-e, --env: 環境変数を設定してコマンドを実行

このオプションを使うと、docker exec で実行するコマンドに対して、特定の環境変数を設定することができます。これは、コンテナ自体の起動時に設定された環境変数に追加または上書きする形で適用されます。

bash
docker exec -e "MY_VAR=my_value" my-container env | grep MY_VAR

上記の例では、my-container 内で env コマンドを実行する際に、MY_VAR という環境変数に my_value という値を設定します。そして、その出力から MY_VAR を含む行を grep で抽出しています。

複数の環境変数を設定することも可能です。-e オプションを複数回指定するか、カンマ区切りでまとめて指定します。

“`bash

オプションを複数回指定

docker exec -e “VAR1=value1” -e “VAR2=value2” my-container env

カンマ区切り(古いDockerバージョンの場合)

docker exec -e “VAR1=value1,VAR2=value2” my-container env # 非推奨、新しいバージョンでは動作しない可能性あり

推奨される方法: オプションを複数回指定

docker exec \
-e “DATABASE_URL=postgresql://user:pass@host:port/dbname” \
-e “API_KEY=abcdef12345” \
my-container printenv
“`

環境変数は、コンテナ内のアプリケーションが設定を読み込むためによく利用されます。docker exec -e は、コンテナ自体を再起動することなく、特定の環境変数を一時的に変更してコマンドの挙動をテストしたい場合に有効です。ただし、この方法で設定された環境変数は、docker exec で起動されたプロセスとその子プロセスにのみ有効であり、コンテナのメインプロセスや、他の docker exec で起動されたプロセスには影響しません。

機密情報(パスワードなど)を環境変数として渡す場合は、シェルの履歴に残らないように注意が必要です。可能であれば、環境変数ファイルを使用したり、機密情報は別の安全な方法でコンテナに渡したりすることを検討しましょう。しかし、docker exec 自体には環境変数ファイルを指定するオプションはありません。docker run の場合は --env-file オプションがありますが、exec の場合は -e で直接指定するか、以下のようにシェルスクリプトを介して実行する必要があります。

“`bash

環境変数ファイルから読み込んでexecする場合の工夫例

env_vars.txt の内容:

MY_VAR=my_value

OTHER_VAR=other_value

docker exec my-container sh -c ‘export $(cat env_vars.txt) && your_command’
“`

ただし、上記の例ではホスト側のファイル env_vars.txt を直接コンテナ内部に読み込むわけではないため、ファイルの内容をコマンド文字列に埋め込む形になります。機密情報の場合は依然として注意が必要です。より安全な方法は、docker exec を使うのではなく、イメージビルド時や docker run 時に環境変数を設定することです。docker exec -e は、一時的なデバッグ用途に限定するのが良いでしょう。

-u, --user: 指定したユーザーでコマンドを実行

このオプションを使用すると、コンテナ内部でコマンドを実行するユーザーを指定できます。ユーザー名またはUID (User ID) と、オプションでグループ名またはGID (Group ID) を指定できます。

“`bash

ユーザー名を指定

docker exec -u www-data my-container whoami

UIDを指定

docker exec -u 33 my-container whoami

UIDとGIDを指定 (UID:GID の形式)

docker exec -u 1000:1000 my-container id

ユーザー名とグループ名を指定 (ユーザー名:グループ名の形式)

docker exec -u root:root my-container id
“`

コンテナ内で実行されるプロセスは、デフォルトではDockerfileで USER 命令によって指定されたユーザー、または指定されていない場合はrootユーザーで実行されます。セキュリティの観点から、コンテナ内部の操作はroot以外の権限の低いユーザーで行うことが推奨される場合があります。-u オプションを使えば、一時的にroot以外のユーザーとしてコマンドを実行し、権限に関する問題をデバッグしたり、意図せずシステム全体に影響を与えるような操作を防いだりできます。

ただし、指定するユーザーやグループは、コンテナイメージ内に存在している必要があります。存在しないユーザー/UIDやグループ/GIDを指定した場合、コマンドは失敗するか、予期しないユーザーで実行される可能性があります。

権限が低いユーザーで実行する場合、一部のディレクトリへのアクセスやコマンドの実行が拒否されることがあります。これはセキュリティ的には望ましい挙動ですが、デバッグ中に必要なファイルが見えなかったり、コマンドが実行できなかったりする場合があるため、その際は -u root を一時的に使用することも検討が必要になるかもしれません。しかし、その場合は操作に十分注意が必要です。

-w, --workdir: 作業ディレクトリを指定してコマンドを実行

このオプションを使用すると、コマンドを実行する際の作業ディレクトリを指定できます。デフォルトでは、Dockerfileの WORKDIR 命令で指定されたディレクトリ、または指定されていない場合はルートディレクトリ (/) が使用されます。

bash
docker exec -w /app/config my-container ls -l

この例では、my-container/app/config ディレクトリを作業ディレクトリとして、ls -l コマンドを実行します。これにより、/app/config ディレクトリの内容が一覧表示されます。

作業ディレクトリを指定することで、コンテナ内部の特定のパスにあるファイルやディレクトリを操作する際に、cd コマンドを使ってから別のコマンドを実行するよりも簡潔に記述できます。特に、複数のコマンドを連結して実行する場合などに便利です。

“`bash

作業ディレクトリを指定しない場合

docker exec my-container sh -c ‘cd /app/scripts && ./run.sh’

作業ディレクトリを指定した場合

docker exec -w /app/scripts my-container ./run.sh
“`

後者の例の方がシンプルに記述できます。

--privileged: 拡張権限でコマンドを実行(非推奨、危険性)

このオプションを指定すると、コンテナ内部で実行されるコマンドにホストシステムへの広範なアクセス権限が与えられます。具体的には、コンテナ内のプロセスがホストのカーネル機能にほぼ無制限にアクセスできるようになります。これは、コンテナが独立した環境として機能するDockerの基本的なセキュリティモデルを破る行為です。

“`bash

絶対に必要な場合以外は使用しないでください!

docker exec –privileged my-container mount /dev/sdb1 /mnt
“`

--privileged オプションは非常に強力で危険なオプションです。コンテナ内部からホストシステムのファイルシステムをマウントしたり、ホストのデバイスに直接アクセスしたりといった、通常は不可能な操作が可能になります。これは、コンテナの分離性が失われ、コンテナからのエスケープ(コンテナを抜け出してホストシステムにアクセスすること)が容易になることを意味します。

このオプションは、特定の特殊なユースケース(例:Dockerコンテナ内でDockerを動かす、特定のハードウェアデバイスにアクセスする必要がある)のために提供されていますが、通常の使用においては絶対に避けるべきです。 デバッグ目的であっても、まずは他の方法(ログの確認、権限の低いユーザーでの実行など)を試み、どうしても必要な場合にのみ、その危険性を十分に理解した上で限定的に使用すべきです。

もし、コンテナ内で特定のデバイスへのアクセスや特定のカーネル機能の使用が必要な場合は、--cap-add, --cap-drop, --device などのより限定的なオプションを使用することを検討してください。これらのオプションは --privileged ほど広範な権限を与えることなく、必要な権限のみをコンテナに付与することができます。

その他のオプション

他にもいくつかのオプションがありますが、利用頻度は前述のものほど高くありません。

  • --env-file=[]: 残念ながら docker exec にはこのオプションはありません。環境変数をファイルから読み込みたい場合は、前述のように sh -c 'export $(cat ...) のようなシェルトリックを使う必要があります。
  • --interactive / -i: 標準入力を開いたままにする。対話的なシェルには必須。
  • --tty / -t: 擬似端末を割り当てる。対話的なシェルには必須。

これらのオプションを適切に組み合わせることで、docker exec の柔軟性は大幅に向上します。

docker execの実践的な利用例

ここでは、docker exec を実際にどのような場面で活用できるのか、具体的なコマンド例を交えて紹介します。

デバッグ

アプリケーションの動作がおかしい、エラーが発生しているといった状況で、コンテナ内部の状態を調査するために docker exec は非常に役立ちます。

  • ログの確認:
    アプリケーションが出力するログファイルがコンテナ内部に保存されている場合、docker exec でそのファイルの内容を確認できます。

    bash
    docker exec my-web-app cat /var/log/my-app/error.log

    もしログファイルが大きすぎる場合は、tail コマンドを使うこともできます。

    bash
    docker exec my-web-app tail -f /var/log/my-app/access.log

    -f オプションを付けることで、リアルタイムでログの追記を確認できます。この場合も、対話的なセッションとして実行するために -it を付けると良いでしょう。

    bash
    docker exec -it my-web-app tail -f /var/log/my-app/access.log

  • プロセスリストの確認:
    コンテナ内でどのようなプロセスが実行されているかを確認できます。想定外のプロセスが動いていないか、必要なプロセスが起動しているかなどをチェックします。

    bash
    docker exec my-web-app ps aux

    または、より詳細な情報やツリー表示が必要な場合は pstree など、コンテナに含まれているコマンドを使用します。

  • ネットワーク状態の確認:
    コンテナ内部からのネットワーク接続を確認したり、開いているポートを調べたりできます。(ただし、これらのコマンドが含まれているイメージである必要があります)

    “`bash

    開いているポートを確認 (netstat が含まれている場合)

    docker exec my-web-app netstat -tulnp

    DNS解決をテスト (ping が含まれている場合)

    docker exec my-web-app ping -c 3 google.com

    ネットワークインターフェースとIPアドレスを確認 (ip が含まれている場合)

    docker exec my-web-app ip addr show
    “`

  • ファイルシステムの確認:
    コンテナ内のファイルやディレクトリの存在、パーミッション、内容などを確認します。

    “`bash

    特定のディレクトリの内容を一覧表示

    docker exec my-web-app ls -l /app/config

    特定のファイルの内容を表示

    docker exec my-web-app cat /etc/nginx/nginx.conf

    ファイルのパーミッションを確認

    docker exec my-web-app stat /app/data/some_file
    “`

  • 特定のコマンドの実行と出力確認:
    アプリケーションの一部機能のみをコンテナ内部で実行して、その出力を直接確認します。

    “`bash

    データベースコンテナで特定のSQLクエリを実行 (psql が含まれている場合)

    docker exec some-postgres-db psql -U myuser -d mydb -c “SELECT COUNT(*) FROM users;”

    アプリケーションのヘルスチェックコマンドを実行

    docker exec my-api-service ./bin/healthcheck.sh
    “`

設定確認

コンテナに渡した環境変数や設定ファイルが正しく反映されているかを確認します。

  • 環境変数の確認:
    コンテナ内部から見える環境変数を確認します。

    bash
    docker exec my-web-app env

    特定の環境変数のみを確認したい場合は、grep などと組み合わせます。

    bash
    docker exec my-web-app env | grep DATABASE_URL

  • 設定ファイルの読み取り:
    アプリケーションが使用する設定ファイルの内容を直接確認します。

    bash
    docker exec my-web-app cat /etc/app/settings.yaml

一時的な操作

コンテナの稼働を維持したまま、一度だけ実行したい管理タスクなどを行います。

  • データベースのバックアップ・リストア:
    データベースコンテナ内で、ダンプツールを使ってデータベースのバックアップを作成したり、リストアを実行したりします。

    “`bash

    PostgreSQLのダンプを作成し、ホストに保存(ボリュームや標準出力を利用)

    docker exec some-postgres-db pg_dump -U myuser mydb > latest_backup.sql

    MySQLのダンプを作成し、ホストに保存

    docker exec some-mysql-db mysqldump -u myuser -p’mypassword’ mydb > latest_backup.sql

    バックアップファイルからリストア (ホストからコンテナへのファイルコピーは別コマンドが必要、例えば docker cp)

    まずホストからコンテナにファイルをコピー

    docker cp my_backup.sql some-postgres-db:/tmp/my_backup.sql

    コンテナ内部でリストア実行

    docker exec some-postgres-db psql -U myuser -d mydb < /tmp/my_backup.sql

    一時ファイルを削除

    docker exec some-postgres-db rm /tmp/my_backup.sql
    ``
    データベースのバックアップ・リストアは重要かつデリケートな操作なので、手順をよく確認し、必要に応じて公式ドキュメントなどを参照してください。上記の例はあくまで
    docker exec` の利用例として示したものです。

  • キャッシュのクリア:
    アプリケーション内部のキャッシュをクリアするコマンドを実行します。

    bash
    docker exec my-php-app php artisan cache:clear
    docker exec my-ruby-app bundle exec rails cache:clear

  • 特定のスクリプトの実行:
    コンテナイメージには含まれているが、メインプロセスとは別に実行したいスクリプトを実行します。

    bash
    docker exec my-worker-app ./scripts/run_migrations.sh

複数のコマンド実行

docker exec は本来一つのコマンドとその引数を実行するためのものですが、シェルを介することで複数のコマンドを連結して実行することも可能です。これは、コンテナ内部で一連の操作を行いたい場合に便利です。

Unix系のシェルでは、セミコロン (;) や && (論理AND) を使って複数のコマンドを一行で記述できます。これを docker execCOMMAND 部分に指定するシェル(例: /bin/sh/bin/bash)の引数として渡します。

“`bash

セミコロンで連結: 前のコマンドの成否に関わらず次のコマンドを実行

docker exec my-container sh -c ‘cd /app && ls -l; echo “Done”‘

&& で連結: 前のコマンドが成功した場合のみ次のコマンドを実行

docker exec my-container sh -c ‘cd /app && ./run_script.sh && echo “Script executed successfully”‘
“`

上記の例で重要なのは、sh -c '...' のようにシェルを明示的に指定し、そのシェルに対して実行したいコマンド文字列を引数として渡している点です。これにより、シェルがコマンド文字列を解釈して複数のコマンドを実行してくれます。

また、パイプ (|) を使ってコマンドの出力を別のコマンドの入力として渡すことも可能です。

“`bash

ps aux の結果を grep でフィルタリング

docker exec my-container sh -c ‘ps aux | grep my_process’
“`

複数のコマンドを連結する場合、コマンド文字列全体のクォーテーション(一重引用符 ' や二重引用符 ")や、コマンド内の特殊文字(スペース、パイプ |、リダイレクト >, ;, &, $ など)のエスケープに注意が必要です。一般的には、一重引用符 ' でコマンド文字列全体を囲むことで、その中の特殊文字がホスト側のシェルではなく、コンテナ内部のシェルによって解釈されるようにするのが安全です。

これらの例からもわかるように、docker exec はデバッグから一時的な管理タスクまで、コンテナ内部での様々な操作を可能にする非常に柔軟なツールです。

docker execの高度なトピック

対話的なシェルがないコンテナ

一部の軽量なコンテナイメージ(例: alpine, busybox, または scratch をベースにしたイメージ)には、完全なBashやZshのようなシェルが含まれていない場合があります。特に、scratch イメージは最小限のバイナリしか含まないため、/bin/bash/bin/sh といったシェルは存在しません。

このようなコンテナに対して docker exec -it my-minimal-container /bin/bash のようなコマンドを実行しようとすると、「executable file not found in PATH」といったエラーが表示され、シェルが起動できません。

しかし、シェルがない場合でも、docker exec はコンテナに含まれている個別の実行可能ファイルを起動することは可能です。例えば、/bin/ls/bin/cat といった基本的なコマンドがコンテナに含まれていれば、それらを直接実行できます。

“`bash

シェルはないが /bin/ls があるコンテナの場合

docker exec my-minimal-container /bin/ls /app
“`

シェルがないコンテナでデバッグを行う必要がある場合、以下のような選択肢が考えられます。

  1. コンテナに含まれている基本的なコマンドのみでデバッグを行う: ls, cat, ps (もし含まれていれば) など、利用可能な最小限のツールで情報を収集します。
  2. 一時的にデバッグツールを追加する: デバッグに必要なツール(例: curl, ping, netstat, デバッガなど)を、docker exec と別のコマンド(例: apk add (Alpine Linuxの場合) や apt-get update && apt-get install (Debian/Ubuntuベースの場合))を組み合わせて一時的にインストールします。

    “`bash

    Alpine Linux ベースのコンテナで curl を一時的にインストールして使う例

    docker exec my-alpine-container apk update
    docker exec my-alpine-container apk add curl
    docker exec my-alpine-container curl http://localhost:8080/health
    docker exec my-alpine-container apk del curl # 使い終わったら削除するのが望ましい
    “`
    ただし、この方法はコンテナのファイルシステムに一時的な変更を加えることになり、不変性の原則に反します。また、インストールしたツールを削除し忘れると、コンテナイメージに余分なものが残ってしまう可能性もあります。デバッグ後はコンテナを再起動(新しいイメージで再作成)することを強く推奨します。
    3. デバッグ用のイメージを作成する: 運用環境用の軽量なイメージとは別に、デバッグ用のツールをあらかじめ含めた別のDockerイメージを作成しておき、問題発生時にそのイメージで新しいコンテナを起動するか、実行中のコンテナをそのデバッグイメージのコンテナに置き換えるというアプローチもあります。これは、コンテナを使い捨てるというDockerの思想に合致したより良い方法と言えます。

exec権限がないユーザー

-u オプションで権限の低いユーザーを指定して docker exec を実行した場合、そのユーザーが実行権限を持っていないコマンドは実行できません。例えば、/root ディレクトリの中身を見ようとしたり、システム設定ファイルを変更しようとしたりすると、Permission denied エラーが発生します。

これはセキュリティ上は望ましい挙動ですが、デバッグ中に必要な情報にアクセスできない場合は困ります。このような場合、以下のような対応が考えられます。

  1. 一時的にrootユーザーで実行する: デバッグに必要な操作に限って、-u root オプションを付けて docker exec を実行します。ただし、この操作は非常に危険なので、細心の注意を払って、必要な操作だけを行い、すぐに終了することが重要です。
  2. コンテナ内部に sudo をインストールし、設定する: コンテナイメージ自体に sudo コマンドを含め、権限の低いユーザーが特定のコマンドを sudo 経由でroot権限で実行できるように設定します。ただし、これはイメージを大きくし、設定も必要になるため、単純なデバッグ目的には過剰かもしれません。
  3. 必要な情報をコンテナ外部に出力させる: デバッグに必要な情報(ログファイルの内容、設定ファイルの内容など)を、docker logs コマンドで取得できるように標準出力/標準エラー出力にリダイレクトするか、共有ボリューム (-v) を使ってホスト側にファイルとして出力するようにアプリケーションやコンテナの設定を変更することを検討します。これにより、docker exec -u root ... のような危険な操作を回避できる場合があります。

非対話的なコマンド実行

-it オプションを付けずに docker exec を実行すると、コマンドは非対話的に実行されます。この場合、標準入力は閉じられ、擬似端末も割り当てられません。これは、コンテナ内部で単一のコマンドを実行してその出力を取得したり、バックグラウンドで特定の処理を開始したりする場合に使用します。

“`bash

非対話的にlsコマンドを実行し、結果をホスト側のターミナルに表示

docker exec my-container ls -l /app

非対話的にバックグラウンドでスクリプトを実行 (-d と組み合わせることが多い)

docker exec -d my-container /app/scripts/background_job.sh
“`

非対話的な実行では、コマンドの標準出力 (stdout) と標準エラー出力 (stderr) は、デフォルトでは docker exec コマンドを実行したホスト側のターミナルに表示されます。コマンドが成功したか失敗したかは、docker exec コマンド自体の終了コードを確認することで判断できます。docker exec の終了コードは、コンテナ内部で実行されたコマンドの終了コードを反映します。

“`bash

コマンドを実行し、終了コードを表示

docker exec my-container ls /non_existent_directory
echo $? # 直前のコマンドの終了コードを表示 (bashの場合)

-> 2 (lsがファイルが見つからない場合などに返す終了コード)

docker exec my-container ls /app
echo $?

-> 0 (lsが成功した場合に返す終了コード)

“`

スクリプトから docker exec を実行して自動化する場合など、対話的な操作が不要な場合は、-it を付けずに非対話的に実行するのが一般的です。

docker execと他のコンテナ操作コマンドとの比較

Dockerには、docker exec 以外にもコンテナを操作するためのコマンドがいくつかあります。docker exec の位置づけを理解するために、それらのコマンドと比較してみましょう。

docker run

docker run コマンドは、指定したイメージから新しいコンテナを作成し、その中でコマンドを実行する際に使用します。

bash
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

  • docker exec: 実行中の既存のコンテナに対して新しいプロセスを起動し、コマンドを実行します。
  • docker run: 新しいコンテナを作成・起動し、そのコンテナのメインプロセスとしてコマンドを実行します。

docker run はコンテナのライフサイクルの開始点です。アプリケーションのデプロイや、一度だけ実行するバッチ処理用のコンテナなどを起動する際に使用します。docker exec は、既に起動しているコンテナの状態を調べたり、一時的な操作を行ったりする際に使用します。

例えば、「ubuntu」イメージを使って簡単なコマンドを実行したい場合:

“`bash

新しいコンテナを作成して ls コマンドを実行し、コンテナは終了する

docker run ubuntu ls -l /

既存の ubuntu コンテナ (例えば run されてバックグラウンドで動いているもの) に対して ls コマンドを実行

(まずコンテナ名やIDを確認する必要がある: docker ps)

docker exec ls -l /

“`

docker attach

docker attach コマンドは、実行中のコンテナの標準入出力や標準エラー出力、または擬似端末に「アタッチ」します。これにより、コンテナのメインプロセス(通常は docker runCOMMAND または Dockerfileの CMD/ENTRYPOINT で指定されたプロセス)と直接対話できるようになります。

bash
docker attach [OPTIONS] CONTAINER

  • docker exec: 新しいプロセス(例: /bin/bash)をコンテナ内部で起動し、そのプロセスと対話します。docker exec で起動したプロセスが終了しても、コンテナのメインプロセスは影響を受けません。
  • docker attach: コンテナのメインプロセスにアタッチします。アタッチしたセッションで exit を入力するなどしてメインプロセスが終了すると、通常コンテナ自体も停止します。

docker attach は、コンテナのメインプロセスが出力するログをリアルタイムで見たい場合や、メインプロセスが対話的なコマンド(例: データベースのシェル)である場合に役立ちます。しかし、メインプロセスを誤って終了させてしまうリスクがあるため、デバッグなどでコンテナ内部に入りたい場合は、安全な docker exec -it /bin/bash の方が一般的に推奨されます。

例えば、docker run -it ubuntu /bin/bash で起動したコンテナにアタッチすると、そのコンテナのBashセッションに入れます。ここで exit するとコンテナが停止します。一方、docker run -d ubuntu sleep infinity でバックグラウンド起動したコンテナに対し、docker exec -it <container_id> /bin/bash で入ったセッションで exit しても、コンテナは sleep infinity を実行し続けて停止しません。

docker execを使う上での注意点とベストプラクティス

docker exec は強力で便利なツールですが、その特性を理解せずに使用すると、予期せぬ問題を引き起こしたり、Dockerのベストプラクティスから外れてしまったりする可能性があります。以下に、使う上での注意点と推奨されるプラクティスをまとめます。

不変性 (Immutability) の原則を理解する

Dockerコンテナは、可能な限り不変(Immutable)であるべきという思想に基づいています。これは、一度ビルドされたコンテナイメージから起動したコンテナは、その実行中に内部状態を外部から変更するべきではなく、状態を変更したい場合は新しい設定やコードを含む新しいイメージを作成し、そのイメージから新しいコンテナを起動・デプロイするという考え方です。

docker exec を使ってコンテナ内部でファイルシステムを変更したり、設定を書き換えたりすることは、この不変性の原則に反します。docker exec による変更は、そのコンテナが停止・削除されると失われてしまいます。また、同じイメージから別のコンテナを起動しても、docker exec で行った変更は引き継がれません。

したがって、docker exec は主に以下の目的に限定して使用するのが適切です。

  • デバッグと調査: 問題発生時にコンテナの現在の状態を確認する。
  • 一時的な操作: 一度だけ実行する必要がある管理タスク。

恒久的な変更や設定の適用は、以下のような代替手段を検討すべきです。

  • Dockerfileを変更してイメージを再ビルドする: アプリケーションコードや設定ファイルを変更する場合。
  • コンテナ起動時にボリュームマウントで設定ファイルを注入する: アプリケーションの実行時設定を変更する場合。
  • コンテナ起動時に環境変数を設定する: アプリケーションの実行時設定を環境変数で渡せる場合。
  • 新しい設定やコードを含む新しいイメージでコンテナを再作成する: デプロイや設定変更の標準的な手順。

デバッグツールについて

運用環境で動作するコンテナイメージは、セキュリティやサイズを考慮して、必要最低限のファイルやツールのみを含むように軽量化されていることが一般的です。ping, curl, netstat, vim などのデバッグや管理ツールは、運用イメージには含まれていないことがあります。

このような場合、デバッグのために一時的に必要なツールを docker exec を使ってコンテナ内部にインストールすることは可能ですが(前述の「対話的なシェルがないコンテナ」の項を参照)、これはあくまで一時的な手段として限定すべきです。

より良いアプローチとしては、以下のようなものが考えられます。

  • デバッグ用のイメージを別途用意する: 運用イメージをベースにしつつ、デバッグ用のツール(シェル、ネットワークツール、エディタなど)を追加したイメージを作成しておきます。問題発生時は、このデバッグイメージを使って新しいコンテナを起動し、ボリュームマウントなどで問題が発生しているコンテナと同じデータにアクセスして調査を行います。
  • マルチステージビルドを活用する: アプリケーションのビルドに必要なツール(コンパイラ、リンター、テストツールなど)はビルドステージのみに含め、最終的な実行可能イメージには含めないようにします。これにより、運用イメージは軽量に保ちつつ、必要であればビルドステージで使われたツールを含むイメージをデバッグに利用することも可能です。

セキュリティに関する注意

docker exec は、実行中のコンテナ内部で任意のコマンドを実行できる強力な機能です。悪意のあるユーザーや、意図しないコマンド実行によって、コンテナ内部やホストシステムに損害を与える可能性があります。

特に以下の点に注意が必要です。

  • --privileged オプションは避ける: 前述の通り、このオプションはコンテナのセキュリティ分離をほぼ無効にしてしまいます。絶対に必要な場合以外は使用しないでください。
  • root権限での実行を最小限にする: -u root オプションを使用する場合、その操作がシステム全体に影響を与える可能性があることを常に意識し、必要最低限の操作に限定します。可能な限り、権限の低いユーザー (-u <username>) で操作するようにします。
  • アクセス制御を適切に行う: docker exec コマンドを実行できるのは、ホストマシンのDockerデーモンにアクセスできるユーザーです。Dockerデーモンへのアクセス権限は慎重に管理し、信頼できるユーザーのみに付与するようにします。Dockerデーモンはデフォルトではroot権限で動作することが多いため、デーモンへのアクセス権限は実質的にroot権限と同等の強力な権限を意味します。
  • コンテナ内部に入れる情報を限定する: コンテナイメージに機密情報(パスワード、APIキーなど)を直接含めないようにします。これらは環境変数やボリュームマウントでコンテナに渡すのが一般的ですが、docker exec env のようなコマンドで容易に見えてしまう可能性があります。機密情報は、Docker Secrets や Kubernetes Secrets といった安全な方法で管理することを検討すべきです。
  • シェル履歴に注意する: 対話的なシェル (-it) でコマンドを実行した場合、そのコマンド履歴はコンテナ内部のユーザーのホームディレクトリなどに保存される可能性があります。機密情報を含むコマンドを実行した場合は、履歴に残らないように注意するか、実行後に履歴ファイルを削除することを検討してください。(ただし、履歴ファイルの保存場所やフォーマットはシェルや設定によって異なります。)

ロギング

docker exec コマンド自体は、ホストシステムのDockerイベントログに残ります(例えば docker events コマンドで見ることができます)。しかし、docker exec で起動したシェルセッション内で実行された個々のコマンドやその操作内容のログは、デフォルトではDockerのログ機構では記録されません。

コンテナ内部での操作を監査したい場合は、コンテナイメージ内にシェル履歴の保存設定を強化したり、コマンド実行を記録するツール(例: auditd や特定のシェルのロギング機能)を組み込む必要があります。これは高度な設定であり、コンテナイメージのサイズや複雑性を増加させる可能性があります。

代替手段の検討

繰り返しになりますが、docker exec によるコンテナ内部の「その場での変更」は、Dockerの不変性という思想とは少し異なります。もしあなたが docker exec を使って以下のような操作を頻繁に行っている場合、それはより良い代替手段がある可能性を示唆しています。

  • 設定ファイルの変更: ボリュームマウント (-v) でホスト側の設定ファイルをコンテナにマウントし、ホスト側でファイルを編集して変更を反映させる。あるいは、新しい設定ファイルを含んだイメージを再ビルドし、コンテナを再作成する。
  • 新しいファイルの追加: 新しいファイルを含んだイメージを再ビルドする。あるいは、データ永続化用のボリューム (-v) をマウントし、ホスト側からファイルをボリュームにコピーする (docker cp)。
  • データベースマイグレーションや初期データの投入: アプリケーションの起動時にマイグレーションを自動実行するようにするか、別のコンテナ(例: マイグレーション実行用のワンオフコンテナ)を docker run で起動して実行する。
  • 定期的なタスクの実行: コンテナ内部でcronのようなスケジューラを動かすか、ホスト側のcronや別のスケジューラから docker exec を定期的に実行するスクリプトを起動する。ただし、定期的なタスクは専用のコンテナ(cronコンテナなど)として分離するのが一般的な設計です。

docker exec はデバッグや緊急時の対応に特化したツールと位置づけ、日常的な運用やデプロイの手順には組み込まないようにするのが、Dockerをより効果的かつ安全に活用するためのベストプラクティスと言えます。

スクリプト化について

複数の docker exec コマンドを連続して実行したり、docker exec の出力を他のコマンドで処理したりする場合、それらをシェルスクリプトとしてまとめることがよくあります。

例えば、複数のコンテナのログファイルの一部をまとめて取得するスクリプトなどです。

“`bash

!/bin/bash

CONTAINERS=(“web-app-1” “web-app-2” “web-app-3″)
LOG_FILE=”/var/log/app/production.log”

for container in “${CONTAINERS[@]}”; do
echo “— Logs from $container —”
docker exec “$container” cat “$LOG_FILE” | tail -n 100
echo “”
done
“`

スクリプト化することで、手作業によるミスを減らし、繰り返しの作業を効率化できます。ただし、前述のように、docker exec で行う操作は不変性の原則に反しない、一時的なものに限定すべきです。スクリプト化するほどの繰り返し作業であれば、それはコンテナのビルドプロセスや、コンテナオーケストレーションツール(Kubernetes, Docker Swarmなど)の機能を使って実現できないかを検討するのが望ましいでしょう。

まとめ

本記事では、Dockerコンテナの内部にアクセスするための主要なコマンドである docker exec について、その基本から応用、そして注意点までを詳しく解説しました。

docker exec は、実行中のコンテナ内に新しいプロセスを起動することで、コンテナのファイルシステムを操作したり、コマンドを実行したりすることを可能にします。これは、開発中のデバッグ、運用環境での問題調査、一時的な管理タスクなど、多岐にわたる場面で非常に役立つ機能です。

最も一般的な使い方は、-it オプションを使用して対話的なシェル(/bin/bash/bin/sh)を起動し、コンテナ内部でコマンドを自由に入力・実行することです。-i は標準入力の維持に、-t は擬似端末の割り当てにそれぞれ必要であり、対話的な操作には不可欠な組み合わせです。

また、-d (バックグラウンド実行), -e (環境変数設定), -u (ユーザー指定), -w (作業ディレクトリ指定) など、様々なオプションを活用することで、docker exec の挙動を細かく制御できます。特に -u オプションは、セキュリティのために権限の低いユーザーでコマンドを実行する際に重要です。ただし、--privileged オプションのように強力すぎる権限を与えるオプションは、その危険性を十分に理解し、避けるべきです。

docker exec を使う上では、Dockerコンテナが「使い捨て」であり、不変(Immutable)であることを理解することが非常に重要です。docker exec によるコンテナ内部への変更は一時的なものであり、コンテナの再起動や再作成によって失われます。永続的な設定変更やアプリケーションの更新は、Dockerイメージの再ビルドやボリュームマウント、環境変数の設定といった、よりDockerの哲学に則った方法で行うべきです。docker exec はあくまで、一時的なデバッグや調査のためのツールとして活用するのがベストプラクティスです。

シェルが含まれていない軽量イメージでの docker exec の使い方や、非対話的な実行によるスクリプトからの利用方法、そして docker rundocker attach といった関連コマンドとの違いについても解説しました。これらの知識を持つことで、Dockerコンテナとのインタラクションにおいて、状況に応じて最適なコマンドを選択できるようになります。

docker exec は非常に強力なツールである反面、セキュリティリスクも伴います。-u root--privileged の使用は最小限に留め、Dockerデーモンへのアクセス制御を適切に行うなど、セキュリティ上の注意点を守ることが重要です。

本記事を通じて、あなたは docker exec コマンドの様々な側面について深く理解できたはずです。ぜひこの知識を活かして、Dockerコンテナでの開発、デバッグ、運用をより効率的かつ安全に進めてください。

付録/参考情報

  • docker exec --help: docker exec コマンドのすべてのオプションと簡単な説明を確認できます。
  • Docker公式ドキュメント: https://docs.docker.com/engine/reference/commandline/exec/
  • 関連するDockerコマンド:
    • docker ps: 実行中のコンテナ一覧を表示します。docker exec するコンテナのIDや名前を確認する際に使用します。
    • docker logs: コンテナの標準出力/標準エラー出力に記録されたログを取得します。ファイルではなく標準出力にログを出力するようにアプリケーションが設定されている場合、こちらの方が簡単にログを確認できます。
    • docker attach: 実行中のコンテナのメインプロセスにアタッチします。docker exec とは異なる用途で使用されます。

これらのリソースも活用しながら、あなたのDockerスキルをさらに磨いていってください。

コメントする

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

上部へスクロール