run是dockerfile中构建镜像时执行命令的核心指令,决定依赖安装、配置生效和代码编译,所有操作在临时容器中完成并固化为镜像层;它有shell格式(默认,用/bin/sh -c执行,支持管道和变量但跨镜像可能不兼容)和exec格式(推荐关键步骤,不经过shell解析,更安全可控)两种写法。

RUN 指令是 Dockerfile 中构建镜像时执行命令的核心方式,它直接决定依赖如何安装、配置如何生效、代码如何编译——所有这些操作都发生在构建阶段的临时容器里,最终固化为镜像的一层。用对 RUN,才能保证依赖干净、可复现、体积小、安全性高。
RUN 的两种写法和关键区别
它有两种语法形式,行为和适用场景不同:
-
Shell 格式(默认):写成
RUN apt-get update && apt-get install -y curl。Docker 自动用/bin/sh -c执行,支持管道、变量展开(如$HOME),但依赖宿主 Shell 行为,跨基础镜像可能出问题(比如 Alpine 默认用ash,不是bash)。 -
Exec 格式(推荐用于关键步骤):写成
RUN ["apt-get", "update"]或RUN ["sh", "-c", "apt-get update && apt-get install -y curl"]。不经过 shell 解析,更确定、更安全,适合需要精确控制执行环境的场景,比如设置环境变量或调用特定解释器。
构建依赖时怎么写才高效又干净
依赖安装最怕冗余层、缓存失效、残留文件。几个实操要点:
- 把多个关联命令合并到一条 RUN 中,避免生成中间层(每条 RUN 都会新增一层镜像)。例如:
RUN apt-get update && apt-get install -y python3-pip && rm -rf /var/lib/apt/lists/*。 - 安装完立刻清理缓存(如
apt-get clean、rm -rf /var/cache/apk/*),别等下一条 RUN —— 后者看不到前一层的磁盘空间变化。 - 用
--no-install-recommends(Debian/Ubuntu)或--no-cache(Alpine)减少非必要包,降低体积和攻击面。 - 避免在 RUN 中使用
curl | bash这类不可控脚本;如必须,应校验 checksum 或用固定 tag 的官方镜像替代。
如何避免常见陷阱
很多构建失败或镜像变大,其实就栽在 RUN 的细节里:
-
不要拆开 update 和 install:分开写两行 RUN,第二条会失效缓存,每次重跑
apt-get install前都得重新 update,拖慢构建且易因源同步延迟失败。 -
注意 WORKDIR 和路径上下文:RUN 执行时工作目录是当前 Dockerfile 的
WORKDIR,不是构建机的当前路径;相对路径基于此,别假设“就在项目根目录”。 -
环境变量不能跨 RUN 生效:
RUN export FOO=bar不会影响下一条 RUN;要用ARG+ENV,或把变量内联进命令里(如RUN FOO=bar sh -c 'echo $FOO')。 -
多阶段构建中 RUN 只作用于当前阶段:前面 stage 安装的编译工具,不会自动带到后面 stage;要用
COPY --from=builder显式传递产物。
实际例子:一个安全、精简的 Python 依赖安装
对比下面两种写法:
❌ 低效、有残留、缓存差:
RUN apt-get updateRUN apt-get install -y python3-pip
RUN pip3 install flask
✅ 推荐写法(合并+清理+指定版本):
RUN apt-get update && \apt-get install -y --no-install-recommends python3-pip && \
rm -rf /var/lib/apt/lists/* && \
pip3 install --no-cache-dir flask==2.3.3
这样既减少层数,又避免缓存污染,还锁定版本提升可重现性。











