0


Dockerfile里的ENTRYPOINT和CMD

文章目录

环境

  • RHEL 9.3
  • Docker Community 24.0.7

总结

如果懒得看详细介绍,可以直接看总结:

  • ENTRYPOINTCMD 都可以单独使用,指定启动容器时所运行的命令以及参数。
  • 更常见的用法是把 ENTRYPOINTCMD 组合使用:- ENTRYPOINT 指定启动容器时所运行的命令和不变的参数。在启动容器时可以显式覆盖,但一般不这么做。- CMD 指定运行参数。在启动容器时可以显式覆盖。
  • ENTRYPOINTCMD 都强烈推荐使用“exec形式”。

例如:

ENTRYPOINT ["ping","-c","20"]
CMD ["localhost"]

讲解

ENTRYPOINT

CMD

指定启动容器时所运行的命令以及参数。二者都可以单独使用,也可以把二者组合使用。接下来通过示例来看一下二者的用法。

(一)不指定ENTRYPOINT和CMD

创建

Dockerfile

文件如下:

FROM ubuntu:trusty

构建:

docker build -t kaidemo0 .

启动容器:

docker run kaidemo0

结果什么也没有发生:既没有报错,也没有任何输出。

通过

docker ps -a

查看容器:

➜  ~ docker ps-a
CONTAINER ID   IMAGE                    COMMAND                  CREATED          STATUS                      PORTS                      NAMES
f3aeb967fdd2   kaidemo0                 "/bin/bash"              49 seconds ago   Exited (0) 48 seconds ago                              affectionate_noyce

可以看到,在没有指定

ENTRYPOINT

CMD

时,实际运行的命令是

/bin/bash

要想与容器交互,可以加上

-it

选项:

➜  ~ docker run -it kaidemo0
root@1389cac2f57e:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@1389cac2f57e:/# ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 09:07 pts/0    00:00:00 /bin/bash
root          18       1  0 09:07 pts/0    00:00:00 ps-ef
root@1389cac2f57e:/# exitexit

注:

  • -i :interactive,交互式的,接收用户输入
  • -t :tty,分配一个伪终端

也可在

docker run

时指定运行的命令,比如:

➜  ~ docker run kaidemo0 ping 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.069 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.032 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.024 ms

在另一个命令行窗口,查看容器:

➜  test1 docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS         PORTS                      NAMES
ab8ed71eae08   kaidemo0         "ping localhost"         3 seconds ago   Up 3 seconds                              interesting_dijkstra

最后,在第一个窗口,按下“Ctrl + C”停止ping命令,同时退出容器。

**总结:若不指定ENTRYPOINT和CMD,启动容器时,默认运行的命令是

/bin/bash

。要显式加上

-it

选项才能进入容器做事。也可以在启动容器时显式指定运行的命令。**

(二)CMD

创建

Dockerfile

文件如下:

FROM ubuntu:trusty
CMD ping localhost

构建:

docker build -t kaidemo1 .

启动容器:

docker run kaidemo1

由于

CMD

指令指定了

ping localhost

命令,容器启动时会自动运行该命令,如下:

➜  ~ docker run kaidemo1      
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.145 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.199 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.065 ms
^C64 bytes from localhost (127.0.0.1): icmp_seq=4 ttl=64 time=0.084 ms
64 bytes from localhost (127.0.0.1): icmp_seq=5 ttl=64 time=0.030 ms
......

注意:按下 “Ctrl + C” 无法停止ping命令,原因稍后解释。

在另一个命令行窗口,查看docker容器:

➜  ~ docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS         PORTS                      NAMES
25e553984c8a   kaidemo1         "/bin/sh -c 'ping lo…"   7 seconds ago   Up 7 seconds                              sleepy_lalande

同样,尝试停止容器也无效:

➜  ~ docker stop 25e553984c8a
^C
docker stop

命令hang住了,只能按“Ctrl + C”中止。

最后,运行

docker rm -f

强制删除容器:

➜  ~ docker rm-f 25e553984c8a
25e553984c8a

原因解释:通过刚才的

docker ps

可以看到,实际运行的命令是

/bin/sh -c 'ping localhost'

  • docker run kaidemo0 ping localhost 时,在容器里查看进程:
➜  docker docker exec <container ID> ps-ef       
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 02:08 ?        00:00:00 ping localhost
root           7       0  0 02:09 ?        00:00:00 ps-ef
  • docker run kaidemo1 时,在容器里查看进程:
