logo
0
0
WeChat Login
docs: 新增 Docker 交互与 Compose 环境变量实践说明

Docker Compose 实践

1. 引言

在前面的课程中,我们学习了如何使用 Docker 容器来运行单个服务。 通过 docker run 命令,我们可以快速启动一个数据库、一个 Web 服务器或者一个缓存服务。这种方式在开发简单应用时非常有效。

然而,随着应用架构的演进,微服务的理念逐渐流行,一个应用由多个服务共同组成。

案例

本节课我们准备了 一个多服务的web应用 的案例如下:

web应用包含四个独立服务

  • Nginx: 路由转发服务
  • 前端: React框架开发 提供前端页面服务
  • 后端: Node.js开发 提供后端API服务
  • 数据库: MongoDB 提供数据库服务

应用架构

四个服务共同组成了应用,整体架构图如下:

试想一下,我们为了部署这个web应用,需要完成哪些工作?

  • 1.为四个服务分别构建镜像
    • 1.1 拉取nginx镜像
    • 1.2 拉取mongodb镜像
    • 1.3 手动书写前端服务的 Dockerfile,构建镜像
    • 1.4 手动书写后端服务的 Dockerfile,构建镜像
  • 2.配置容器见公共的基础设置 如Docker网络、存储卷等
  • 3.配置服务间的依赖关系 如前端服务依赖后端服务,后端服务依赖数据库等
  • 4.启动容器

可以看到,随着工程复杂度的提升,服务数量增加,手动管理服务容器会变得越来越复杂。 这不仅增加了运维的复杂度,还增加了手动操作出错的概率。

所以我们需要一个工具来对多容器应用进行管理。 于是Docker Compose应运而生。

2. Docker Compose 简介

Docker Compose 是一个用于定义和运行多容器的工具。 它允许我们使用 YAML 文件来定义各容器,然后通过一个命令来启动所有服务。

核心步骤

1)书写docker-compose.yml文件

  • 定义各个服务基本信息(如镜像、端口、环境变量等)
  • 定义网络、存储卷等通用设置
  • 定义服务之间的依赖关系

运行

  • docker compose up: 创建和启动所有服务

3. docker-compose.yml语法

3.1 Yaml文件格式

Docker Compose 使用 YAML 文件来定义服务。 YAML 是一种人类可读的数据序列化语言,它支持多种数据类型,如字符串、数字、列表、映射等。 Docker Compose 使用 YAML 文件来定义服务,因此我们需要了解 YAML 的基本语法。

  • 用缩进表示层级关系,必须为偶数个空格
  • 三种基本类型:标量(单个值)、映射(键值对)、序列(列表)

3.2 docker-compose.yaml语法

docker-compose.yml详解

  • 服务 (Services): 容器的定义,包括使用哪个镜像、端口映射、环境变量等
  • 网络 (Networks): 定义容器之间如何通信
  • 卷 (Volumes): 定义数据的持久化存储
  • 依赖关系 (Dependencies): 定义服务之间的启动顺序
  • 环境变量 (Environment Variables): 管理不同环境的配置

4. Docker Compose 原理

流程:

  1. 解析YAML文件
  2. 检查/创建所需网络、卷
  3. 创建和启动每个服务的容器
  4. 统一管理生命周期 • 源码:https://github.com/docker/compose/blob/cb959100188e9bfa2a463d7b0a6e3e1679bd5d0f/pkg/compose/up.go#L39

5. 实践项目:使用 docker compose 构建 Todo 应用

Docker Compose 配置解析

服务定义

  1. nginx 服务

    • 使用官方 nginx 镜像
    • 映射端口 8080,作为应用的访问入口
    • 将 nginx.conf 配置文件打包进镜像
  2. frontend 服务

    • 使用本地 Dockerfile 构建
    • 暴露端口 3000,仅容器网络访问
    • 设置 API URL 环境变量
    • 依赖于 backend 服务
    • 使用卷挂载实现热重载
  3. backend 服务

    • 使用本地 Dockerfile 构建
    • 映射端口 3001,仅容器网络访问
    • 设置 MongoDB 连接环境变量
    • 依赖于 mongodb 服务
    • 使用卷挂载实现热重载
  4. mongodb 服务

    • 使用官方 MongoDB 镜像
    • 映射端口 27017,仅容器网络访问
    • 使用命名卷持久化数据

