AWS共有云架构落地指南:账号边界、高可用与WAF加固

AWS共有云架构落地指南:账号边界、高可用与WAF加固 简介面向云架构师、解决方案工程师及技术决策者的AWS共有云架构入门级讲解PPT系统梳理了亚马逊从电商平台到全球云计算领导者的演进脉络并围绕全球13个区域Region与可用区AZ的物理布局深入讲解高可用基础设施设计、合规认证体系、VPC网络架构及多AZ业务容灾等核心主题帮助读者快速建立对AWS公有云服务体系的全景认知。资源共含1个PPTX演示文稿文件大小10.82MB内容紧凑且图文并茂既适合作为技术方案汇报、内部培训的现成课件也可供自学者逐页拆解理解Region/AZ分层架构与同城灾备设计。目前已有131人学习其中跨AZ关系型数据库部署、超过40%业务采用跨AZ架构等实战数据对企业上云规划、高可用应用设计及AWS认证备考均有直观参考价值尤其适合需要从庞杂服务目录中快速抓住AWS架构主线的中高级技术人员。1. 为什么「AWS共有云架构」容易被画成翻车现场给一份《AWS共有云架构介绍.pptx》补内容页时最常见的处理方式是把 Region、AZ、VPC、子网、ALB、RDS 连成一张大图连线越多图越显得完整。但实际接手这张图的人要按图落地马上会碰到三个坑账号边界没定SCP 没有收敛区域子网路由把数据库段指到了公网网关WAF 规则一开就把正常流量挡在外头。这个标题真正要讲清楚的不是云里有哪几个组件而是组件之间的边界和参数。按账号边界、网络边界、实例层高可用、WAF 边界这条线走下来结尾再给一套可验证的 SLO 和账单兜底是我处理这一类架构梳理时固定的做法。适合做方案、做预算和准备运维交接的工程师也适合给售前补充素材。2. 先把「共有云」拆开账号组织、责任边界与SCP参数2.1 公有云、共有云与多租户第一个容易歧义的词AWS 官方叫法是 Public Cloud翻译为公有云国内不少项目资料里写“共有云”。多数场景指同一个东西多个租户共享一套基础设施租户之间靠网络、账号和加密做隔离。架构设计首先要想清楚隔离粒度。最小可用单元不是 VPC而是 AWS Account。VPC 只是网络逻辑隔离账号才是运维和账单的物理边界。PPT 上通常只画一个 VPC落地时却往往以账号为单位铺开所以第一页架构图之前应该先把账号结构画出来。2.2 责任共担模型图里经常漏画的一条横线层级AWS负责用户负责计算虚拟化物理主机、Hypervisor、底层网络设备实例 OS 补丁、SSH 密钥、实例内安全配置网络Region/AZ 基础设施、VPC 数据面IAM 策略、安全组、子网路由、NACL数据存储设备耐久性、托管服务底座数据库账号、数据加密、备份恢复策略容器与中间件EKS/ECS 控制面、编排组件镜像扫描、运行时安全、Pod 网络策略这条横线决定 SCP 和安全组各自该管到哪个层。责任边界不画清楚故障定位时会先在内部互相质疑再回头查云厂商文档问题被拖长。2.3 用 Organizations 把账号拉出来CLI 参数与 SCP 示例创建生产账号aws organizations create-account \ --email prod-opsexample.com \ --account-name production \ --role-name OrganizationAccountAccessRoleemail会接收临时密码必须保持可收信account-name是账号别名不是登录名role-name会自动创建组织角色后续通过角色切换进入新账号。创建不会立刻返回可用账号需要轮询状态aws organizations describe-create-account-status \ --create-account-request-id 请求ID账号建好后把它挪到对应 OU 下aws organizations create-organizational-unit \ --parent-id r-xxxx \ --name tu-production aws organizations move-account \ --account-id 123456789012 \ --source-parent-id r-xxxx \ --destination-parent-id ou-xxxxSCP 文件用 JSON 描述常见做法是拒绝未批准区域{ Version: 2012-10-17, Statement: [ { Effect: Deny, Action: ec2:*, Resource: *, Condition: { StringNotEquals: { aws:RequestedRegion: ap-southeast-1 } } } ] }SCP 不授予权限只修剪权限用 Deny 列出未批准区域比写一条长长的 Allow 列表更安全可靠因为新建区域默认被拒。要注意它作用于整个 OU账号内 IAM 无论如何配置都不能越过这个区域约束。2.4 账号边界优先于标签注意标签是费用归属的说明不是隔离手段。生产、测试、后台管理至少拆到不同 OU每个 OU 绑定各自区域的 SCP。预算告警也应该建在账号层级而不是整个组织层级否则一个部门超支会被大账本抹平。根账号开启 MFA 并避免在根账号上跑负载这些写在架构图的“账号治理”一页里比画一堆实例图标更有价值。3. 用CDK把 AWS 共有云架构的最小网络骨架拉起来3.1 为什么用 CDK 而不是控制台点一点控制台手动建 VPC、子网、NAT、路由表一小时内能完成但没法评审和回滚。CDK v2 写基础设施即代码的效果是git diff 即架构 diff。对实施共有云架构的团队来说不是把所有资源都搬进代码而是先把网络骨架代码化这一层影响面最广后面再补计算和存储的资源配置就只动局部了。3.2 三个 AZ 三类子网到底怎么切子网角色用途默认路由CDK SubnetTypePublicALB、NAT 网关、跳板机0.0.0.0/0 - Internet GatewayPUBLICAppECS/EC2 应用实例0.0.0.0/0 - NAT GatewayPRIVATE_WITH_EGRESSDataRDS、ElastiCache无出向路由PRIVATE_ISOLATED三个角色是最小集合更多角色比如 VPC Endpoint 专用子网等规模上来再加。3 个 AZ 乘 3 类子网共 9 个 /24 子网VPC 用 /16 足够。CIDR 规划时避免与其他 VPC 重叠方便日后做对等连接或 Transit Gateway。NAT 网关数量是成本敏感点1 个 NAT 只有一条出向路径成本约为 3 个的三分之一但 NAT 所在 AZ 故障时依赖它出向的 App 子网会受影响预算允许就配 3 个不允许也要给关键出口准备第二条路径。3.3 可复现的 CDK 骨架import * as cdk from aws-cdk-lib; import * as ec2 from aws-cdk-lib/aws-ec2; export class NetworkStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); const vpc new ec2.Vpc(this, SharedVpc, { ipAddresses: ec2.IpAddresses.cidr(10.24.0.0/16), maxAzs: 3, natGateways: 1, subnetConfiguration: [ { name: Public, subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24 }, { name: App, subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, cidrMask: 24 }, { name: Data, subnetType: ec2.SubnetType.PRIVATE_ISOLATED, cidrMask: 24 }, ], }); } }maxAzs: 3与 /16 配合会生成 9 个子网natGateways: 1让 Public 段存在一个 NAT 网关App 子网路由指向它。PRIVATE_WITH_EGRESS是旧版PRIVATE_WITH_NAT的替代写法升级 CDK 后如果报错优先看版本迁移说明。部署命令npm install -g aws-cdk cdk bootstrap aws://ACCOUNT/REGION cdk synth cdk diff NetworkStack cdk deploy NetworkStackbootstrap是一次性操作为 CDK 部署准备存储桶cdk diff是部署前必须看的一步确认不会误改已有资源。3.4 部署后验证路由表aws ec2 describe-route-tables \ --filters Namevpc-id,Valuesvpc-0xxxx \ --query RouteTables[].{Id:RouteTableId,Routes:Routes} \ --output table重点确认 Data 子网的表里没有0.0.0.0/0Public 表的默认路由指向igw-开头App 表指向nat-开头。见到多条local条目是正常的。这个检查可以写进 CICD 规则凡 Data 子网出现公网路由直接阻断。注意不要为了让 RDS 自动更新系统补丁把数据子网悄悄挂到 NAT 上出网。RDS 侧更新由 AWS 负责数据子网保持完全隔离更安全。4. 把高可用参数钉进 AWS 共有云架构ALB、ASG与RDS4.1 9月13日AWS故障复盘AZ不是装饰9月13日AWS故障之后很多团队的翻盘点集中在“图上是三个 AZ实际资源只在一个 AZ”。所以高可用参数第一条不是健康检查而是让实例和数据库物理上跨 AZ。ASG 的部署策略要覆盖至少两个 AZALB 自身是多 AZ 的不需要额外部署。真正的功夫在于让每个 AZ 都有可用实例并给健康检查留足反应时间。这里还要分清状态就算三个 AZ 都有实例带会话粘性的请求在 AZ 故障时仍然会中断所以粘性会话在高可用架构里是一个需要显式取舍的点。4.2 ALB 目标组健康检查参数创建目标组aws elbv2 create-target-group \ --name app-tg \ --protocol HTTP \ --port 8080 \ --vpc-id vpc-0xxxx \ --health-check-protocol HTTP \ --health-check-path /healthz \ --health-check-interval-seconds 30 \ --healthy-threshold-count 2 \ --unhealthy-threshold-count 3 \ --target-type instance参数推荐值说明health-check-path/healthz应用自带的探活端点不要用 / 或 /index.htmlinterval-seconds30小于 15 秒会对下游造成额外压力unhealthy-threshold-count3约 90 秒内剔除故障实例healthy-threshold-count2恢复过快会导致频繁注册与注销探活端点只检查进程是否在不要让它检查数据库连接等下游依赖探活里带数据库检查数据库一抖动整个 ASG 会被打到最小实例数故障反而被放大。4.3 ASG 扩缩容和替换参数aws autoscaling create-auto-scaling-group \ --auto-scaling-group-name app-asg \ --launch-template LaunchTemplateNameapp-launch-template,Version1 \ --min-size 2 \ --max-size 8 \ --desired-capacity 3 \ --vpc-zone-identifier subnet-app-a,subnet-app-b,subnet-app-c \ --health-check-type ELB \ --health-check-grace-period 300 \ --target-group-arns arn:aws:elasticloadbalancing:ap-southeast-1:123456789012:targetgroup/app-tg/abcdef加一条目标跟踪策略aws autoscaling put-scaling-policy \ --auto-scaling-group-name app-asg \ --policy-name cpu60 \ --policy-type TargetTrackingScaling \ --target-tracking-configuration {PredefinedMetricSpecification:{PredefinedMetricType:ASGAverageCPUUtilization},TargetValue:60}TargetValue: 60表示平均 CPU 大于 60% 扩容小于 60% 缩容。不要在同一组上再叠加 Step Scaling两套策略会互相拆台。health-check-grace-period: 300给足实例冷启动和应用初始化时间如果应用启动超过 5 分钟就该把它调整到启动耗时加 1 分钟而不是调高健康检查阈值。4.4 RDS 多 AZ、备份保留与可读写边界aws rds create-db-instance \ --db-instance-identifier app-db \ --engine postgres \ --db-instance-class db.t4g.large \ --allocated-storage 200 \ --storage-type gp3 \ --multi-az \ --backup-retention-period 30 \ --preferred-backup-window 22:00-22:30 \ --db-subnet-group-name app-data-subnets \ --no-publicly-accessible--multi-az是同步备用实例切换时应用端会有连接中断通常几十秒业务不能接受的话需要在前面加数据库代理层做读写拆分。备份保留 30 天不代表被攻击后一定能恢复还应该在备份策略之外做快照加密和跨区域复制。4.5 故障演练让 ASG 真的替换一次aws autoscaling terminate-instance-in-auto-scaling-group \ --instance-id i-0xxx \ --should-decrement-desired-capacityshould-decrement-desired-capacity设为 false 时ASG 会补一台新实例设为 true 等于有意缩容。做完马上看活动历史aws autoscaling describe-scaling-activities \ --auto-scaling-group-name app-asg提示演练前先把 ASG 最小实例数临时抬到 3 以上再终止一台确认新实例能在健康检查宽限期内进入 ALB 目标组否则容易误判成扩容故障。5. 给 AWS 共有云架构补上 WAF 边界payload 绕过与加固参数5.1 WAF 挂在 ALB 前还是 CloudFront 前AWS WAF 可以关联 CloudFront也可以关联 ALB。scope参数不同CloudFront 用CLOUDFRONTALB 用REGIONAL。前面有 CDN 时WAF 放到边缘层能拦截一部分流量再回源如果只有 ALBWAF 挂在 ALB 前就够。架构图上不要把 WAF 画成一个独立盒子它实际上是 ALB 的一个关联规则集。5.2 托管规则组默认会挡住什么、漏掉什么AWSManagedRulesSQLiRuleSet、AWSManagedRulesCommonRuleSet 覆盖常见的注入和异常路径。但绕过大多发生在请求解析层托管规则默认对 body 只解析前 8KBmultipart/form-data的文件内容不解析。所以 WAF 的第二个话题是开启 Body 和 JSON Body 解析而不是急着调高规则优先级。5.3 常见绕过路径与对应加固表绕过路径命中原因加固参数大小写混淆与双重 URL 编码规则未做统一归一化文本变换按 URL_DECODE 后 LOWERCASE 的顺序处理multipart 字段伪装body 解析只扫根字段上传路径单独加速率限制和扩展名白名单请求体不是标准 JSON未开启 JSON Body Inspection配置 JSON 解析器指定 Content-Type 与规则优先级超长 body 截断8KB 上限导致后半段载荷不被检查Body Oversize 设置继续检查并在应用层限制 body 长度重复 Content-Type 或重复 HeaderWAF 只匹配第一个且要求精确匹配在 ALB 或 CloudFront 上规范化 Header使重复头失效这些现象的本质不是规则不够而是 WAF 解析器与应用对待请求的方式不一致。加固顺序是先统一请求解析再开规则最后才切 Block。5.4 用 Count 模式上线 WAF而不是直接 Block先创建 Web ACL默认动作放行aws wafv2 create-web-acl \ --name app-waf \ --scope REGIONAL \ --default-action {Allow:{}} \ --visibility-config {SampledRequestsEnabled:true,CloudWatchMetricsEnabled:true,MetricName:app-waf}关联到 ALBaws wafv2 associate-web-acl \ --web-acl-arn arn:aws:wafv2:ap-southeast-1:123456789012:webacl/app-waf/xxxx以 Count 模式加入托管规则组[ { Name: mgr-sqli-count, Priority: 1, Statement: { ManagedRuleGroupStatement: { VendorName: AWS, Name: AWSManagedRulesSQLiRuleSet } }, OverrideAction: { Count: {} }, VisibilityConfig: { SampledRequestsEnabled: true, CloudWatchMetricsEnabled: true, MetricName: mgr-sqli-count } } ]Count 模式记录但不拦截可以在 CloudWatch 的 AWS/WAFV2 命名空间看CountedRequests确认规则命中的是什么流量。观察一两个业务周期确认没有大量正常流量被命中后再把 OverrideAction 调整为 Block。常见做法是从 SQLi 和 Common 两个规则组开始不要一次开齐全部托管规则组否则排误报的成本大于收益。6. 上线前的验证共有云架构的 SLO 与账单兜底6.1 用 CloudWatch 告警替代人工巡检aws cloudwatch put-metric-alarm \ --alarm-name unhealthy-host-0 \ --namespace AWS/ApplicationELB \ --metric-name UnHealthyHostCount \ --dimensions NameTargetGroup,Valuetargetgroup/app-tg/abcdef \ --statistic Average \ --period 60 \ --evaluation-periods 3 \ --threshold 0 \ --comparison-operator GreaterThanThreshold \ --alarm-actions arn:aws:sns:ap-southeast-1:123456789012:ops-topic这个告警表达的是“目标组内不健康实例数大于 0连续 3 分钟触发”。周期 60 秒、连续 3 次比单个周期触发更能过滤抖动。如果有多个目标组维度按 LoadBalancer 与 TargetGroup 拆开不要合并成一条。6.2 用预算告警锁住成本aws budgets create-budget \ --account-id 123456789012 \ --budget file://budget.json \ --notifications-with-subscribers file://notify.jsonbudget.json{ BudgetName: monthly-arch, BudgetLimit: { Amount: 8000, Unit: USD }, TimeUnit: MONTHLY, BudgetType: COST, CostFilters: { TagKeyValue: [app:arch$web] } }notify.json[ { Subscribers: [{ Address: opsexample.com, SubscriptionType: EMAIL }], Notifications: [ { ComparisonOperator: GREATER_THAN, Threshold: 80, ThresholdType: PERCENTAGE, NotificationType: ACTUAL }, { ComparisonOperator: GREATER_THAN, Threshold: 100, ThresholdType: PERCENTAGE, NotificationType: FORECASTED } ] } ]ACTUAL80% 看到的是已发生成本FORECASTED100% 预测当月会超支后者提醒更早。CostFilters只统计打了对应标签的资源没打标签的资源不会计入这个预算所以第 2 章说标签不是隔离手段而是账单归属手段。告警接 SNS 后直接连到值班 IM 或工单系统只发邮箱的告警在节假日等于没告警。本文还有配套的精品资源点击获取