☰
aws-cdk-examples:用 CDK(Python)实现 Route 53 Failover DNS——跨地域健康检查自动故障转移与 SNS 告警
2026/10/15 10:05:39 网站建设 项目流程

【免费下载链接】aws-cdk-examples

Example projects using the AWS CDK

项目地址:https://gitcode.com/gh_mirrors/aw/aws-cdk-examples
点击查看免费下载

本文基于 aws-cdk-examples 仓库中的 python/route53-failover 示例,讲解如何使用 AWS CDK 构建一套 Route 53 故障转移 DNS 方案:通过 Route 53 健康检查持续探测部署在不同 Region 的两个端点,当主端点失效时自动把流量切换到备用端点,并在发生故障转移时通过 SNS 邮件通知管理员。读完本文,你将掌握 Failover DNS 记录(Failover属性 +Identifier)的 CDK 建模方式、AWS/Route53的HealthCheckStatus指标如何联动 CloudWatch 告警,以及跨 Region 部署 Stack 时cross_region_references的正确用法。

一、方案架构:七个核心组件

该示例要解决的问题是:一个站点/服务部署在两个地域(如us-east-1和us-west-2),希望 DNS 层能在主地域不可用时自动切流。整体架构由以下组件构成(见 README):

  1. Primary Endpoint(主端点):正常情况下承载全部流量的端点。在本示例中是一个 Fargate 服务 + 网络负载均衡器(NLB);
  2. Secondary Endpoint(备用端点):主端点故障时接管流量的备用端点,同样是一个 Fargate 服务 + NLB;
  3. Route 53 Hosted Zone:创建 Failover DNS 记录所在的公网托管区;
  4. Route 53 Health Checks:分别探测主、备端点可用性的健康检查(HTTP 类型,80 端口);
  5. Route 53 Failover DNS Record:默认指向主端点、主端点健康检查失败时自动指向备端点的 DNS 记录;
  6. SNS Topic:发生故障转移/健康检查失败事件时发布通知的主题;
  7. SNS Subscription:订阅该主题并投递邮件(或其他通道)的订阅,确保管理员及时感知。

从源码结构看,上述组件被拆分为 5 个 Stack,由入口 app.py 统一组装:

文件职责
app.py读取cdk.json上下文,装配全部 Stack
fargate_app_stack.py构造一个“VPC + Fargate 服务 + NLB”的示例端点,被实例化两次(主/备)
hosted_zone_stack.py创建(或复用)Route 53 公网托管区
healthcheck_alarm_stack.py独立 Health Check + Failover 记录 + CloudWatch 告警 + SNS 邮件
alias_healthcheck_record_stack.py另一种方案:Alias 记录 +EvaluateTargetHealth内置健康评估

依赖版本要求见 requirements.txt:aws-cdk-lib>=2.262.0,<3.0.0、constructs>=10.5.0,<11.0.0。

二、上下文参数配置(cdk.json)

部署前必须提供 4 个上下文值(README “Getting Started” 第 5 步强调的重点)。cdk.json 中context段给出的默认示例值为:

"context": { "domain": "aws-cdk-samples.com", "email": "user@aws-cdk-samples.com", "primaryRegion": "us-east-1", "secondaryRegion": "us-west-2" }
  • domain:Failover 记录所在的 Hosted Zone 域名(本示例同时用它在 CDK 中创建一个新 Hosted Zone);
  • email:接收故障转移/健康检查失败告警的邮箱地址;
  • primaryRegion/secondaryRegion:主/备示例应用分别部署到的 Region。

app.py 通过app.node.try_get_context(...)读取这四个值,并额外从环境变量CDK_DEFAULT_ACCOUNT、CDK_DEFAULT_REGION获取当前账号与默认 Region:

domain = app.node.try_get_context('domain') email = app.node.try_get_context('email') primaryRegion = app.node.try_get_context('primaryRegion') secondaryRegion = app.node.try_get_context('secondaryRegion') account = os.getenv('CDK_DEFAULT_ACCOUNT') region = os.getenv('CDK_DEFAULT_REGION')

