使用 FIS 模拟单 AZ Network Firewall Endpoint 故障

本文描述了如何使用 AWS Fault Injection Service(FIS)模拟单个可用区内 Network Firewall Endpoint 故障的方法。由于 NFW 各 AZ 独立部署且默认不具备跨 AZ 自动故障转移能力,单 AZ Endpoint 故障会导致该 AZ 流量中断。文中通过 FIS 的网络连通性中断动作封锁 firewall 子网流量来复现该场景,并介绍了实验角色配置、模板编写、运行与观测、自动恢复等步骤,最后给出基于修改路由表下一跳实现跨 AZ 高可用引流的方案。

一、背景

1、关于 NFW 的 AZ 独立性

AWS Network Firewall 在每个可用区(AZ)部署独立的 Endpoint,工作负载子网的路由表将流量指向本 AZ 的 Endpoint。正常情况下各 AZ 独立处理流量,互不干扰。这样的架构设计下 AZ1 的 NFW Endpoint 只处理 AZ1 流量,AZ2 的 NFW Endpoint 只处理 AZ2 流量,二者不发生 Cross-AZ 的流量,这也避免了跨 AZ 流量费。

现在探讨一种情况,如果本 AZ1 没有完全挂掉,应用 Workload 都正常运行,仅本 AZ1 内的 NFW Endpoint 挂掉,此时由于 NFW 默认不具备跨 AZ 自动故障转移能力,流量不会 Cross-AZ 发送到 AZ2 处理,因此这时候 AZ1 内的所有流量都将断网。这个结果是 By-Design 符合当前架构预期的。

针对以上场景,如果需要解决一个 AZ 内 NFW Endpoint 挂掉导致的单 AZ 流量失效,需要使用额外机制例如自定义 Lambda 更新路由表实现故障切换。不过此时需要面临 Cross-AZ 流量费的问题。

本文使用 FIS 服务模拟这一场景。

2、FIS 服务是什么

AWS Fault Injection Service(FIS)是一项托管的混沌工程服务,用于在 AWS 环境中安全地注入故障以验证系统韧性。FIS 支持对 EC2、ECS、RDS、网络等资源执行预定义的故障动作(如终止实例、注入延迟、中断网络连通性),并提供自动恢复和紧急熔断机制确保实验可控。通过 FIS 可在非生产或生产环境中模拟真实故障场景,提前发现架构弱点。

FIS 没有直接的 Network Firewall action,但可以用 aws:network:disrupt-connectivity 封锁 firewall subnet 的所有进出流量,间接模拟该 AZ 的 endpoint 挂掉。FIS 底层是用临时 NACL 实现的,实验结束自动恢复。

二、NFW 环境搭建

参考如下博客中的 CloudFormation 模版可快速搭建环境,这个环境包括一个模拟 IDC 的 VPC,一个运行业务应用和 NFW 的 VPC。二者之间的双向流量将被 NFW 审查。

https://blog.bitipcman.com/post/nfw-vgw-demo/

部署完成后,云上业务应用和 NFW 所在的 VPC 包含如下:

  • VPC 跨 2+ AZ,每个 AZ 有独立的 firewall subnet(各含一个 Network Firewall endpoint)
  • 应用程序 Workload 所在子网的路由表下一跳指向本 AZ 的 firewall endpoint
  • NFW 已经开启了日志输出到 CloudWatch (方便查看),也可以选择 S3 成本更低
  • 有持续流量(发起测试流量)

三、针对 NFW 实施 FIS 测试

1、添加 FIS 的 IAM 角色

FIS 运行实验时会扮演(assume)一个 IAM 角色,用它的权限去创建临时 NACL 实现网络封锁。这个角色需要通过 FIS 模板 JSON 里 roleArn 指向进行调用。本文的名字是 FISExperimentRole。该角色需要两部分配置:

  • 信任关系:允许 FIS 服务主体 fis.amazonaws.com 扮演本角色。
  • 权限策略:附加 AWS 托管策略 AWSFaultInjectionSimulatorNetworkAccessaws:network:disrupt-connectivity 动作靠它授予创建/管理 NACL、描述子网等权限。

(1) 创建信任关系并建立角色

文件名:fis-role-trust-policy.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": ["fis.amazonaws.com"]
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "aws:SourceAccount": "123456789012"
        },
        "ArnLike": {
          "aws:SourceArn": "arn:aws:fis:us-east-1:123456789012:experiment/*"
        }
      }
    }
  ]
}

