Docker 容器环境变量配置的 7 个建议

说明: 本文为配置思路与示例整理,不代表作者已在自己的服务器上逐项验证全部命令。执行涉及公网暴露、账户权限、数据删除或服务重启的操作前,请先备份,并结合官方文档与实际环境核验。

本文总结了 Docker 环境变量配置中最常见的 7 个坑点和建议,帮助你在部署容器化应用时少走弯路。无论你是刚接触 Docker 的新手还是有经验的运维工程师,这些经验都值得收藏。

前言

在 Docker 容器化部署中,环境变量(Environment Variables)是最基础也最容易出问题的配置方式。一个拼写错误、一个缺失的变量、一个暴露的密钥,都可能导致服务无法启动甚至安全事故。

本文将通过真实场景中常见的 7 个问题,带你掌握 Docker 环境变量配置的建议。


实践一:永远不要在 Dockerfile 中硬编码敏感信息

❌ 错误做法

DOCKERFILE
# Dockerfile - 千万不要这样写!
FROM node:20-alpine
ENV DB_PASSWORD=super_secret_123
ENV JWT_SECRET=my_jwt_secret_key
COPY . /app
CMD ["node", "server.js"]

问题分析

  • docker history 命令可以查看镜像的所有层,硬编码的密钥一览无余
  • 镜像推送到 Docker Hub 后,所有人都能看到这些密钥
  • 即使后续删除,git 历史中仍然保留

✅ 正确做法

DOCKERFILE
# Dockerfile - 只声明变量名,不赋值
FROM node:20-alpine
# 声明需要的环境变量(可选,仅做文档用途)
ENV DB_HOST=""
ENV DB_PORT=5432
ENV DB_NAME=""
COPY . /app
CMD ["node", "server.js"]

敏感值在运行时通过 docker run -e.env 文件注入。


实践二:使用 .env 文件集中管理配置

当你的应用有十几个环境变量时,在命令行一个个 -e 显然不现实。.env 文件是最简洁的方案。

项目结构

TEXT
my-project/
├── docker-compose.yml
├── .env              # 实际配置(不提交到 git)
├── .env.example      # 示例配置(提交到 git)
├── Dockerfile
└── src/

.env.example(提交到 git,供团队参考)

BASH
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_USER=app_user
DB_PASSWORD=         # 请填写实际密码

# Application
APP_PORT=3000
JWT_SECRET=          # 请生成随机密钥
LOG_LEVEL=info

.env(实际使用,加入 .gitignore)

BASH
DB_HOST=192.168.1.100
DB_PORT=5432
DB_NAME=myapp_prod
DB_USER=app_user
DB_PASSWORD=r4nd0m_str0ng_p@ss!

APP_PORT=3000
JWT_SECRET=a1b2c3d4e5f6randomsecretkey789
LOG_LEVEL=warn

docker-compose.yml 中使用

YAML
services:
  app:
    build: .
    env_file:
      - .env
    ports:
      - "${APP_PORT}:3000"
  db:
    image: postgres:16
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}

关键提醒:务必在 .gitignore 中加入 .env,只提交 .env.example


实践三:区分环境的配置管理

开发、测试、生产环境的配置通常不同。推荐使用多 .env 文件策略:

TEXT
.env.dev       # 开发环境
.env.staging   # 预发布环境
.env.prod      # 生产环境

使用方式

BASH
# 开发环境
docker compose --env-file .env.dev up -d

# 生产环境
docker compose --env-file .env.prod up -d

环境变量优先级

Docker 的环境变量有明确的优先级(从高到低):

  1. docker run -e KEY=VALUE(命令行直接指定)
  2. docker-compose.yml 中的 environment 字段
  3. docker-compose.yml 中的 env_file 字段
  4. Dockerfile 中的 ENV 指令

理解这个优先级,可以让你在调试时快速定位问题。


实践四:变量缺失不应该是"静默失败"

这是最常见的问题之一:某个环境变量没有设置,应用使用了空字符串或 undefined 作为配置值,运行时才暴露问题。

服务端优雅处理(Node.js 示例)

JAVASCRIPT
// config.js - 启动时校验所有必要配置
const requiredEnvVars = [
  'DB_HOST',
  'DB_PORT',
  'DB_NAME',
  'DB_USER',
  'DB_PASSWORD',
  'JWT_SECRET',
];

const missing = requiredEnvVars.filter(key => !process.env[key]);

if (missing.length > 0) {
  console.error(`❌ 缺少必要的环境变量:\n  ${missing.join('\n  ')}`);
  console.error('请检查 .env 文件或 docker-compose.yml 配置');
  process.exit(1);  // 启动失败,而不是带着错误配置运行
}

module.exports = {
  db: {
    host: process.env.DB_HOST,
    port: parseInt(process.env.DB_PORT, 10),
    name: process.env.DB_NAME,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
  },
  jwtSecret: process.env.JWT_SECRET,
  logLevel: process.env.LOG_LEVEL || 'info',  // 有默认值的可以不校验
};

Dockerfile 中使用 shell 校验

对于简单脚本,可以在入口点做校验:

BASH
#!/bin/sh
# entrypoint.sh