三、示例端点:FargateAppStack(主/备各一份)

FargateAppStack 负责搭建一个可被 Route 53 探测的 HTTP 端点:

  1. VPC:ec2.Vpc(..., max_azs=2),源码注释明确说明“Limit AZs to avoid reaching resource quotas”(限制可用区数量以避免触及配额上限);
  2. ECS 集群 + Fargate 服务:使用 L2 构图ecs_patterns.NetworkLoadBalancedFargateService,容器镜像为 AWS 官方示例镜像amazon/amazon-ecs-sample,该镜像内置一个监听 80 端口、返回 200 的 HTTP 应用,天然适合作为健康检查目标;
  3. 安全组规则:为服务安全组显式放通来自 VPC CIDR 的 TCP 80 入站(add_ingress_rule,description="Allow http inbound from VPC")。这一点很关键——NLB 位于 VPC 内,若安全组不放行 VPC 网段,健康检查将无法从 NLB 探测到容器;
  4. 输出:CfnOutput("LoadBalancerDNS")输出 NLB 的 DNS 名称,方便部署后验证端点可访问性。

app.py 将该 Stack 实例化两次:

app1 = FargateAppStack(app, "PrimaryFargateApp", env=cdk.Environment( account=account, region=primaryRegion)) app2 = FargateAppStack(app, "SecondaryFargateApp", env=cdk.Environment( account=account, region=secondaryRegion))

即主应用部署在primaryRegion、备应用部署在secondaryRegion,实现跨地域双端点。

四、托管区:HostedZoneStack

HostedZoneStack 只有一处关键逻辑——直接创建一个公网托管区:

self.zone = route53.PublicHostedZone(self, "HostedZone", zone_name=domain)

源码中同时保留了被注释掉的第二种用法:若你的账号里已经存在目标托管区,应改用查找导入而非重复创建,否则会因托管区名冲突导致部署失败:

# self.hostzone = route53.HostedZone.from_lookup(self, "HostedZone", domain_name=domain)

README “Getting Started” 第 4 步“Select a Route 53 HostedZone that can be used for this project”即对应这一选择:新建(默认代码路径)或复用(改用HostedZone.from_lookup)。

五、方案 A:独立 Health Check + Failover 记录 + SNS 告警

这是 README 主打的完整方案,实现在 HealthcheckAlarmStack 中,按顺序做了四件事:

1. 为两个端点各建一个 Route 53 健康检查

使用 L1 资源CfnHealthCheck,检查类型为HTTP、端口 80,探测目标分别是主/备 NLB 的 DNS 名:

primaryHealthCheck = route53.CfnHealthCheck(self, "DNSPrimaryHealthCheck", health_check_config=route53.CfnHealthCheck.HealthCheckConfigProperty( fully_qualified_domain_name=primaryLoadBalancer.load_balancer_dns_name, type="HTTP", port=80))

(备端点 同样 创建一个HTTP/80 端口的检查。)

2. 创建成对的 Failover A 记录

两条ARecord使用相同的记录名failover(最终域名为failover.<domain>),通过 L1 属性failover和set_identifier构成主备对——这是 Route 53 Failover 路由策略的硬性要求:PRIMARY 与 SECONDARY 记录必须共享同一个Identifier,且标识符不能以数字开头。

primary = route53.ARecord(self, "PrimaryRecordSet", zone=zone, record_name="failover", target=route53.RecordTarget.from_alias(route53_targets.LoadBalancerTarget(primaryLoadBalancer))) primaryRecordSet = primary.node.default_child primaryRecordSet.failover = "PRIMARY" primaryRecordSet.health_check_id = primaryHealthCheck.attr_health_check_id primaryRecordSet.set_identifier = "Primary"

