Skip to content

元数据卡

  • 前置知识:第14章(微服务基础模式)
  • 预计时间:40 分钟
  • 核心难度:进阶
  • 完成标志:理解常见部署模式和发布策略

你的进度

你的引擎们都跑起来了,数据一致了。但你每次发布新版本都要停掉整条流水线 30 秒——工坊的体验是“任务系统维护中”。而且有一次你错配了一根管道参数,整条产线炸了半天。

你不敢随便升级,但工坊主想一天迭代 20 次。

你需要一种不停机就能换零件的技术。 你的任务

代码写好了、测试过了、镜像构建了——最后一步是最危险的:把新代码送进生产环境。用错策略会导致客户端错误、数据丢失、或者最糟的——回滚困难。这章讲你需要的部署与运维模式:Sidecar 做服务辅助进程、蓝绿/金丝雀发布让切换无损、Feature Flag 让功能开关随心控制、Ambassador 做通信代理。

本章分层

  • 必读:蓝绿部署、金丝雀发布、Feature Flag
  • 选读:Sidecar、Ambassador
  • 进阶:混沌工程入门

本章不会要求你掌握

  • Kubernetes Operator 开发
  • 完整的可观测性体系

破局 · 溯源

你的服务晚上 10 点上线新版本。部署完成后,玩家积分全部变成 0。回滚花了 8 分钟——这 8 分钟里用户看到了积分清零、报错页面、然后恢复原状。客服电话被 30 个玩家打爆。

原因:新版本的积分计算脚本里 score * 0.5 写成了 (int) (score / 0.5)——测试没覆盖到。如果能以 1% 的流量先试跑一下,发现有问题就停下——8 分钟的回滚窗口可以缩短到 30 秒。

第一层:蓝绿部署——两个环境,一键切换

蓝绿部署维护两套完全相同的环境:

蓝环境 (Blue): 目前运行 v1.0(生产流量)
绿环境 (Green): 新部署 v2.0(无流量)

切换时:负载均衡器从蓝切到绿

绿环境 (Green): 现在运行 v2.0(生产流量)
蓝环境 (Blue): 保持 v1.0(备用)
yaml
# Kubernetes 蓝绿切换示例
# 两个 deployment 共存
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tournament-api-blue
  labels:
    app: tournament-api
    version: blue
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: tournament-api
        version: blue

# Service 通过 label selector 控制流量
apiVersion: v1
kind: Service
metadata:
  name: tournament-api
spec:
  selector:
    app: tournament-api
    version: blue   # 切换到 green 时只改这里

切换命令(假设你用 Nginx 做负载均衡器):

bash
# 切到绿环境
sed -i 's/server green.*/server green:8080 weight=1;/' nginx.conf
nginx -s reload

# 验证 5 分钟后没发现问题,可以下线蓝环境
# 如果发现问题:切回蓝环境就是一条命令的事
sed -i 's/server green.*//' nginx.conf
nginx -s reload

蓝绿的最大优点是瞬时回滚——切回蓝环境只需几十毫秒。缺点是资源加倍(两套环境运行)。对于中小型服务,蓝绿是推荐的发布策略。

第二层:金丝雀发布——百分之一流量先试

金丝雀发布不直接切 100% 流量——先给新版本 1% 的流量,观察几分钟没问题,再逐步提升到 5%、20%、100%。

v1.0 (95% 流量)
v2.0 (5% 流量,金丝雀)
    ↓ 监控无问题
v1.0 (70% 流量)
v2.0 (30% 流量)
    ↓ 逐步提升
v2.0 (100% 流量)
yaml
# Kubernetes 金丝雀发布
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tournament-api-canary
spec:
  replicas: 1  # 只启动一个金丝雀实例
---
apiVersion: v1
kind: Service
metadata:
  name: tournament-api
spec:
  # Service 通过版本标签分配流量
  # v1.0 有 19 个实例,v2.0 有 1 个实例 → 5% 左右的流量到 v2.0

在 Spring Cloud Gateway 中可以通过权重路由实现:

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: tournament-api-v1
          uri: lb://tournament-api-v1
          predicates:
            - Weight=api-group, 95
        - id: tournament-api-v2
          uri: lb://tournament-api-v2
          predicates:
            - Weight=api-group, 5

金丝雀的精髓不只是流量分配,还有监控对比——你需要对比新版本的错误率、延迟、资源消耗和旧版本比,有没有显著差异。只要金丝雀的错误率高了 0.5%,自动终止发布。

bash
# 金丝雀退出条件示例(伪代码)
if (canary.error_rate > baseline.error_rate * 1.1) {
    canary.rollback();
}

第三层:Feature Flag——功能开关随心控制

Feature Flag(功能开关、特性门禁)是一种技术:用条件判断控制某个功能是否启用,而不是通过部署代码。

java
// ch16/featureflag/FeatureFlagService.java
@Service
public class FeatureFlagService {
    private final Map<String, Boolean> flags = new ConcurrentHashMap<>();

    public boolean isEnabled(String flagName) {
        return flags.getOrDefault(flagName, false);
    }

    public void setEnabled(String flagName, boolean enabled) {
        flags.put(flagName, enabled);
    }
}