Condition 里的 aws:SourceAccount / aws:SourceArn 用于防范 confused deputy 问题,官方推荐加上,按实际账号和区域替换。

# 创建实验角色
aws iam create-role \
  --role-name FISExperimentRole \
  --assume-role-policy-document file://fis-role-trust-policy.json

(2) 附加 AWS 托管策略

# 给角色附加网络故障所需的托管策略
aws iam attach-role-policy \
  --role-name FISExperimentRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSFaultInjectionSimulatorNetworkAccess

附加完成后,把模板里的 roleArn 填成这个角色的 ARN(如 arn:aws:iam::123456789012:role/FISExperimentRole)即可。

2、设计 FIS 实验配置文件

新建文件:fis-nfw-az-failure.json。在文件中指定:

  • 模拟故障的子网 ARN
  • 故障范围和类型 scope 参数设置为 all
  • 持续时间duration。参数 PT5M 是 ISO 8601 时间间隔(duration)的写法,P = Period(周期),T = Time(时间), 10M = 10 分钟,表示从当前时刻起持续 10 分钟。如果需要更长,可酌情修改。例如 1 小时 30 分写作:PT1H30M
  • FIS 服务运行角色 roleArn (倒数第二行),替换为上一个章节新建的 Role 的 ARN 。

文件内容如下:

{
  "description": "Simulate single-AZ Network Firewall endpoint failure",
  "targets": {
    "firewall-subnet-az1": {
      "resourceType": "aws:ec2:subnet",
      "resourceArns": [
        "arn:aws:ec2:us-east-1:123456789012:subnet/subnet-0abc1234def56789a"
      ],
      "selectionMode": "ALL"
    }
  },
  "actions": {
    "disrupt-firewall-subnet": {
      "actionId": "aws:network:disrupt-connectivity",
      "parameters": {
        "scope": "all",
        "duration": "PT10M"
      },
      "targets": {
        "Subnets": "firewall-subnet-az1"
      }
    }
  },
  "stopConditions": [
    {
      "source": "none"
    }
  ],
  "roleArn": "arn:aws:iam::123456789012:role/FISExperimentRole"
}

⚠️ 补充讲解:在 FIS 服务的 disrupt-firewall-subnet 操作中,有一个 scope 的概念,这里必须使用参数 all 才能阻断本 AZ 内所有针对本子网(也就是 VPC Endpoint 所在)的流量。如果不使用 all 参数,而是使用 availability-zone 参数,那么这个故障只断开本 AZ 与其他 AZ 之间的流量,而同 AZ 的流量不受影响。这样可以看出,使用 availability-zone 参数是无法阻断本 AZ 对 NFW 的流量的。

3、使用 AWSCLI 创建并运行 FIS 实验

# 创建模板
aws fis create-experiment-template \
  --region us-east-1 \
  --cli-input-json file://fis-nfw-az-failure.json

# 启动实验
aws fis start-experiment \
  --region us-east-1 \
  --experiment-template-id EXT1234567890abc

# 查看状态
aws fis get-experiment \
  --region us-east-1 \
  --id EXPKysxrjBi1iorYS EXT1234567890abc

4、验证故障注入生效并观测流量行为

(1) 检查故障注入

# 检查 FIS 创建的临时 NACL(tag managedByFIS=true)
aws ec2 describe-network-acls \
  --filters "Name=tag:managedByFIS,Values=true"

# 从故障 AZ 的工作负载子网上测试(应该超时)
curl -m 5 https://example.com

(2) CloudWatch 指标

进入 CloudWatch 服务,进入 Metrics 指标,查看命名空间 AWS/NetworkFirewall 中:

  • ReceivedPackets / DroppedPackets — 按 AZ 维度看故障 AZ 流量是否归零
  • PassedPackets — 健康 AZ 的 endpoint 流量是否相应增加

⚠️ 注意两点,否则命令行查询会返回空:

  • AWS/NetworkFirewall 没有名为 Packets 的指标,实际指标名是 ReceivedPackets / PassedPackets / DroppedPackets 等。
  • NFW 指标固定带 FirewallName + AvailabilityZone + Engine 三个维度。get-metric-statistics 要求维度集合与发布时完全一致,缺少 Engine(取值 StatefulStateless)会匹配不到任何数据点。

如果有配置 AWSCLI,还可以命令行查看:

