T
traeai
登录
返回首页
AWS Architecture Blog

在 AWS 上构建混合多租户架构以支持有状态服务

8.5Score
在 AWS 上构建混合多租户架构以支持有状态服务

TL;DR · AI 摘要

AWS提出了一种混合多租户架构,通过预集成模型、Route 53加权路由、PrivateLink连接和分层架构,解决了大规模广告服务平台中的隔离性和效率问题。

核心要点

  • 通过预集成模型,将VPC、IAM角色和下游服务连接配置一次并复用,减少了重复工作。
  • 使用Route 53加权路由实现平滑的流量迁移,无需客户端更改。
  • 通过分层架构和配置驱动的租户接入,显著减少了新租户上线的时间和手动工作量。

结构提纲

按章节快速跳转。

  1. 介绍大规模广告服务平台面临的挑战。

  2. 详细描述了早期细胞架构的规模、效率、接入、扩展和性能问题。

  3. 解释了状态服务的内存需求导致的共享基础设施问题。

  4. 介绍了新的混合多租户架构,包括预集成模型、Route 53加权路由、PrivateLink连接和分层架构。

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • 混合多租户架构
    • 引言
    • 细胞架构问题
      • 规模问题
      • 效率问题
      • 接入问题
      • 扩展问题
      • 性能问题
    • 为什么需要专用计算
    • 解决方案概述
      • 预集成模型
      • Route 53加权路由
      • PrivateLink连接
      • 分层架构
      • 配置驱动的租户接入

金句 / Highlights

值得收藏与分享的关键句。

  • 我们的服务器超过98%的时间处于等待状态,不到1%的时间在执行代码。

    细胞架构问题

    ⬇︎ 下载 PNG𝕏 分享到 X
  • 我们实现了Route 53加权路由,使集群之间的流量迁移无需客户端更改。

    解决方案概述

    ⬇︎ 下载 PNG𝕏 分享到 X
  • 新租户接入成为配置变更而非基础设施配置工作,显著减少了时间和手动工作量。

    解决方案概述

    ⬇︎ 下载 PNG𝕏 分享到 X
#AWS#多租户#广告平台#架构设计
打开原文

在AWS上构建面向有状态服务的混合多租户架构

运行大规模广告投放基础设施时,需在租户隔离与运维效率之间取得平衡,这带来了独特挑战。我们的基础设施每秒处理数百万请求,年广告收入达数十亿美元,需为多个产品和系统提供广告投放服务。

蜂窝式架构问题

此前我们采用蜂窝式架构,为每个租户分配独立的AWS账号、应用负载均衡器(ALB)和Amazon弹性容器服务(Amazon ECS)。该方案虽能实现精准隔离,却带来以下重大运维挑战。

  • 规模问题:仅支持四个AWS区域内的18个客户,就需要181个独立目标。团队需为每个客户配置专用AWS账号、VPC、负载均衡器、AWS身份和访问管理(IAM)角色及下游服务连接。
  • 效率问题:服务器98%的时间处于等待状态,执行代码时间不足1%。平均CPU利用率仅为3%,内存利用率为19%。我们为大量闲置的基础设施支付了高额费用。
  • 上线问题:新客户上线需约52天——其中AWS账号配置两周,VPC与网络搭建三周,IAM角色配置一周,下游服务集成与测试两周。
  • 扩展问题:流量增长或新客户加入时,唯一选择是新建蜂窝单元并迁移租户。无法支持并发的Tier-1实时活动——多个高价值游戏无法同时运行,迫使流量转向备用系统。
  • 噪音邻居问题:尽管实施了隔离措施,租户共享基础设施时仍导致性能下降,影响服务质量和可靠性。

为何需要专用计算

我们的广告投放平台是有状态服务,为每个租户在内存中加载并维护数据,而非每次请求都从数据库获取。这种内存状态提升了性能,但租户共享基础设施时会引发噪音邻居问题。当两个租户共享集群时,其内存数据会争夺同一堆内存。数据集较大的租户可能触发内存溢出,影响相邻租户。这使得共享任务和共享集群方案难以支撑我们的有状态工作负载。我们需要一种在保持集群级隔离的同时大幅提升运维效率的解决方案。

