全书此前是真·五五开:含中文的括号 336 处半角 : 336 处全角,而且**在同一文件 内混用**——`18_security/18.1_kernel_ns.md` 第 11/17/20 行写 `(如 runC)`, 第 31 行写 `(用户命名空间映射)`。这不是体例问题,是它自己跟自己不一致。 集群里其余 13 本都一边倒(llm_internals 5 : 1049、oc_guide 2 : 713、 forward_deployed_engineering_guide 0 : 108),blockchain_guide 已在 2026-08-05 按同一方向统一过 97 处,故取全角,而非另立新规。全角括号前后不留空格, 原先 `概念 (镜像、容器、仓库) 和` 两侧的空格一并收掉。 **纯拉丁内容的半角括号一个没动**——`新建一个层 (Layer)`、`项目 (project)` 保持原样:改动前后「半角且内容纯拉丁」的数量都是 **1163**,而「半角且含中文」 从 **336 变成 0**。 其余验证:改动前后各自抹掉全部空白与圆括号后**逐字节相同**(两侧均 19559 字符); 左右括号总数改动前后都是 399 : 399,证明没有吞括号也没有配错对。 `check_project_rules` 202/202、`check_emphasis` 202/202、37 tests OK、 根 `format_checker` 与 `validate_codeblocks` 干净、`git diff --check` 干净。
4.8 KiB
7.1 RUN 执行命令
何时使用 RUN
RUN 是 Dockerfile 中最常用的指令,主要用于在镜像构建阶段执行命令来修改镜像。具体来说:
- 安装依赖:
RUN apt-get install nginx - 编译程序:
RUN gcc -o app main.c - 下载文件:
RUN curl -O https://example.com/file.tar.gz - 配置系统:
RUN mkdir -p /app/data
理解 RUN 的核心是理解镜像分层:每一个 RUN 都会在当前层之上创建新的一层,这会影响镜像大小。因此,合理使用 RUN(特别是合并多个 RUN)是构建轻量级镜像的关键。
7.1.1 基本语法
RUN <command>
RUN ["executable", "param1", "param2"]
RUN 指令是 Dockerfile 中最常用的指令之一。它在 当前镜像层 之上创建一个新层,执行指定的命令,并提交结果。
7.1.2 两种格式对比
1. Shell 格式
RUN apt-get update
- 特点:默认通过
/bin/sh -c执行。 - 优势:可以使用环境变量、管道、重定向等 Shell 特性。
- 示例:
RUN echo "Hello" > /test.txt
2. Exec 格式
RUN ["apt-get", "update"]
- 特点:直接调用可执行文件,不经过 Shell。
- 优势:避免 Shell 字符串解析问题,适用于参数中包含特殊字符的情况。
- 注意:无法使用
$VAR环境变量替换(除非显式调用 shell)。
7.1.3 常见最佳实践
1. 组合命令:减少层数
每一个 RUN 指令都会新建一层镜像。为了减少镜像体积和层数,应使用 && 连接命令。
❌ 糟糕的写法(创建 3 层):
RUN apt-get update
RUN apt-get install -y nginx
RUN rm -rf /var/lib/apt/lists/*
✅ 推荐写法(创建 1 层):
RUN apt-get update && \
apt-get install -y nginx && \
rm -rf /var/lib/apt/lists/*
2. 清理缓存
在安装完软件后,立即清除缓存,可以显著减小镜像体积。
-
Debian/Ubuntu:
RUN apt-get update && apt-get install -y package-bar \ && rm -rf /var/lib/apt/lists/* -
Alpine:
RUN apk add --no-cache package-bar
3. 使用 set -e 和 pipefail
默认情况下,管道命令 cmd1 | cmd2 的返回值由最后一个命令 (cmd2) 决定,即使前面的命令失败了,整个 RUN 仍可能视为成功。
❌ 隐蔽的错误:
## 如果下载失败,gzip 可能会报错,但如果不影响后续,构建可能继续
RUN wget http://error-url | gzip -d > file
✅ 推荐写法:
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN wget http://url | gzip -d > file
7.1.4 常见问题
Q:为什么 RUN cd /app 不生效?
RUN cd /app
RUN touch hello.txt
结果:hello.txt 会出现在根目录 /,而不是 /app。原因:每个 RUN 都在一个新的 Shell/容器环境中执行。cd 只影响当前 RUN 的环境。解决:使用 WORKDIR 指令。
WORKDIR /app
RUN touch hello.txt
Q:环境变量不生效?
RUN export MY_VAR=hello
RUN echo $MY_VAR
结果:输出为空。原因:同上,环境变量只在当前 RUN 有效。解决:使用 ENV 指令,或在同一行 RUN 中导出。
ENV MY_VAR=hello
RUN echo $MY_VAR
7.1.5 高级技巧
1. 使用 BuildKit 的挂载缓存
BuildKit 支持在 RUN 指令中使用 --mount 挂载缓存,加速构建。
## 缓存 apt 包
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y gcc
## 缓存 Go 模块
RUN --mount=type=cache,target=/go/pkg/mod \
go build -o app
2. 挂载密钥
安全地使用 SSH 密钥或 Token,而不将其记录在镜像中。
RUN --mount=type=secret,id=mysecret \
my-private-tool --token-file /run/secrets/mysecret
不要在构建命令中 cat、echo 或打印密钥内容;应把 /run/secrets/<id> 路径交给真正消费密钥的工具。
3. Heredoc 语法
BuildKit 支持使用 heredoc 语法编写多行脚本,无需行末反斜杠 \ 连接:
RUN <<EOF
apt-get update
apt-get install -y \
build-essential \
curl \
git
rm -rf /var/lib/apt/lists/*
EOF
优势:
- 可读性更强,不需要在每行末尾添加
\ - 避免在脚本中转义引号
- 支持多个 heredoc 块,可指定不同的 Shell
RUN <<EOF
echo "使用默认 /bin/sh"
EOF
RUN <<'EOF'
#!/bin/bash
set -euo pipefail
echo "使用 bash"
wget http://example.com/file.tar.gz
EOF
使用 heredoc 需要在 Dockerfile 首行声明 BuildKit 语法版本:
# syntax=docker/dockerfile:1