JSON 数组形式是什么
RUN、CMD、ENTRYPOINT 都有两种写法:shell 形式把整行交给容器的 shell 解释,exec 形式用 JSON 数组直接指定可执行文件与参数。JSON 形式的规则很简单也很严格:必须是双引号的 JSON 数组,且数组内的参数原样传递,不做变量替换与通配符展开。理解这一点,就能解释为什么同样的变量写在数组里变成了字面量。
基本信息
| 正确写法 | CMD ["node", "server.js"] |
|---|---|
| 错误写法 | CMD ['node', 'server.js'](单引号不是合法 JSON) |
| 变量行为 | 数组内 $HOME 之类不会被替换,原样作为参数传入 |
| 执行方式 | exec 形式不经过 shell,因此没有管道、重定向与通配符 |
| 需要展开时 | 套一层 shell:["/bin/sh", "-c", "exec app --port ${PORT:-8080}"] |
| 组合规则 | ENTRYPOINT 数组 + CMD 数组时,CMD 作为默认参数追加到 ENTRYPOINT 后 |
典型使用场景
- 信号传递:exec 形式让应用进程成为 PID 1,docker stop 的 SIGTERM 能直达进程
- 参数固定、不需要 shell 特性的启动命令
- 需要变量展开或管道时显式套 sh -c,把 shell 行为写清楚而不是依赖隐式规则
- 多阶段构建的 COPY --from 与 ENTRYPOINT 配合,产物路径完全可控
- 排查启动报错时查看镜像配置里保存的原始数组内容
写法对照
写法对照
# exec 形式:推荐,PID 1 就是应用本身
CMD ["node", "server.js"]
# 字面量陷阱:这里输出的是 $HOME 这几个字符,不是家目录路径
CMD ["echo", "$HOME"]
# 确实需要变量展开与默认值,就显式套一层 shell
CMD ["/bin/sh", "-c", "exec node server.js --port ${PORT:-8080}"]
# shell 形式:整行交给 /bin/sh -c,能用管道,但 PID 1 变成 shell
CMD node server.js | tee /var/log/app.log两种形式对比
| 维度 | exec(JSON 数组) | shell(整行) |
|---|---|---|
| 解析方式 | 直接执行,不经 shell | /bin/sh -c 解释整行 |
| 变量展开 | 不展开 | 按 shell 规则展开 |
| 管道与重定向 | 不支持 | 支持 |
| 信号接收方 | 应用进程本身 | 最外层 shell |
| 适用建议 | 启动应用首选 | 需要 shell 能力时使用 |
校验方法:docker inspect 镜像后看 Config.Cmd 与 Config.Entrypoint 里保存的数组,即可确认是否被当成字符串处理;容器启动报可执行文件找不到时,先核对这里。
Windows 路径在 JSON 字符串里要写成双反斜杠或改用正斜杠,反斜杠转义错误是这类镜像启动失败的高频原因。