Dockerfile 中的 RUN、ENTRYPOINT 和 CMD 指令都有两种写法,shell form 和 exec form。exec form 和 shell form 不同,exec form 不调用 shell,这就意味着不会发生正常的 shell 处理,如 RUN ["echo","$HOME"] 不会对 $HOME 执行变量替换。如果想要 shell 处理,要么使用shell form,要么直接执行 shell,exec form 格式命令会被解析成 json 数组,这意味着你必须使用双引号而不是单引号来括住数组中的每一个元素,例如:exec form 运行 ["sh","-c","echo $HOME"] 。
我们通过例子看一下。
exec form
Dockerfile内容:
FROM ubuntu
CMD ["watch","-n 1","date"]
###构建镜像。
root@docker:~# docker build -t execform .
Sending build context to Docker daemon 64kB
Step 1/2 : FROM ubuntu
---> ba6acccedd29
Step 2/2 : CMD ["watch","-n 1","date"]
---> Running in 2dcf4e97d6cf
Removing intermediate container 2dcf4e97d6cf
---> b2625b0c9002
Successfully built b2625b0c9002
Successfully tagged execform:latest
###查看镜像。
root@docker:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
execform latest b2625b0c9002 25 seconds ago 72.8MB
ubuntu latest ba6acccedd29 15 months ago 72.8MB
###运行容器,--rm参数,容器停止后自动删除容器。
root@docker:~# docker run --rm --name execform -it execform
容器运行效果:
###再另外开启一个新终端。
###查看内容器运行状态信息。
root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0cb43b162629 execform "watch '-n 1' date" 6 seconds ago Up 4 seconds execform
###执行ps命令查看容器进程情况。
root@docker:~# docker exec -it execform ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 13:05 pts/0 00:00:00 watch -n 1 date
root 346 0 0 13:07 pts/1 00:00:00 ps -ef
###执行命令停止容器,发现很快就能停下。
root@docker:~# docker stop execform
execform
shell form
Dockerfile内容:
FROM ubuntu
CMD watch -n 1 date
###构建镜像。
root@docker:~# docker build -t shellform -f Dockerfile .
Sending build context to Docker daemon 60.42kB
Step 1/2 : FROM ubuntu
---> ba6acccedd29
Step 2/2 : CMD watch -n 1 date
---> Running in 0dc36e7c4127
Removing intermediate container 0dc36e7c4127
---> 74dae56ad62b
Successfully built 74dae56ad62b
Successfully tagged shellform:latest
###查看镜像。
root@docker:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
shellform latest 74dae56ad62b 53 seconds ago 72.8MB
execform latest 9a9ad5e91072 12 minutes ago 72.8MB
ubuntu latest ba6acccedd29 15 months ago 72.8MB
###运行容器,--rm参数,容器停止后自动删除容器。
root@docker:~# docker run --rm --name shellform -it shellform
容器运行效果:
###再另外开启一个新终端。
###查看内容器运行状态信息。
root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e656d89e3d1f shellform "/bin/sh -c 'watch -…" 28 minutes ago Up 28 minutes shellform
###执行ps命令查看容器进程情况,可以看到比exec多了一个/bin/sh的进程,且它的PID是1。
root@docker:~# docker exec -it shellform ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 10:26 pts/0 00:00:00 /bin/sh -c watch -n 1 date
root 7 1 0 10:26 pts/0 00:00:01 watch -n 1 date
root 4199 0 0 10:49 pts/1 00:00:00 ps -ef
###执行停止容器命令,明显要比exec模式慢。
root@docker:~# docker stop shellform
shellform
再来看一个例子。
exec form
Dockerfile内容:
FROM centos
ARG app=httpd
RUN ["yum","install","-y","$app"]
###执行创建镜像命令,最后失败了,失败的原因是exec模式没有对变量进行替换。
root@docker:~# docker build -t execform -f Dockerfile .
Sending build context to Docker daemon 66.56kB
Step 1/3 : FROM centos
---> dea00f9b3407
Step 2/3 : ARG app=httpd
---> Using cache
---> 0c50017f9ea3
Step 3/3 : RUN ["yum","install","-y","$app"]
---> Running in 4e6693c278af
Last metadata expiration check: 0:04:34 ago on Fri Jan 20 16:18:48 2023.
No match for argument: $app
Error: Unable to find a match: $app
The command 'yum install -y $app' returned a non-zero code: 1
shell form
Dockerfile内容:
FROM centos
ARG app=httpd
RUN yum install -y $app
###构建镜像,可以成功构建。
root@docker:~# docker build -t shellform -f Dockerfile .
Sending build context to Docker daemon 66.56kB
Step 1/3 : FROM centos
---> dea00f9b3407
Step 2/3 : ARG app=httpd
---> Running in 58e9499fe5ea
Removing intermediate container 58e9499fe5ea
---> 0c50017f9ea3
Step 3/3 : RUN yum install -y $app
---> Running in 2e184a123c2c
Last metadata expiration check: 0:02:57 ago on Fri Jan 20 16:18:48 2023.
Dependencies resolved.
================================================================================
Package Arch Version Repo Size
================================================================================
Installing:
httpd x86_64 2.4.37-43.module_el8.5.0+1022+b541f3b1 AppStream 1.4 M
Installing dependencies:
apr x86_64 1.6.3-12.el8 AppStream 129 k
apr-util x86_64 1.6.1-6.el8 AppStream 105 k
brotli x86_64 1.0.6-3.el8 base 323 k
centos-logos-httpd
noarch 85.8-2.el8 base 75 k
httpd-filesystem noarch 2.4.37-43.module_el8.5.0+1022+b541f3b1 AppStream 39 k
httpd-tools x86_64 2.4.37-43.module_el8.5.0+1022+b541f3b1 AppStream 107 k
mailcap noarch 2.1.48-3.el8 base 39 k
mod_http2 x86_64 1.15.7-3.module_el8.4.0+778+c970deab AppStream 154 k
Installing weak dependencies:
apr-util-bdb x86_64 1.6.1-6.el8 AppStream 25 k
apr-util-openssl x86_64 1.6.1-6.el8 AppStream 27 k
Enabling module streams:
httpd 2.4
Transaction Summary
================================================================================
Install 11 Packages
Total download size: 2.4 M
Installed size: 7.1 M
Downloading Packages:
(1/11): mailcap-2.1.48-3.el8.noarch.rpm 90 kB/s | 39 kB 00:00
(2/11): centos-logos-httpd-85.8-2.el8.noarch.rp 151 kB/s | 75 kB 00:00
(3/11): apr-util-1.6.1-6.el8.x86_64.rpm 208 kB/s | 105 kB 00:00
(4/11): apr-1.6.3-12.el8.x86_64.rpm 192 kB/s | 129 kB 00:00
(5/11): apr-util-bdb-1.6.1-6.el8.x86_64.rpm 133 kB/s | 25 kB 00:00
(6/11): apr-util-openssl-1.6.1-6.el8.x86_64.rpm 182 kB/s | 27 kB 00:00
(7/11): httpd-filesystem-2.4.37-43.module_el8.5 148 kB/s | 39 kB 00:00
(8/11): brotli-1.0.6-3.el8.x86_64.rpm 200 kB/s | 323 kB 00:01
(9/11): httpd-tools-2.4.37-43.module_el8.5.0+10 201 kB/s | 107 kB 00:00
(10/11): mod_http2-1.15.7-3.module_el8.4.0+778+ 196 kB/s | 154 kB 00:00
(11/11): httpd-2.4.37-43.module_el8.5.0+1022+b5 222 kB/s | 1.4 MB 00:06
--------------------------------------------------------------------------------
Total 321 kB/s | 2.4 MB 00:07
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
Preparing : 1/1
Installing : apr-1.6.3-12.el8.x86_64 1/11
Running scriptlet: apr-1.6.3-12.el8.x86_64 1/11
Installing : apr-util-bdb-1.6.1-6.el8.x86_64 2/11
Installing : apr-util-openssl-1.6.1-6.el8.x86_64 3/11
Installing : apr-util-1.6.1-6.el8.x86_64 4/11
Running scriptlet: apr-util-1.6.1-6.el8.x86_64 4/11
Installing : httpd-tools-2.4.37-43.module_el8.5.0+1022+b541f3b1 5/11
Running scriptlet: httpd-filesystem-2.4.37-43.module_el8.5.0+1022+b54 6/11
Installing : httpd-filesystem-2.4.37-43.module_el8.5.0+1022+b54 6/11
Installing : mailcap-2.1.48-3.el8.noarch 7/11
Installing : centos-logos-httpd-85.8-2.el8.noarch 8/11
Installing : brotli-1.0.6-3.el8.x86_64 9/11
Installing : mod_http2-1.15.7-3.module_el8.4.0+778+c970deab.x86 10/11
Installing : httpd-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_6 11/11
Running scriptlet: httpd-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_6 11/11
Verifying : brotli-1.0.6-3.el8.x86_64 1/11
Verifying : centos-logos-httpd-85.8-2.el8.noarch 2/11
Verifying : mailcap-2.1.48-3.el8.noarch 3/11
Verifying : apr-1.6.3-12.el8.x86_64 4/11
Verifying : apr-util-1.6.1-6.el8.x86_64 5/11
Verifying : apr-util-bdb-1.6.1-6.el8.x86_64 6/11
Verifying : apr-util-openssl-1.6.1-6.el8.x86_64 7/11
Verifying : httpd-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_6 8/11
Verifying : httpd-filesystem-2.4.37-43.module_el8.5.0+1022+b54 9/11
Verifying : httpd-tools-2.4.37-43.module_el8.5.0+1022+b541f3b1 10/11
Verifying : mod_http2-1.15.7-3.module_el8.4.0+778+c970deab.x86 11/11
Installed:
apr-1.6.3-12.el8.x86_64
apr-util-1.6.1-6.el8.x86_64
apr-util-bdb-1.6.1-6.el8.x86_64
apr-util-openssl-1.6.1-6.el8.x86_64
brotli-1.0.6-3.el8.x86_64
centos-logos-httpd-85.8-2.el8.noarch
httpd-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_64
httpd-filesystem-2.4.37-43.module_el8.5.0+1022+b541f3b1.noarch
httpd-tools-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_64
mailcap-2.1.48-3.el8.noarch
mod_http2-1.15.7-3.module_el8.4.0+778+c970deab.x86_64
Complete!
Removing intermediate container 2e184a123c2c
---> edaed2fc23b0
Successfully built edaed2fc23b0
Successfully tagged shellform:latest
RUN 指令是在 docker build 构建 docker 镜像时执行的命令,推荐使用 shell 模式,shell 模式书写简洁,而且可以使用变量,多个命令可以使用 && 进行连接,可以减少镜像的层数。
ENTRYPOINT 和 CMD 指令用于指定容器启动时需要运行的命令,推荐使用 exec 模式。如果使用shell 模式,程序会以 /bin/sh -c 的子命令启动,shell 进程默认不会处理 SIGTERM 信号,自己不会退出,也不会将信号传递给子进程,导致业务程序不会触发停止逻辑,只能等待超时关闭。