网络配置

  • Docker Compose 会自动创建一个默认网络 (bridge模式)
  • 所有服务都在同一网络中
  • 服务可以通过服务名互相访问

数据持久化

  • 使用命名卷 mongodb_data 持久化数据库数据
  • 使用绑定挂载实现开发时的代码热重载

常用命令演示

  • docker compose build: 构建镜像
  • docker compose up: 创建和启动所有服务
  • docker compose down: 停止和删除所有服务
  • docker compose ps: 查看服务状态
  • docker compose logs: 查看服务日志

使用说明

  1. 在后台启动服务

    docker compose up -d
  2. 查看服务状态

    docker compose ps
  3. 查看服务日志

    docker compose logs frontend docker compose logs backend docker compose logs mongodb
  4. 停止所有服务

    docker compose down
  5. 重新构建服务

    docker compose build [${service}]

    当 service 省略时,默认构建所有配置了 build 的服务。

  6. 重启单个服务

    docker compose restart frontend

6. 环境变量与 .env 文件最佳实践

在实际项目中,我们经常需要管理不同环境(开发、测试、生产)的配置。 Docker Compose 提供了灵活的环境变量管理机制,让我们能够轻松地在不同环境之间切换配置。

6.1 环境变量的三种设置方式

方式一:直接在 compose.yaml 中定义

services: backend: image: node:18 environment: - NODE_ENV=development - PORT=3001 - DATABASE_HOST=mongodb

优点:简单直观,配置集中
缺点:敏感信息(如密码)会暴露在版本控制中

方式二:使用 .env 文件(推荐)

compose.yaml 同级目录创建 .env 文件:

# .env 文件 NODE_ENV=development MONGODB_PORT=27017 MONGODB_DATABASE=todos MONGODB_ROOT_USER=admin MONGODB_ROOT_PASSWORD=secret123

compose.yaml 中引用:

services: mongodb: image: mongo:6 environment: - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USER} - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD} ports: - "${MONGODB_PORT}:27017"

优点:敏感信息与配置分离,可以将 .env 加入 .gitignore
缺点:需要额外维护 .env 文件

方式三:使用 env_file 指令

services: backend: image: node:18 env_file: - ./backend.env # 后端专用配置 - ./common.env # 公共配置

优点:可以按服务或功能拆分配置文件
缺点:文件较多时管理复杂

6.2 .env 文件最佳实践

✅ 推荐做法

  1. 创建 .env.example 模板文件

    # .env.example - 提交到 Git,作为配置模板 NODE_ENV=development MONGODB_PORT=27017 MONGODB_DATABASE=todos MONGODB_ROOT_USER= MONGODB_ROOT_PASSWORD=
  2. .env 加入 .gitignore

    # .gitignore .env .env.local .env.*.local
  3. 为不同环境创建专用配置

    .env # 默认开发环境 .env.production # 生产环境 .env.test # 测试环境
  4. 使用有意义的变量命名

    # ❌ 不推荐 DB_P=3306 # ✅ 推荐 MYSQL_PORT=3306

❌ 避免做法

  1. 不要在 .env 中存储大段文本或 JSON
  2. 不要将生产环境的 .env 提交到版本控制
  3. 不要在变量值中使用未转义的特殊字符

6.3 实践案例:优化 Todo 应用的环境变量管理

第一步:创建 .env.example 模板

