CMD 和 ENTRYPOINT 支架与非支架版本

Wak*_*nka 6 docker

我见过许多具有各种语法的 Dockerfile。他们中的一些人CMD不使用括号,其中一些人使用括号。根据我的实验,这两个是等价的:

CMD ["/bin/ping", "localhost"] 
CMD /bin/ping localhost        
Run Code Online (Sandbox Code Playgroud)

但事实并非如此,ENTRYPOINT根据我的实验,这些并不相同,只有带括号的版本才有效。

ENTRYPOINT ["/docker-entrypoint.sh"]
ENTRYPOINT /docker-entrypoint.sh
Run Code Online (Sandbox Code Playgroud)

我正在试验这些文件:

Dockerfile:

FROM debian:wheezy
COPY docker-entrypoint.sh /
RUN chmod +x /docker-entrypoint.sh

# WORKS AS EXPECTED - FIRST PASS EXECUTION TO /docker-entrypoint.sh THEN TO CMD
ENTRYPOINT ["/docker-entrypoint.sh"]

# PRINTS "PASSED ARGUMENTS" AND EXITS (CMD IS NOT EXECUTED)
# ENTRYPOINT /docker-entrypoint.sh

# WORKS
CMD ["/bin/ping", "localhost"] 

# WORKS
# CMD /bin/ping localhost   

# ERROR
# CMD "/bin/ping localhost" 
Run Code Online (Sandbox Code Playgroud)

docker-entrypoint.sh

#!/bin/bash
echo "PASSED ARGUMENTS"
echo "$@"
exec "$@"
Run Code Online (Sandbox Code Playgroud)

配置:

wakatana@ubuntu:~/df$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.3 LTS
Release:        18.04
Codename:       bionic


wakatana@ubuntu:~/df$ docker info
Client:
 Debug Mode: false

Server:
 Containers: 44
  Running: 8
  Paused: 0
  Stopped: 36
 Images: 98
 Server Version: 19.03.5
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Native Overlay Diff: true
 Logging Driver: json-file
 Cgroup Driver: systemd
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: b34a5c8af56e510852c35414db4c1f4fa6172339
 runc version: 3e425f80a8c931f88e6d94a8c831b9d5aa481657
 init version: fec3683
 Security Options:
  apparmor
  seccomp
   Profile: default
 Kernel Version: 4.15.0-99-generic
 Operating System: Ubuntu 18.04.3 LTS
 OSType: linux
 Architecture: x86_64
 CPUs: 2
 Total Memory: 3.83GiB
 Name: ubuntu
 ID: GIQ3:BYEM:O3GZ:OWNP:VROK:V3IV:SVMI:MRZ7:CLHG:GDBR:PG3S:6NQU
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Username: xclbr
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false

WARNING: No swap limit support


wakatana@ubuntu:~/df$ docker --version
Docker version 19.03.5, build 633a0ea838
Run Code Online (Sandbox Code Playgroud)

我认为支架版本是首选方式,但我想知道为什么甚至允许构建非支架版本?它有什么好处吗?CMDENTRYPOINT仅适用于支架版本时,为什么同时适用于两个版本?

Dav*_*aze 10

有两个规则:

  1. 如果您使用 JSON 数组(“括号”)版本,则它是组成命令的“单词”的显式列表;如果您使用裸字符串,它将被包裹在/bin/sh -c '...'.
  2. 应用第一规则后,将ENTRYPOINTCMD组合成单个命令

还有一个细节:如果你打电话/bin/sh -c 'command',其余参数成为位置参数$0$1扩大的command字符串; 它们不会作为参数进一步传递给command.

所以如果你的 Dockerfile 说

ENTRYPOINT ["/entrypoint.sh"]
CMD ["/command.sh"]
Run Code Online (Sandbox Code Playgroud)

然后当 Docker 启动容器时,它们将组合成一个命令行

/entrypoint.sh /command.sh
Run Code Online (Sandbox Code Playgroud)

但如果你用字符串形式来做

ENTRYPOINT /entrypoint.sh
CMD /command.sh
Run Code Online (Sandbox Code Playgroud)

你会得到

/bin/sh -c '/entrypoint.sh' /bin/sh -c '/command.sh'
Run Code Online (Sandbox Code Playgroud)

它计算字符串/entrypoint.sh并按原样执行它,没有参数;命令丢失。

通常,您希望命令部分成为标准的 shell 命令(docker run --rm -it some-image /bin/sh对于调试非常有用)。为了支持这一点,ENTRYPOINT必须是 JSON 数组形式,并且它需要确保运行在其参数中传递的命令(在 shell 脚本中,以 结尾exec "$@")。