docker 中的entrypoint和cmd指令

一直知道这两者有区别,但是一直没有详细了解。最近在使用 docker 封装一个工具,来生成 license。正好可以作为一个案例来讲解两者的区别和用法

背景

计划使用 ruby 来生成 license 文件,因此需要在环境中:

  1. 安装 ruby
  2. 切换 gem 源(访问快),并安装项目依赖
  3. 编写 ruby 程序文件
  4. 执行 ruby 程序文件

计划基于 ubuntu:22.04 构建一个镜像,在镜像中安装好 ruby ,设置好 gem 源,安装项目依赖,源文件通过 volume 挂载到容器,最后通过 docker run 来执行源文件

构建镜像

Dockerfile 复制代码
FROM ubuntu:22.04
RUN apt update && apt install -y ruby && apt autoremove && apt-get autoclean && mkdir /workdir
RUN gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/
WORKDIR /workdir

上面的命令保存为 Dockerfile 文件,然后执行 docker build -t aa:aa . 来构建需要的镜像。

docker run 报错

构建一个项目目录,结构如下:

复制代码
| context
| -- license.rb

我最初执行 docker run --rm -v /path/context:/workdir --entrypoint "ruby license.rb" aa:aa 但是报错

复制代码
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "ruby license.rb": executable file not found in $PATH: unknown.

重点是 unable to start container process: exec: "ruby license.rb": executable file not found in $PATH: unknown.

通过查阅文档,我们知道在 Dockerfile 中我们可以有两种方式指定 ENTRYPOINT,分别是:ENTRYPOINT ["executable", "param1", "param2"]ENTRYPOINT command param1 param2。我在这里想模拟的是第二种方式。

这两种方式的区别是:

  • ENTRYPOINT ["executable", "param1", "param2"] Docker 会直接调用命令,避免通过 Shell
  • ENTRYPOINT command param1 param2 Docker 会使用 /bin/sh -c 来执行命令

但这依然解释不了上面的报错。

通过询问 ChatGPT 得知,--entrypoint 的作用是覆盖镜像中指定的 ENTRYPOINT,从而定义容器启动时执行的主命令。但它仅接受单个命令(或可执行文件),而不是带参数的完整命令行。

问题解决

要想解决问题,并举一反三就要回到 ENTRYPOINTCMD 的作用上来。

ENTRYPOINTCMD 仅能使用一次,当出现多个 ENTRYPOINTCMD 时,仅最后一个 ENTRYPOINTCMD 生效。

Dockerfile 支持通过 shell 方式或者 exec 方式指定执行的命令及参数,但是两者有以下区别:

  1. 使用 shell 方式指定的命令及参数会以 /bin/sh -c 的方式执行。exec 方式指定的命令和参数会直接执行
  2. ENTRYPOINT 使用 shell 方式执行命令时,这个命令的进程不是 1 号进程,也就不能接收到 docker stop <container> 发送的 SIGTERM 信号
  3. ENTRYPOINT 使用 exec 方式执行的命令,进程 id 为 1,可以接收到 docker stop <container> 发送的 SIGTERM 信号
  4. 因为 exec 方式不是通过 shell 执行,而是直接执行命令,因此最好通过绝对路径的方式来运行命令

两者关系

  1. 当两者都没有指定时,容器自动退出。
  2. 当仅指定了 ENTRYPOINTCMD 时,指定了哪个,执行哪个

ENTRYPOINT 与 CMD 的结合

Shell 方式

执行逻辑:

  1. ENTRYPOINT 的内容通过 Shell 执行。
  2. CMD 的内容被拼接成字符串,并附加到 ENTRYPOINT 后面。
dockerfile 复制代码
FROM ubuntu:latest
ENTRYPOINT echo
CMD ["hello", "world"]

---

FROM ubuntu:latest
ENTRYPOINT echo "aa"
CMD ["hello", "world"]

实际执行的命令:

bash 复制代码
/bin/sh -c "echo hello world"
---
/bin/sh -c "echo 'aa' hello world"

特点:

  1. CMD 被拼接为单个字符串,作为 Shell 的一部分执行。
  2. 容易受到 Shell 特性的影响,例如需要正确转义特殊字符。
Exec 方式

执行逻辑:

  1. ENTRYPOINT 和 CMD 被独立解析。
  2. 如果两者都存在,CMD 的参数直接传递给 ENTRYPOINT 的命令。
dockerfile 复制代码
FROM ubuntu:latest
ENTRYPOINT ["echo"]
CMD ["hello", "world"]

实际执行的命令:

bash 复制代码
echo hello world

特点:

  1. 参数传递更直观,直接作为数组中的元素。
  2. 不需要考虑 Shell 特性,避免转义和环境差异问题。

CLI 和 Dockerfile 区别

当使用 CLI 时,--entrypoint 指令会覆盖 Dockerfile 中的 ENTRYPOINT 。但是此时 --entrypoint 仅支持可执行文件,不支持添加参数。

因此 docker run --rm -v /path/context:/workdir --entrypoint "ruby license.rb" aa:aa 可以按照以下任意形式修改

  1. docker run --rm -v /path/context:/workdir --entrypoint "ruby" aa:aa license.rb
  2. docker run --rm -v /path/context:/workdir --entrypoint "/bin/bash" aa:aa -c ruby license.rb
相关推荐
用户0328472220701 小时前
如何搭建本地yum源(上)
运维
武子康2 小时前
调查研究-183 Apple container:Mac 上用轻量 VM 跑 Linux 容器,Swift 会改写本地容器体验吗?
docker·容器·apple
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
Inhand陈工3 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
Alsn863 天前
等待学习-学习目录:Docker 容器安全攻防
学习·安全·docker
酣大智3 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_3 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
施努卡机器视觉3 天前
SNK施努卡侧滑门锁上滑轮总成自动化装配线,从零件到组件,全流程精密制造方案
运维·自动化·制造