// 使用 Feature Flag
public class MatchService {
    private final FeatureFlagService featureFlags;

    public int calculateScore(Match match) {
        if (featureFlags.isEnabled("new-scoring-algorithm")) {
            return newAlgorithm(match);  // 新算法
        }
        return oldAlgorithm(match);      // 旧算法
    }
}

Feature Flag 的典型用途:

  1. 渐进式发布——新功能先对内部员工开启,再过几天 10% 用户,最后全量。
  2. A/B 测试——随机分配用户到 A 组或 B 组,对比指标。
  3. 立即回滚——爆了?切掉 flag 不用重新部署。
  4. 暗部署——代码已经部署了,但功能关着——上线时远程开启。
java
// 基于用户的 Feature Flag
public boolean isFeatureEnabled(String userId, String flagName) {
    // 按用户 ID hash,保证同一个用户始终看到同一个版本
    int bucket = Math.abs(userId.hashCode()) % 100;
    int threshold = featureConfig.getPercentage(flagName); // 10
    return bucket < threshold; // 前 10% 的用户看到新功能
}

Feature Flag 的陷阱:flag 积累——半年后代码里有 30 个 if (isEnabled("xxx")),没人记得这些 flag 是做什么用的。所有 flag 必须有生命周期——开启后追踪一段时间,确认没问题就清理 flag 代码

第四层:Sidecar 与 Ambassador——辅助进程模式

Sidecar(边车模式): 在你的主服务旁边运行一个辅助进程,它们共享同一个网络命名空间。日志收集、监控代理、服务代理都用 Sidecar 实现,不需要侵入主服务的代码。

+-------------------------+
| Pod                     |
| +---------------------+ |
| | 主服务 (API)       | |
| +---------------------+ |
| +---------------------+ |
| | Sidecar (日志收集) | |  ← 不修改主服务代码
| +---------------------+ |
+-------------------------+
yaml
# Kubernetes Sidecar 示例
apiVersion: v1
kind: Pod
metadata:
  name: tournament-api
spec:
  containers:
    - name: api
      image: tournament-api:1.0
    - name: log-collector  # Sidecar
      image: fluentd:latest
      volumeMounts:
        - name: logs
          mountPath: /var/log/app

Ambassador(大使模式): 一个专门做网络通信代理的 Sidecar。负责处理重试、超时、断路器等网络逻辑——主服务不需要知道网络细节。

主服务 → localhost:9999 → Ambassador(代理) → 外部服务
                              ├── 重试逻辑
                              ├── 超时设置
                              └── 断路器
java
// 主服务代码——不知道外部服务的网络细节
public class TournamentService {
    private final HttpClient httpClient;

    public TournamentService() {
        // 指向本地的 Ambassador 代理
        this.httpClient = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(1))
            .build();
    }

    public int getExternalScore(String playerId) {
        // 访问 localhost:9999——Ambassador 负责转发到真正的外部服务
        HttpRequest req = HttpRequest.newBuilder()
            .uri(URI.create("http://localhost:9999/scores/" + playerId))
            .build();
        // Ambassador 处理了重试、熔断等——主服务代码干净
    }
}

Envoy 和 Istio 的 Sidecar 代理就是 Ambassador 模式在生产中的具体实现。


常见陷阱

陷阱一:蓝绿部署的资源成本。 两套环境就是双倍的服务器费用(或 Kubernetes 节点)。对于小服务,可以只用灰度 + 回滚脚本——不需要完整的两套环境。

陷阱二:金丝雀排量太随意。 "先 5%,然后 100%"——没有中间的逐步提升和监控对比。金丝雀发布必须有自动化的"成功/失败"判断——人工盯着 Grafana 等 10 分钟太容易走神。

陷阱三:Feature Flag 的过时代码。 "这个 flag 三个月前开的——但我忘了删掉旧分支。" 需要定期清理 flag:根据 flag 的创建日期、状态、命中率来判断是否该删。

陷阱四:没有瞬时回滚能力。 金丝雀 100% 以后你把旧版本实例全杀掉了。然后出了问题——你只能重新构建、重新部署旧版本。金丝雀到 100% 后仍然保留至少一个旧版本的副本 30 分钟。


通关挑战

  • 热身:在你的项目中引入一个 Feature Flag(不需要框架,一个 Map<String, Boolean> 就行)。把你最近写的一个功能包在 flag 里。
  • 挑战:设计你的服务的发布流程——手动或自动画一个流程图:从 git tag 到生产环境,经过哪些环境、用什么发布策略、回滚方案是什么。
  • 观察:调研一个你用的 SaaS 产品(GitHub、Slack),看它是如何做灰度发布的——通常它们会在 Changelog 或 Status page 里提到"逐步推出"。

旅人笔记

部署模式保证了"代码变更"这个风险最高的操作可以被控制——蓝绿让你瞬时切换,金丝雀让你带着刹车前行,Feature Flag 让你不部署也能控制功能开关,Sidecar 与 Ambassador 把基础设施能力放进服务旁边。


下一站预告

部署策略解决的是"怎么把代码安全地送出去"。但你的系统运行起来后,各个服务之间是怎么通信的?下两章深入通信模式:事件驱动架构和 AI 系统模式。

Built with VitePress | Software Systems Atlas