Azure 韧性演进给云上系统的一堂工程课

2026-07-09 41 预计阅读时间: 1 分钟
来源: azure.microsoft.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:11 分钟

云韧性不是一句“高可用”的口号,而是系统在真实约束下继续工作的能力:硬件会坏,网络会抖,区域会遇到容量压力,依赖服务会变慢。Microsoft Azure 关于韧性演进的讨论,核心并不只是“云平台越来越可靠”,更重要的是云上应用也要被设计成能适应、恢复、降级并持续提供服务的系统。

韧性不是单点能力,而是一组工程习惯

很多团队第一次谈 resiliency,会把注意力放在 SLA、可用区、备份这些名词上。它们都重要,但单独拿出来都不够。

真正的云韧性通常由几层能力叠在一起:

  • 基础设施层:实例、磁盘、网络、机房、区域都可能失效,平台需要隔离故障并提供恢复路径。
  • 架构层:应用要避免把所有流量、状态、队列和数据库写死在一个故障域里。
  • 运行层:监控、告警、自动扩缩容、健康探针、发布回滚要能在压力下工作。
  • 业务层:不是所有功能都必须同等可用,核心交易链路和非核心报表链路应该有不同的降级策略。

Azure 这类云平台的演进方向,是让底层能力更标准化:区域、可用区、负载均衡、托管数据库副本、备份恢复、服务健康事件、自动化运维。但平台只提供材料,应用是否能“弹回来”,还取决于你怎么使用这些材料。

从“避免故障”转向“接受故障并恢复”

传统机房时代,很多设计目标是尽量避免故障:买更好的机器、加更多冗余、把变更窗口压到深夜。云环境里,这种思路会变得不够用,因为分布式系统的故障形态更细碎:一个可用区延迟升高、某个下游 API 超时、连接池耗尽、DNS 缓存过期、队列积压。

更实际的目标是:

  • 缩小故障影响范围,而不是假设故障不会发生。
  • 让服务能检测异常,而不是等用户报障。
  • 让恢复流程自动化,而不是依赖临时手工操作。
  • 让系统有降级路径,而不是非核心依赖失败就拖垮主链路。

可以把韧性设计拆成三个问题:

  1. 这个组件坏掉时,影响半径多大?
  2. 系统多久能发现它坏了?
  3. 恢复动作是否经过演练?

这三个问题比“我们用了几个云产品”更能暴露真实风险。

可以这样实践:用 Azure CLI 建一个带健康探针的负载入口

下面示例不是原文中的具体配置,而是一个可改造的最小实践:用 Azure CLI 创建资源组、虚拟网络、公网 IP、负载均衡器和 HTTP 健康探针。你可以把后端池接到 VM、VM Scale Set 或其他合适的计算资源上。

运行前需要安装并登录 Azure CLI,并把变量改成你的命名和区域。

az login

RESOURCE_GROUP="rg-resiliency-demo"
LOCATION="eastus"
VNET="vnet-resiliency-demo"
SUBNET="snet-app"
PUBLIC_IP="pip-resiliency-demo"
LB="lb-resiliency-demo"
BACKEND_POOL="bep-app"
PROBE="probe-http"
RULE="rule-http"

az group create \
  --name "$RESOURCE_GROUP" \
  --location "$LOCATION"

az network vnet create \
  --resource-group "$RESOURCE_GROUP" \
  --name "$VNET" \
  --address-prefix 10.20.0.0/16 \
  --subnet-name "$SUBNET" \
  --subnet-prefix 10.20.1.0/24

az network public-ip create \
  --resource-group "$RESOURCE_GROUP" \
  --name "$PUBLIC_IP" \
  --sku Standard \
  --allocation-method Static

az network lb create \
  --resource-group "$RESOURCE_GROUP" \
  --name "$LB" \
  --sku Standard \
  --public-ip-address "$PUBLIC_IP" \
  --frontend-ip-name fe-public \
  --backend-pool-name "$BACKEND_POOL"

az network lb probe create \
  --resource-group "$RESOURCE_GROUP" \
  --lb-name "$LB" \
  --name "$PROBE" \
  --protocol Http \
  --port 8080 \
  --path /healthz \
  --interval 5 \
  --threshold 2