➜  docker docker exec <container ID> ps-ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 02:12 ?        00:00:00 /bin/sh -c ping localhost
root           7       1  0 02:12 ?        00:00:00 ping localhost
root           8       0  0 02:12 ?        00:00:00 ps-ef
  • docker run -it kaidemo1 时,在容器里查看进程:
➜  docker docker exec <container ID> ps-ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 02:16 pts/0    00:00:00 /bin/sh -c ping localhost
root           8       1  0 02:16 pts/0    00:00:00 ping localhost
root           9       0  0 02:16 ?        00:00:00 ps-ef

注意PID为1的进程,分别为:

  • ping localhost
  • /bin/sh -c ping localhost (没有TTY)
  • /bin/sh -c ping localhost (有TTY)

当我们在容器外部发送POSIX信号(比如“Ctrl + C”)到容器里,对于

docker run kaidemo1

/bin/sh

命令不会转发消息给实际运行的ping命令,所以无法停止ping。

之所以出现这样的问题,是因为我们在Dockerfile里使用了“shell形式”,即:

CMD ping localhost

Docker会把该命令作为shell的子命令(即

/bin/sh -c xxxxx

),这就带来了问题。

为了避免这个问题,可以使用“exec形式”。

创建

Dockerfile

文件如下:

FROM ubuntu:trusty
CMD ["ping","localhost"]

构建:

docker build -t kaidemo2 .

启动容器:

docker run kaidemo2

在另一个命令行窗口,查看容器:

➜  docker docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS         PORTS                      NAMES
a6e67096ca3a   kaidemo2         "ping localhost"         6 seconds ago   Up 4 seconds                              stupefied_euler

查看容器里的进程:

➜  ~ docker exec <container ID> ps-ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 02:33 ?        00:00:00 ping localhost
root           7       0  0 02:33 ?        00:00:00 ps-ef

可见,应尽量使用“exec形式”,以避免子shell的问题。

可以在启动容器时,显式指定要运行的命令,覆盖

CMD

的命令。例如:

➜  ~ docker run kaidemo2 ls-l 
total 8
drwxr-xr-x.   2 root root 4096 Dec 17  2019 bin
drwxr-xr-x.   2 root root    6 Apr 10  2014 boot
drwxr-xr-x.   5 root root  340 Jan  6 02:39 dev
......

**总结:假设没有指定

ENTRYPOINT

指令,则

CMD

指令可以指定启动启动时要运行的命令。强烈推荐使用“exec形式”,以避免子shell的问题。可以在启动容器时,显式指定要运行的命令,覆盖

CMD

指定的命令。**

(三)ENTRYPOINT

创建

Dockerfile

文件如下:

FROM ubuntu:trusty
ENTRYPOINT ping localhost

构建:

docker build -t kaidemo3 .

启动容器,可以发现,

ENTRYPOINT

CMD

的表现几乎一模一样。

因此,应尽量使用“exec形式”:

ENTRYPOINT ["ping","localhost"]

另外,在启动容器时,覆盖

ENTRYPOINT

的方法和

CMD

不同,例如:

➜  ~ docker run kaidemo4 ls       
ping: unknown host ls

可见,运行的还是ping命令,只不过参数变成了

ls

。这是因为覆盖的是

CMD

指令,而不是

ENTRYPOINT

指令。关于二者的组合,稍后会有介绍。

要想覆盖

ENTRYPOINT

指令,可以使用

--entrypoint

选项:

➜  ~ docker run --entrypoint ls kaidemo4 
bin
boot
dev
......

但是,不推荐使用这种做法,原因稍后会有介绍。

**总结:

ENTRYPOINT

的表现几乎和

CMD

完全一致。假设没有指定

CMD

指令,则

ENTRYPOINT

指令可以指定启动启动时要运行的命令。强烈推荐使用“exec形式”,以避免子shell的问题。可以在启动容器时,通过

--entrypoint

选项显式指定要运行的命令,覆盖

ENTRYPOINT

指定的命令,但一般不这么做。**

(四)ENTRYPOINT和CMD的组合

前面说了这么多,

ENTRYPOINT

CMD

貌似也没什么本质的区别,那为什么Dockerfile里要有两个相似的指令呢?

实际上,二者的设计理念不一样,典型的用法是把它们组合起来使用:

  • ENTRYPOINT :指定默认的启动程序
  • CMD :指定默认的运行参数

创建

