Docker 容器配置怎么管理?环境变量、挂载和配置中心怎么选
Docker 容器的配置管理不是把配置写死在镜像里——那样每次改配置都得重新构建。正确做法是配置与镜像分离,容器启动时注入配置,运行时能动态更新。这篇讲清楚 Docker 环境下四种配置管理方式的适用场景和实现方法。
环境变量:最简单的配置注入
适合少量、扁平的配置项(数据库地址、端口、开关):
yaml# docker-compose.yml services: app: image: myapp:latest environment: - DB_HOST=postgres - DB_PORT=5432 - LOG_LEVEL=info - FEATURE_FLAG=true
或者用 .env 文件:
bash# .env DB_HOST=postgres DB_PORT=5432 LOG_LEVEL=info
yamlservices: app: env_file: .env
优势:简单、Docker 原生支持、docker-compose 直接读取 局限:只有字符串值、不能表示嵌套结构、修改需要重启容器
敏感信息别放环境变量
环境变量会被 docker inspect 暴露,也会出现在进程列表里。密码、密钥等用 Docker Secret 或文件挂载:
bash# Docker Swarm Secret echo "my_password" | docker secret create db_password -
yamlservices: app: secrets: - db_password secrets: db_password: external: true
配置文件挂载:复杂配置的首选
当配置是结构化的(YAML、JSON、TOML),用 Volume 挂载比环境变量更合适:
yamlservices: app: image: myapp:latest volumes: - ./config/app.yml:/app/config/app.yml:ro - ./config/nginx.conf:/etc/nginx/conf.d/default.conf:ro
:ro 表示只读挂载——容器不能修改配置文件,只能读取。防止容器内进程意外修改配置。
只挂载需要的文件,别挂载整个目录
yaml# 好:只挂载需要的文件 volumes: - ./config/app.yml:/app/config/app.yml:ro # 差:挂载整个目录,可能暴露无关文件 volumes: - ./config:/app/config:ro
配置热更新:不重启容器更新配置
挂载的文件修改后,容器内立即可见——但应用是否自动重新加载取决于应用本身:
- Nginx:
docker exec nginx nginx -s reload - Spring Boot:配合 Spring Cloud Config 自动刷新
- Node.js:用
chokidar监听文件变化
如果应用不支持热加载,可以配合 inotifywait 检测文件变化后发送信号:
bash#!/bin/bash inotifywait -m -e modify /app/config/app.yml | while read event; do kill -SIGHUP 1 # 发送 HUP 信号给主进程 done
配置中心:分布式系统的统一配置
多服务、多实例的场景,配置文件挂载管理成本太高——改一个配置要同步到所有机器。配置中心解决的就是这个问题。
Consul + Consul-Template
yaml# docker-compose.yml services: consul: image: consul:1.15 ports: - "8500:8500" command: agent -dev -client=0.0.0.0 app: image: myapp:latest volumes: - ./templates:/templates command: > consul-template -consul-addr=consul:8500 -template="/templates/app.ctmpl:/app/config/app.yml:docker restart app"
consul-template 监听 Consul KV 变化,自动重新生成配置文件并触发容器重启。
Etcd + Confd
和 Consul-Template 类似的模式,Etcd 做存储,Confd 做模板渲染:
bash# 写入配置 etcdctl set /myapp/db_host "postgres.prod" # confd 读取 etcd 并渲染模板 confd -onetime -backend etcd -node http://etcd:2379
Spring Cloud Config Server
Java 生态的标准方案:
yamlservices: config-server: image: springcloud/configserver ports: - "8888:8888" environment: - SPRING_CLOUD_CONFIG_SERVER_GIT_URI=https://github.com/org/config-repo app: image: my-spring-app environment: - SPRING_CLOUD_CONFIG_URI=http://config-server:8888
配置存在 Git 仓库里,有版本历史。应用启动时从 Config Server 拉取配置,配合 /actuator/refresh 端点实现热更新。
Kubernetes ConfigMap 和 Secret
如果跑在 K8s 上,环境变量和文件挂载都由 ConfigMap/Secret 管理:
yaml# ConfigMap apiVersion: v1 kind: ConfigMap metadata: name: app-config data: DB_HOST: postgres app.yml: | server: port: 8080 logging: level: info --- # Pod 使用 ConfigMap apiVersion: v1 kind: Pod spec: containers: - name: app envFrom: - configMapRef: name: app-config volumeMounts: - name: config mountPath: /app/config volumes: - name: config configMap: name: app-config
Secret 和 ConfigMap 用法一样,但值是 base64 编码的,且访问可以加 RBAC 控制:
bashkubectl create secret generic db-credentials \ --from-literal=username=admin \ --from-literal=password=s3cret
ConfigMap/Secret 更新后 Pod 不会自动重启——需要手动 kubectl rollout restart 或用 Reloader 之类的工具自动触发。
选择决策
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 单容器、少量配置 | 环境变量 + .env | 最简单 |
| 单容器、复杂配置 | Volume 挂载配置文件 | 支持结构化配置 |
| 多容器、同一台主机 | docker-compose + 挂载 | compose 管理方便 |
| 多主机、多服务 | Consul/Etcd 配置中心 | 统一管理、动态更新 |
| Kubernetes 环境 | ConfigMap + Secret | K8s 原生方案 |
| 敏感信息 | Docker Secret / K8s Secret | 不暴露在环境变量里 |
原则:配置与代码分离,敏感信息加密,变更可追溯。能用简单的就不用复杂的——别为了用 Consul 而用 Consul。