[docker] CMD とENTRYPOINT の違いを試してみた

本記事は、[Docker Advent Calendar 2015](http://qiita.com/advent-calendar/2015/docker) の12月6日の記事になります。

# 概要
Dockerfile に`CMD` と`ENTRYPOINT` ってあるけどなにが違うのか調査とそのメモ

# 環境
* OS : cent7.1

```bash
cat /etc/redhat-release
  # CentOS Linux release 7.1.1503 (Core)
```
* docker : 1.9.0

```console
[root@docker-engine /]# docker version
  # Client:
  #  Version:      1.9.0
  #  API version:  1.21
  #  Go version:   go1.4.2
  #  Git commit:   76d6bc9
  #  Built:        Tue Nov  3 18:00:05 UTC 2015
  #  OS/Arch:      linux/amd64

  # Server:
  #  Version:      1.9.0
  #  API version:  1.21
  #  Go version:   go1.4.2
  #  Git commit:   76d6bc9
  #  Built:        Tue Nov  3 18:00:05 UTC 2015
  #  OS/Arch:      linux/amd64
```
※ install は[こちら](https://docs.docker.com/installation/centos/)を参照

# Dockerfile
* 今回の主なDockerfile はこちらになります。
  これに`CMD` や`ENTRYPOINT` を追記して試します。

```Dockerfile
FROM ubuntu

MAINTAINER hihihiroro
```

# `CMD` と`ENTRYPOINT`
* docker image からdocker container を実行するときに`CMD` や`ENTRYPOINT` の記述内容が実行されます。
* 1つのDockerfile に`CMD`、`ENTRYPOINT` は1度のみ。<br>
  ※ 複数記載されている場合、最後の1個が実行される。

# agenda
* CMD について
 * CMD ["command"]
 * CMD command
 * CMD 上書き
* ENTRYPOINT について
 * ENTRYPOINT ["command"]
 * ENTRYPOINT command
 * ENTRYPOINT 上書き?
* CMD とENTRYPOINT の併用について

# CMD
## CMD ["command"]
```Dockerfile
FROM ubuntu

MAINTAINER hihihiroro

CMD ["ping","127.0.0.1","-c","100"]
```

build

```console
[root@localhost ~]# docker build -t test .
  # Sending build context to Docker daemon 37.89 kB
  # Step 1 : FROM ubuntu
  #  ---> e9ae3c220b23
  # Step 2 : MAINTAINER hihihiroro
  #  ---> Using cache
  #  ---> 5b349e87daf9
  # Step 3 : CMD ping 127.0.0.1 -c 100
  #  ---> Running in 401e07b7dca7
  #  ---> 0ac2c0ea0e28
  # Removing intermediate container 401e07b7dca7
  # Successfully built 0ac2c0ea0e28
```

run

```console
[root@localhost ~]# docker run test
  # PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
  # 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.033 ms
  # 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.040 ms
  # 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.034 ms
  # ...
```

プロセス確認

```console
root@0b2a435c091b:/# ps auxf
  # USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
  # root         6  0.6  0.0  18136  1864 ?        Ss   06:00   0:00 /bin/bash
  # root        19  0.0  0.0  15560  1104 ?        R+   06:00   0:00  \_ ps auxf
  # root         1  0.2  0.0   6500   612 ?        Ss   06:00   0:00 ping 127.0.0.1 -c 100
```
`ping` が実行されていることが分かります。
シェルを介さずに実行されていることが分かります。

## CMD command
```Dockerfile
FROM ubuntu

MAINTAINER hihihiroro

CMD ping 127.0.0.1 -c 100
```

build

```console
[root@localhost ~]# docker build -t test .
  # Sending build context to Docker daemon 37.89 kB
  # Step 1 : FROM ubuntu
  #  ---> e9ae3c220b23
  # Step 2 : MAINTAINER hihihiroro
  #  ---> Running in 7d6a1adc76ee
  #  ---> 03615a451ecf
  # Removing intermediate container 7d6a1adc76ee
  # Step 3 : CMD ping 127.0.0.1 -c 100
  #  ---> Running in c304f24111ab
  #  ---> dea31bd0fe0f
  # Removing intermediate container c304f24111ab
  # Successfully built dea31bd0fe0f
```

run

```console
[root@localhost ~]# docker run test
  # PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
  # 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.033 ms
  # 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.040 ms
  # 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.034 ms
  # ...
```

プロセス確認

```console
root@b86ea4cdcd54:/# ps auxf
  # USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
  # root         7  0.4  0.0  18136  1860 ?        Ss   06:04   0:00 /bin/bash
  # root        20  0.0  0.0  15560  1100 ?        R+   06:04   0:00  \_ ps auxf
  # root         1  0.2  0.0   4440   628 ?        Ss   06:04   0:00 /bin/sh -c ping 127.0.0.1 -c 100
  # root         6  0.0  0.0   6500   624 ?        S    06:04   0:00 ping 127.0.0.1 -c 100
```
`ping` が実行されていることが分かります。
`/bin/sh -c` の引数としてコマンドが実行されていることが分かります。

## CMD 上書き
```Dockerfile
FROM ubuntu

MAINTAINER hihihiroro

CMD ping 127.0.0.1 -c 100
```

build

```console
[root@localhost ~]# docker build -t test .
  # Sending build context to Docker daemon 37.89 kB
  # Step 1 : FROM ubuntu
  #  ---> e9ae3c220b23
  # Step 2 : MAINTAINER hihihiroro
  #  ---> Running in 7d6a1adc76ee
  #  ---> 03615a451ecf
  # Removing intermediate container 7d6a1adc76ee
  # Step 3 : CMD ping 127.0.0.1 -c 100
  #  ---> Running in c304f24111ab
  #  ---> dea31bd0fe0f
  # Removing intermediate container c304f24111ab
  # Successfully built dea31bd0fe0f
```

run

```console
[root@localhost ~]# docker run test cat /etc/lsb-release
  # DISTRIB_ID=Ubuntu
  # DISTRIB_RELEASE=14.04
  # DISTRIB_CODENAME=trusty
  # DISTRIB_DESCRIPTION="Ubuntu 14.04.3 LTS"
```

`ping` ではなく、`docker run` 時につけた引数の`cat /etc/lsb-release` が実行されていることが分かります。
`CMD` はこのように、`docker run` 時にコマンドの上書きが可能になります。

# ENTRYPOINT
## ENTRYPOINT ["command"]
```Dockerfile
FROM ubuntu

MAINTAINER hihihiroro

ENTRYPOINT ["ping","127.0.0.1","-c","100"]
```

build

```console
[root@localhost ~]# docker build -t test .
  # Sending build context to Docker daemon 37.89 kB
  # Step 1 : FROM ubuntu
  #  ---> e9ae3c220b23
  # Step 2 : MAINTAINER hihihiroro
  #  ---> Running in 4bd4f60f6a50
  #  ---> 2ffc6e2fdd8a
  # Removing intermediate container 4bd4f60f6a50
  # Step 3 : ENTRYPOINT ping 127.0.0.1 -c 100
  #  ---> Running in 173206d72cb6
  #  ---> e5b47e6740cd
  # Removing intermediate container 173206d72cb6
  # Successfully built e5b47e6740cd
```

run

```console
[root@localhost ~]# docker run test
  # PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
  # 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.025 ms
  # 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.034 ms
  # 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.029 ms
  # ...
```

プロセス確認

```console
root@124425f5edd0:/# ps auxf
  # USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
  # root         6  0.4  0.0  18136  1864 ?        Ss   06:33   0:00 /bin/bash
  # root        19  0.0  0.0  15560  1104 ?        R+   06:33   0:00  \_ ps auxf
  # root         1  0.1  0.0   6500   604 ?        Ss   06:33   0:00 ping 127.0.0.1 -c 100
```
`ping` が実行されていることが分かります。
シェルを介さずに実行されていることが分かります。

## ENTRYPOINT command
```Dockerfile
FROM ubuntu

MAINTAINER hihihiroro

ENTRYPOINT ping 127.0.0.1 -c 100
```

build

```console
[root@localhost ~]# docker build -t test .
  # Sending build context to Docker daemon 37.89 kB
  # Step 1 : FROM ubuntu
  #  ---> e9ae3c220b23
  # Step 2 : MAINTAINER hihihiroro
  #  ---> Running in 8679d9b8120c
  #  ---> bdc1d6d8e004
  # Removing intermediate container 8679d9b8120c
  # Step 3 : ENTRYPOINT ping 127.0.0.1 -c 100
  #  ---> Running in ec29024e6074
  #  ---> 3963c62c7464
  # Removing intermediate container ec29024e6074
  # Successfully built 3963c62c7464
```

run

```console
[root@localhost ~]# docker run test
  # PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
  # 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.030 ms
  # 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.031 ms
  # 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.028 ms
  # ...
```

プロセス確認

```console
root@7d8390b4e1f4:/# ps auxf
  # USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
  # root         7  1.0  0.0  18136  1856 ?        Ss   06:37   0:00 /bin/bash
  # root        20  0.0  0.0  15560  1104 ?        R+   06:37   0:00  \_ ps auxf
  # root         1  0.2  0.0   4440   632 ?        Ss   06:37   0:00 /bin/sh -c ping 127.0.0.1 -c 100
  # root         6  0.0  0.0   6500   632 ?        S    06:37   0:00 ping 127.0.0.1 -c 100
```
`ping` が実行されていることが分かります。
`/bin/sh -c` の引数としてコマンドが実行されていることが分かります。

## ENTRYPOINT 上書き？
```Dockerfile
FROM ubuntu

MAINTAINER hihihiroro

ENTRYPOINT ping 127.0.0.1 -c 100
```

build

```console
[root@localhost ~]# docker build -t test .
  # Sending build context to Docker daemon 37.89 kB
  # Step 1 : FROM ubuntu
  #  ---> e9ae3c220b23
  # Step 2 : MAINTAINER hihihiroro
  #  ---> Running in 2f72cd03c162
  #  ---> dd99c6e04b1a
  # Removing intermediate container 2f72cd03c162
  # Step 3 : ENTRYPOINT ping 127.0.0.1 -c 100
  #  ---> Running in 449d1f1b2280
  #  ---> 61277c329249
  # Removing intermediate container 449d1f1b2280
  # Successfully built 61277c329249
```

run

```console
[root@localhost ~]# docker run test cat /etc/lsb-release
  # PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
  # 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.028 ms
  # 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.053 ms
  # 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.032 ms
  #  ...
```

`CMD` とは違い、`docker run` 時につけた引数の`cat /etc/lsb-release` ではなく、`ENTRYPOINT` で指定されている`ping` が実行されていることが分かります。
`ENTRYPOINT` は`CMD` と違い、`docker run` 時にコマンドの上書きができません。
※ `docker run --entrypoint=""` で上書きはすることが可能です。

# CMD とENTRYPOINT の併用について
## 引数なし
```Dockerfile
FROM ubuntu

MAINTAINER hihihiroro

ENTRYPOINT ["ping"]
CMD ["127.0.0.1", "-c", "50"]
```

build

```console
[root@localhost ~]# docker build -t test .
  # Sending build context to Docker daemon 39.42 kB
  # Step 1 : FROM ubuntu
  #  ---> e9ae3c220b23
  # Step 2 : MAINTAINER hihihiroro
  #  ---> Running in 33a067cc7f3e
  #  ---> 57ff677a0cca
  # Removing intermediate container 33a067cc7f3e
  # Step 3 : ENTRYPOINT ping
  #  ---> Running in 2957c1b4f09e
  #  ---> 3c30112fe595
  # Removing intermediate container 2957c1b4f09e
  # Step 4 : CMD 127.0.0.1 -c 50
  #  ---> Running in 09f1cff264bd
  #  ---> c7160151ed57
  # Removing intermediate container 09f1cff264bd
  # Successfully built c7160151ed57
```

run

```console
[root@localhost ~]# docker run test
  # PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
  # 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.024 ms
  # 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.033 ms
  # 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.031 ms
  # ...
```

プロセス確認

```console
root@23a769d5c382:/# ps auxf
  # USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
  # root         6  1.0  0.0  18136  1860 ?        Ss   07:16   0:00 /bin/bash
  # root        19  0.0  0.0  15560  1100 ?        R+   07:16   0:00  \_ ps auxf
  # root         1  0.2  0.0   6500   632 ?        Ss   07:16   0:00 ping 127.0.0.1 -c 50
```

`ENTRYPOINT` で指定した`ping` に`CMD` で指定した`127.0.0.1 -c 50` が引数として実行されています。

## 引数あり
```Dockerfile
FROM ubuntu

MAINTAINER hihihiroro

ENTRYPOINT ["ping"]
CMD ["127.0.0.1", "-c", "50"]
```

build

```console
[root@localhost ~]# docker build -t test .
  # Sending build context to Docker daemon 39.42 kB
  # Step 1 : FROM ubuntu
  #  ---> e9ae3c220b23
  # Step 2 : MAINTAINER hihihiroro
  #  ---> Running in 33a067cc7f3e
  #  ---> 57ff677a0cca
  # Removing intermediate container 33a067cc7f3e
  # Step 3 : ENTRYPOINT ping
  #  ---> Running in 2957c1b4f09e
  #  ---> 3c30112fe595
  # Removing intermediate container 2957c1b4f09e
  # Step 4 : CMD 127.0.0.1 -c 50
  #  ---> Running in 09f1cff264bd
  #  ---> c7160151ed57
  # Removing intermediate container 09f1cff264bd
  # Successfully built c7160151ed57
```

run

```console
[root@localhost ~]# docker run test localhost
  # PING localhost (127.0.0.1) 56(84) bytes of data.
  # 64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.027 ms
  # 64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.037 ms
  # 64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.029 ms
  # ...
```

プロセス確認

```console
root@947cf9dfb3c1:/# ps auxf
  # USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
  # root         6  1.3  0.0  18136  1864 ?        Ss   07:26   0:00 /bin/bash
  # root        19  0.0  0.0  15560  1104 ?        R+   07:26   0:00  \_ ps auxf
  # root         1  0.3  0.0   8596   832 ?        Ss   07:26   0:00 ping localhost
```

`ENTRYPOINT` で指定した`ping` に引数の`localhost` が`CMD` で指定した`127.0.0.1 -c 50`  を上書きして実行しています。

# まとめ
## 共通点
* `docker run` 時に実行されるコマンドです。
* `docker run` 時に引数を渡すとコマンドを上書きできます。
  ※`ENTRYPOINT` は`--entrypoint` オプションが必要です。

## 差分
* `CMD` は`docker run` 時の引数で`CMD command`、`CMD ["command"]` の`command` 分をまるっと上書きして実行されます。
* `ENTRYPOINT` は`docker run` 時の引数が`ENTRYPOINT ["command"]` の`command` の引数として実行されます。

## 併用
* `ENTRYPOINT` と`CMD` の両方が書いてある場合には、`CMD` に書かれている内容が、`ENTRYPOINT` に書いてある`command` のオプションとして実行されます。
* `docker run` 時に引数をつけた場合、`CMD` の内容が上書きされ`ENTRYPOINT` に書いてある`command` が実行されます。

# 残作業
```Dockerfile
FROM ubuntu

MAINTAINER hihihiroro

ENTRYPOINT ping -c 50
```

build

```console
[root@localhost ~]# docker build -t test .
  # Sending build context to Docker daemon 39.42 kB
  # Step 1 : FROM ubuntu
  #  ---> e9ae3c220b23
  # Step 2 : MAINTAINER hihihiroro
  #  ---> Running in 403dff6b0a29
  #  ---> a3be8411fe60
  # Removing intermediate container 403dff6b0a29
  # Step 3 : ENTRYPOINT ping -c 50
  #  ---> Running in 98beb40ba98d
  #  ---> f7d65704e8bc
  # Removing intermediate container 98beb40ba98d
  # Successfully built f7d65704e8bc
```

run

```console
[root@localhost ~]# docker run test 127.0.0.1
  # Usage: ping [-aAbBdDfhLnOqrRUvV] [-c count] [-i interval] [-I interface]
  #             [-m mark] [-M pmtudisc_option] [-l preload] [-p pattern] [-Q tos]
  #             [-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option]
  #             [-w deadline] [-W timeout] [hop1 ...] destination
```

* `ENTRYPOINT ["command"]` の際には`docker run` 時の引数を`command` の引数に付けて実行してくれるが、`ENTRYPOINT command` の際には引数を付けてくれない。

# 参考
[Docker](http://docs.docker.com/)
[Docker run reference](https://docs.docker.com/engine/reference/run/)
[Dockerfile reference](http://docs.docker.com/engine/reference/builder/)