构建参数与动态配置让 Dockerfile 更灵活本文基于 Docker 24.x教你用一份 Dockerfile 搞定开发、测试、生产多环境。场景引入三个环境三个 Dockerfile组里之前是这么干的docker/ Dockerfile.dev # 开发环境带热重载、调试工具 Dockerfile.test # 测试环境带测试框架、覆盖率工具 Dockerfile.prod # 生产环境精简、安全、优化三个文件 80% 内容一样就中间几行不同。每次改基础镜像版本三个文件一起改漏一个就出问题。有没有办法一份 Dockerfile多环境复用有就是ARG和ENV这对组合拳。ARG vs ENV到底啥区别这是最容易搞混的两个指令咱们一次说清楚特性ARGENV作用时机构建时docker build构建时 运行时能否在运行时用❌ 不能✅ 能能否在 Dockerfile 里用✅ 能定义后✅ 能是否写入镜像层❌ 不写入✅ 写入适用场景构建配置版本、镜像源运行配置端口、日志级别类比装修房子ARG像装修时的临时决定——“客厅刷什么颜色的漆”装修完构建完就不重要了ENV像房子的永久设施——“水龙头装在哪”住进去运行时还能用实战一份 Dockerfile多环境构建基础模板# 构建参数构建时可覆盖 ARG NODE_VERSION18 ARG APP_ENVproduction # 基础镜像 FROM node:${NODE_VERSION}-alpine AS base WORKDIR /app # 依赖安装阶段 FROM base AS deps COPY package*.json ./ # 根据环境决定安装方式 ARG APP_ENV RUN if [ $APP_ENV development ]; then \ npm install; \ else \ npm ci --onlyproduction; \ fi # 构建阶段 FROM base AS builder COPY --fromdeps /app/node_modules ./node_modules COPY . . ARG APP_ENV ENV NODE_ENV${APP_ENV} # 生产环境才构建 RUN if [ $APP_ENV production ]; then \ npm run build; \ fi # 运行阶段 FROM base AS runner # 运行时环境变量会保留在镜像中 ARG APP_ENV ENV NODE_ENV${APP_ENV} ENV PORT3000 # 非 root 用户上篇讲的 RUN addgroup -g 1001 -S nodejs \ adduser -S appuser -u 1001 USER appuser WORKDIR /app # 根据环境拷贝不同内容 COPY --fromdeps --chownappuser:nodejs /app/node_modules ./node_modules COPY --frombuilder --chownappuser:nodejs /app/package.json ./ # 生产环境拷贝构建产物开发环境拷贝源码 ARG APP_ENV COPY --frombuilder --chownappuser:nodejs /app/dist ./dist COPY --chownappuser:nodejs . . EXPOSE ${PORT} # 根据环境选择启动命令 CMD if [ $NODE_ENV development ]; then \ npm run dev; \ else \ npm start; \ fi多环境构建命令# 开发环境热重载、完整依赖dockerbuild\--build-argNODE_VERSION20\--build-argAPP_ENVdevelopment\--targetrunner\-tmy-app:dev.# 测试环境dockerbuild\--build-argAPP_ENVtest\--targetrunner\-tmy-app:test.# 生产环境默认参数dockerbuild\-tmy-app:prod.进阶技巧动态选择镜像源国内构建时npm 源慢得要死。用ARG动态切换ARG NPM_REGISTRYhttps://registry.npmjs.org RUN npm config set registry ${NPM_REGISTRY} \ npm ci --onlyproduction# 国内构建dockerbuild\--build-argNPM_REGISTRYhttps://registry.npmmirror.com\-tmy-app.# 海外构建默认不用传参数dockerbuild-tmy-app.进阶技巧构建时注入版本信息想在应用里显示构建版本用ARG注入ARG BUILD_VERSIONunknown ARG BUILD_TIMEunknown ARG GIT_COMMITunknown ENV APP_VERSION${BUILD_VERSION} ENV APP_BUILD_TIME${BUILD_TIME} ENV APP_GIT_COMMIT${GIT_COMMIT}# 构建时传入真实信息dockerbuild\--build-argBUILD_VERSION1.2.3\--build-argBUILD_TIME$(date-u%Y-%m-%dT%H:%M:%SZ)\--build-argGIT_COMMIT$(gitrev-parse--shortHEAD)\-tmy-app:1.2.3.代码里读取// Node.jsconsole.log(Version:${process.env.APP_VERSION});console.log(Build:${process.env.APP_BUILD_TIME}(${process.env.APP_GIT_COMMIT}));坑ARG 的作用域ARG有个容易踩的坑从FROM开始前面的ARG失效。ARG NODE_VERSION18 # ✅ 这里有效 FROM node:${NODE_VERSION}-alpine # ✅ 可以用 ARG NODE_VERSION # ❌ 必须重新声明否则后面用不了 RUN echo ${NODE_VERSION} # 空值正确写法ARG NODE_VERSION18 FROM node:${NODE_VERSION}-alpine # 在 FROM 之后重新声明不赋值则继承前面的值 ARG NODE_VERSION RUN echo Building with Node ${NODE_VERSION}坑ENV 会永久留在镜像里# ❌ 错误敏感信息用 ENV ENV DATABASE_PASSWORDsecret123 # 即使后面覆盖原值仍在镜像层里 ENV DATABASE_PASSWORD攻击者可以通过docker history看到历史 ENV 值。正确做法敏感信息只通过运行时传入不用ENV硬编码# Dockerfile 里只声明不赋值 ENV DATABASE_PASSWORD# 运行时注入dockerrun-eDATABASE_PASSWORDsecret123 my-app动态配置的最佳实践总结场景推荐方式示例构建版本/时间戳ARGENVBUILD_VERSION镜像源/代理地址ARGNPM_REGISTRY运行时端口/日志级别ENV默认值PORT3000数据库连接串/密钥运行时-e注入DATABASE_URL环境标识dev/test/prodARGENVNODE_ENV一句话总结ARG管构建时ENV管运行时敏感信息绝不写进镜像动态配置一份 Dockerfile 走天下。