解决方案概览

我们设计了混合多租户架构,在共享账号内实现集群级隔离。具体实现如下:

  • 预集成模式:不再为每个新租户配置VPC、IAM角色及下游服务连接,而是建立基于配置的基础设施,将这些集成一次性完成并复用至所有租户。
  • Amazon Route 53加权路由:通过Route 53加权路由实现集群间流量的渐进迁移,无需客户端变更。这使我们能随租户流量模式演变动态调整集群分配。
  • AWS PrivateLink连接:建立所有租户共享的AWS PrivateLink端点,避免为每个租户配置新的VPC对等连接或传输网关,将网络配置开销降低80%。
  • 分层架构:将基础设施划分为层级(高TPS、标准TPS、低TPS),每层包含多个蜂窝单元,实现水平扩展,无需为每个租户配置独立AWS账号。
  • 配置驱动式上线:新租户上线仅需配置变更,而非基础设施部署,大幅缩短时间并减少人工操作。

该架构围绕三层嵌套层级组织。层级为最顶层分组——租户的逻辑分类,共享相同基础设施规模。层级覆盖一个或多个蜂窝单元,每个蜂窝单元代表AWS账号边界,是账号级别的水平扩展单元。每个蜂窝单元内,一个或多个基础设施组作为自包含基础设施单元:包含VPC、应用负载均衡器、一组ECS集群(每租户一个)、IAM角色及监控堆栈。

为什么需要三层架构?当您从10个租户扩展到100个、1,000个时,不同规模会触及AWS的不同限制。Application Load Balancer目标组限制了单个负载均衡器可容纳的租户数量。AWS账户对弹性网络接口(ENIs)和VPC端点的限制则约束了单个账户内可部署的负载均衡器数量。这种三层架构为您提供两个独立的扩展杠杆:通过添加基础设施组实现账户内扩展,通过添加单元实现跨账户扩展。核心设计原则是在层级创建时预先配置下游服务依赖,而非在租户开通时配置。层级VPC与各下游服务VPC之间的AWS PrivateLink连接在层级部署后建立。租户加入该层级后,将自动继承完整的下游连接能力。这一架构决策是基础设施设置步骤减少80%的主要原因。Route 53通过跨多个基础设施组的Application Load Balancer实现加权DNS路由,支持水平扩展而无需客户端配置变更。

下图展示了完整架构:Route 53将流量分发至单个单元账户内多个基础设施组的ALB,每个ALB通过监听器规则和目标组路由至租户专属的ECS集群,集群则共享层级级的AWS PrivateLink连接至下游服务。

Image 1: Multi-Tenant Architecture Diagram

图1:混合多租户架构图,展示Route 53加权路由、Application Load Balancer监听器规则、租户专属ECS集群,以及共享的下游服务AWS PrivateLink连接。

先决条件

构建此架构前,请确保已满足以下条件:

  • 配置了最小权限的AWS账户(具备创建VPC、Application Load Balancer、ECS集群、Route 53托管区域及VPC端点的权限)
  • 已安装并配置AWS命令行界面(AWS CLI) 2.x或更高版本
  • 熟悉Amazon ECS、Application Load Balancer和Amazon Route 53(特别是ECS任务定义、ALB监听器规则及Route 53路由策略)
  • 至少有一个下游服务通过VPC端点服务提供AWS PrivateLink连接

预计完成时间:2–3小时。

操作指南

本操作指南将指导您构建前述混合多租户架构。您将配置Route 53加权路由、部署带租户专属监听器规则的ALB、为每个租户创建专用ECS集群,并建立共享下游服务的AWS PrivateLink连接。所有操作将确保未来租户开通仅需配置操作。

**步骤1:配置Route 53区域端点的加权路由**

每个层级提供单一区域DNS端点(例如tier-1.us-east-1.example.com),由Route 53加权路由记录支持。您可配置Route 53使用加权路由,将流量分发至跨多个AWS账户的ALB。当为水平扩展向层级添加新账户时,只需新增加权记录,无需修改现有租户DNS条目。