# 检查该 AZ 的 Network Firewall 流量是否降为 0
# 维度必须同时带 FirewallName + AvailabilityZone + Engine 三个
aws cloudwatch get-metric-statistics \
  --namespace AWS/NetworkFirewall \
  --metric-name ReceivedPackets \
  --dimensions Name=FirewallName,Value=my-firewall \
               Name=AvailabilityZone,Value=us-east-1a \
               Name=Engine,Value=Stateful \
  --period 60 --statistics Sum \
  --start-time $(date -u -d '10 minutes ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S)

(3) 查看 VPC Flow Logs(可选)

⚠️ VPC Flow Logs 默认是不开启的,需手动创建。开启后会持续产生费用(CloudWatch Logs 摄取 + 存储,或 S3 存储费用)。因此本试验中为可选,可以不使用本功能也能正常观察到流量。如果仅用于本次实验验证,建议实验结束后及时删除 Flow Log 和对应的 Log Group,避免产生不必要的费用。

如果尚未开启,先为目标 VPC 创建 Flow Log:

# 创建 Flow Log(发送到 CloudWatch Logs)
aws ec2 create-flow-logs \
  --resource-type VPC \
  --resource-ids vpc-0123456789abcdef0 \
  --traffic-type ALL \
  --log-destination-type cloud-watch-logs \
  --log-group-name /vpc/flowlogs \
  --deliver-logs-permission-arn arn:aws:iam::123456789012:role/VPCFlowLogsRole

等待 2-3 分钟后数据开始写入,然后查询 REJECT 记录:

aws logs filter-log-events \
  --log-group-name /vpc/flowlogs \
  --filter-pattern "REJECT" \
  --start-time $(date -u -d '10 minutes ago' +%s)000

实验结束后关闭并清理 VPC Flow Log 以避免额外费用:

# 查询 Flow Log ID
aws ec2 describe-flow-logs \
  --filter "Name=log-group-name,Values=/vpc/flowlogs"

# 删除 Flow Log
aws ec2 delete-flow-logs --flow-log-ids fl-0123456789abcdef0

# 删除 Log Group(可选,如不再需要历史数据)
aws logs delete-log-group --log-group-name /vpc/flowlogs

5、FIS 实验结束恢复

(1) 自动恢复

在发起 FIS 实验时候,实验定义文件中有一个 duration 参数,例如填写 PT10M 表示 10 分钟。实验时间到期后, FIS 自动删除阻断网络的临时 NACL,恢复原有关联,无需手动操作。

(2) 手动提前终止

aws fis stop-experiment 
  --region us-east-1 \
  --id EXP1234567890abc

(3) 确认恢复

查看实验状态:

# 查看实验状态,替换为前文启动实验时候获得的实验 ID
aws fis get-experiment \
  --region us-east-1 \
  --id EXT1234567890abc 
# 临时 NACL 应该已消失
aws ec2 describe-network-acls \
  --region us-east-1 \
  --filters "Name=tag:managedByFIS,Values=true"
# 预期返回空

6、FIS 小结

  • 首先在测试环境中模拟:首先在测试环境中模拟,要在模拟通过且给生产环境准备预案完备后,再用生产环境测试。
  • Network Firewall 不会自动 failover:单 AZ 的 NFW Endpoint 故障后,流量不会自动切到健康 AZ 的 endpoint,不会发生 Cross-AZ 的流量。这是预期行为。
  • 在生产环境中增设 Stop Condition:设置 CloudWatch Alarm 作为紧急熔断,业务影响超预期时自动停实验。这个停止条件的意思是,为 FIS 注入故障后的实验过程设置一个熔断保护条件作为保险丝。例如停止了 AZ1 的 NFW Endpoint,会导致 AZ2 流量增高。当 AZ2 某个指标超过负载时候,认为 AZ2 可能存在压力过大风险,一旦触发这个告警,自动停止实验。如果要设置的话,在 FIS 配置文件中写法如下:
  "stopConditions": [
    {
      "source": "aws:cloudwatch:alarm",
      "value": "arn:aws:cloudwatch:us-east-1:123456789012:alarm:critical-packet-loss-alarm"
    }

建议对生产环境的 FIS 测试都配置合理的熔断条件作为自动熔断手段。

四、在单 AZ 故障后实现 NFW 跨 AZ 流量的高可用能力

1、实现原理

虽然 AWS 有 Application Recovery Controller 即 ARC 服务,支持 Zonal Shift / Zonal Autoshift 功能,但是这个服务只能针对如下四种服务调度流量:

  • Application Load Balancer(ALB)
  • Network Load Balancer(NLB)
  • Amazon EKS
  • EC2 Auto Scaling Group

对于本文中的 NFW 流量引流,是通过修改路由表下一跳来实现的。而直接修改路由表的方式,并不被 ARC 服务支持。由此 ARC 服务无法满足自动切除 AZ 的要求。

基于这种情况,需要在检测到本 AZ 的 NFW Endpoint 故障后,把本 AZ 业务路由表里去往对端的下一跳,从「本 AZ Endpoint」改为「健康 AZ 的 Endpoint」,实现跨 AZ 引流。故障 AZ 恢复后再改回本 AZ Endpoint。

由于实现监控的方法可不相同,很多用户已经有了自己的云上云下混合监控体系,本方案中不提供如何架设监控探针。当用户自己的监控系统检测到单个 AZ 内的 NFW Endpoint 故障后,可以调用一条 AWSCLI 完成路由切换。

2、使用 AWSCLI 执行路由切换

NFW 是有状态检测,去回流量必须经过同一个 Endpoint。因此 AZ1 的 Endpoint 故障时,要把两个方向的下一跳都从「AZ1 Endpoint」切到「AZ2 Endpoint」:

  • 云端 VPC → IDC 方向:改业务路由表 prv-rt-az1
  • IDC → 云端 VPC 方向:改 VGW Ingress 路由表 vgw-ingress-rt

只切一个方向会导致回程流量到达健康 AZ 的 Endpoint 时因无连接状态而被丢弃。

(1) 从云端 VPC 到 IDC 方向

确认本 AZ(如 AZ1)的 NFW Endpoint 故障后,把该 AZ 业务路由表中去往对端(IDC)网段的下一跳,改为健康 AZ(AZ2)的 NFW Endpoint:

# 把 AZ1 业务路由表去往 IDC 网段的下一跳,切到 AZ2 的 NFW Endpoint
# 按实际环境替换:路由表 ID、目的网段、健康 AZ 的 vpc endpoint ID
aws ec2 replace-route \
  --route-table-id rtb-0aaa1111bbbb2222a \
  --destination-cidr-block 10.132.0.0/16 \
  --vpc-endpoint-id vpce-0bbb2222cccc3333b

参数说明:

  • --route-table-id:故障 AZ 的业务路由表(对应本项目里的 prv-rt-az1
  • --destination-cidr-block:需要引流的目的网段,本场景填模拟 IDC 的网段
  • --vpc-endpoint-id:目标 NFW Endpoint,故障切换时填健康 AZ(AZ2)的 vpce- ID

(2) 从 IDC 到云端 VPC 方向(即 VGW Ingress 路由表)

回程方向由 VGW Ingress 路由表 vgw-ingress-rt 控制。它针对每个 AZ 的工作负载网段分别指向本 AZ 的 NFW Endpoint。AZ1 故障时,把去往 AZ1 工作负载网段的下一跳,从「AZ1 Endpoint」切到「AZ2 Endpoint」:

# 把 VGW Ingress 路由表去往 AZ1 工作负载网段的下一跳,切到 AZ2 的 NFW Endpoint
# 按实际环境替换:路由表 ID、AZ1 工作负载网段、健康 AZ 的 vpc endpoint ID
aws ec2 replace-route \
  --route-table-id rtb-0ccc3333dddd4444c \
  --destination-cidr-block 10.0.1.0/24 \
  --vpc-endpoint-id vpce-0bbb2222cccc3333b

参数说明:

  • --route-table-id:VGW Ingress 路由表(对应本项目里的 vgw-ingress-rt
  • --destination-cidr-block:故障 AZ(AZ1)的工作负载子网网段
  • --vpc-endpoint-id:目标 NFW Endpoint,故障切换时填健康 AZ(AZ2)的 vpce- ID

(3) 故障 AZ 恢复后回切

故障 AZ 恢复后,对上面两条命令分别把 --vpc-endpoint-id 改回 AZ1 本 AZ 的 Endpoint(如 vpce-0aaa1111bbbb2222a),即可把两个方向都回切到本 AZ Endpoint。

⚠️ 注意:

  • 切换期间流量跨 AZ 送到健康 AZ 的 Endpoint 检测,会产生 Cross-AZ 流量费;恢复后务必回切,避免长期跨 AZ 引流持续计费。
  • NFW 是有状态检测,切换瞬间已建立的连接状态不会迁移到新 Endpoint,长连接可能被重置,评估业务影响后再上生产。
  • 去回两个方向要同步切换、同步回切,保证同一连接的双向流量都经过同一个 Endpoint。

五、参考文档

FIS 相关:

ARC 不支持直接修改 VPC 路由的说明:

AWSCLI 修改路由表:

NFW 监控指标说明:


最后修改于 2026-06-15