备记录 对应 设置failover = "SECONDARY"、set_identifier = "Secondary",并绑定第二个健康检查。这里同时演示了 L2 与 L1 的混用技巧:route53.ARecord(L2)创建后,通过primary.node.default_child拿到其底层 L1 资源,再设置 L2 未暴露的Failover/Identifier/HealthCheckId属性。记录目标用route53.RecordTarget.from_alias(route53_targets.LoadBalancerTarget(...)),即指向 NLB 的 Alias 记录(免额外收费、无需在 LB 变更时手工更新 A 记录值)。

3. 基于 HealthCheckStatus 指标的 CloudWatch 告警

健康检查本身的状态会以AWS/Route53命名空间下的HealthCheckStatus指标上报(0=失败,1=成功,2=灰区)。源码 L56-L70 据此构造告警:

healthCheckMetric = cloudwatch.Metric( metric_name="HealthCheckStatus", namespace="AWS/Route53", statistic="Minimum", period=Duration.minutes(1), region="us-east-1", dimensions_map={"HealthCheckId": primaryHealthCheck.attr_health_check_id}) healthCheckAlarm = healthCheckMetric.create_alarm(self, 'HealthCheckFailureAlarm', evaluation_periods=1, threshold=1, comparison_operator=cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD)

含义:按主健康检查 ID 维度过滤,1 分钟周期取最小值,只要有一个评估周期低于阈值 1(即健康检查变红)就触发告警。

4. 告警动作:SNS 主题 + 邮件订阅

snsTopic = sns.Topic(self, "AlarmNotificationTopic") snsTopic.add_subscription(EmailSubscription(email_address=email)) ... healthCheckAlarm.add_alarm_action(SnsAction(snsTopic))

email参数来自cdk.json的上下文(在 app.py L38-L49 传入)。至此形成闭环:端点挂掉 → Route 53 健康检查失败 → DNS 自动切到 Secondary →HealthCheckStatus指标跌到 0 → CloudWatch 告警 → SNS 邮件通知管理员。

六、方案 B:Alias 记录 + EvaluateTargetHealth(无独立 Health Check 资源)

AliasHealthcheckRecordStack 演示了实现同一目标的另一种路线:不创建CfnHealthCheck资源,而是让 Alias 记录自身对目标做健康评估:

primary = route53.ARecord(self, "PrimaryRecordSet", zone=zone, record_name="alias", target=route53.RecordTarget.from_alias(route53_targets.LoadBalancerTarget(primaryLoadBalancer))) primaryRecordSet = primary.node.default_child primaryRecordSet.failover = "PRIMARY" primaryRecordSet.add_property_override('AliasTarget.EvaluateTargetHealth', True) primaryRecordSet.set_identifier = "Primary"

要点:

  • 记录名改为alias(alias.<domain>),与方案 A 的failover.<domain>共存,便于两种方案在同一个托管区中对照验证;
  • EvaluateTargetHealth在 CDK 中无 L2/L1 直接暴露,故用add_property_override('AliasTarget.EvaluateTargetHealth', True)注入(L21、L32);开启后 Route 53 会按默认间隔探测目标本身,而非引用独立 Health Check 资源;
  • 源码中 CloudWatch 告警与 SNS 部分被 注释掉——因为该方案没有独立的HealthCheckId可用作指标维度,若需告警需另行处理。

两种方案对比:方案 A 的健康检查可独立配置探测间隔、请求路径、失败判定(FailureIsolated相关参数在 L1 中均可设),且HealthCheckStatus指标可按HealthCheckId维度精确建告警,可观测性更好;方案 B 少一个 Health Check 资源(每个 Health Check 是独立计费项),配置更简洁,适合“记录目标本身即探测目标”的场景。

七、跨 Region 装配的关键细节

在 app.py 中,两个承载健康检查与 DNS 记录的 Stack 均显式部署到us-east-1,并设置了cross_region_references=True:

HealthcheckAlarmStack( app, "HealthCheckAlarm", zone=hostedZoneStack.zone, primaryLoadBalancer=app1.fargate_service.load_balancer, secondaryLoadBalancer=app2.fargate_service.load_balancer, email=email, env=cdk.Environment(account=account, region="us-east-1"), cross_region_references=True)

