Docker コンテナで起動したシェルに接続する (docker container run/start/exec/attach)

何をするか?

ここでは、Ubuntu の Docker イメージを使ってコンテナを起動し、その上で実行した bash シェルに接続して自由にコマンドを実行できるようにします。 docker container runstartattachexec など似たようなコマンドがたくさんありますが、用途はそれぞれ違うのでここでひととおり理解しておきましょう。

コマンド意味
docker container run「イメージの取得」「コンテナの作成」「コンテナの起動」を連続して行う
docker container start作成済みのコンテナを「起動」する
docker container attach動作しているコンテナに「接続」する
docker container exec動作しているコンテナで「プロセスを起動」する

docker container run はコンテナの「作成」と「起動」

docker container run(旧: docker run)は、もっとも頻繁に紹介されているコマンドですが、内部で複数のことを行うので 一番分かりにくいコマンド かもしれません。 docker container run は次のようなことを一度に実行します。

  • 指定したイメージがなければダウンロード (docker image pull)
  • Docker コンテナを作成する (docker container create)
  • Docker コンテナを起動する (docker container start)

例えば、次のコマンドを実行すると、Docker イメージ (ubuntu:20.04) のダウンロード、Docker コンテナ (mycon) の作成、そのコンテナの起動までを一気に実行します。

$ docker container run --name mycon -it ubuntu:20.04 /bin/bash

