跳到主要内容

a6-recipe-multi-tenant

概览

API 网关的多租户模式是指通过同一个网关实例为多个相互隔离的租户(客户、团队或应用)提供服务,并为每个租户配置独立的限流、身份认证和路由规则。

APISIX 通过以下机制实现多租户:

  1. 消费者组——将消费者按租户分组,并共享插件配置。
  2. 基于 Host、路径或请求头的路由——将请求路由到指定租户的上游。
  3. 租户级限流——为每个消费者组执行独立配额。
  4. proxy-rewrite——通过请求头将租户上下文转发给后端。

适用场景

  • 共享单一 API 网关的多个客户
  • 为不同团队服务的内部平台
  • SaaS 应用,要求每租户费率限额和认证
  • 需要将租户身份转给后端服务

方法 A:使用消费者组隔离租户

按租户对消费者进行分组。每个租户通过消费者组获得共享的插件配置,例如限流和请求转换。

1. 为每个租户创建消费者组

# Free tier — 100 requests/day
a6 consumer-group create -f - <<'EOF'
{
"id": "tenant-free",
"desc": "Free tier tenant",
"plugins": {
"limit-count": {
"count": 100,
"time_window": 86400,
"key_type": "var",
"key": "consumer_name",
"rejected_code": 429,
"rejected_msg": "Free tier quota exceeded"
}
}
}
EOF

# Pro tier — 10000 requests/day
a6 consumer-group create -f - <<'EOF'
{
"id": "tenant-pro",
"desc": "Pro tier tenant",
"plugins": {
"limit-count": {
"count": 10000,
"time_window": 86400,
"key_type": "var",
"key": "consumer_name",
"rejected_code": 429,
"rejected_msg": "Pro tier quota exceeded"
}
}
}
EOF

2. 创建消费者并分配到组

a6 consumer create -f - <<'EOF'
{
"username": "acme-corp",
"group_id": "tenant-pro",
"plugins": {
"key-auth": { "key": "acme-secret-key" }
}
}
EOF

a6 consumer create -f - <<'EOF'
{
"username": "startup-xyz",
"group_id": "tenant-free",
"plugins": {
"key-auth": { "key": "startup-xyz-key" }
}
}
EOF

3. 创建启用身份认证的共享路由

a6 route create -f - <<'EOF'
{
"id": "api-v1",
"uri": "/api/v1/*",
"upstream": {
"type": "roundrobin",
"nodes": { "api-backend:8080": 1 }
},
"plugins": {
"key-auth": {}
}
}
EOF

现在,acme-corp 每天可发送 10,000 个请求,startup-xyz 每天可发送 100 个请求, 两者都通过同一路由。

方法 B:基于 Host 的租户路由

根据 Host 请求头,将每个租户的流量路由到各自的后端。

1. 为每个租户创建上游

a6 upstream create -f - <<'EOF'
{
"id": "upstream-tenant-a",
"type": "roundrobin",
"nodes": { "tenant-a-backend:8080": 1 }
}
EOF

a6 upstream create -f - <<'EOF'
{
"id": "upstream-tenant-b",
"type": "roundrobin",
"nodes": { "tenant-b-backend:8080": 1 }
}
EOF

2. 创建基于 Host 的路由

a6 route create -f - <<'EOF'
{
"id": "tenant-a-route",
"host": "tenant-a.example.com",
"uri": "/*",
"upstream_id": "upstream-tenant-a",
"plugins": { "key-auth": {} }
}
EOF

a6 route create -f - <<'EOF'
{
"id": "tenant-b-route",
"host": "tenant-b.example.com",
"uri": "/*",
"upstream_id": "upstream-tenant-b",
"plugins": { "key-auth": {} }
}
EOF

方法 C:基于请求头的租户路由

使用自定义请求头(例如 X-Tenant-ID),通过 traffic-split 将流量路由到不同上游。

a6 route create -f - <<'EOF'
{
"uri": "/api/*",
"plugins": {
"key-auth": {},
"traffic-split": {
"rules": [
{
"match": [{ "vars": [["http_x_tenant_id", "==", "tenant-a"]] }],
"weighted_upstreams": [
{ "upstream": { "type": "roundrobin", "nodes": { "tenant-a-backend:8080": 1 } }, "weight": 1 }
]
},
{
"match": [{ "vars": [["http_x_tenant_id", "==", "tenant-b"]] }],
"weighted_upstreams": [
{ "upstream": { "type": "roundrobin", "nodes": { "tenant-b-backend:8080": 1 } }, "weight": 1 }
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": { "default-backend:8080": 1 }
}
}
EOF

向后端转发租户上下文

使用 proxy-rewrite 将租户身份写入 HTTP 请求头,使后端能够识别请求所属的租户。

a6 route update api-v1 -f - <<'EOF'
{
"plugins": {
"key-auth": {},
"proxy-rewrite": {
"headers": {
"set": {
"X-Consumer-Name": "$consumer_name",
"X-Consumer-Group": "$consumer_group_id"
}
}
}
}
}
EOF

后端会收到 X-Consumer-Name: acme-corpX-Consumer-Group: tenant-pro 请求头。

声明式多租户配置

使用 a6 config sync 统一管理所有租户:

# apisix-tenants.yaml
consumer_groups:
- id: tenant-free
desc: "Free tier"
plugins:
limit-count:
count: 100
time_window: 86400
key_type: var
key: consumer_name
- id: tenant-pro
desc: "Pro tier"
plugins:
limit-count:
count: 10000
time_window: 86400
key_type: var
key: consumer_name

consumers:
- username: acme-corp
group_id: tenant-pro
plugins:
key-auth:
key: acme-secret-key
- username: startup-xyz
group_id: tenant-free
plugins:
key-auth:
key: startup-xyz-key

routes:
- id: api-v1
uri: "/api/v1/*"
upstream:
type: roundrobin
nodes:
"api-backend:8080": 1
plugins:
key-auth: {}
proxy-rewrite:
headers:
set:
X-Consumer-Name: "$consumer_name"
X-Consumer-Group: "$consumer_group_id"
# Preview changes
a6 config diff -f apisix-tenants.yaml

# Apply
a6 config sync -f apisix-tenants.yaml

注意事项

  • 消费者组插件合并——消费者组配置的插件会与单个消费者配置的插件合并。如果两者定义了同一个插件,以消费者级配置为准。
  • group_id 是字符串——其值必须与现有消费者组 ID 完全一致。
  • 限流键——组合使用 key_type: "var"key: "consumer_name",可在同一消费者组内按消费者执行限流;否则限额会由组内所有消费者共享。
  • 重写中的变量名称——$consumer_name$consumer_group_id 是 APISIX 内置变量,只有在身份认证完成后才可用。应确保身份认证插件(如 key-authjwt-auth)先于 proxy-rewrite 执行。

验证

# List consumer groups
a6 consumer-group list

# Verify consumer assignment
a6 consumer get acme-corp --output json | grep group_id

# Test rate limiting for free tier
for i in $(seq 1 101); do
curl -s -o /dev/null -w "%{http_code}\n" \
-H "apikey: startup-xyz-key" http://localhost:9080/api/v1/hello
done
# Request 101 should return 429

# Verify tenant headers reach backend
curl -H "apikey: acme-secret-key" http://localhost:9080/api/v1/headers
# Response should show X-Consumer-Name and X-Consumer-Group headers

本文根据 api7/a6 仓库中的 a6-recipe-multi-tenant/SKILL.md 生成。可在 AI Agent Skills 页面浏览全部 Skill。