check_env() {
  for var in "$@"; do
    eval value=\$$var
    if [ -z "$value" ]; then
      echo "❌ ERROR: Environment variable $var is not set"
      exit 1
    fi
  done
}

check_env DB_HOST DB_PORT DB_PASSWORD JWT_SECRET

echo "✅ All required environment variables are set"
exec "$@"
DOCKERFILE
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["node", "server.js"]

实践五:注意环境变量的类型转换

环境变量本质上都是字符串。在代码中使用时,需要显式做类型转换。

常见错误

JAVASCRIPT
// ❌ 字符串拼接而不是数值运算
const port = process.env.DB_PORT || 5432;
console.log(`Connecting to port ${port + 1}`);  
// 输出: "Connecting to port 54321" (字符串拼接!)

正确做法

JAVASCRIPT
// ✅ 显式类型转换
const port = parseInt(process.env.DB_PORT || '5432', 10);
const maxRetries = parseInt(process.env.MAX_RETRIES || '3', 10);
const enableDebug = process.env.ENABLE_DEBUG === 'true';  // 布尔值

console.log(`Connecting to port ${port + 1}`);
// 输出: "Connecting to port 5433"

Python 示例

PYTHON
import os

DB_PORT = int(os.environ.get('DB_PORT', '5432'))
MAX_CONNECTIONS = int(os.environ.get('MAX_CONNECTIONS', '10'))
DEBUG = os.environ.get('DEBUG', 'false').lower() == 'true'

实践六:使用 Docker Secrets 管理生产环境密钥

对于 Swarm 模式或 Kubernetes 环境,推荐使用 Secrets 而非环境变量来管理敏感信息。

Docker Swarm Secrets

BASH
# 创建 secret
echo "my_super_secret_password" | docker secret create db_password -

# 在 docker-compose.yml 中使用
YAML
services:
  app:
    image: myapp:latest
    secrets:
      - db_password
      - jwt_secret
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password
      JWT_SECRET_FILE: /run/secrets/jwt_secret

secrets:
  db_password:
    external: true
  jwt_secret:
    external: true

应用代码中读取 Secret 文件

JAVASCRIPT
const fs = require('fs');

function readSecret(filePath) {
  try {
    return fs.readFileSync(filePath, 'utf8').trim();
  } catch {
    return null;
  }
}

const dbPassword = process.env.DB_PASSWORD 
  || readSecret(process.env.DB_PASSWORD_FILE || '');

优势:Secrets 以文件形式挂载到容器中,不会出现在 docker inspect 或环境变量列表中。


实践七:调试时快速排查环境变量问题

容器启动失败,第一步往往就是检查环境变量是否正确注入。

常用调试命令

BASH
# 查看容器的所有环境变量
docker exec <container_id> env

# 更友好的排序输出
docker exec <container_id> env | sort

# 只看特定变量
docker exec <container_id> printenv DB_HOST

# 查看 docker-compose 解析后的变量值(不启动容器)
docker compose config

# 查看容器的完整配置(包括环境变量)
docker inspect <container_id> --format '{{.Config.Env}}'

docker compose config 的妙用

当你在 .env 中使用变量引用时,docker compose config 可以帮你确认变量是否正确解析:

BASH
# 显示最终解析的配置
docker compose config | grep -A 20 "environment"

输出示例:

YAML
services:
  app:
    environment:
      DB_HOST: "192.168.1.100"
      DB_PORT: "5432"
      DB_PASSWORD: "r4nd0m_str0ng_p@ss!"

常见问题速查表

症状 可能原因 排查命令
容器启动即退出 缺少必要环境变量 docker logs <id>
连接数据库失败 DB_HOST 或 DB_PORT 错误 docker exec <id> printenv DB_HOST
变量值为空 .env 文件未正确加载 docker compose config
变量是旧值 容器未重建,用了缓存 docker compose down && up -d
密钥泄露 Dockerfile 中硬编码了密钥 docker history <image>

完整项目模板

下面是一个整合了所有建议的最小化项目模板:

docker-compose.yml

YAML
services:
  app:
    build: .
    container_name: myapp
    restart: unless-stopped
    env_file:
      - .env
    ports:
      - "${APP_PORT:-3000}:3000"
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  db:
    image: postgres:16-alpine
    container_name: myapp-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  pgdata:

.gitignore

TEXT
.env
.env.dev
.env.staging
.env.prod

总结

实践 核心要点
不硬编码密钥 Dockerfile 中只声明变量名,不赋敏感值
.env 文件管理 .env 提交 gitignore,.env.example 提交仓库
区分环境 多 .env 文件 + --env-file 参数
缺失校验 启动时检查必要变量,缺失则立即失败
类型转换 环境变量都是字符串,使用时显式转换
Secrets 生产环境用 Docker Secrets 代替纯环境变量
调试技巧 docker compose config + docker exec printenv

掌握这 7 个实践,就能避免 Docker 环境变量配置中绝大多数常见问题。记住:配置问题的最佳解法不是记住所有坑点,而是建立一套标准化的配置管理流程


如果这篇文章对你有帮助,欢迎收藏和分享。有问题可以在评论区留言讨论。

评论