Docker Compose 环境变量配置实战:三种方式与避坑指南
在 Docker Compose 编排多容器应用时,环境变量配置是连接服务、注入配置、管理敏感信息的关键环节。很多开发者会遇到变量不生效、覆盖顺序混乱、敏感信息泄露等问题。本文将从配置方式、优先级规则、常见报错三个维度,给出可直接落地的解决方案。
它解决什么问题 / 适用场景
Docker Compose 环境变量机制解决的核心问题是:如何为不同服务(Web 应用、数据库、缓存等)动态注入配置,同时保持 compose.yaml 的通用性。典型场景包括:
- 开发环境快速切换:通过
.env文件切换数据库连接串、API 端点等配置,无需修改compose.yaml。 - CI/CD 流水线注入:在构建阶段通过 shell 环境变量或命令行参数注入构建号、版本标签等。
- 敏感信息分离:将密码、密钥等敏感信息从代码仓库中剥离,通过外部文件或 secrets 管理。
Docker Compose 提供了三种互补的变量设置方式,支持变量插值(interpolation)、多文件覆盖、可选文件(required: false)以及替代文件格式(format),在灵活性和安全性之间取得了良好平衡。
核心配置 / 参数说明
Docker Compose 支持三种环境变量配置方式,各有适用场景。下表对比了它们的核心差异:
| 配置方式 | 定义位置 | 变量作用域 | 安全性 | 灵活性 |
|---|---|---|---|---|
environment | compose.yaml 内联 | 服务级 | 低(明文在 YAML 中) | 高(支持列表和映射语法) |
env_file | 外部 .env 文件 | 服务级 | 中(文件可设置权限) | 高(支持多文件、可选文件、格式选择) |
-e / --env | 命令行 | 临时服务级 | 中(可能暴露在 shell 历史) | 中(适合临时调试) |
1. environment 属性
直接在 compose.yaml 的服务定义中设置环境变量,支持两种语法:
YAML# 映射语法(推荐) services: web: image: nginx:alpine environment: DEBUG: "true" APP_ENV: production DB_URL: "postgres://user:pass@db:5432/mydb" # 列表语法(等效) services: web: image: nginx:alpine environment: - DEBUG=true - APP_ENV=production - DB_URL=postgres://user:pass@db:5432/mydb
注意:映射语法中,值必须用引号包裹(如 "true"),否则 YAML 解析器可能将 true 解释为布尔值。列表语法中,值不需要引号,但等号两边不能有空格。
2. env_file 属性
引用外部 .env 文件,适合管理大量变量或在不同服务间共享同一组变量:
YAMLservices: web: image: nginx:alpine env_file: - path: ./common.env required: true - path: ./optional.env required: false # 从 Docker Compose 2.24.0 开始支持 - path: ./app.env format: properties # 支持 properties 格式(默认是 env 格式)
关键细节:
env_file的路径是相对于compose.yaml文件的,而非当前工作目录。- 多文件覆盖时,后指定的文件会覆盖先指定的文件中的同名变量。
- 从 Docker Compose 2.24.0 开始,可以设置
required: false使文件可选,避免文件不存在时报错。
3. -e / --env 命令行参数
通过 docker compose run 命令临时设置环境变量,适合调试或一次性任务:
BASH# 设置单个变量 docker compose run -e DEBUG=1 web # 从 shell 环境传递变量(不赋值) export MY_SECRET="s3cr3t" docker compose run -e MY_SECRET web # 从环境文件传递 docker compose run --env-file ./extra.env web
注意:使用 -e VARIABLE(不赋值)时,如果 shell 环境中未设置该变量,不会报错,但容器内该变量会为空。
变量优先级与覆盖规则
理解 Docker Compose 的变量优先级至关重要,否则很容易出现“我明明设置了,为什么容器里没有”的问题。
优先级从高到低:
docker compose run -e命令行参数environment属性(compose.yaml内联)env_file属性(外部文件).env文件(项目根目录,用于变量插值)
变量插值(Interpolation):在 compose.yaml 中使用 ${VARIABLE} 语法时,Docker Compose 会从以下位置查找变量值(按优先级从高到低):
- 当前 shell 环境变量
- 项目根目录下的
.env文件 - 如果都找不到,Compose 会发出警告,并将变量替换为空字符串
YAML# compose.yaml services: web: image: nginx:alpine environment: - APP_ENV=${APP_ENV:-development} # 默认值语法 - DB_URL=${DB_URL}
BASH# .env 文件(项目根目录) APP_ENV=staging DB_URL=postgres://user:pass@db:5432/mydb
重要限制:变量插值仅在 Docker Compose CLI 中生效,不适用于 docker run --env-file。
常见报错与排查
1. 环境变量未生效,容器内变量为空
报错现象:容器启动后,应用读取不到预期的环境变量。
排查步骤:
- 检查
compose.yaml中environment或env_file的配置是否正确。 - 如果使用
-e VARIABLE(不赋值),确保 shell 环境中已设置该变量。 - 如果使用插值
${VARIABLE},确保.env文件或 shell 环境中已定义该变量。
验证命令:
BASH# 进入容器查看环境变量 docker compose exec web env | grep MY_VAR # 或直接打印 docker compose run web env
2. env_file 文件未找到(FileNotFoundError)
报错现象:启动时提示 file not found 错误。
解决方案:
- 确认
env_file的路径是相对于compose.yaml文件的。 - 检查文件是否存在,以及文件名是否拼写正确。
- 从 Docker Compose 2.24.0 开始,可以设置
required: false使文件可选:
YAMLenv_file: - path: ./optional.env required: false
3. 变量覆盖顺序不符合预期
报错现象:设置了多个变量源,但容器内取到的值不是期望的那个。
解决方案:理解并遵循优先级规则。对于 env_file 中的多个文件,后指定的文件会覆盖先指定的文件中的同名变量:
YAMLenv_file: - ./base.env # 先加载 - ./override.env # 后加载,同名变量会覆盖 base.env 中的值
4. 使用 docker run --env-file 时变量插值不生效
报错现象:在 docker run 命令中使用 --env-file 时,${VARIABLE} 没有被替换。
根因:变量插值是 Docker Compose CLI 的特性,docker run --env-file 不支持。如果需要在 docker run 中使用,请直接在 .env 文件中写入字面值。
生产环境实践与注意事项
1. 敏感信息管理
绝对不要将密码、API 密钥等敏感信息存储在环境变量中。官方建议使用 Docker Secrets:
YAML# compose.yaml services: web: image: myapp:latest secrets: - db_password secrets: db_password: file: ./secrets/db_password.txt
应用代码从 /run/secrets/db_password 文件读取密码,而非环境变量。这样 secret 不会在日志、docker inspect 输出或子进程中泄露。
2. 文件权限控制
确保 .env 文件权限设置正确,避免被其他用户读取:
BASHchmod 600 .env
3. 路径问题
env_file 的路径是相对于 compose.yaml 文件的,而非当前工作目录。如果使用 -f 指定了非默认路径的 compose.yaml,务必注意:
BASH# compose.yaml 在 ./config/ 目录下 docker compose -f ./config/compose.yaml up # env_file 路径应相对于 ./config/ # 例如:./config/.env
4. 并发冲突
多个 docker compose 实例同时操作同一项目可能导致状态不一致。建议使用 --project-name 隔离:
BASHdocker compose --project-name myapp-dev up -d docker compose --project-name myapp-staging up -d
5. 变量插值默认值
使用 ${VARIABLE:-default} 语法提供默认值,避免变量未定义时容器内变量为空:
YAMLenvironment: - APP_ENV=${APP_ENV:-development}
常见问题 FAQ
Q: 如何在 Docker Compose 中安全地管理数据库密码等敏感信息?
A: 官方建议不要使用环境变量传递敏感信息,而应使用 Docker Secrets。在 Docker Compose 中,你可以通过 secrets 顶级元素定义 secret,然后在服务中使用 secrets 属性挂载到容器内的文件(如 /run/secrets/db_password)。应用代码再从该文件读取。这比环境变量更安全,因为 secret 不会在日志、docker inspect 输出或子进程中泄露。
Q: environment 属性和 env_file 属性有什么区别?我该用哪个?
A: environment 属性直接在 compose.yaml 中定义变量,适合少量、非敏感的配置,或需要明确展示在配置文件中的变量。env_file 属性引用外部 .env 文件,适合管理大量变量、在不同服务间共享同一组变量、或希望将配置与主配置文件分离的场景。通常建议:对于开发环境,使用 env_file 配合 .env 文件;对于生产环境,使用 environment 配合 CI/CD 注入的变量或 secrets。
Q: 为什么我在 compose.yaml 中使用了 ${MY_VAR},但容器内该变量为空?
A: Docker Compose 的变量插值(interpolation)会从以下位置查找变量值(按优先级从高到低):1. 当前 shell 环境变量;2. 项目根目录下的 .env 文件;3. 如果都找不到,Compose 会发出警告,并将变量替换为空字符串。请确保在 shell 中 export MY_VAR=value,或在项目根目录创建 .env 文件并写入 MY_VAR=value。注意:env_file 属性指定的文件不会用于插值,它仅用于将文件中的变量注入容器环境。