这里有两层原因:

  1. Route 53 托管区与 Health Check 的控制平面位于 us-east-1:Hosted Zone 是全局资源,在 us-east-1 管理最自然,源码中HealthCheckAlarmStack、AliasHealthcheckRecordStack的Metric也都写死region="us-east-1";
  2. 跨 Region 引用必须开启cross_region_references:app1/app2的 NLB 分别位于primaryRegion/secondaryRegion,而 DNS Stack 在 us-east-1。CDK 默认不允许一个 Stack 的属性引用其他 Region Stack 中的资源属性,cross_region_references=True告知 CDK 允许这类跨 Region 属性引用(对 NLB 而言其 DNS 名是跨 Region 可达的全局标识,因此可安全引用)。同理 hosted_zone_stack 的 zone 也被传入另外两个 Stack。

八、部署步骤(Getting Started)

完整继承 README 的 6 步流程:

  1. 克隆仓库;
  2. 安装依赖。README 写的是npm install(CDK CLI 场景);Python 项目还需按 requirements.txt 安装 CDK 库,例如pip install -r requirements.txt;
  3. 配置好 AWS 凭证与环境变量(CDK_DEFAULT_ACCOUNT/CDK_DEFAULT_REGION由cdk deploy上下文提供);
  4. 选择一个可使用的 Route 53 Hosted Zone(沿用默认代码则是 CDK 新建domain指定的托管区;复用已有托管区需按 HostedZoneStack 注释改为HostedZone.from_lookup);
  5. 在cdk.json中提供必需的上下文值:domain(托管区)、email(告警邮箱)、primaryRegion与secondaryRegion(示例应用部署地域)——cdk.json L69-L72 已给出示例默认值,请替换为自己的值;
  6. 执行cdk deploy部署全部 Stack。

部署后可用两个验证点确认生效:cdk deploy输出的LoadBalancerDNS输出值(fargate_app_stack.py L41-L44)应可 curl 访问;对failover.<domain>执行 DNS 查询应返回主 NLB 的地址,停止主 Region 的 Fargate 服务使健康检查失败后,应观察到解析切换到备 NLB,并收到 SNS 邮件。

九、适用前提与注意事项

  • 域名所有权:CDK 创建的托管区(route53.PublicHostedZone)要求你拥有该域名的注册权限才能完成域名解析对接;若使用已有托管区,请改用from_lookup路径;
  • 计费:每个独立 Health Check、每个 Alias 记录之外的标准 DNS 记录变更都会计费,示例共创建 2 个 Health Check 与 4 条 Failover 记录,评估成本时可参照;
  • Region 硬编码:DNS 与告警链路固定在 us-east-1(托管区与HealthCheckStatus指标的所在地),若主/备应用恰好部署在 us-east-1 之外,跨 Region 引用即生效,这也是必须设置cross_region_references的原因;
  • 探测端口:示例探测 80 端口,若你的端点使用 HTTPS,需把CfnHealthCheck的type/port与 Fargate 服务的安全组入站规则同步调整(fargate_app_stack.py L35-L39 目前仅放行 TCP 80)。

至此,本示例完整覆盖了“CDK 声明 Route 53 Failover DNS → 独立健康检查驱动切流 → CloudWatch 指标 + SNS 邮件闭环告警”这条链路,并额外提供了基于EvaluateTargetHealth的轻量化替代写法,可作为跨地域 DNS 容灾的标准参考实现。

【免费下载链接】aws-cdk-examples

Example projects using the AWS CDK

项目地址:https://gitcode.com/gh_mirrors/aw/aws-cdk-examples
点击查看免费下载
上一篇:NTLM 认证 3 步拆解:从一条哈希到拿下整个域,AD 安全实战指南
下一篇:SMUDebugTool:免费 Ryzen SMU 底层调试工具,支持 PBO 手动超频

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询