Docker 环境变量传递进阶:优先级、覆盖与排错
Docker run 中 -e 覆盖镜像 ENV;Compose 的 environment 覆盖 env_file,再覆盖镜像 ENV,compose run -e 可覆盖该次配置;ARG 只存在于构建期。本文讲清覆盖关系、ARG 与 ENV 的分工及变量"不生效"的排查。
核心规则:同一个变量,运行期注入覆盖构建期设定——docker run 与Compose是两条独立启动路径。run中-e覆盖镜像ENV;Compose中environment覆盖env_file再覆盖镜像ENV,compose run -e可覆盖该次容器配置,不能把docker run排到Compose链上。ARG 则完全不同:只在构建期存在,运行期容器里根本看不到。
优先级一览(高→低)
# Dockerfile
ENV MODE=image-default
# compose
services:
app:
env_file: .env # Compose中低于environment
environment:
- MODE=compose-value # Compose中覆盖env_file
docker run -e MODE=runtime-value ... # 独立docker run路径,覆盖镜像ENV,不修改Compose服务
ARG vs ENV 分工
ARG VERSION=latest # 构建期参数,docker build --build-arg VERSION=1.2
FROM base:$VERSION
ARG VERSION
ENV APP_VERSION=$VERSION # 想在容器里也能看到,显式转存 ENV
ARG:构建期有效,docker history可见(别放密码),容器运行期不存在ENV:构建期与运行期都有效,除非被 -e 覆盖
变量"不生效"排查顺序
# 1. 容器里实际的值
docker exec myapp printenv MY_VAR
# 2. 创建时注入的值
docker inspect myapp --format '{{.Config.Env}}'
# 3. compose 渲染后的最终配置
docker compose config | grep -A5 environment
三层应分开:inspect反映创建配置,exec默认沿用创建配置而非PID1后来export的变量,不能用exec推断主应用环境;Compose渲染结果则用于核对插值与容器注入。
compose 里两个 .env 的坑
docker compose自动读项目根的.env——用于yml 里的 ${VAR} 替换- 要注入容器内,可用 env_file 或 environment 显式传入;自动读取.env作插值并不等于全部注入
两个用途同名不同命,混用是最高频错误。
敏感变量的正确姿势
环境变量可被 docker inspect 读出,生产秘钥建议:秘钥文件挂载、编排层 Secret(K8s Secret/Swarm secret)、或启动时从密管系统拉取。
常见问题(FAQ)
Q:改了 .env 文件容器内怎么没变?
A:环境变量是创建期注入的,改文件后要 docker compose up -d 重建容器。
Q:数组型变量怎么传(如多个 host)?
A:环境变量只能是字符串,约定分隔符(逗号)由应用解析,或改用配置文件挂载。
Q:能在 ENTRYPOINT 里动态计算变量吗?
A:可以,entrypoint 脚本里 export 的变量对随后启动的主进程生效——这是运行期动态配置的常见模式。