Chapter 02

イメージとコンテナ

この章で理解できること

イメージとコンテナについてと、それらを操作するdocker rundocker buildDockerfileついて確認します。

この章で理解できることを、先に整理します。

コマンド 引数 結果
docker run イメージ コンテナを起動
docker ps 実行中のコンテナを確認
docker exec コンテナ コンテナでコマンドを実行
docker pull イメージ Docker Hub から取得
docker push イメージ Docker Hub に登録
docker build Dockerfile イメージを作成
Dockerfile の命令 実行タイミング 影響コマンド
RUN イメージ作成 docker build
CMD コンテナ起動 docker run

上記の理解を整理した絵はこの通りです。

docker run と docker ps と docker exec

Docker Desktop をインストールして、docker runをしてみます。
alpine:latestの部分がイメージを指定しています。[1]

$ docker run -it alpine:latest
Unable to find image 'alpine:latest' locally
latest: Pulling from library/alpine
ca3cd42a7c95: Pull complete
Digest: sha256:ec14c7992a97fc11425907e908340c6c3d6ff602f5f13d899e6b7027c9b4133a
Status: Downloaded newer image for alpine:latest

# hostname
bea42ec418aa

# whoami
root

接続できました。
( 以降、この本では$をホストマシンのターミナル、#をコンテナのターミナルとします )

別のイメージでも試してみます。

$ docker run -it -e MYSQL_ROOT_PASSWORD=secret mysql:latest
Unable to find image 'mysql:latest' locally
latest: Pulling from library/mysql
:
:
:
2021-04-07T13:18:39.299014Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.23'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server - GPL.

alpine:latestと違い、上のメッセージを最後にターミナルが固まってしまいました。

別のタブを開いてdocker psをしてみると、mysql:latestが実行中であることが確認できます。

$ docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                 NAMES
dabd03143ac6   mysql:latest   "docker-entrypoint.s…"   3 minutes ago   Up 3 minutes   3306/tcp, 33060/tcp   mystifying_cray

docker psで確認できたCONTAINER IDを使って、docker execをしてみます。
パスワードはdocker run-eで指定したsecretです。

$ docker exec -it dabd03143ac6 mysql -p
Enter password:
:
:
:
mysql>

ホストマシン ( Windows や Mac ) に MySQL をインストールせずに、MySQL サーバに接続できました。

整理: docker run は何を指定して、何をするコマンドか

docker runは、イメージを指定して、コンテナを起動します。

引数がイメージであること、実行後にdocker psで確認できることから理解できます。

$ docker run -it -e MYSQL_ROOT_PASSWORD=secret mysql:latest

先ほどの実行でUnable to find image 'alpine:latest' locallyみたいな出力が出ていたと思いますが、docker runで指定したイメージがローカルにない場合は、Docker Hub で探してローカルに持ってきます。

イメージの取得だけを行うdocker pullというコマンドもありますが、docker runなどに含まれるため、この記事で実行することはありません。

整理: docker exec は何を指定して、何をするコマンドか

docker execは、コンテナを指定して、コンテナ内でコマンドを実行します。

引数がCONTAINER IDであること、ホストマシンに用意していない[2] MySQL サーバに接続できることから理解できます。

$ docker exec -it dabd03143ac6 mysql -p

docker build

docker buildもやってみます。

まずはDockerfileを用意します。

Dockerfile
FROM centos:centos7

LABEL desc="php-sandbox cli"

RUN yum install -y epel-release
RUN yum install -y http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
RUN yum update -y
RUN yum install -y --enablerepo=remi-php80 php

CMD php --version

そしてdocker buildをしてphp-sandbox:cliというイメージを作り、ローカルのイメージを確認します。

$ docker build -t php-sandbox:cli .
$ docker image ls
REPOSITORY         TAG               IMAGE ID       CREATED          SIZE
php-sandbox        cli               57df689f8af6   29 seconds ago   807MB

php-sandbox:cliが作られています。

イメージを指定するdocker runで起動してみましょう。

$ docker run -it php-sandbox:cli
PHP 8.0.3 (cli) (built: Mar  2 2021 16:37:06) ( NTS gcc x86_64 )
Copyright (c) The PHP Group
Zend Engine v4.0.3, Copyright (c) Zend Technologies

インストールした PHP のバージョンを確認できました。

整理: docker build は何を指定して、何をするコマンドか

docker buildは、Dockerfileを指定して、イメージを作成します。

docker buildはデフォルトでカレントディレクトリのDockerfileを使うため、先ほどのコマンドにDockerfileは現れませんでしたが、-fで指定することも可能です。

$ docker build -t php-sandbox:cli -f Dockerfile .

であれば、引数がDockerfileであり結果をdocker image lsで確認できることから理解できます。

FROM centos:centos7の記載も、docker run -it alpine:latestと同じくローカルにイメージがない場合はdocker pullを行います。

この本では行いませんが、作成したイメージはdocker pushで Docker Hub に登録することが可能です。

Dockerfile の RUN と CMD

DockerfileRUNCMDだけ確認しておきます。

Dockerfile ( 再掲 )
FROM centos:centos7

LABEL desc="php-sandbox cli"

RUN yum install -y epel-release
RUN yum install -y http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
RUN yum update -y
RUN yum install -y --enablerepo=remi-php80 php

CMD php --version

RUN には、イメージを作る段階で実行するコマンドを記載します。
つまりdocker buildに影響します。

CMD には、コンテナ起動時に実行するデフォルトのコマンドを記載します。
つまりdocker runに影響します。

Dockerfile CMDphp --versionになっているので、docker runにより PHP のバージョンが表示されました。

( 再掲 )
$ docker run -it php-sandbox:cli
PHP 8.0.3 (cli) (built: Mar  2 2021 16:37:06) ( NTS gcc x86_64 )
Copyright (c) The PHP Group
Zend Engine v4.0.3, Copyright (c) Zend Technologies

また、CMDdocker runコマンドで上書きをすることができます。
php -aで対話シェルを起動してみます。

$ docker run -it php-sandbox:cli php -a
Interactive shell

php > var_dump(5);
int(5)

イメージを作り直さなくても任意のコマンドを実行できるということです。

整理: Dockerfile の CMD と docker run

細かい調査内容は割愛しますが、いろいろ試した結果をここにまとめます。

Dockerfile CMD docker run での指定 docker run の結果
php --version なし バージョンを表示して終わり
php --version php -a 対話シェルに接続
php --version bash bashで接続
なし なし bashで接続
なし php --version バージョンを表示して終わり

docker run > Dockerfile CMD > bashの順で優先されるということですね。

整理: コンテナに接続するのは docker run と docker exec どっちなのか

docker runで対話シェルに接続できる例と、docker execで MySQL サーバに接続できる例を見ました。

docker runDockerfile CMDの命令を実行するコマンドなので、この違いはイメージの違いによるものだと理解できます。

イメージの作り方で決まるので「execsshのこと」みたいな覚え方はふさわしくないということですね。

docker runのたびにコンテナを作る、作ってから何が行われるかはイメージ次第、ということを理解しましょう。

僕は Vagrant の感覚で「サーバは立てっぱなしにして、必要なときにsshで接続して操作する」という発想になっていて、それがdocker runの理解を妨げていました。

docker run-dオプションをつけるとバックグラウンド実行になるので、mysql:latestのイメージでもdocker run -dならターミナルは固まりません。

docker runの結果でターミナルが固まらなかったから、コンテナが即時破棄されている」の様な覚え方は避けるべきでしょう。

整理: この章のまとめ

冒頭のまとめを再掲します。

コマンド 引数 結果
docker run イメージ コンテナを起動
docker ps 実行中のコンテナを確認
docker exec コンテナ コンテナでコマンドを実行
docker pull イメージ Docker Hub から取得
docker push イメージ Docker Hub に登録
docker build Dockerfile イメージを作成
Dockerfile の命令 実行タイミング 影響コマンド
RUN イメージ作成 docker build
CMD コンテナ起動 docker run

脚注
  1. alpine というのは軽量な Linux のディストリビューションの 1 つです ↩︎

  2. 僕のホストマシンはwhich mysqlmysql not foundとなります ↩︎