# 在 5_compose 目录下创建 .env.example cat > .env.example << 'EOF' # =================== # 应用环境配置 # =================== NODE_ENV=development # =================== # 服务端口配置 # =================== NGINX_PORT=8080 FRONTEND_PORT=3000 BACKEND_PORT=3001 # =================== # MongoDB 配置 # =================== MONGODB_PORT=27017 MONGODB_DATABASE=todos MONGODB_ROOT_USER=admin MONGODB_ROOT_PASSWORD= # =================== # API 配置 # =================== API_URL=/api EOF

第二步:创建实际的 .env 文件

# 复制模板并填写实际值 cp .env.example .env # 编辑 .env 文件,填写密码等敏感信息 # MONGODB_ROOT_PASSWORD=your_secure_password_here

第三步:优化 compose.yaml 使用环境变量

创建 compose.env.yaml 展示最佳实践:

services: # Nginx 反向代理 nginx: build: ./nginx ports: - "${NGINX_PORT:-8080}:80" # 提供默认值 depends_on: - frontend - backend # 前端服务 frontend: build: ./frontend expose: - "${FRONTEND_PORT:-3000}" environment: - NODE_ENV=${NODE_ENV:-development} - REACT_APP_API_URL=${API_URL:-/api} depends_on: - backend volumes: - ./frontend:/app - /app/node_modules # 后端服务 backend: build: ./backend expose: - "${BACKEND_PORT:-3001}" environment: - NODE_ENV=${NODE_ENV:-development} - MONGODB_URI=mongodb://${MONGODB_ROOT_USER}:${MONGODB_ROOT_PASSWORD}@mongodb:${MONGODB_PORT:-27017}/${MONGODB_DATABASE:-todos}?authSource=admin depends_on: - mongodb volumes: - ./backend:/app - /app/node_modules # MongoDB 数据库 mongodb: image: mongo:6 expose: - "${MONGODB_PORT:-27017}" environment: - MONGO_INITDB_ROOT_USERNAME=${MONGODB_ROOT_USER:-admin} - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_ROOT_PASSWORD:?请在.env文件中设置MONGODB_ROOT_PASSWORD} - MONGO_INITDB_DATABASE=${MONGODB_DATABASE:-todos} volumes: - mongodb_data:/data/db volumes: mongodb_data:

6.4 环境变量语法详解

语法说明示例
${VAR}直接引用变量${MONGODB_PORT}
${VAR:-default}变量未设置时使用默认值${MONGODB_PORT:-27017}
${VAR:?error}变量未设置时报错并退出${PASSWORD:?密码不能为空}
${VAR:+value}变量已设置时使用替代值${DEBUG:+--verbose}

6.5 验证环境变量配置

# 查看 compose 解析后的完整配置(包含变量替换结果) docker compose config # 仅查看某个服务的配置 docker compose config --services # 验证配置文件语法 docker compose config --quiet && echo "配置文件语法正确"

6.6 多环境部署实践

使用 --env-file 指定不同环境

# 开发环境(默认读取 .env) docker compose up -d # 生产环境 docker compose --env-file .env.production up -d # 测试环境 docker compose --env-file .env.test up -d

使用 profiles 实现环境差异化

services: # 仅在开发环境启用的调试工具 mongo-express: image: mongo-express profiles: - debug environment: - ME_CONFIG_MONGODB_SERVER=mongodb ports: - "8081:8081"
# 启动时包含调试工具 docker compose --profile debug up -d # 生产环境不包含调试工具 docker compose up -d

6.7 小结

场景推荐方案
简单项目/本地开发直接在 compose.yaml 中定义
团队协作项目.env + .env.example 模板
微服务架构env_file 按服务拆分配置
多环境部署.env.{environment} + --env-file
敏感信息管理Docker Secrets(生产环境推荐)

通过合理使用环境变量和 .env 文件,我们可以:

  • ✅ 实现配置与代码分离
  • ✅ 保护敏感信息安全
  • ✅ 简化多环境部署流程
  • ✅ 提高团队协作效率 现在让我创建配套的示例文件,包括 .env.example 模板文件和优化后的 compose.env.yaml 文件: