3. 多环境模型设计

多环境模型是持续交付平台能否稳定运行的基础。它要回答三个问题:代码从哪来、在哪个环境验证、什么条件下允许进入下一环境。如果环境边界模糊,最常见的结果是“测试通过但上线失败”,或者开发人员直接改生产配置导致事故。本节围绕环境定义、隔离策略、配置差异和晋升规则四件事展开,并给出可直接参考的落地方式。

一、环境定义:每个环境承担不同的验证职责

环境不是简单的“多套机器”,而是按验证目标划分的责任区。典型四环境模型如下:

  • 开发环境(dev):面向开发者自测,允许频繁部署、数据可重置,不要求高可用。用于验证功能是否可运行、单元与接口是否连通。
  • 测试环境(test):面向集成测试与回归测试,要求版本相对稳定,数据接近真实但可脱敏。用于验证多服务协同、接口契约、业务流程是否正确。
  • 预发环境(staging):面向发布前验收,配置、拓扑、依赖版本尽量与生产一致,只允许通过测试的候选版本进入。用于验证部署脚本、配置项、数据库变更和回滚路径。
  • 生产环境(prod):面向真实用户,变更必须可审计、可回滚、可灰度。用于承载真实流量和真实数据。

四类环境的核心差异不在机器数量,而在“验证目标”和“变更容忍度”。开发环境可以随时重建,生产环境必须保证变更可控。因此,环境定义应写成可执行的约束,例如:dev 允许每日多次部署,test 每日构建一次,staging 只接受通过 test 的候选版本,prod 只接受通过 staging 验收并完成审批的版本。

二、环境隔离策略:从网络、数据、配置到权限

隔离不足会导致环境互相污染。建议从四个层面做隔离:

  • 网络隔离:每个环境使用独立命名空间或独立 VPC/子网,环境间默认不互通。只有 CI/CD 流水线通过受控入口访问目标环境。
  • 数据隔离:每个环境独立数据库实例或独立 schema,禁止跨环境直连。生产数据不得直接复制到测试环境,必须脱敏后使用。
  • 配置隔离:配置按环境存放,代码中不写死环境相关值。推荐使用“基础配置 + 环境覆盖”的方式,例如 GitLab CI 中通过 environment 和变量作用域区分。
  • 权限隔离:开发人员对 dev/test 有部署权限,对 staging 只读或需审批,对 prod 无直接部署权限,只能通过流水线触发。

隔离的目标不是把环境变成孤岛,而是让变更只能沿既定路径流动。例如,staging 可以访问与生产同版本的依赖服务,但不能访问生产数据库;prod 的密钥只存在于 prod 的 CI/CD 变量中,其他环境不可见。

三、配置差异:用“同一制品 + 环境覆盖”管理

配置差异是多环境模型中最容易出错的部分。推荐原则是:一次构建,多环境部署。即同一个制品(如 jar、镜像、前端包)在 dev、test、staging、prod 中复用,差异只通过环境变量、配置中心或部署参数注入。

以 GitLab CI 为例,可以按环境定义变量作用域:

stages:
  - build
  - deploy

build:
  stage: build
  script:
    - mvn package -DskipTests
  artifacts:
    paths:
      - target/app.jar

.deploy_template: &deploy
  stage: deploy
  script:
    - ./deploy.sh --env $ENV_NAME --jar target/app.jar

deploy_dev:
  <<: *deploy
  variables:
    ENV_NAME: dev
    DB_URL: jdbc:mysql://dev-db:3306/app
    FEATURE_FLAG: "true"
  environment:
    name: dev

deploy_test:
  <<: *deploy
  variables:
    ENV_NAME: test
    DB_URL: jdbc:mysql://test-db:3306/app
    FEATURE_FLAG: "false"
  environment:
    name: test

这里的关键是:制品相同,只有 DB_URL、FEATURE_FLAG 等环境变量不同。对于敏感配置,应使用 GitLab CI/CD Variables 的 protected 和 masked 选项,并限定作用环境。例如生产数据库密码只允许在 prod 环境使用,且只有 maintainer 可查看。

配置差异还应包括资源规格和副本数。例如 dev 使用 1 副本、512Mi 内存;test 使用 2 副本、1Gi 内存;staging 与 prod 使用 3 副本、2Gi 内存。这些差异应写入部署模板,而不是手工修改。

四、晋升规则:用质量门禁控制环境流动

晋升规则决定一个版本能否从 dev 进入 test、从 test 进入 staging、从 staging 进入 prod。建议把晋升条件写成流水线中的质量门禁,而不是靠人工记忆。

一个可参考的晋升规则如下:

晋升路径触发条件质量门禁审批
dev → test开发分支合并到 test 分支单元测试通过、构建成功无需审批
test → stagingtest 环境验证通过集成测试通过、接口契约检查通过、无高危漏洞测试负责人确认
staging → prodstaging 验收通过冒烟测试通过、数据库变更脚本演练通过、回滚脚本可用发布负责人审批

晋升规则要可执行、可追溯。每次晋升都应记录:制品版本、提交 SHA、触发人、审批人、门禁结果。GitLab 中可以通过 environment 的部署记录和 when: manual 实现人工审批,通过 rules 控制哪些分支可以触发哪些环境。

建议把“环境”当作一等公民管理:每个环境有明确负责人、明确的准入条件、明确的配置来源。环境越多,越需要自动化门禁,否则隔离和晋升都会退化为人工协调。

五、多环境模型整体关系

下图展示四环境之间的制品流动、配置注入和晋升门禁关系。同一制品从 dev 构建后,依次经过 test、staging、prod,每个环境注入自己的配置,并在进入下一环境前经过质量门禁和审批。

多环境模型:同一制品 + 环境配置 + 晋升门禁 构建制品 app.jar / image 开发 dev 自测 / 快速部署 1 副本 · 可重置 测试 test 集成 / 回归 2 副本 · 脱敏数据 预发 staging 验收 / 演练 3 副本 · 近生产 生产 prod 真实流量 / 灰度 3 副本 · 可回滚 门禁1 门禁2 门禁3 + 审批 环境配置注入(同一制品,不同变量) dev 变量 DB_URL=dev-db test 变量 DB_URL=test-db staging 变量 DB_URL=stage-db prod 变量 DB_URL=prod-db 敏感变量:protected + masked,仅限对应环境使用 资源差异:副本数、内存、限流阈值按环境覆盖 晋升门禁要点 门禁1:单元测试 + 构建成功 门禁2:集成测试 + 契约 + 漏洞扫描 门禁3:冒烟 + 变更演练 + 回滚可用

图中可以看到,制品只构建一次,四个环境共享同一版本;每个环境通过变量注入自己的数据库地址、功能开关和资源规格;晋升路径上设置三道门禁,分别对应单元测试、集成测试与发布前验收。这样既保证了环境隔离,又避免了“每个环境重新构建”带来的版本漂移。

常见误区:把配置直接写进代码或镜像,导致同一制品无法跨环境复用;或者跳过 staging 直接上生产,使预发环境形同虚设。多环境模型的价值在于让每个环境承担明确的验证职责,并用自动化门禁保证晋升过程可重复、可审计。

落地时建议先固定环境清单和负责人,再把配置差异收敛到变量和模板中,最后把晋升条件写成流水线规则。环境模型稳定后,后续的灰度发布、回滚和审计才有可靠的基础。