az network lb rule create \
  --resource-group "$RESOURCE_GROUP" \
  --lb-name "$LB" \
  --name "$RULE" \
  --protocol Tcp \
  --frontend-port 80 \
  --backend-port 8080 \
  --frontend-ip-name fe-public \
  --backend-pool-name "$BACKEND_POOL" \
  --probe-name "$PROBE"

这个例子里最值得关注的不是负载均衡器本身,而是 /healthz 的含义。健康检查不应该只返回“进程还活着”,更应该覆盖关键依赖的最低可用性,例如配置是否加载、数据库连接是否可建、队列客户端是否可用。但也不要把健康检查写得太重,否则下游短暂抖动会导致实例被频繁摘除,反而放大故障。

一个简单的 Node.js 健康端点可以这样写:

import http from "node:http";

const server = http.createServer(async (req, res) => {
  if (req.url === "/healthz") {
    // 可以在这里加入轻量级依赖检查,例如连接池状态或本地配置状态。
    res.writeHead(200, { "content-type": "application/json" });
    res.end(JSON.stringify({ status: "ok" }));
    return;
  }

  res.writeHead(200, { "content-type": "text/plain" });
  res.end("hello from resilient app\n");
});

server.listen(8080, "0.0.0.0", () => {
  console.log("server listening on :8080");
});

本地试运行:

node server.mjs
curl -i http://127.0.0.1:8080/healthz

恢复能力要被测试,不要只写在架构图里

韧性设计最容易变成文档幻觉:图上有两个区域,实际数据库连接串只有一个;图上写了自动恢复,实际告警没人值守;图上说支持降级,实际前端没有处理部分接口失败。

可以从小规模演练开始:

  • 停掉一个应用实例,确认负载入口能摘除异常节点。
  • 人为让一个下游接口返回 500,确认主流程不会无限等待。
  • 降低数据库连接池上限,观察排队、超时和告警是否符合预期。
  • 在非生产环境模拟区域级配置切换,记录人工步骤和耗时。

对于 Azure 上的系统,还可以把这些检查固化进发布流程。下面是一个简化的 GitHub Actions 示例,用来在部署后探测健康端点。把 APP_HEALTH_URL 配成你的真实地址。

name: post-deploy-health-check

on:
  workflow_dispatch:
  push:
    branches: ["main"]

jobs:
  health-check:
    runs-on: ubuntu-latest
    steps:
      - name: Check application health
        env:
          APP_HEALTH_URL: ${{ secrets.APP_HEALTH_URL }}
        run: |
          test -n "$APP_HEALTH_URL"
          for i in $(seq 1 12); do
            code=$(curl -sS -o /tmp/health.txt -w "%{http_code}" "$APP_HEALTH_URL")
            if [ "$code" = "200" ]; then
              cat /tmp/health.txt
              exit 0
            fi
            echo "health check failed with HTTP $code, retry $i/12"
            sleep 10
          done
          echo "application did not become healthy in time"
          cat /tmp/health.txt || true
          exit 1

这不是完整的韧性体系,但它能防止一种常见事故:部署已经结束,入口已经切流,应用却没有真正准备好服务请求。

采用建议:把韧性预算花在关键路径上

云韧性不是把所有组件都堆到最高规格。多区域、主动主动、跨区域数据库复制、复杂流量调度都会增加成本和运维复杂度。更成熟的做法是按业务影响分层:

  • 核心写入链路:明确 RTO、RPO、幂等策略、重试边界和人工兜底流程。
  • 用户读取链路:优先考虑缓存、只读副本、部分数据降级展示。
  • 异步任务链路:关注队列积压、死信队列、重放能力和重复消费。
  • 内部管理功能:可以接受更低可用性,但要避免影响主链路。

落地时可以用一个短清单做自查:

  • 每个服务是否有明确的健康端点?
  • 重试是否带退避和最大次数,而不是无限重试?
  • 超时是否小于调用方的整体请求预算?
  • 状态是否跨故障域保护?
  • 恢复流程是否在最近一个季度演练过?
  • 告警是否指向可执行动作,而不是只告诉你“出事了”?

Azure 的韧性演进提醒我们:云平台会持续提高底座能力,但应用团队不能把恢复责任完全外包。真正可靠的系统,通常不是从不失败,而是在失败发生时能限制影响、快速判断、自动恢复,并把业务留在可接受的状态里。


相关推荐