Dockerfile 中 RUN 和 CMD 有什么区别?
RUN 在构建镜像时执行(装包、建目录,结果固化进镜像层),CMD 指定容器启动时的默认命令(可被 docker run 覆盖)。构建期用 RUN,运行期用 CMD。
一句话区别:RUN 在构建镜像时执行,命令结果(装的包、建的目录)作为新的镜像层永久固化;CMD 在容器启动时才生效,指定默认运行的命令,且可被 docker run 后面跟的参数整个覆盖。时机不同:RUN 属于 build,CMD 属于 run。
RUN:构建期执行
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y curl
RUN mkdir -p /app/logs
docker build 时每条 RUN 执行一次,结果存为新层。典型用途:装依赖、写配置、建目录、赋权限。
优化要点:多个关联命令用 && 合并成一条 RUN,减少层数;apt-get 装完顺手 rm -rf /var/lib/apt/lists/* 清理缓存,镜像更小。
CMD:运行期默认命令
CMD ["python", "app.py"]
docker run myimg 时执行;docker run myimg python debug.py 则 CMD 被替换。一个 Dockerfile 里写多条 CMD,只有最后一条生效。
对照记忆
| RUN | CMD | |
|---|---|---|
| 执行时机 | docker build | docker run(容器启动) |
| 生效次数 | 每条都执行 | 只有最后一条 |
| 结果去向 | 固化成镜像层 | 不固化,仅启动进程 |
| 能否被覆盖 | 不能 | run 参数整体覆盖 |
组合示例
FROM python:3.12-slim
RUN pip install flask gunicorn # 构建期装依赖
COPY . /app
WORKDIR /app
CMD ["gunicorn", "-w", "4", "app:app"] # 启动期默认命令
观测云对照
构建期动作影响运行期可观测性。 在 RUN 阶段装好 DataKit 需要的集成依赖、规划好日志输出目录(或直接 stdout),容器启动后日志采集与 APM 探针才能即插即用。
常见问题(FAQ)
Q:CMD 用 shell 形式还是 exec 形式?
A:推荐 exec 数组形式(CMD ["python","app.py"]),主进程为 PID 1,能收到 SIGTERM 优雅停机;shell 形式会裹一层 sh,信号传递有问题。
Q:ENTRYPOINT 和 CMD 又是什么关系?
A:ENTRYPOINT 定主程序、CMD 提供默认参数,详见本系列《Dockerfile 中 CMD 和 ENTRYPOINT 的区别》。
Q:RUN 执行失败会怎样?
A:构建立即中止报错;排查时可把复杂 RUN 拆小定位,或用 docker build --progress=plain 看完整输出。