配置Route 53加权路由的步骤:

  1. 打开Amazon Route 53控制台,选择托管区域
  2. 选择或创建层级对应的托管区域
  3. 选择创建记录,将路由策略设为加权
  4. 设置记录名称为层级端点(例如tier-1.us-east-1.example.com),记录类型设为A,并配置指向首个AWS账户ALB的别名
  5. 设置权重为50,提供唯一集合ID(例如account-1
  6. 启用评估目标健康状态,确保Route 53在配置健康检查时仅将流量导向健康ALB
  7. 为层级中每个额外AWS账户重复上述步骤,使用匹配权重

也可通过以下AWS CLI命令创建首个加权记录:

code
aws route53 change-resource-record-sets \
  --hosted-zone-id YOUR_HOSTED_ZONE_ID \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "tier-1.us-east-1.example.com",
        "Type": "A",
        "SetIdentifier": "account-1",
        "Weight": 50,
        "AliasTarget": {
          "HostedZoneId": "Z35S*****K",
          "DNSName": "your-alb.us-east-1.elb.amazonaws.com",
          "EvaluateTargetHealth": true
        }
      }
    }]
  }'

注意:将Z35S*****K替换为ALB所在AWS区域的托管区域ID。详情参见弹性负载均衡端点和配额

Route 53支持单个托管区域最多10,000个加权记录,此方案无需架构变更即可扩展至数千个AWS账户。关于加权路由的更多信息,请参阅加权路由

**步骤2:部署带租户专属监听器规则的Application Load Balancer**

每个基础设施组包含一个Application Load Balancer。负载均衡器分析传入请求,根据请求路径或自定义HTTP头部提取的租户标识符,将请求转发至对应租户的ECS服务。

两个应用负载均衡器配额决定了每个基础设施组的容量:每个负载均衡器最多 100 个目标组,每个监听规则最多 5 个目标组。通过 20 个监听规则各自转发到 5 个目标组,单个负载均衡器可以支持每个基础设施组最多 50 个租户。每个租户最多有 5 个 ECS 集群,因此单个基础设施组可以托管最多 100 个 ECS 集群。

