コンテナーとしてのイメージ実行

読む時間の目安: 3 分

前提条件

Node イメージのビルド において説明した手順をひととおり行っていること。

概要

前節においてはサンプルアプリケーションを構築し、イメージビルドに利用する Dockerfile を生成しました。 イメージのビルドにはdocker buildというコマンドを使いました。 イメージのビルドができたので、そのイメージを実行して、アプリケーションが正しく動作することを確認します。

コンテナーというものは、オペレーティングシステム内の通常のプロセスにすぎません。 ただしこのプロセスは他から隔離されていて、独自のファイルシステム、独自のネットワークを持ちます。 またホストからは引き離された独自のプロセスツリーを持ちます。

コンテナー内においてイメージを実行するにはdocker runコマンドを使います。 docker runコマンドには 1 つの引数を必要とします。 それはイメージ名です。 ここまでに作り出したイメージを使って、正しく動作することを確認します。 ターミナルから以下のコマンドを実行します。

$ docker run node-docker

このコマンドを実行しても、コマンドプロンプトの入力状態には戻りません。 この理由は、実行したアプリケーションが REST サーバーであって、入ってくる要求を待ち受けるためのループ内で実行されるからです。 つまり OS へ戻る制御はなく、戻るためにはコンテナーを停止させることになります。

さてそこで新たなターミナルを開いて、このサーバーに対してcurlコマンドを使ってGETリクエストを行ってみます。

$ curl --request POST \
  --url http://localhost:8000/test \
  --header 'content-type: application/json' \
  --data '{"msg": "testing"}'
curl: (7) Failed to connect to localhost port 8000: Connection refused

上の curl コマンドは、サーバーへの接続が拒否されたために失敗しました。 localhost のポート 8000 には接続できなかったということです。 この動作は期待どおりであって、コンテナーはネットワークも含めて隔離された環境内で実行されているからです。 一度コンテナーを停止させて、今度はローカルネットワーク上にポート 8000 を公開した上で再起動してみます。

コンテナーを停止するには Ctrl-C を入力します。 これによってターミナルのコマンドプロンプトが入力状態に戻ります。

コンテナーのポートを公開するには、docker run コマンドにおいて--publishフラグ(あるいは-pフラグ)を指定します。 --publishフラグの書式は[ホストポート]:[コンテナーポート]です。 そこでコンテナー内部のポート 8000 をコンテナー外部のポート 3000 として公開するには、--publish フラグに 3000:8000 と指定します。

コンテナーを起動させ、ポート 8000 をホスト上のポート 8000 として公開します。

$ docker run --publish 8000:8000 node-docker

そこで先ほどの curl コマンドを再度実行してみます。 新しいターミナルを開くことを忘れないでください。

$ curl --request POST \
  --url http://localhost:8000/test \
  --header 'content-type: application/json' \
  --data '{"msg": "testing"}'
{"code":"success","payload":[{"msg":"testing","id":"dc0e2c2b-793d-433c-8645-b3a553ea26de","createDate":"2020-09-01T17:36:09.897Z"}]}

成功しました。 コンテナー内部で実行されているアプリケーションに対して、ポート 8000 による接続ができました。 コンテナーを実行させたターミナルに戻ってみると、コンソール上に POST リクエストのログが出力されているはずです。

2020-09-01T17:36:09:8770 INFO: POST /test

Ctrl-C を入力してコンテナーを停止します。

デタッチモード実行

ここまではうまくいきました。 もっともこのサンプルアプリケーションはウェブサーバーなので、ターミナルからコンテナーに接続するものではありません。 Docker ではコンテナーをデタッチモード(detached mode)、つまりバックグラウンドで実行することができます。 これを行うには--detachフラグ、あるいは短く-dフラグを指定します。 Docker は先ほどと同じようにコンテナーを起動させますが、今回はコンテナーから「デタッチされた」つまり切り離されて実行されるので、ターミナルにプロンプトが戻ってきます。

$ docker run -d -p 8000:8000 node-docker
ce02b3179f0f10085db9edfccd731101868f58631bdf918ca490ff6fd223a93b

Docker はバックグラウンドでコンテナーを起動させ、ターミナル上にそのコンテナー ID を出力します。

同じようにコンテナーが適切に動作していることを確認します。 前とまったく同じ curl コマンドを実行してみます。

$ curl --request POST \
  --url http://localhost:8000/test \
  --header 'content-type: application/json' \
  --data '{"msg": "testing"}'
{"code":"success","payload":[{"msg":"testing","id":"dc0e2c2b-793d-433c-8645-b3a553ea26de","createDate":"2020-09-01T17:36:09.897Z"}]}

コンテナーの一覧表示

コンテナーをバックグラウンドで実行したので、コンテナーが実行しているかどうか、他にどんなコンテナーがマシン上で動作しているかは、どうやって調べたらよいでしょう。 それはdocker psコマンドを使います。 Linux 上であれば、マシン上のプロセス一覧を確認するには ps コマンドを実行します。 同じように Docker ではdocker psを実行します。 このコマンドによって、マシン上で稼働しているコンテナーの一覧が表示されます。

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
ce02b3179f0f        node-docker         "docker-entrypoint.s…"   6 minutes ago       Up 6 minutes        0.0.0.0:8000->8000/tcp   wonderful_kalam

psコマンドは実行中コンテナーに関して、いろいろな情報を表示します。 その情報とは以下です。 コンテナー ID、コンテナー内部で実行しているイメージ、コンテナー起動時に用いられた実行コマンド、コンテナー生成時刻、ステータス、公開ポート、コンテナー名です。

