1. 概述
当编写 Dockerfile 时,我们通常需要指定容器启动时运行的默认命令。虽然我们可以在运行时覆盖这个命令,但定义一个合理的默认行为仍然非常重要。

这就是 Docker 中 ENTRYPOINT 指令的作用所在。它用于指定容器启动后运行的主进程。ENTRYPOINT 支持两种形式:shell 形式exec 形式,并通常与 CMD 指令配合使用。

本文将讨论这两种形式的区别,并重点讲解如何在 ENTRYPOINT 中使用环境变量来实现运行时的动态配置。


2. ENTRYPOINT 的 Shell 形式 vs Exec 形式
ENTRYPOINT 支持两种语法形式,适用于不同场景。

2.1. Exec 形式(推荐)

Exec 形式以 JSON 数组方式书写命令和参数,直接执行指定程序,不经过 shell 解析。
示例:

FROM alpine
ENTRYPOINT ["echo", "Hello, world!"]

✅ 优点:

  • 更加高效,直接执行命令
  • 安全性更高,避免 shell 注入
  • 更适合生产环境使用

❌ 缺点:

  • 不支持环境变量替换
  • 不支持 shell 特性(如管道、重定向等)

2.2. Shell 形式

Shell 形式通过启动一个 shell 来执行命令,允许使用 shell 特性,如变量替换、重定向等。

示例:

FROM alpine
ENTRYPOINT sh -c "echo Hello, world!"

✅ 优点:

  • 支持环境变量替换
  • 可使用 shell 特性(如管道、重定向)

❌ 缺点:

  • 多了一个 shell 进程,性能略差
  • 存在 shell 注入风险,需谨慎处理输入

3. 在 ENTRYPOINT 中使用变量
在构建复杂应用时,我们通常希望容器启动时能根据环境变量动态调整行为,比如数据库地址、日志级别等。

3.1. 使用 ENV 设置运行时变量

通过 ENV 指令定义环境变量,并在 ENTRYPOINT 中引用:

FROM python:3.12-slim

ENV LOG_LEVEL="INFO"
ENV MAX_WORKERS="4"

ENTRYPOINT sh -c 'exec python -m myapp --log-level "$LOG_LEVEL" --max-workers "$MAX_WORKERS"'

这种方式允许我们在容器运行时通过 -e 参数动态设置值:

docker run -e LOG_LEVEL="DEBUG" -e MAX_WORKERS="8" myapp

⚠️ 注意:这种方式依赖 shell 解析变量,因此必须使用 shell 形式的 ENTRYPOINT

3.2. 使用 ARG 设置构建时变量

如果希望在构建时动态传参,可以使用 ARG 指令:

FROM python:3.12-slim

ARG LOG_LEVEL="INFO"
ARG MAX_WORKERS="4"

ENTRYPOINT sh -c 'exec python -m myapp --log-level "$LOG_LEVEL" --max-workers "$MAX_WORKERS"'

构建时传入参数:

docker build -t myapp --build-arg LOG_LEVEL="DEBUG" --build-arg MAX_WORKERS="8" .

⚠️ 注意:ARG 仅在构建阶段有效,运行时无法修改。


4. 安全考虑
使用 shell 形式的 ENTRYPOINT 时,务必注意输入安全,防止 shell 注入。

例如下面这个例子就存在风险:

FROM alpine
ENTRYPOINT echo "Hello, $GREETING"

如果运行时传入恶意变量:

docker run -e GREETING='world; rm -rf / --' myapp

最终执行的命令会变成:

/bin/sh -c "echo Hello, world; rm -rf / --"

这会导致容器中执行危险命令,造成严重后果。

✅ 建议:

  • 避免直接拼接变量到命令中
  • 如果必须使用 shell 形式,确保变量经过严格过滤或转义

5. 总结
本文介绍了 Docker 中 ENTRYPOINT 的两种形式:exec 和 shell,并讨论了它们的适用场景和优缺点。

形式 是否支持变量 是否经过 shell 性能 安全性 推荐用途
Exec ✅ 高 ✅ 高 简单、高效的主进程启动
Shell ⚠️ 中 ⚠️ 低 需要变量替换或 shell 特性时使用

最终选择哪种形式,取决于你的实际需求:

  • ✅ 如果你只需要执行一个固定命令,推荐使用 exec 形式
  • ⚠️ 如果你需要运行时动态配置,可以使用 shell 形式,但务必注意安全问题。

原始标题:Integrating Docker Environment Variables in ENTRYPOINT Array