bigywater.tech
Docker · 12

ARG 与 ENV:构建参数在镜像里留下了什么痕迹

很多人以为 ARG 只在构建时可见、用完就消失,所以拿它传 token。这篇用一个故意传了假 token 的构建,把它留在了哪些地方一处处找出来,并验证 ARG 对构建缓存的影响。

3 行
docker history --no-trunc 里含有 --build-arg 明文值的行数
2 个 RUN 都重做
只改 VERSION 这个 ARG 时,连只用到 TOKEN 的 RUN 也重新执行
空值
FROM 之后的阶段里没有重新声明 ARG 就读不到 FROM 之前声明的 ARG

ARG 不是秘密

实测 · 本机执行输出
  构建退出码 0;BuildKit 的警告:#1 WARN: SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "TOKEN") (line 2) | - SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "TOKEN") (line 2)
  运行时环境变量里有 APP_MODE=prod;TOKEN=[];VERSION=[]
  构建时 TOKEN=abc123SECRET
  版本 2.0
  docker inspect 的 Config.Env = ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","APP_MODE=prod"]
  docker history --no-trunc 里含有 "abc123SECRET" 的行数 = 3:
    RUN |2 TOKEN=abc123SECRET VERSION=2.0 /bin/sh -c echo "版本 $VERSION" > /version.txt # buildkit
    RUN |2 TOKEN=abc123SECRET VERSION=2.0 /bin/sh -c echo "构建时 TOKEN=$TOKEN" > /build.txt # buildkit
    ARG TOKEN=abc123SECRET
  docker save 导出的 tar 里,包含 "abc123SECRET" 的文件(含各层 blob 与配置 JSON)有 1 个

运行时环境变量里确实没有 TOKEN,但镜像的构建历史把它写得清清楚楚:RUN |2 TOKEN=abc123SECRET VERSION=2.0 … 和 ARG TOKEN=abc123SECRET,而且 docker save 导出的配置里也有。拿到镜像的人不需要运行它。BuildKit 构建时自己就警告 SecretsUsedInArgOrEnv。需要在构建期使用密钥,用下面会讲的 --mount=type=secret。

ARG 与缓存

实测 · 本机执行输出
  [什么都不变] 3/4 'RUN echo "构建时 TOKEN=t1" > /build.txt'→CACHED;4/4 'RUN echo "版本 1" > /version.txt'→CACHED
  [只改 VERSION] 3/4 'RUN echo "构建时 TOKEN=t1" > /build.txt'→重新执行;4/4 'RUN echo "版本 2" > /version.txt'→重新执行
  [只改 TOKEN] 3/4 'RUN echo "构建时 TOKEN=t2" > /build.txt'→重新执行;4/4 'RUN echo "版本 1" > /version.txt'→重新执行

ARG 声明之后的每个 RUN,缓存键里都带着所有 ARG 的值(history 里的 |2 TOKEN=… VERSION=… 前缀)。所以改任何一个 ARG,后面所有 RUN 都要重新执行,哪怕有的 RUN 根本没用到它。把经常变化的 ARG(比如构建号)放在 Dockerfile 的靠后位置。

作用域

实测 · 本机执行输出
  FROM 之后直接用 BASE=[] | 重新声明 ARG BASE 之后=[alpine:3.20]

写在 FROM 之前的 ARG 只能用在 FROM 行本身,进入阶段后得到空值,要在阶段里再写一行 ARG BASE 才能用。

ENV

实测 · 本机执行输出
  docker run -e APP_MODE=dev:APP_MODE=dev

ENV 会一直留在镜像配置里并成为容器环境变量,docker run -e 可以覆盖。放默认配置可以,放密钥不行(docker inspect 能直接看到,环境变量泄漏那一篇会专门验证)。

分步演示

ARG 与 ENV
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Docker Desktop(Engine 29.8.2,容器跑在 Docker Desktop 自带的 Linux 虚拟机里,内核与宿主机的 macOS 不同);镜像为 linux/arm64;需要看虚拟机内部时用特权容器 + nsenter;计时用 time / date +%s%N,数字每次运行会有小幅抖动。假的 token 字符串 abc123SECRET;docker history --no-trunc、docker inspect、docker save + Python tarfile 搜索字符串;缓存判断来自构建输出里各 RUN 步骤的 CACHED;每次运行前加了一个带时间戳的 RUN 层避免命中之前实验的缓存;实验后已删除所有镜像
    • 没有验证:多阶段构建中 ARG 是否泄漏到最终阶段的镜像历史(各阶段的 history 不同)、docker buildx imagetools 看 provenance 里是否记录 build-arg、预定义 ARG(HTTP_PROXY、TARGETARCH 等)、层压缩(squash)后历史是否还在
    京ICP备2026067502号-1京公网安备11010502063487号