文章目录
环境
- RHEL 9.3
- Docker Community 24.0.7
总结
如果懒得看详细介绍,可以直接看总结:
ENTRYPOINT和CMD都可以单独使用,指定启动容器时所运行的命令以及参数。- 更常见的用法是把
ENTRYPOINT和CMD组合使用:-ENTRYPOINT指定启动容器时所运行的命令和不变的参数。在启动容器时可以显式覆盖,但一般不这么做。-CMD指定运行参数。在启动容器时可以显式覆盖。 ENTRYPOINT和CMD都强烈推荐使用“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
版权归原作者 蓝黑2020 所有, 如有侵权,请联系我们删除。