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
相关推荐
JoyCong199811 分钟前
ToDesk个人版、专业版、游戏版、设计版、性能版、团队版权益介绍
大数据·运维·游戏·远程工作·远程操作
tryCbest1 小时前
Docker 从零到精通:知识与跨平台使用指南
docker·容器
Elastic 中国社区官方博客2 小时前
ES95:面向 Elasticsearch 时间序列指标的自适应压缩
大数据·运维·elasticsearch·搜索引擎·架构·全文检索
mqiqe2 小时前
AgentScope Java 2.0 Agent 状态存储(AgentStateStore)深度解析:构建可恢复、可扩展的智能体运行时
java·运维·网络
mennekes3 小时前
数据中心安全配电设备如何选择?
运维·人工智能·科技·安全·制造
开始学AI4 小时前
Codex2API Docker 使用宿主机代理:OAuth Token 兑换 403 问题排查与解决方案
运维·docker·容器
晨枫阳4 小时前
@changesets/cli是什么?哪些情况下需要使用?怎么使用
linux·运维·ubuntu
做前端的娜娜子5 小时前
Docker 常用命令全梳理:从镜像拉取到容器编排
docker·容器·掘金·金石计划
天远Date Lab5 小时前
零信任架构实战:基于天远行驶证核查构建自动化车队准入网关
运维·人工智能·架构·自动化
不会就选b5 小时前
Linux之网络基础(二)
linux·运维·网络