Dockerfile

文件如下:

FROM ubuntu:trusty
ENTRYPOINT ["ping","-c","20"]
CMD ["localhost"]

注:

-c

ping

命令的选项,c表示count,

-c 20

就是ping 20次。

构建:

docker build -t kaidemo5 .

启动容器:

docker run kaidemo5

在另一个命令行窗口,查看容器:

➜  ~ docker ps                      
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS         PORTS                      NAMES
135c6466f888   kaidemo5         "ping -c 20 localhost"   3 seconds ago   Up 2 seconds                              upbeat_vaughan

查看容器里的进程:

➜  ~ docker exec <container ID> ps-ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 03:16 ?        00:00:00 ping -c 20 localhost
root           7       0  0 03:17 ?        00:00:00 ps-ef

可见,实际运行的命令,是把

ENTRYPOINT

的内容和

CMD

的内容组合起来了

  • ENTRYPOINT["ping", "-c", "20"]
  • CMD["localhost"]

最终命令是:

ping -c 20 localhost

如果我们想ping另外一台主机,只需在

docker run

时覆盖

CMD

的值:

docker run kaidemo5 127.0.0.1

在另一个命令行窗口,查看容器:

➜  ~ docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS         PORTS                      NAMES
b438242b4e84   kaidemo5         "ping -c 20 127.0.0.1"   3 seconds ago   Up 2 seconds                              intelligent_torvalds

查看容器里的进程:

➜  ~ docker exec <container ID> ps-ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 03:25 ?        00:00:00 ping -c 20 127.0.0.1
root           8       0  0 03:25 ?        00:00:00 ps-ef

可见,通过覆盖

CMD

的值,就可以改变运行的参数。

在上面的例子里,

-c 20

是写死在

ENTRYPOINT

里的,如果想要用户可以配置ping的次数,则应放在

CMD

里,以便用户在

docker run

时覆盖。

创建

Dockerfile

文件如下:

FROM ubuntu:trusty
ENTRYPOINT ["ping"]
CMD ["-c","20","localhost"]

构建:

docker build -t kaidemo6 .

启动容器:

docker run kaidemo6

效果和

docker run kaidemo5

是一样的,运行的都是

ping -c 20 localhost

启动容器时替换

CMD

参数:

docker run kaidemo6 -c 30 127.0.0.1

在另一个命令行窗口查看容器:

➜  ~ docker ps                      
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS         PORTS                      NAMES
7885692cb508   kaidemo6         "ping -c 30 127.0.0.1"   4 seconds ago   Up 3 seconds                              wizardly_shtern

查看容器里的进程:

➜  ~ docker exec <container ID> ps-ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 03:38 ?        00:00:00 ping -c 30 127.0.0.1
root           7       0  0 03:38 ?        00:00:00 ps-ef

可见,实际运行的是

ping -c 30 127.0.0.1

另外要注意的是,二者一定都要使用“exec形式”。如果使用“shell形式”,则会转为

/bin/sh xxx

,二者组合后,会造成混乱。
CMD localhostCMD [“localhost”]ENTRYPOINT ping -c 10/bin/sh -c ‘ping -c 10’ /bin/sh -c localhost/bin/sh -c ‘ping -c 3’ localhost**ENTRYPOINT [“ping”,“-c”,“10”]**ping -c 10 /bin/sh -c localhostping -c 3 localhost
可见,只有 二者都是“exec形式”时,才能组合出期望的结果。

**总结:

ENTRYPOINT

CMD

组合使用:把运行命令和不变的参数放到

ENTRYPOINT

里,把可变的参数放到

CMD

里,以便在

docker run

时替换。二者都要使用“exec形式”。可用

--entrypoint

覆盖命令,但一般不这么做,因为

ENTRYPOINT

代表的是容器用途,一般不会改变,可变的是运行参数。**

参考

  • https://spacelift.io/blog/docker-entrypoint-vs-cmd (里面有些内容和我实际测试结果不同,可能是Docker版本不同?)
  • https://zhuanlan.zhihu.com/p/30555962 (里面有些内容和我实际测试结果不同,可能是Docker版本不同?)
  • https://docs.docker.com/engine/reference/builder
标签: docker

本文转载自: https://blog.csdn.net/duke_ding2/article/details/135418179
版权归原作者 蓝黑2020 所有, 如有侵权,请联系我们删除。

“Dockerfile里的ENTRYPOINT和CMD”的评论:

还没有评论