各オプションは次のような意味を持っています。

  • --name mycon 作成するコンテナに mycon という名前を付ける(省略するとランダムに単語を組み合わせた名前が付けられます。例: cool_blackwell
  • -i 標準入力を有効にしたままにする(ようするに現在の端末上からキーボード入力できるようにする)
  • -t 起動するコマンドに対して TTY 端末を割り当てる(ようするに現在の端末上に bash のプロンプトを表示する)

オプションで -i-t を指定しないと、一瞬で Docker コンテナが終了してしまうので、シェル接続する場合はこれらのオプションを指定する必要があります。 -it というオプション指定は、コンテナ起動してシェル接続する場合のおまじないと考えておけばよいです。

さて、ここで Docker を始めたばかりの人がハマるのが、次のようにコンテナ停止後にふたたび docker container run した場合です。

# 次のようにシェルを終了すると、Docker コンテナが停止する
root@65da3272d493:/# exit

# 再び run しようとするとエラー! どうして!?
$ docker container run --name mycon -it ubuntu:20.04 /bin/bash
docker: Error response from daemon: Conflict. The container name "/mycon" is already
in use by container "65da3272d493c77c2034f58d9a6e0c80f302db3c058e8345e170dc72d67811f4".
You have to remove (or rename) that container to be able to reuse that name.
See 'docker run --help'.

これは、すでに存在する mycon という名前のコンテナをもう一度作成しようとしているからです。 docker container run コマンドが、docker container create を兼ねていることを思い出してください。 コンテナで動作している bash シェルを終了すると Docker コンテナも「停止」するのですが、停止状態のコンテナは残ったままです。 停止状態のコンテナ一覧は、次のようにして確認できます(-a オプションを付けないと、動作中のコンテナしか表示されません)。

$ docker container ls -a
CONTAINER ID   IMAGE          COMMAND       CREATED          STATUS                        PORTS     NAMES
65da3272d493   ubuntu:20.04   "/bin/bash"   12 minutes ago   Exited (0) 4 minutes ago                mycon

停止状態のコンテナがすでに存在するときに、コンテナの起動&シェルへの接続だけを行うには、次のように docker container start(後述)を使用します。

$ docker container start -ai mycon
root@65da3272d493:/#

別の方法として、docker container run--rm オプションを指定するという方法もあります。 このオプションを指定すると、コンテナが停止したときに、自動的にコンテナが削除されます。

$ docker container run --rm --name mycon -it ubuntu:20.04 /bin/bash

今回の例の場合、シェルから exit すると、mycon コンテナが停止してさらに削除まで行われます。 よって、その後もう一度 docker container run を実行しようとすると、mycon コンテナの作成からやり直しになるので結果的にうまく動作します。 つまり、1 コマンドだけ実行するための使い捨てのコンテナということですね。 すぐに破棄されるので、--name mycon オプションでコンテナに名前を付ける必要はあまりないかもしれません。

docker container start は存在するコンテナを起動する

すでに上記で使いましたが、docker container start コマンド(旧: docker start)は「停止状態のコンテナ」を起動するためのコマンドです。 docker container run とは異なり、コンテナの作成は行わないので、存在しないコンテナを起動しようとするとエラーになります。

$ docker container start mycon
Error response from daemon: No such container: mycon

コンテナを起動するには、あらかじめ docker container create でコンテナを作成しておく必要があります。 過去に docker container run で作成したコンテナでも大丈夫です。

$ docker container create -it --name mycon ubuntu:20.04 /bin/bash
327ad0cfd0047eb90a1ae4ec63769d0680697c48b074d640ed83b793c5d19beb

docker container create で作成したコンテナは、次のように停止状態のコンテナの一覧に表示されます。

$ docker container ls -a
CONTAINER ID   IMAGE          COMMAND       CREATED          STATUS                        PORTS     NAMES
327ad0cfd004   ubuntu:20.04   "/bin/bash"   23 seconds ago   Created                                 mycon

あとは、docker container start でこのコンテナを起動できます。 起動と同時にシェルプロセスに接続するには、-ai オプションを付けます。

$ docker container start -ai mycon
root@327ad0cfd004:/#

docker container attach は動作しているコンテナに接続する

docker container attach コマンド(例: docker attach)は、ローカルの標準入出力をコンテナのプロセス (PID=1) にアタッチします。

この振る舞いを調べるために、まずコンテナを起動してシェルに接続しておきます。

$ docker container create -it --name mycon ubuntu:20.04 /bin/bash
$ docker container start -ai mycon
root@9bf22d6b0a5c:/#

ここで起動した bash シェルのプロセス ID (PID) は 1 になっています。

root@9bf22d6b0a5c:/# ps
  PID TTY          TIME CMD
    1 pts/0    00:00:00 bash
   10 pts/0    00:00:00 ps

別のローカル端末を起動して、次のように docker container attach すると、上記の PID=1 の bash プロセスの標準入出力に接続されます。

$ docker container attach mycon
root@9bf22d6b0a5c:/#

2 つの端末で bash シェルを表示している状態になりますが、どちらも PID=1 の同じプロセスに接続しているため、一方で入力したコマンドの結果がもう一方にも表示されます。 どちらかで exit すると、もう一方のシェルも閉じて Docker コンテナが終了します。

一方の接続だけを切りたい場合は、Ctrl + P Ctrl + Q と入力します。 こうすれば、もう一方のシェルプロセスは残ったままになり、コンテナも終了しません。

docker container exec は動作しているコンテナでプロセスを起動する

docker container exec コマンド(旧: docker exec)を使うと、動作しているコンテナ上で新しいプロセスを起動することができます。 これを使うと、1 つのコンテナ上で 2 つのシェルを起動して接続する ということができます。

停止状態のコンテナを指定するとエラーになるので、まずはコンテナを起動してシェル接続しておきます。 この bash シェルの PID は前述のように 1 です。

$ docker container create -it --name mycon ubuntu:20.04 /bin/bash
$ docker container start -ai mycon
root@e3d9f66349d4:/# ps
  PID TTY          TIME CMD
    1 pts/0    00:00:00 bash
   10 pts/0    00:00:00 ps

この状態で別の端末を起動して、docker container exec で新しい bash シェルを起動して接続します。 この bash シェルは新しいプロセスとして起動するので、別の PID が割り当てられます。

$ docker container exec -it mycon /bin/bash
root@e3d9f66349d4:/# ps
  PID TTY          TIME CMD
   11 pts/1    00:00:00 bash
   20 pts/1    00:00:00 ps

これで、1 つのコンテナに 2 つのシェルを起動して接続できました。 一方のシェルを exit で終了しても、もう一方のシェル(およびコンテナ)は実行されたままです。 ここで docker container attach を使うと、同じプロセス (PID=1) に接続されてしまうので、docker container exec の方を使って新しいシェルのプロセスを作るのがポイントです。