コンテナー名はどうやって決まったんだろうと不思議に思うかもしれません。 コンテナーを起動したときにはコンテナー名を指定したわけではないので、Docker がランダムに名前を生成します。 コンテナー名はこの後すぐに変更していきますが、そのためにはまずコンテナーを停止する必要があります。 コンテナーを停止するにはdocker stopコマンドを実行します。 このコマンドはそのとおりにコンテナーを停止させます。 この際にはコンテナー名あるいはコンテナー ID を指定します。

$ docker stop wonderful_kalam
wonderful_kalam

docker psコマンドをもう一度実行して、実行中コンテナーの一覧を確認します。

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

コンテナーの停止、起動、名前変更

Docker コンテナーは起動させ停止させ、再起動することができます。 コンテナーを停止してもコンテナーが削除されるわけではなく、ステータスが停止となり、コンテナー内のプロセスが停止します。 docker psコマンドを実行したときに、出力されるコンテナーはデフォルトでは実行中コンテナーのみです。 --allまたは-aを指定すると、システム上にあるコンテナーすべて、つまり停止、起動にかかわらずすべてが表示されます。

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
ce02b3179f0f        node-docker         "docker-entrypoint.s…"   16 minutes ago      Exited (0) 5 minutes ago                        wonderful_kalam
ec45285c456d        node-docker         "docker-entrypoint.s…"   28 minutes ago      Exited (0) 20 minutes ago                       agitated_moser
fb7a41809e5d        node-docker         "docker-entrypoint.s…"   37 minutes ago      Exited (0) 36 minutes ago                       goofy_khayyam

ここまでの手順に沿って作業をしていれば、コンテナーがいくつか出力されたはずです。 このコンテナーはこれまでに起動し停止し、そのまま削除していないものです。

停止したコンテナーをここで再起動してみましょう。 停止したコンテナーの名前を確認して、再起動を行う以下のコマンドにおけるコンテナー名を書き換えて指定してください。

$ docker restart wonderful_kalam

そこで再び ps コマンドを使ってコンテナー一覧を確認します。

$ docker ps --all
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS                    NAMES
ce02b3179f0f        node-docker         "docker-entrypoint.s…"   19 minutes ago      Up 8 seconds                0.0.0.0:8000->8000/tcp   wonderful_kalam
ec45285c456d        node-docker         "docker-entrypoint.s…"   31 minutes ago      Exited (0) 23 minutes ago                            agitated_moser
fb7a41809e5d        node-docker         "docker-entrypoint.s…"   40 minutes ago      Exited (0) 39 minutes ago                            goofy_khayyam

上で再起動したコンテナーはデタッチモードで起動され、ポート 8000 が公開されています。 またコンテナーのステータスを見てみると「Up X seconds」(起動後 X 秒)となっています。 コンテナーを再起動したときには、はじめに起動したときと同じフラグ、同じコマンドによって起動されます。

ではコンテナーすべてを停止して削除します。 そしてランダムな名前になってしまう点を修正していきます。

起動したばかりのコンテナーを停止させます。 システム上の実行中コンテナーの名前を確認して、以下のコマンドにおけるコンテナー名を書き換えて指定してください。

$ docker stop wonderful_kalam
wonderful_kalam

こうしてコンテナーはすべて停止したので削除します。 コンテナーを削除するということは、実行することも停止状態にすることもできなくなります。 コンテナー内のプロセスはすでに停止され、コンテナーに対するメタデータも削除されています。

$ docker ps --all
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS                    NAMES
ce02b3179f0f        node-docker         "docker-entrypoint.s…"   19 minutes ago      Up 8 seconds                0.0.0.0:8000->8000/tcp   wonderful_kalam
ec45285c456d        node-docker         "docker-entrypoint.s…"   31 minutes ago      Exited (0) 23 minutes ago                            agitated_moser
fb7a41809e5d        node-docker         "docker-entrypoint.s…"   40 minutes ago      Exited (0) 39 minutes ago                            goofy_khayyam

コンテナーを削除するには、docker rmコマンドにコンテナー名を与えて実行します。 このコマンド 1 つに対して複数のコンテナー名を指定することもできます。

同じように、以下のコマンドにおけるコンテナー名を書き換えて指定してください。

$ docker rm wonderful_kalam agitated_moser goofy_khayyam
wonderful_kalam
agitated_moser
goofy_khayyam

再度docker ps --allコマンドを実行し、コンテナーすべてがなくなっていることを確認してください。

それでは、うっとうしいランダム名を解決します。 コンテナーに名前をつける理由は単純なものです。 どんなコンテナーが実行されているのかがすぐにわかります。 またどんなアプリケーションやサービスが関連づいているのかがわかります。 ソースコード上での変数の命名規則のようにすれば、単純に読みやすくなります。 だからコンテナーの命名も同じようにします。

コンテナー名をつけるには run コマンドに--nameフラグをつけます。

$ docker run -d -p 8000:8000 --name rest-server node-docker
1aa5d46418a68705c81782a58456a4ccdb56a309cb5e6bd399478d01eaa5cdda
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
1aa5d46418a6        node-docker         "docker-entrypoint.s…"   3 seconds ago       Up 3 seconds        0.0.0.0:8000->8000/tcp   rest-server

こうしてコンテナーの識別は、名前を使って簡単に行えるようになりました。

次のステップ

本節ではコンテナーの実行、ポート公開、デタッチモードでの実行を行いました。 またコンテナーの起動、停止、再起動という制御を行いました。 そしてコンテナーに名前づけを行って、識別しやすくしました。 次節ではコンテナー内においてデータベースを実行して、アプリケーションを接続する方法を示します。 以下を参照してください。

アプリケーションの開発方法

フィードバック

本トピック改善のためにフィードバックをお寄せください。 お気づきの点があれば Docker Docs の GitHub リポジトリに issue をあげてください。 あるいは PR の生成 により変更の提案を行ってください。


get started, Node JS, run, container