为了清晰的展示两者之间的关系

我们现在有个需求,运行容器时打印出当前ip地址

那么我们来编写Dockerfile

  1. FROM centos
  2. LABEL maintainer "lbxlh<1318659507@qq.com>"
  3. # curl 工具
  4. RUN yum install -y curl
  5. # 查看IP地址
  6. CMD [ "curl","-s","http://myip.ipip.net/" ]

cmd

注意看我们在Dockerfile文件中添加了一个 curl工具,然后用CMD命令执行当前IP.

构建镜像

  1. ╰─ docker build -f Dockerfile -t centos-ip .
  2. [+] Building 10.9s (6/6) FINISHED
  3. => [internal] load build definition from Dockerfile 0.0s
  4. => => transferring dockerfile: 204B 0.0s
  5. => [internal] load .dockerignore 0.0s
  6. => => transferring context: 2B 0.0s
  7. => [internal] load metadata for docker.io/library/centos:latest 0.0s
  8. => CACHED [1/2] FROM docker.io/library/centos 0.0s
  9. => [2/2] RUN yum install -y curl 10.5s
  10. => exporting to image 0.2s
  11. => => exporting layers 0.2s
  12. => => writing image sha256:0a29edbad1ce7b74fbd8d745ee5cecc2669a2e44fcab8334bb2dfe5dc4aefc6a 0.0s
  13. => => naming to docker.io/library/centos-ip

万年叮嘱 别忘了 尾部的 .

运行容器

  1. ╰─ docker run --rm centos-ip
  2. 当前 IP118.73.0.214 来自于:中国 山西 临汾 联通

确实按照我们预想的内容输出了,但是如果我想输出协议头咋办。
这时候熟悉curl的小伙伴就要说了,加个 -i 参数不就行了吗。

那么我们试试看

  1. ╭─░▒▓ ~/Public/docker-test-volume ▓▒░──────────────────────────────────────────────────────────────────────────────────────────────────░▒▓ at 18:47:05 ▓▒░
  2. ╰─ docker run --rm centos-ip -i
  3. docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "-i": executable file not found in $PATH: unknown.

我们发现这里的错误是,-i 不是一个有效的命令。

通过查阅官方文档后我们发现:

There can only be one CMD instruction in a Dockerfile. If you list more than one CMD then only the last CMD will take effect.

一个 Dockerfile中,如果有多条CMD命令,那么只会有最后一条生效。

因此我们在编写Dockerfile时要注意这点,如果需要多行命令,那么建议写成脚本,然后用CMD命令去执行。

当我们使用 docker run 命令去执行镜像时,如果我们能附加参数,那么这里的参数就会替代掉我们写的 CMD。

这就是为什么上面 显示 -i 不是有效执行文件。

既然我们发现这个这个问题,那么如何解决呢。

前面知道了,会替换命令,那么我们写个完整的命令不就行了?

  1. ╭─░▒▓ ~/Downloads/darwin_amd64 ▓▒░──────────────────────────────────────────────────────────────░▒▓ at 15:09:19 ▓▒░
  2. ╰─ docker run --rm centos-ip curl -s myip.ipip.net -i
  3. HTTP/1.1 200 OK
  4. Date: Tue, 21 Dec 2021 07:09:27 GMT
  5. Content-Type: text/plain; charset=utf-8
  6. Content-Length: 68
  7. Connection: keep-alive
  8. X-Via-JSL: fdc330b,-
  9. Set-Cookie: __jsluid_h=121b221c391f3d706dabf95003f1687d; max-age=31536000; path=/; HttpOnly
  10. X-Cache: bypass
  11. 当前 IP118.73.202.27 来自于:中国 山西 临汾 联通

想必到这里已经清楚了CMD命令如何用了吧。

但是如果每次都这样替换,未免有些太过于繁琐,那么有没有一种方案,给我们已经写好的命令中添加参数呢?

entrypoint

entry和cmd是一样的都是在容器运行时执行对应的指令,和cmd不同的是

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令

那么继续上面的例子,我们来将cmd命令替换成entrypoint

  1. FROM centos
  2. LABEL maintainer "lbxlh<1318659507@qq.com>"
  3. # curl 工具
  4. RUN yum install -y curl
  5. # 查看IP地址
  6. ENTRYPOINT [ "curl","-s","http://myip.ipip.net/" ]
  1. ╭─░▒▓ ~/Public/docker-test-volume ▓▒░───────────────────────────────────────────────────────────░▒▓ at 15:21:14 ▓▒░
  2. ╰─ docker run --rm centos-ip
  3. 当前 IP118.73.202.27 来自于:中国 山西 临汾 联通
  4. ╭─░▒▓ ~/Public/docker-test-volume ▓▒░───────────────────────────────────────────────────────────░▒▓ at 15:21:16 ▓▒░
  5. ╰─ docker run --rm centos-ip -i
  6. HTTP/1.1 200 OK
  7. Date: Tue, 21 Dec 2021 07:21:20 GMT
  8. Content-Type: text/plain; charset=utf-8
  9. Content-Length: 68
  10. Connection: keep-alive
  11. X-Via-JSL: 2633f0d,-
  12. Set-Cookie: __jsluid_h=b4b4e6ee1a68675af8ecb3fee1406613; max-age=31536000; path=/; HttpOnly
  13. X-Cache: bypass
  14. 当前 IP118.73.202.27 来自于:中国 山西 临汾 联通

这样是不是就解决了我们上面的问题呢。

还有一件事:
一个 Dockerfile中,如果有多条ENTRYPOINT命令,那么只会有最后一条生效。