TOC
Docker 中通过 Dockerfile 对镜像进行分层(Layer)构建,使用 ENTRYPOINT、CMD 来控制容器初始化后的执行入口点。而在 Kubernetes (K8s) pod 中,则使用 command、args 来控制 pod 中 容器的执行入口点。
当我们使用 Docker 为运行时接口(CRI),在 K8s 中两者参数是怎样控制来决定最终的容器行为呢?
本文通过对 Docker 中 ENTRYPOINT、CMD 和 K8s 中 command、args 进行对比分析,并使用对应的 Demo 说明了两者的区别和联系。
Dockerfile
FROM busybox
ENTRYPOINT ["printf", "This is entrypoint from %s.\n"]
运行结果:
docker build -t busybox-demo . && docker run busybox-demo
This is entrypoint from .
可以看到,ENTRYPOINT 未解析到任何参数,所以占位符 %s 输出为空。
【动态参数】 如果在运行时传递了 CLI 参数,则可以解析到:
docker build -t busybox-demo . && docker run busybox-demo cli-args
运行结果:
This is entrypoint from cli-args.
【替换 ENTRYPOINT】 如果在运行时传递了 CLI 参数 --entrypoint,则会覆盖默认 ENTRYPOINT:
docker build -t busybox-demo . && docker run --entrypoint echo busybox-demo new-entrypoint
运行结果:
new-entrypoint
Dockerfile
FROM busybox
CMD ["dockerfile cmd"]
运行结果:
docker: Error response from daemon: failed to create shim: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "dockerfile cmd": executable file not found in $PATH: unknown.
可以看到,当仅仅只有 CMD 而没有 ENTRYPOINT 的时候,CMD 第一个参数为在 $PATH 路径可找到的内置命令或可执行文件。 此时找不到 dockerfile 的可执行文件,所以看到输出为对应的 OCI 报错。
只需要稍微改一下,则可以运行: Dockerfile
FROM busybox
CMD ["echo", "dockerfile cmd"]
运行结果:
dockerfile cmd
Dockerfile
FROM busybox
ENTRYPOINT ["printf", "This is entrypoint from %s.\n"]
CMD ["dockerfile cmd"]
运行结果:
This is entrypoint from dockerfile cmd.
可以看到,当 ENTRYPOINT + CMD 都存在的时候,则 CMD 作为 ENTRYPOINT 的参数。
当 CMD 参数列表有多个,即使第一个参数为可执行文件,也只是作为字符串参数传递给 ENTRYPOINT,如下所示:
Dockerfile
FROM busybox
ENTRYPOINT ["printf", "This is entrypoint from %s.\n"]
CMD ["echo", "dockerfile cmd"]
运行结果:
This is entrypoint from echo.
This is entrypoint from dockerfile cmd.
Note:当有多个参数时,printf 会换行分别输出格式化字符串。
【替换 CMD】 如果在运行时传递了 CLI 参数,则会覆盖 CMD 参数列表:
docker build -t busybox-demo . && docker run busybox-demo cli-args
运行结果:
This is entrypoint from cli-args.
Dockerfile
FROM busybox
运行结果为空,表示只是运行启动了 container,但没有执行任何命令就退出了。
在 K8s pod 中,以下面的包含 ENTRYPOINT + CMD 为 container image,进行 command vs args 相关 demo 测试。
镜像已推送到 Docker Hub: astraw99/busybox-demo:latest
Dockerfile
FROM busybox
ENTRYPOINT ["printf", "This is entrypoint from %s.\n"]
CMD ["dockerfile cmd"]
pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: cmd-demo
spec:
containers:
- name: demo-container
image: astraw99/busybox-demo
command: ["echo", "cmd"]
运行 pod:
kubectl apply -f ./pod.yaml
kubectl logs cmd-demo
运行结果:
cmd
可以看到,当 pod 中只有 command 时,会忽略镜像 Dockerfile 中默认的 ENTRYPOINT + CMD,而仅仅执行提供的 command。
pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: cmd-demo
spec:
containers:
- name: demo-container
image: astraw99/busybox-demo
args: ["pod arg1", "pod arg2"]
运行结果:
This is entrypoint from pod arg1.
This is entrypoint from pod arg2.
可以看到,当 pod 中只有 args 时,会使用镜像 Dockerfile 中默认的 ENTRYPOINT,但会忽略其默认的 CMD,然后将 args 传递给 ENTRYPOINT 解析。
pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: cmd-demo
spec:
containers:
- name: demo-container
image: astraw99/busybox-demo
command: ["echo", "cmd"]
args: ["pod arg1", "pod arg2"]
运行结果:
cmd pod arg1 pod arg2
可以看到,当 pod 中有 command + args 时,会忽略镜像 Dockerfile 中默认的 ENTRYPOINT + CMD,而仅仅执行提供的 command + args。
pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: cmd-demo
spec:
restartPolicy: OnFailure
containers:
- name: demo-container
image: astraw99/busybox-demo
运行结果:
This is entrypoint from dockerfile cmd.
可以看到,当 pod 中没有 command + args 时,会使用镜像 Dockerfile 中默认的 ENTRYPOINT + CMD,进行执行输出。
根据上文所述,Docker、K8s 分别有两个字段相对应,如下:
Description | Docker field name | Kubernetes field name |
---|---|---|
The command run by the container | ENTRYPOINT | command |
The arguments passed to the command | CMD | args |
考虑 Docker、K8s 各 4 种组合,则一共有 4 * 4 = 16 种两者的组合方式。汇总如下:
#1# K8s command with Docker ENTRYPOINT
#2# K8s command with Docker CMD
#3# K8s command with Docker ENTRYPOINT + CMD
#4# K8s command with Docker empty
#5# K8s args with Docker ENTRYPOINT
#6# K8s args with Docker CMD
#7# K8s args with Docker ENTRYPOINT + CMD
#8# K8s args with Docker empty
#09# K8s command + args with Docker ENTRYPOINT
#10# K8s command + args with Docker CMD
#11# K8s command + args with Docker ENTRYPOINT + CMD
#12# K8s command + args with Docker empty
#13# K8s empty with Docker ENTRYPOINT
#14# K8s empty with Docker CMD
#15# K8s empty with Docker ENTRYPOINT + CMD
#16# K8s empty with Docker empty
篇幅有限,读者感兴趣,请自行验证以上组合方式。
操作可能会花费一定时间,但只有通过动手实践过,才能对本文要讲述的核心要点有更深刻的理解,并加深记忆。
本文通过对 Docker 中 ENTRYPOINT、CMD 和 K8s 中 command、args 进行对比分析,并使用对应的 Demo 说明了两者的区别和联系。小结如下:
- 当 K8s pod 中没有 command + args,则使用镜像 Dockerfile 中默认的 ENTRYPOINT + CMD;
- 当 K8s pod 中只有 command 时,会忽略镜像 Dockerfile 中默认的 ENTRYPOINT + CMD,而仅仅执行提供的 command;
- 当 K8s pod 中只有 args 时,会使用镜像 Dockerfile 中默认的 ENTRYPOINT,但会忽略其默认的 CMD,然后将 args 传递给 ENTRYPOINT;
- 当 K8s pod 中有 command + args 时,会忽略镜像 Dockerfile 中默认的 ENTRYPOINT + CMD,而执行提供的 command + args;
Image Entrypoint | Image Cmd | Container command | Container args | Command run |
---|---|---|---|---|
[/ep-1] |
[foo bar] |
<not set> |
<not set> |
[ep-1 foo bar] |
[/ep-1] |
[foo bar] |
[/ep-2] |
<not set> |
[ep-2] |
[/ep-1] |
[foo bar] |
<not set> |
[zoo boo] |
[ep-1 zoo boo] |
[/ep-1] |
[foo bar] |
[/ep-2] |
[zoo boo] |
[ep-2 zoo boo] |
PS: 更多内容请关注 k8s-club