要创建特定于租户的监听规则:

  1. 打开 Amazon EC2 控制台并选择导航窗格中的负载均衡器
  2. 选择您的应用负载均衡器并选择监听器选项卡。
  3. 选择 HTTPS 监听器的查看/编辑规则
  4. 选择加号 (+) 图标以添加新规则。
  5. 添加条件:路径为/tenant-a/*(或使用基于头部的路由时的 HTTP 头部)。
  6. 添加操作:转发到租户-a 的目标组。
  7. 设置唯一的规则优先级并保存。

要使用 AWS CLI 创建目标组和监听规则:

code
# 创建租户的目标组
aws elbv2 create-target-group \
  --name tg-tenant-a \
  --protocol HTTP --port 8080 \
  --vpc-id YOUR_VPC_ID \
  --target-type ip
# 添加一个监听规则,将 /tenant-a/* 路由到目标组
aws elbv2 create-rule \
  --listener-arn YOUR_LISTENER_ARN \
  --conditions '[{"Field":"path-pattern","Values":["/tenant-a/*"]}]' \
  --actions '[{"Type":"forward","TargetGroupArn":"YOUR_TARGET_GROUP_ARN"}]' \
  --priority 10

TypeScript

更多信息,请参阅Application Load Balancer 的监听器规则

**步骤 3:为每个租户创建专用的 ECS 集群**

在此步骤中,您将在基础设施组的 VPC 中为每个租户创建一个专用的 ECS 集群。使用一致的命名约定,将层级、单元、基础设施组和租户标识符编码在名称中(例如,tier-1-cell-1-ig-1-tenant-a),以便在操作和事件响应期间明确所有权。要为租户创建专用的 ECS 集群:

  1. 打开 Amazon ECS 控制台并选择集群
  2. 选择创建集群
  3. 按照您的命名约定输入集群名称(例如,tier-1-cell-1-ig-1-tenant-a)。
  4. 选择EC2 Linux + 网络并配置适合租户工作负载的实例类型和自动扩展组设置。
  5. 选择基础设施组 VPC 和子网。
  6. 选择创建

要使用 AWS CLI 创建集群:

code
aws ecs create-cluster \
  --cluster-name tier-1-cell-1-ig-1-tenant-a \
  --region us-east-1

Code

在该租户的 ECS 任务定义中,将租户标识符作为环境变量传递。应用程序在启动时读取此值以限定其数据访问范围——仅从共享远程缓存中加载该租户的配置和状态:

code
{
  "containerDefinitions": [{
    "name": "app",
    "image": "your-ecr-image:latest",
    "environment": [
      { "name": "TENANT_ID", "value": "tenant-a" },
      { "name": "CACHE_ENDPOINT", "value": "cache.tier-1.internal" }
    ]
  }]
}

CSS

注意:将 your-ecr-image:latest 替换为您的 Amazon 弹性容器注册表 (Amazon ECR) 镜像 URI。

将 ECS 服务注册为步骤 2 中创建的 ALB 目标组的目标。根据中央处理单元 (CPU) 和内存利用率指标配置 ECS 服务的自动扩展,这些指标限定于单个服务。由于每个集群都是单租户的,ECS 每个服务 5,000 个任务的限制仅适用于该租户。一个租户的资源消耗不会影响另一个租户的集群。更多信息,请参阅 Amazon ECS 开发者指南中的创建集群

**步骤 4:建立与共享依赖项的 AWS Private Link 连接**

此步骤发生在层级创建时,而不是租户上板时——这一区别是设计的核心。对于您的应用程序集成的每个下游服务,在基础设施组 VPC 中创建一个 VPC 接口端点。层级中的 ECS 任务通过这些端点路由流量到下游服务。上板到该层级的租户可以通过预配置的端点访问下游连接。

每个 VPC 接口端点每月大约花费 7.30 美元加上数据传输费用(每 GB 0.01 美元)。对于一个层级中有 50 个租户共享一个端点的情况,这笔费用相对于运营节省来说微不足道。如果您的下游服务在同一 VPC 中,可以考虑使用 VPC 对等连接或 AWS Transit Gateway 作为成本更低的替代方案。当您需要连接到不同 AWS 账户中的服务或需要私有连接的安全性和隔离优势时,使用 AWS PrivateLink。

要为下游服务创建 VPC 接口端点:

  1. 打开 Amazon VPC 控制台并选择导航窗格中的端点
  2. 选择创建端点
  3. 选择按名称查找服务并输入下游服务所有者提供的 VPC 端点服务名称。
  4. 选择基础设施组 VPC 和 ECS 任务使用的子网。
  5. 附加一个允许 ECS 任务在所需端口上向端点发送出站流量的安全组。
  6. 选择创建端点

要使用 AWS CLI 创建端点:

code
aws ec2 create-vpc-endpoint \
  --vpc-id YOUR_VPC_ID \
  --service-name com.amazonaws.vpce.us-east-1.vpce-svc-YOUR_SERVICE_ID \
  --vpc-endpoint-type Interface \
  --subnet-ids subnet-*** subnet-*** \
  --security-group-ids sg-YOUR_SG_ID

TypeScript

定义层级 IAM 角色,这些角色具有访问下游服务所需的权限,并在层级级别将这些角色分配给 ECS 任务定义。新租户可以通过共享的 IAM 角色获得层级级别的权限,而无需为每个租户创建角色。有关更多信息,请参阅使用接口 VPC 终端节点访问 AWS 服务

**步骤 5:配置租户隔离、扩展和可观测性**

此架构通过客户配置在三个层面上强制执行租户隔离。在 _路由层_,ALB 监听器规则根据租户标识符将流量唯一地路由到正确的租户目标组。ALB 监听器规则帮助根据您的配置将流量路由到正确的租户目标组。在 _计算层_,每个租户都有一个专用的 ECS 集群,因此资源限制适用于每个集群,集群级别的隔离旨在最小化一个租户的资源消耗对另一个租户的影响。在 _内存状态_ 层,因为每个 ECS 集群都是单租户的,启动时加载的内存数据仅属于该租户,租户之间没有共享堆。

扩展策略

当单个租户的流量增长但尚未达到每个基础设施组的 50 租户限制时,使用垂直扩展——它更快(分钟级与小时级)且不需要 Route 53 变更。增加任务定义中的 ECS 任务 CPU 和内存预留,或在自动扩展组中切换到更大的 EC2 实例类型。

当您接近 50 租户限制或多个租户同时需要容量时,在同一单元内添加一个新的基础设施组——新的 VPC、ALB 和一组 ECS 集群。Route 53 加权路由在不进行客户端更改的情况下将流量分布在基础设施组之间:

code
aws route53 change-resource-record-sets \
  --hosted-zone-id YOUR_HOSTED_ZONE_ID \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "tier-1.us-east-1.example.com",
        "Type": "A",
        "SetIdentifier": "cell-1-ig-2",
        "Weight": 50,
        "AliasTarget": {
          "HostedZoneId": "Z3******K",
          "DNSName": "your-alb-ig-2.us-east-1.elb.amazonaws.com",
          "EvaluateTargetHealth": true
        }
      }
    }]
  }'

CSS

仅在接近账户级别的限制时才使用单元级别的扩展——通常是在每个单元有 3-4 个基础设施组之后。每个 AWS 账户对 ENI、VPC 终端节点和其他资源都有硬性限制。当单元接近这些限制时,通过在新的 AWS 账户中预配相同的层级基础设施堆栈并将其 ALB 在 Route 53 中注册为加权记录来添加新的单元:

code
aws route53 change-resource-record-sets \
  --hosted-zone-id YOUR_HOSTED_ZONE_ID \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "tier-1.us-east-1.example.com",
        "Type": "A",
        "SetIdentifier": "cell-2",
        "Weight": 50,
        "AliasTarget": {
          "HostedZoneId": "Z35SXDOTRQ7X7K",
          "DNSName": "your-alb-cell-2.us-east-1.elb.amazonaws.com",
          "EvaluateTargetHealth": true
        }
      }
    }]
  }'

CSS

层级端点(tier-1.us-east-1.example.com)保持稳定。随着层级的增长,租户无需更新其 DNS 配置。下表总结了何时使用每种扩展杠杆:

触发器 动作 增加的单位 应用负载均衡器目标组限制(每个基础设施组约 50 个租户)在同一单元内添加基础设施组 基础设施组(VPC + 应用负载均衡器 + ECS 集群) AWS 账户级别限制(ENI、VPC 终端节点)添加新的单元 单元(新的 AWS 账户)

可观测性

可观测性结构化为两个层面。从每个 ECS 服务 _发出租户级别_ 的指标,将租户标识符作为 Amazon CloudWatch 维度。关键监控指标包括:

每个 ECS 服务的内存使用率是内存状态增长的主要信号。突然激增通常表明数据模型发生变化或数据管道配置错误。在 70%(警告)和 85%(严重)设置 CloudWatch 警报。当内存使用率超过 70% 时,调查租户的数据模型是否已更改或数据管道是否配置错误。在 85% 时,准备垂直扩展 ECS 任务定义。TargetResponseTime 和每个 ALB 目标组的请求计数用于测量每个租户的延迟和吞吐量。在租户入职期间建立基线(通常是 100-200 毫秒的状态化服务),然后在延迟超过基线 2 倍且持续超过 5 分钟时发出警报。HTTPCode_Target_5XX_Count 每个目标组跟踪每个租户的错误率。对于层级级别的健康状况,监控 ALB 的 ActiveConnectionCountProcessedBytes,每个负载均衡器的 Route 53 健康检查状态,以及 ECS 集群的 CPU 预留和内存预留以进行容量规划。在每个日志条目中配置 Amazon CloudWatch Logs,包含结构化的日志字段,包括 tenant_idtier_idregion。每个层级使用一个日志组,并使用编码租户标识符的日志流前缀。以下 CloudWatch Logs Insights 查询识别整个层级中按租户的错误率:

code
fields @timestamp, tenant_id, @message
| filter @message like /ERROR/
| stats count() as error_count by tenant_id
| sort error_count desc

代码

**步骤 6:验证架构**

在上生产租户之前,通过以下检查验证您的架构:

  1. 向您的层级端点发送带有不同租户标识符的测试请求。
  2. 验证 Route 53 是否将流量分配到应用负载均衡器:aws route53 test-dns-answer --hosted-zone-id YOUR_ID --record-name tier-1.us-east-1.example.com
  3. 通过检查 ALB 访问日志,确认负载均衡器将请求路由到正确的租户 ECS 集群。
  4. 通过从 ECS 任务向下游服务发送请求来测试 AWS PrivateLink 连接。
  5. 通过加载大型数据集来模拟租户内存峰值,并确认这不会影响其他租户。
  6. 验证 CloudWatch 指标是否以正确的 tenant_id 维度发出。

**结果**

这些结果来自为一个有状态的广告投放应用程序实施此架构。在此架构之前,新租户的上线需要 52 天。使用此架构后,上线时间缩短到 7 天——主要是测试和验证,因为基础设施已预先配置。

测量的改进:

  • 租户上线时间:从 52 天减少到 7 天(减少了 86%)
  • 每个租户的基础设施设置步骤:减少了 80%
  • 每次上线所需的工程工作量:减少了 80%
  • 功能发布时间:从 2-3 天减少到 1 天
  • 租户容量:每个 AWS 账户最多可支持 100 个租户,具有强大的集群级隔离

**清理**

为了避免产生未来的费用,请按以下顺序删除资源:

  1. 从目标组中注销 ECS 服务,然后删除 ECS 集群(这可能需要 5-10 分钟)。
  2. 删除应用负载均衡器监听规则,然后删除与测试租户关联的目标组。
  3. 删除 Route 53 加权路由记录中的测试层级端点。
  4. 删除在层级设置期间创建的 VPC 接口端点(AWS PrivateLink)。
  5. 终止自动扩展组中的 EC2 实例,然后删除自动扩展组。
  6. (可选)如果没有任何其他资源依赖于它,可以删除 VPC。

注意:删除这些资源会立即停止计费。如果您计划重用此架构,可以考虑停止 ECS 服务而不是删除集群。

**结论**

在这篇文章中,我向您展示了如何构建一个混合多租户架构,该架构提供强大的租户隔离,而无需为每个租户创建单独的 AWS 账户。您学习了如何配置 Route 53 加权路由以跨多个账户分配流量,部署应用负载均衡器监听规则以实现特定租户的路由,为每个租户创建专用的 ECS 集群,并建立 AWS PrivateLink 连接到共享依赖项。这种方法将租户上线时间减少了 86%,并将基础设施设置步骤减少了 80%。

最重要的设计决策是将依赖项设置与租户上线解耦。在层级创建时预连接 PrivateLink 连接、IAM 角色和远程缓存端点,将上线从一个多周的基础设施项目转变为仅需配置的操作。三层结构(层级、单元、基础设施组)为您提供两个独立的扩展杠杆。当应用负载均衡器接近其目标组限制时,增加基础设施组。当 AWS 账户接近其 ENI 或 VPC 端点限制时,增加单元。Route 53 加权路由透明地吸收这两种变化。

下一步

准备好实施此架构了吗?以下是开始的方法:

  1. 评估您当前的租户分布并确定适合层级整合的候选租户。
  2. 根据您的延迟和隔离要求定义层级提升标准。
  3. 从单个层级和 2-3 个测试租户开始,验证架构。
  4. 使用分阶段方法逐步迁移现有租户。
  5. 在扩展到更多层级之前,监控租户级别的指标 2-4 周。

如需更多指导,请查阅 AWS Well-Architected Framework — SaaS Lens 并探索 GitHub 网站上的 SaaS ECS 参考架构

可选增强功能

实施此架构后,您可以考虑以下额外改进:制定正式的层级迁移手册并使用自动化工具,使租户在层级之间的移动成为可预测且低风险的操作;以及跨层级进行装箱分析,识别那些内存占用允许在同一 EC2 实例上共存而不共享集群的租户,从而在保持隔离特性的同时降低 EC2 成本。您是否实现了类似的多租户架构?请留言或联系我们分享您的故事。

**相关资源**

  • * *

关于作者

AI 可能会生成不准确的信息,请核实重要内容

在 AWS 上构建混合多租户架构以支持有状态服务 | AWS Architecture Blog | traeai