核心是让源码不进入最终镜像,通过多阶段构建剥离源码(仅保留在builder阶段)、运行阶段使用alpine/distroless镜像并copy产物、禁用root运行、配合.dockerignore精确控制输入。

构建保护源代码的生产环境安全镜像,核心不是“隐藏代码”,而是通过设计让源码不进入最终镜像、不以可读形式残留、不被容器内进程意外暴露。关键在于分阶段剥离、权限隔离与最小化交付。
用多阶段构建彻底剥离源码
源码只存在于构建阶段,运行阶段镜像中不应包含任何 .go、.py、.js 等源文件或构建缓存目录(如 node_modules、__pycache__)。
- 第一阶段(builder)使用完整工具链(如 golang:1.21、node:20)编译或打包,生成可执行文件或静态资源
- 第二阶段(runtime)仅基于 Alpine 或 distroless 镜像,COPY --from=builder 复制成品(如二进制、dist/ 目录),不复制源码、package.json、Dockerfile 或 .git 目录
- 避免在 RUN 指令后保留源码:不要写 COPY . . && go build && rm -rf *.go —— 这仍会让源码短暂存在于该层;应先 COPY 源码 → 构建 → 删除源码 → 仅保留产物
禁止 root 运行并限制文件权限
即使源码未进入镜像,若容器以 root 身份运行且挂载了宿主机卷或存在配置注入漏洞,仍可能被利用提权读取敏感路径。非 root 用户是纵深防御的关键一环。
- 在运行阶段镜像中创建专用用户(如 RUN adduser -D -u 1001 appuser),再用 USER appuser 切换身份
- COPY 产物后立即修正所有权:RUN chown -R appuser:appuser /app && chmod -R 755 /app
- 确保 CMD 或 ENTRYPOINT 启动的进程由该用户执行;检查日志或 ps aux 输出确认 UID 不为 0
精简基础镜像并禁用包管理器
Alpine 或 distroless 镜像本身不含 shell、包管理器和调试工具,天然降低攻击面,也使源码反向提取几乎不可行。
- 优先选用 FROM gcr.io/distroless/static-debian12 或 FROM alpine:3.20(而非 latest)
- 避免在运行阶段镜像中安装 apk/apt —— distroless 无包管理器,Alpine 若需少量工具(如 curl),应在构建阶段完成,运行阶段保持“只读+无工具”状态
- 删除证书缓存以外的所有冗余:RUN rm -rf /var/cache/apk/* /tmp/* /var/tmp/*
配合构建上下文与.dockerignore控制输入
源码保护始于构建起点——防止本不该进镜像的文件被意外打包。
- 在项目根目录创建 .dockerignore,明确排除:.git、.env、*.md、tests/、node_modules/、__pycache__/、Dockerfile
- 避免 COPY . .;改为显式 COPY package*.json . 和 COPY src/ .,确保只传必要内容
- 若使用 CI/CD,确保构建上下文目录不包含敏感凭证或本地开发配置











