docker exec を使いこなす

多くのdockerイメージは単体デーモンが起動する形式で作られているため、コンテナ内のコマンドを実行する手順が思いつかなくなりがちです。
このような場合、たいていはdocker execコマンドの理解を深めることで必要な操作をカバーできます。

「dockerコンテナにSSHログインする手順は?」と思うケースの多くは、特別な準備の必要なくdocker execでカバーされます。
コンテナにシェルログインさえできてしまえば、従来型のサーバー管理と同じように操作できるため、まずはコンテナに入る手順を覚えることがお勧めです(コンテナログイン不要のケースも後述)。

また、やや特殊なケースながら、ワーキングディレクトリに依存する操作への対応を後半で補足します。

コンテナ上でコマンドを実行

docker execの基本的な使い方は、コンテナ名の後にコマンドを並べる書式です。たとえばコンテナ内で実行中のプロセスをpsコマンドで調べる場合、以下のように実行します。

$ docker exec your-container-name ps aux

dockerコンテナへのbashログインは、atachではなくexec -itで

dockerのコンテナ・インスタンスの状態を知りたいとき、通常のOSと同じようにbashなどでシェル操作したい場合があります。この場合、docker exec -itオプションが使えます。

起動中のdockerに接続するコマンドにはdocker attachがあります。メインのコマンドとしてシェルを動作させているコンテナであれば、docker attachで接続して仮想マシンのように使えます(切断は Ctrl-p Ctrl-q コマンド)。

ただし、docker attachは使える環境が限定されるため、あまり使う必要がありません。
一般的なdockerコンテナはデーモンを起動するように作られているため、docker attachで接続するとデーモンのログが表示されるだけで何もできず、Ctrl-cで接続を切るとコンテナ・インスタンスごと停止する、という挙動になります。

場合によってはログも出力されないこともあるため、「docker attachできない。」と思ってしまいがちですが、おそらく正確にはデーモンがフォアグラウンド動作しているttyに接続できていて、シェルで操作できない状況です。
(これは、リアルタイムにログを見る、という操作になっています。ただし、ログを見るにはdocker logs -fの方が性能が良く、ここでもdocker attachには出番がありません)

SSHのようにコンテナにログインしたい場合は、以下のようにdocker execでbashなどのシェルを起動して、別のttyから操作する必要があります。

$ docker exec -it INSTANCE_NAME /bin/bash

-uオプションで実行ユーザーを指定することも可能です。

リモートホスト上のdockerコンテナについては、dockerにsshdをバンドルするのではなく、ホストOSにSSHログインしたうえでdocker exec -itという手順になります。

クラスタ構成については Kubernetes の方がこなれており、dockerには限界があります。Kubernetesにもkubectl execというdocker execと同じ用途のコマンドがあります。

接続に成功すると、コンテナ上のプロンプトが表示され、psやlsなども使えます。ただし、実行可能なコマンドはdockerイメージにインストールされているものに限られます。往々にしてviはインストールされていなかったりします。

いずれも作業が終了したら、exitコマンドで切断できます。

コンテナログインは不要

VPSや仮想マシンと同様の使い方をしたい場合にコンテナログインは直感的ですが、コンテナ上のコマンドを実行するには単にdocker execに直接コマンドを指定するだけで足りるため、ログインしないオペレーションに慣れた方が良いでしょう。

コンテナとは標準入出力でパイプ接続可能なため、たとえばPostgreSQLのバックアップ・リストアは以下のようなコマンドでネットワーク越しに直接実行可能です。

$ docker exec pg_container pg_dump -Upostgres target_db > backup.sql
$ cat backup.sql | docker exec -i pg_container psql -Upostgres

以下のように標準入出力の前後でコンテナ上の動作とクライアント側の動作が分かれており、シェルのリダイレクトでネットワーク転送できます。
また、dockerとkubernetesのコマンド構成はほぼ同じです。

また、REPL環境がある場合には

$ docker exec -it pg_container psql -Upostgres

のようにpsqlに直接接続したり、rails consoleを起動することも可能です。

コンテナが停止しているとdocker execは実行できない

docker execはコンテナのデバッグなどに役立つのですが、ターゲットのコンテナが起動中でないと実行できないという制約には注意が必要です。
起動中のコンテナはdocker psで確認できます。

単純に停止しているコンテナであればdocker start your-container-nameで起動すれば良いのですが、コンテナが起動途中で異常終了してしまっているようなケースでは利用できません。

この場合、docker logs your-container-imageでログを確認する必要があります。アプリケーションレベルのエラーをdocker logsに出力するためには、アプリケーションの設定でログ出力先を/dev/stdoutや/dev/stderrに指定します。

docker execを使いたいケースの多くはコンテナ起動エラーであったりするので、ログの確保も重要なポイントになります。

Tips: bashコマンド化

シェルログインを頻繁に行う場合、~/.bashrcに以下のような関数を定義しておくと、dbash コンテナ名のように実行できて便利です。末尾のbashを付け忘れてエラー、という失敗がなくなります。

function dbash() {
  command docker exec -it $1 bash
}

Tips: docker execのカレントディレクトリを変更してコマンド実行

Rubyのbundlerなどでbundle execを実行したい場合のように特定のカレントディレクトリでコマンドを実行しなくてはならないケースがあります。
特にdockerを利用して完成済のコンテナのカレントディレクトリを変更したうえでワンライナーを実行する手順が意外に見当たりませんでした。

docker runには-wオプションでワーキングディレクトリを指定する方法がありますが、docker execにはありません。

いろいろとためした結果、たどりついたのは

$ docker exec some_container "bash -c cd /opt && bundle exec rails s"

のように&&で2つのコマンドを連続実行する手順でした。
また、cdはbashなどのシェルにバンドルされたコマンドのためbash -c cdの形式で呼び出す必要がありました。
dockerを活用するには、dockerそのものというよりむしろシェルの知識が必要な印象です。

関連記事

Alpine Linuxのログインシェル ash
多くのLinuxディストリビューションはシェルにbashを採用していますが、Alpine Linuxは……
dockerイメージのパッケージ管理
Dockerのイメージ作成・管理では多くの場合、ベースイメージとしてDockerHubのオフィシャル……
docker-composeの環境変数設定
docker-composeを活用するとマルチコンテナを一限管理しやすくなります。 Compose file(YAM……
Docker Composeの起動順序を確保する
Docker Composeを利用すると異種コンテナの設定をまとめて記述してdocker-compose upでき……
dockerでゾンビ化したポートを解放する
ローカルPC上のdockerは唐突に終了する場合が多くあります。このような利用を続けていたら、コンテ……