6月20日 00:02

Docker 环境变量怎么配置?ENV、ARG 和 Compose 怎么选?

Docker 环境变量到底能配置在哪?

Docker 容器环境变量常见配置方式有四类:写在 Dockerfile 里、运行容器时传入、通过环境变量文件批量传入、在 Docker Compose 中配置。它们看起来都叫“环境变量”,但生效时机、覆盖规则和安全风险并不一样。

一句话先说结论:固定默认值可以放 Dockerfile,环境差异参数用运行时传入,生产敏感信息不要直接当环境变量裸奔。

Dockerfile 里的 ENV 和 ARG 有什么区别?

Dockerfile 里最容易混淆的是 ENVARG

ENV:构建后仍然存在

ENV 用来设置镜像默认环境变量,构建出的镜像和启动后的容器里都能看到。

dockerfile
FROM node:20-alpine ENV NODE_ENV=production ENV APP_PORT=3000 CMD ["node", "server.js"]

适合放默认运行参数,比如 NODE_ENV、默认端口、默认语言环境。它不适合放密码、Token、数据库连接串,因为这些值可能进入镜像层历史,也可能被 docker inspect 看到。

ARG:只在构建阶段使用

ARG 只在镜像构建时生效,默认不会保留到运行时环境里。

dockerfile
FROM node:20-alpine ARG BUILD_VERSION RUN echo "build version: $BUILD_VERSION"

构建时传入:

bash
docker build --build-arg BUILD_VERSION=2026.06.19 -t my-app .

如果需要把 ARG 写入运行时环境,必须显式转成 ENV。这也意味着它会进入最终镜像环境。不要把“ARG 构建时用”误解成“ARG 一定安全”,构建日志、镜像历史和 CI 记录里仍可能留下痕迹。

docker run 怎么传环境变量?

运行容器时最直接的方式是 -e--env

bash
docker run -e NODE_ENV=production -e APP_PORT=3000 my-app

也可以只写变量名,让 Docker 从当前 Shell 环境里取值:

bash
export API_BASE_URL=https://api.example.com docker run --env API_BASE_URL my-app

如果变量比较多,用 --env-file 更清爽。

bash
docker run --env-file .env.production my-app

.env.production 示例:

env
NODE_ENV=production APP_PORT=3000 API_BASE_URL=https://api.example.com

--env-file 不是 Shell 脚本,通常按 KEY=VALUE 读取,不要指望它执行命令、展开复杂表达式。值里如果有空格、引号、换行,最好先确认解析行为。

Docker Compose 里 environment、env_file 和 .env 有什么区别?

Compose 里有三个名字很像的东西:.envenv_fileenvironment。它们不是一回事。

.env:主要用于 compose 文件变量插值

项目根目录的 .env 常用于替换 compose.yml 里的占位变量。

env
IMAGE_TAG=1.2.0 HOST_PORT=8080
yaml
services: web: image: my-app:${IMAGE_TAG} ports: - "${HOST_PORT}:3000"

这里 .env 的作用是让 Compose 在解析配置文件时,把 ${IMAGE_TAG}${HOST_PORT} 替换掉。它不等于自动把所有变量都塞进容器。

env_file:把文件里的变量传给容器

yaml
services: web: image: my-app:1.2.0 env_file: - .env.production

这会把 .env.production 里的变量注入容器运行环境。

environment:直接在 compose 里声明容器变量

yaml
services: web: image: my-app:1.2.0 environment: NODE_ENV: production APP_PORT: "3000" API_BASE_URL: ${API_BASE_URL}

environment 的好处是配置直观,适合少量关键变量;变量很多时,env_file 更容易维护。

环境变量优先级怎么算?

对单个 docker run 来说,通常可以按这个理解:

  1. docker run -e KEY=value 显式传入的值优先级最高;
  2. docker run --env-file 中的值次之;
  3. 镜像里 Dockerfile ENV 的默认值最后兜底。

Compose 的优先级更细一些:environment 里直接声明的值通常会覆盖 env_file 同名变量;env_file 会覆盖镜像 ENV 默认值;.env 主要影响 ${VAR} 插值,本身不自动等于容器环境变量;命令行临时覆盖通常最高。

如果变量来源很多,不要靠猜。可以用下面命令看 Compose 最终解析结果:

bash
docker compose config

再进容器确认实际值:

bash
docker compose exec web env | grep NODE_ENV

敏感信息能不能放环境变量?

能用,但不推荐把它当成安全存储。环境变量很方便,也很容易泄漏:docker inspect 可能看到容器环境变量,应用启动日志可能把配置整体打印出来,CI/CD 日志中也可能出现明文。

所以数据库密码、私钥、访问 Token 这类内容,生产环境更建议使用 Docker Swarm Secrets、Kubernetes Secrets、云厂商密钥管理服务,或者运行平台提供的 Secret 注入能力。

Secrets 不是环境变量,那应用怎么读?

很多运行平台会把 Secret 挂载成文件,而不是直接放进环境变量。Docker Swarm Secrets 常见路径类似:

text
/run/secrets/db_password

不少官方镜像支持 _FILE 约定,例如:

env
MYSQL_PASSWORD_FILE=/run/secrets/mysql_password POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password

这个约定不是 Docker 强制标准,而是很多镜像和框架采用的习惯。自己写应用时也可以照这个思路做:普通配置走环境变量,敏感值走文件或密钥服务。

一个更接近生产的配置方式

开发环境可以简单一点:

yaml
services: web: build: . ports: - "3000:3000" env_file: - .env.development environment: NODE_ENV: development

生产环境建议把默认值、环境差异和密钥分开:

yaml
services: web: image: my-app:1.2.0 environment: NODE_ENV: production APP_PORT: "3000" DB_HOST: db DB_PASSWORD_FILE: /run/secrets/db_password secrets: - db_password secrets: db_password: external: true

应用启动时读取:

js
import fs from 'node:fs'; function readSecret(name) { const file = process.env[`${name}_FILE`]; if (file) return fs.readFileSync(file, 'utf8').trim(); return process.env[name]; } const dbPassword = readSecret('DB_PASSWORD');

配置后怎么验证?

查看 Compose 渲染后的配置:

bash
docker compose config

查看容器环境变量:

bash
docker exec <container> env | sort

查看镜像默认环境变量:

bash
docker image inspect my-app --format '{{json .Config.Env}}'

查看容器环境变量时要小心,不要在共享终端、CI 日志或工单截图里暴露敏感值。很多泄漏不是黑客攻破系统,而是排查问题时顺手贴了一段日志。

生产环境建议怎么定规则?

比较稳的做法是把变量按用途分层:镜像默认值放无敏感、跨环境都合理的默认配置;部署环境变量放不同环境会变化的普通配置;Secret 放密码、Token、私钥、证书;CI/CD 参数放构建版本、提交哈希、构建时间这类构建期信息。

还要给变量命名留点规矩。比如统一使用 APP_DB_REDIS_ 前缀;布尔值固定用 true/false;端口统一写字符串,避免 YAML 把值解析成奇怪的类型。

最后,给应用启动加一层配置校验。缺了关键变量就直接失败,不要带着默认空值跑起来。Docker 环境变量配置没有唯一答案,关键是边界清楚:默认配置归默认配置,环境差异归部署系统,敏感信息归 Secrets。

标签:Docker