Azure API Management:Gateway、产品订阅、策略与网络治理
Product 已经下架,为什么旧 Key 还能调用接口
一个团队准备停用旧订单 API,于是在 developer portal 中把 Product 改成 unpublished,并确认页面上已经搜不到它。几分钟后,监控仍看到旧客户端持续调用 gateway,原有 subscription key 也没有失效。问题不在缓存,而在对象语义:Product 的发布状态控制消费者能否在 developer portal 发现和订阅,不是运行时拒绝规则。
同一套服务里还存在另一种错觉:Azure portal 显示 API 配置已经保存,不代表 managed gateway 已经收到并执行最新策略;self-hosted gateway 能在断网时继续转发旧流量,也不代表它拥有独立的 management plane。要安全运营 API Management,必须把管理入口、运行时 gateway、消费者门户、产品订阅、策略传播、revision 和网络方向逐一拆开。
三个入口服务三类人
Azure API Management 托管服务由运行请求的 gateway、保存和分发配置的 management plane、服务 API consumer 的 developer portal 组成。默认 managed gateway 随 instance 提供;workspace gateway 与 self-hosted gateway 是不同的隔离和部署对象,支持的协议、策略、网络和套餐条件必须从当前 gateway 比较说明 逐项确认。
部署层级决定了谁承担容量、网络和故障责任。API Management service 是 Azure 资源与管理边界,默认 managed gateway 是随 service 提供的数据面;workspace 是服务内部的委派管理边界,workspace gateway 是按 unit 单独计费和扩缩的托管数据面;self-hosted gateway 则是团队运行在自有容器平台上的远端数据面。workspace gateway 的网络配置独立于 service,创建后不能再切换网络形态,并且当前不支持 inbound private endpoint、自定义 gateway hostname,也不能与 self-hosted gateway 关联。它适合把 API 所有权下放给业务团队,却不能被误当成任意网络能力都可组合的“小 APIM”。这些限制应在创建 workspace gateway 前写入选型记录,因为迁移通常意味着新建网关和切流,而不是原地改一个开关。
Gateway 是数据面运行时。客户端请求在这里匹配 API 与 operation,校验 subscription key、JWT 或证书,顺序执行 policy,调用 backend,再生成响应和遥测。它是否健康,要用真实请求、策略结果、backend 证据和关联日志证明。
Management plane 保存 API、Product、Subscription、Policy、Revision、Gateway resource 和网络配置。API provider 通过 Azure portal、CLI、PowerShell、REST API 或 SDK操作这些对象。管理请求成功只证明配置被接受,不证明配置已传播到每个 gateway,也不证明 backend 可达。
Developer portal 面向 API consumer,负责 API 发现、文档、试用、订阅和凭证自助。Azure portal 面向管理员操作 Azure 资源。两者的用户、域名、网络可达性和数据敏感度不同,日志和告警中不能都简写成“Portal”。核心对象关系可对照 API Management 概念说明。
状态分别存放在 Azure 资源配置、gateway 内存与可选的 self-hosted configuration backup、Subscription Key、developer portal 内容和外部遥测系统中。删除 API 不会自动删除 Key 的外部副本、日志工作区、private endpoint、DNS link、Kubernetes Secret/PVC 或 backend。退出时必须按这些状态所有者逐项核销。
Product、Subscription、Policy 和 Revision各管一件事
这四个对象经常都被口头称为“版本”或“套餐”,但它们的状态和切换动作完全不同。
| 对象 | 保存什么 | 运行时影响 | 最容易误判的地方 |
|---|---|---|---|
| Product | API 集合、可见性、订阅要求、审批、条款、Product 级 policy | Product-scoped subscription 调用时可进入 Product policy | unpublished 只影响发现和新订阅,不自动阻断 gateway 调用 |
| Subscription | 一对 Key、名称、状态和作用域 | Key 可在 Product、单 API 或 all APIs 作用域放行 | 它不是 Azure billing subscription,也不是终端用户身份 |
| Policy | XML 规则和继承位置 | 在 inbound、backend、outbound、on-error 顺序执行 | 漏掉 <base/> 可能静默绕过父级安全与日志规则 |
| Revision | 同一 API 的可测试定义和 policy 变体 | current revision 使用默认路径,特定 revision 可经 ;rev={id} 访问 | Revision 不等于面向消费者的不兼容 API Version |
Product 的 Published/Unpublished、Open/Protected 语义可从 Product 指南 复核。即使 Product unpublished,其中的 API 仍可能经 gateway 可达;真正的关闭动作应落到 Subscription 状态、JWT/证书策略、网络或 API/operation 状态,并用拒绝请求证明。
Subscription 可以作用于 Product、单 API 或 instance 的 all APIs。API-scoped、all-APIs 和内置 all-access subscription 不经过 Product scope,因此 Product policy 不能被当成全局安全底座。内置 all-access Key 只适合受控诊断,不应嵌入应用。Subscription Key 没有完整的终端用户身份语义,也没有可替代外部凭证生命周期的自动到期模型;生产系统需要主/备 Key 轮换流程,或改用带过期语义的 token、证书和主体授权。
Policy 是 gateway 内执行的 XML 规则,不是 Azure Policy 服务。scope 从宽到窄是 global、workspace、product、API、operation;<base/> 出现的位置决定父 policy 在子 policy 中的插入位置。某一 policy 是否支持目标 gateway、套餐、scope 和 section,应在 Policy 参考 按部署对象核对,不能因为 managed gateway 可用就假设 self-hosted gateway 同样可用。
Revision 用于非破坏性修改和测试,再通过 release 把某个 revision 设为 current。非 current revision 的显式 URL 可能仍能从公网访问,因此测试 revision 也要有身份、IP 或网络限制。面向消费者的破坏性契约变更应使用 API Version 与独立生命周期,不能拿 revision 隐藏。
在现有隔离 instance 上接入两个后端
创建 API Management instance 可能持续占用云资源并产生显著费用,因此从已有专用测试 instance 开始:两个由团队控制、能直接访问且响应体可区分的 HTTPS backend,分别放在 BLUE_BACKEND 与 GREEN_BACKEND;执行者具有目标 instance 的最小 RBAC,Azure CLI 已登录正确 tenant/subscription,本机还要有 jq 与 OpenSSL。传播时延、套餐能力和账单必须从目标环境取证,不能用 management plane 的成功响应推断 gateway 已生效。
若环境还没有 instance,先做一次不可逆或高成本属性评审:Region、classic/v2 代际、SKU、容量单位、可用区、是否需要多区域、入口是否私有、gateway 到 backend 是否进 VNet、workspace 与 self-hosted gateway 是否存在。不要先建 Consumption 或 Developer 再假设可以无损打开生产网络能力;不同层级的 VNet injection、outbound integration、private endpoint、workspace 和多区域支持并不对称。创建动作进入 IaC 前,至少让平台、网络、安全和成本 owner 共同确认资源组、命名、标签、托管身份、诊断目标、预算和删除保护;部署完成后再用下面的查询建立“请求的是哪个 SKU”和“实际得到哪个 endpoint”的基线。
先记录 CLI、主体、subscription、instance、SKU、Region 和 endpoint。原始 az account show 与 az apim show 可能暴露 tenant、subscription、managed identity 和网络信息,只保存脱敏证据。
set -euo pipefail
export AZURE_SUBSCRIPTION='<lab-subscription-id>'
export RG='<lab-resource-group>'
export APIM='<lab-apim-name>'
export BLUE_BACKEND='https://blue-api.example.invalid'
export GREEN_BACKEND='https://green-api.example.invalid'
export PRODUCT_ID='orders-lab'
export TEST_SUBSCRIPTION_ID='orders-lab-caller'
export ARM_API_VERSION='<supported-management-api-version>'
az version
az account set --subscription "$AZURE_SUBSCRIPTION"
az account show --query '{name:name,tenantId:tenantId,user:user.name}'
az apim show --resource-group "$RG" --name "$APIM" \
--query '{name:name,location:location,sku:sku.name,gatewayUrl:gatewayUrl,portalUrl:developerPortalUrl}'
curl -fsS "$BLUE_BACKEND/probe"
curl -fsS "$GREEN_BACKEND/probe"只有两个直连基线都稳定、响应能明确显示 blue 和 green、TLS 校验正常时,才创建 API。.invalid 是占位域名,必须替换为隔离环境中真实可达的合成 backend;不能拿生产客户地址做教程实验。
az apim api create \
--resource-group "$RG" \
--service-name "$APIM" \
--api-id orders-blue \
--display-name 'Orders Blue Lab' \
--path orders-blue \
--api-type http \
--protocols https \
--service-url "$BLUE_BACKEND" \
--subscription-required true
az apim api operation create \
--resource-group "$RG" \
--service-name "$APIM" \
--api-id orders-blue \
--operation-id probe \
--display-name 'Probe Blue' \
--method GET \
--url-template /probe
az apim api create \
--resource-group "$RG" \
--service-name "$APIM" \
--api-id orders-green \
--display-name 'Orders Green Lab' \
--path orders-green \
--api-type http \
--protocols https \
--service-url "$GREEN_BACKEND" \
--subscription-required true
az apim api operation create \
--resource-group "$RG" \
--service-name "$APIM" \
--api-id orders-green \
--operation-id probe \
--display-name 'Probe Green' \
--method GET \
--url-template /probe
az apim product create \
--resource-group "$RG" \
--service-name "$APIM" \
--product-id "$PRODUCT_ID" \
--product-name 'Orders Lab Product' \
--description 'Isolated API Management verification product' \
--subscription-required true \
--approval-required true \
--state published
az apim product api add \
--resource-group "$RG" --service-name "$APIM" \
--product-id "$PRODUCT_ID" --api-id orders-blue
az apim product api add \
--resource-group "$RG" --service-name "$APIM" \
--product-id "$PRODUCT_ID" --api-id orders-green这里的字段不是显示信息。--path 决定消费者看到的 gateway 路径,修改它会破坏现有调用地址;--service-url 决定 gateway 实际连接的 backend,填错会把管理面成功变成运行时 5xx。--protocols https 约束客户端到 gateway 的协议,不代表 gateway 到 backend 的证书、DNS 和网络已经正确。API 与 Product 的 --subscription-required true 共同建立 Key 门槛,--approval-required true 只控制新订阅审批;已经 active 的 Subscription 不会因此自动失效。--state published 控制 developer portal 中的发现和订阅,不是停流开关。
创建成功只证明 management plane 接受了对象。随后至少查询 API、operation、Product 关联,并创建一个只作用于该 Product 的实验 Subscription,再从 gateway 发起无 Key、错误 Key、合法 Product Key 三组调用。下面在内存中生成 primary key,并用权限收紧的临时文件提交;az rest 禁止回显响应,文件提交后立即删除。团队流水线应把这一步接到 Key Vault 或等价 secret store,不要把 primary/secondary key 写入终端录屏、构建日志或工单。
az apim api list --resource-group "$RG" --service-name "$APIM" --output table
az apim api operation list \
--resource-group "$RG" --service-name "$APIM" \
--api-id orders-blue --output table
az apim product api list \
--resource-group "$RG" --service-name "$APIM" \
--product-id "$PRODUCT_ID" --output table
SUB_ID="$(az account show --query id --output tsv)"
APIM_BASE="https://management.azure.com/subscriptions/$SUB_ID/resourceGroups/$RG/providers/Microsoft.ApiManagement/service/$APIM"
umask 077
TEST_KEY="$(openssl rand -hex 32)"
jq -n \
--arg displayName 'Orders lab caller' \
--arg scope "/products/$PRODUCT_ID" \
--arg primaryKey "$TEST_KEY" \
'{properties:{displayName:$displayName,scope:$scope,state:"active",primaryKey:$primaryKey}}' \
> subscription-body.json
az rest --method put \
--url "$APIM_BASE/subscriptions/$TEST_SUBSCRIPTION_ID?api-version=$ARM_API_VERSION" \
--headers 'Content-Type=application/json' \
--body @subscription-body.json --output none
rm -f subscription-body.json
GATEWAY_HOST="$(az apim show --resource-group "$RG" --name "$APIM" \
--query gatewayUrl --output tsv)"
curl -i "$GATEWAY_HOST/orders-blue/probe"
curl -i -H 'Ocp-Apim-Subscription-Key: <wrong-key>' \
"$GATEWAY_HOST/orders-blue/probe"
curl -i -H "Ocp-Apim-Subscription-Key: $TEST_KEY" \
"$GATEWAY_HOST/orders-blue/probe"
curl -i -H "Ocp-Apim-Subscription-Key: $TEST_KEY" \
"$GATEWAY_HOST/orders-green/probe"
az apim product update \
--resource-group "$RG" --service-name "$APIM" \
--product-id "$PRODUCT_ID" --state notPublished
curl -i -H "Ocp-Apim-Subscription-Key: $TEST_KEY" \
"$GATEWAY_HOST/orders-blue/probe"
az apim product update \
--resource-group "$RG" --service-name "$APIM" \
--product-id "$PRODUCT_ID" --state published正向证据必须同时显示 gateway 已命中不同 API、backend 响应分别为 blue/green、Subscription scope 正确、请求可在 gateway 与 backend 日志中用关联 ID 对上。反向证据应显示无 Key 和错误 Key 被 gateway 拒绝且 backend 没收到请求;Product 改为 notPublished 后,合法 Key 仍能调用则证明下架只改变 portal 可见性,恢复 published 后再继续后续实验。若 Product 已关联但合法 Key 仍失败,依次查 Subscription 状态与 scope、API 的 subscriptionRequired、header 名、传播状态和调用 host;不要反复生成 Key 掩盖根因。
Policy 的正向继承与反向缺口
一个可治理的策略先回答四个问题:在哪个 scope 执行;正常请求按什么顺序进入 inbound -> backend -> outbound;哪一步失败后转到 on-error;父 scope 是否通过 <base/> 继承。下面的 API policy 为每个请求使用 APIM 自己生成的 context.RequestId 建立可信关联 ID,并把它传给 backend 和响应。客户端提供的关联值可以另存为低信任业务字段,但不能覆盖这个定位键;策略也不记录 Key、JWT 或 body。
<policies>
<inbound>
<base />
<set-header name="x-correlation-id" exists-action="override">
<value>@(context.RequestId.ToString())</value>
</set-header>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<set-header name="x-apim-request-id" exists-action="override">
<value>@(context.RequestId.ToString())</value>
</set-header>
</outbound>
<on-error>
<base />
<set-header name="x-apim-request-id" exists-action="override">
<value>@(context.RequestId.ToString())</value>
</set-header>
</on-error>
</policies>Policy 应由 IaC 或 management REST API 发布,并在部署时固定一个目标 subscription 支持的 management API version。该日期形态值是 Azure Resource Manager 的 API 版本标识,不是文章日期;每次升级前从目标 cloud 的 Microsoft.ApiManagement provider metadata 与对应 REST 参考确认支持值,不要把公共云的值盲目用于其他 cloud。
jq -Rs '{properties:{format:"rawxml",value:.}}' policy.xml > policy-body.json
az rest --method put \
--url "$APIM_BASE/apis/orders-blue/policies/policy?api-version=$ARM_API_VERSION" \
--headers 'Content-Type=application/json' \
--body @policy-body.json
az rest --method get \
--url "$APIM_BASE/apis/orders-blue/policies/policy?format=rawxml&api-version=$ARM_API_VERSION"
rm -f policy-body.json正向请求应在 backend 和响应中看到同一关联 ID,并保留父 scope 的身份、限流和日志行为。稳定暴露继承错误的反向实验不是在 current production API 上直接删 <base/>,而是在新 revision 的专用 operation 上暂时移除它:若父级添加的 header、认证或日志消失,就证明继承缺口;随后立即恢复原 XML。父级 policy 若负责强制认证,反向实验还必须有网络隔离,避免测试 URL 变成绕过入口。
Revision 让配置可测试,但不会自动隐藏测试入口
先记录 current revision,再创建 revision 2,只把该 revision 的 backend 改为 green,在显式 ;rev=2 URL 上与默认 blue 路径做正反对照,最后通过 release 把它设为 current。创建 release 会改变默认路径指向,操作前必须记录旧 current revision 和恢复命令。
az apim api revision list \
--resource-group "$RG" --service-name "$APIM" \
--api-id orders-blue --output table
OLD_REVISION="$(az apim api revision list \
--resource-group "$RG" --service-name "$APIM" \
--api-id orders-blue --query '[?isCurrent].apiRevision | [0]' --output tsv)"
az apim api revision create \
--resource-group "$RG" --service-name "$APIM" \
--api-id orders-blue \
--api-revision 2 \
--api-revision-description 'candidate policy and backend'
az apim api update \
--resource-group "$RG" --service-name "$APIM" \
--api-id 'orders-blue;rev=2' \
--service-url "$GREEN_BACKEND"
curl -i -H "Ocp-Apim-Subscription-Key: $TEST_KEY" \
"$GATEWAY_HOST/orders-blue;rev=2/probe"
curl -i -H "Ocp-Apim-Subscription-Key: $TEST_KEY" \
"$GATEWAY_HOST/orders-blue/probe"
az apim api release create \
--resource-group "$RG" --service-name "$APIM" \
--api-id orders-blue \
--api-revision 2 \
--notes 'candidate promoted after paired gateway checks'
curl -i -H "Ocp-Apim-Subscription-Key: $TEST_KEY" \
"$GATEWAY_HOST/orders-blue/probe"发布前,显式 revision URL 应返回 green,而默认 URL 仍返回 blue;发布后默认 URL 应改为 green。三次响应都要和 gateway/backend 关联日志对齐,才能排除客户端缓存或误调 host。失败时不要修改旧 revision;用下面的 release 恢复记录下来的 current revision,再重跑无 Key、合法 Key、blue/green 和 on-error 请求。Revision 的显式 URL 若能从公网访问,必须在测试期间用 JWT、IP 或网络 policy 收窄,不能依赖“不是 current”来保密。
az apim api release create \
--resource-group "$RG" --service-name "$APIM" \
--api-id orders-blue \
--api-revision "$OLD_REVISION" \
--notes 'rollback after candidate verification failed'
curl -i -H "Ocp-Apim-Subscription-Key: $TEST_KEY" \
"$GATEWAY_HOST/orders-blue/probe"Self-hosted gateway 是远端数据面,不是独立平台
Self-hosted gateway 是可运行在 Docker、Kubernetes 或其他容器环境的 Linux gateway runtime。它仍与云端 API Management instance 的 Gateway resource 关联,从 management plane 获取配置并上报状态或遥测;它没有独立替代 Azure API Management 的完整管理能力。单机 Docker 更适合评估和开发,生产通常要由团队负责 Kubernetes 高可用、扩缩、证书、持久卷、升级和基础设施安全,具体要求见 self-hosted gateway 概览 与 Kubernetes 生产指导。
它需要到 Azure 配置和可选遥测 endpoint 的 443 出站连接,并且每个 self-hosted gateway 只关联一个 API Management instance。白名单应使用当前 FQDN、service tag 和实际功能依赖,不能固化底层 public IP。与 Azure 失联时,正在运行的实例可以继续使用内存中的最近配置,也就是 fail static;它收不到新配置,状态和遥测可能中断。未启用 configuration backup 时,实例停止后可能无法离线重启;启用 backup 后,PVC 上的最近配置才提供冷启动候选。Developer tier 的 self-hosted gateway 规模和支持承诺不适合作为生产 HA 结论,生产部署还要确认目标 tier、支持策略、镜像支持窗口和最少副本。
生产 Deployment 应固定明确镜像版本或 digest,避免 rolling tag 让扩容后的 Pod 混跑不同版本。至少执行两组断网实验:运行实例断开 Azure 后,旧配置流量是否继续、新配置是否停止传播、遥测是否缺口;停止实例后,在无 backup 与有 backup 条件下能否冷启动。每组都要预先验证 DNS/443 恢复路径、readiness、PVC、Secret 轮换和后端连通。
kubectl -n '<gateway-namespace>' get deploy,pod,pvc
kubectl -n '<gateway-namespace>' get deploy '<gateway-deployment>' \
-o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
kubectl -n '<gateway-namespace>' rollout status deploy/'<gateway-deployment>'
kubectl -n '<gateway-namespace>' logs deploy/'<gateway-deployment>' --since=10m输出中的 Gateway token、host、内部地址与业务请求都按高敏资产处理。只有“旧流量可转发、新配置不可达、遥测中断”三项同时被记录,才能准确描述 fail static;单独看到 HTTP 200 不能证明 self-hosted gateway 完整健康。
网络能力要按流量方向组合
API Management 的网络选项不是统一的“进 VNet”。不同套餐与代际提供 classic external/internal VNet injection、v2 outbound VNet integration、Premium v2 injection、inbound private endpoint 等能力;支持矩阵、区域和创建条件应从当前 网络概念 与 功能比较 读取目标 instance 的实际值。
Inbound private endpoint 当前只为 service 的 managed gateway 建立 Private Link 入口,不会把 management plane、developer portal、workspace gateway 或 self-hosted gateway 一并私有化。只有 private endpoint 已配置后才能关闭 service 的 public network access;关闭后必须从公网保留一条失败请求,证明拒绝发生在 APIM 公网入口而不是测试机 DNS 故障。classic tier 已做 internal/external VNet injection 时,private endpoint 还存在组合限制。网络方案因此必须列出每个 endpoint 的 DNS 名称、解析视图、证书、调用主体和允许方向,不能只在架构图上写“APIM 已进私网”。
| 目标方向 | 候选机制 | 仍需单独验证 |
|---|---|---|
| client -> managed gateway 私有入口 | inbound private endpoint 或适用的 internal VNet 形态 | public network access、private DNS、证书、自定义域名和公网拒绝 |
| managed gateway -> private backend | outbound VNet integration、VNet injection 或适用网络形态 | backend DNS、NSG、UDR、TLS、健康检查与返回路径 |
| operator -> management plane | 管理 endpoint 的网络和 RBAC | Azure portal/CLI 可达、DNS、NSG、控制操作审计 |
| consumer -> developer portal | portal endpoint 的网络和身份 | 文档、订阅、自助 Key 是否按预期暴露 |
| self-hosted gateway -> Azure | 443 出站到配置与遥测依赖 | FQDN/service tag、代理、证书、backup 与失联行为 |
Classic internal VNet mode 可能把 gateway、developer portal、direct management 等 endpoint 都放进受控网络。DNS、NSG、UDR、依赖服务或证书错误会表现为“instance 已部署但管理面/门户不可达”。Outbound integration 只解决 gateway 到 backend,不会自动私有化 inbound;inbound private endpoint 也不会自动解决 backend 出站。最终矩阵要从公网测试机、VNet 内测试机和 backend subnet 分别验证 DNS、TCP/TLS 和真实请求。
私网正反实验要保存四份结果:VNet 内默认 gateway hostname 解析到 private endpoint 地址并成功调用;公网测试机在 public network access 关闭后收到 APIM 明确拒绝;management endpoint 与 developer portal 按各自设计保持可达或不可达;backend 日志只看到来自预期数据面的请求。只看到内网 HTTP 200 不能证明公网已关闭,也不能证明管理面没有被意外暴露。若回滚 private endpoint,先恢复 public network access 或备用入口,再撤销 DNS link 和 endpoint;顺序相反会把故障误判成证书或服务宕机。
nslookup '<apim-name>.azure-api.net'
nslookup '<apim-name>.management.azure-api.net'
curl -v "https://<gateway-host>/status-0123456789abcdef"
az network private-endpoint list --resource-group "$RG" --output tablecurl -k 只能临时收集证书诊断,不能作为通过标准。split-horizon DNS、private DNS link、NSG/UDR 和 public network access 的每次变更都要保存旧值和恢复动作。
故障要先判断卡在管理传播、gateway、policy 还是 backend
| 现象 | 第一证据 | 继续分叉 |
|---|---|---|
| Azure portal/CLI 保存成功,运行时仍旧行为 | API/revision/policy 快照、gateway request ID、传播状态 | 调错 revision、配置尚未传播、命中另一 gateway 或 self-hosted gateway 离线 |
| gateway 拒绝且 backend 无请求 | Subscription scope/state、JWT/证书、Product/API policy、request ID | Key 无效、API-scoped Key 绕过 Product policy、<base/> 顺序或网络入口限制 |
| gateway 5xx,backend 无日志 | policy trace、LastError、DNS/TLS、VNet/NSG/UDR | inbound/backend policy 提前失败、backend 解析/连接/证书/超时 |
| backend 返回错误 | backend status、关联 ID、outbound policy | 业务错误被 outbound policy 改写,或重试造成重复副作用 |
| self-hosted gateway 仍转发但看不到新配置 | image digest、配置更新时间、Azure 443、Pod/PVC 与遥测 | fail static、认证过期、代理/DNS 问题或 backup 不可用 |
| Product unpublished 但仍可调用 | Subscription scope、API 状态、gateway 正反请求 | 这是正常对象语义;需撤销 Key、收紧身份/网络或停用 API |
Trace、Subscription Key、JWT、证书、policy XML、配置导出和 developer portal 数据都可能包含敏感资产。诊断请求只使用合成数据和短期凭证,按请求开启受控 trace,输出进入受限存储并设置删除期限。日志至少保留 request ID、API/operation、revision、gateway location、Subscription 标识的非秘密部分、policy error section、backend status 和关联 ID;不能记录完整 Key 或 Authorization header。
权限、容量与成本要跟着部署形态走
管理角色只授予维护目标 instance、API、Product、Subscription、Policy、Revision 和 Gateway resource 所需动作,避免把 Owner、Subscription Key 与 backend 管理凭证交给同一个永久主体。运行时身份分成 consumer -> gateway 与 gateway -> backend 两段:前段使用 JWT、证书或受控 Subscription,后段优先托管身份、证书或独立 secret;轮换任一段都要验证缓存、传播和失败语义。
容量不能只看 instance units。还要记录 API 请求率和尾延迟、policy CPU 代价、缓存命中、backend 连接与超时、日志/Trace 量、区域和可用区、workspace gateway units、self-hosted Pod 资源与副本、配置失联窗口。API、operation、revision、Product、Subscription、named value 和 workspace 等管理对象也有按 service、workspace 或 tier 计算的上限,revision 还可能被计入 API 相关资源总量。接近限制时应先删除无 owner 的旧 revision 与实验对象,再评估拆分 service 或升级 tier;不能等发布 API 失败后才发现控制面容量耗尽。限流与 quota 是保护手段,不是严格账单上限,也不能替代 DDoS、WAF、身份和业务幂等。
套餐价格、请求价、SLA、区域、单位、workspace、self-hosted gateway、private endpoint、VNet 和 policy 支持都会变化。估算时从 API Management 价格页、价格计算器 和 服务限制 读取目标 tenant/offer/Region 的实际条件,并分别列出 APIM、出网、private endpoint、Public IP、Log Analytics/Application Insights、Key Vault、DNS、Kubernetes 计算与存储。预算告警、标签、owner 和到期时间应在创建前设置。
回滚与清理必须撤销消费者能力
API 或 policy 变更优先在新 revision 验证,旧 current revision 保留到回滚窗口结束。回滚时创建 release 恢复旧 revision,随后重跑无 Key、合法 Key、两个 backend、on-error 和网络内外请求。Self-hosted gateway 升级则固定新旧 digest,用滚动发布观察副本、配置版本和真实流量;失败时回退 Deployment image,并确认旧镜像仍受支持且配置兼容。
实验结束先撤销测试 Subscription 或 regenerate 两把 Key,再解除 Product API 关联、删除 Product、API revisions 和 API。命令中的 --delete-revisions 会删除全部 revision,执行前必须确认目标 API ID 只属于隔离实验。
az rest --method delete \
--url "$APIM_BASE/subscriptions/$TEST_SUBSCRIPTION_ID?api-version=$ARM_API_VERSION" \
--output none
unset TEST_KEY
az apim product api delete \
--resource-group "$RG" --service-name "$APIM" \
--product-id "$PRODUCT_ID" --api-id orders-blue
az apim product api delete \
--resource-group "$RG" --service-name "$APIM" \
--product-id "$PRODUCT_ID" --api-id orders-green
az apim product delete \
--resource-group "$RG" --service-name "$APIM" \
--product-id "$PRODUCT_ID" \
--delete-subscriptions true --yes
az apim api delete \
--resource-group "$RG" --service-name "$APIM" \
--api-id orders-blue --delete-revisions true --yes
az apim api delete \
--resource-group "$RG" --service-name "$APIM" \
--api-id orders-green --delete-revisions true --yes
az apim api list --resource-group "$RG" --service-name "$APIM" --output table
az apim product list --resource-group "$RG" --service-name "$APIM" --output table如果实验还创建了 self-hosted Gateway resource、Kubernetes Deployment/Secret/PVC、private endpoint、private DNS link、VNet/NSG/Public IP、Log Analytics/Application Insights、Key Vault secret 或 backend,它们需要按依赖关系单独删除。最终退出以三类证据收口:gateway 与 developer portal 不再暴露实验对象;资源组和集群盘点为空或每个保留项都有 owner;Cost Management 中相关标签不再持续增长。迁出 API Management 时还要导出非敏感契约与 policy,建立新旧 gateway 双跑和 request ID 对照,验证身份、订阅、限流、转换、错误映射、revision 回滚和 DNS 切换,再撤销旧 Key 与域名。
上线前最后过一遍真实证据
Azure portal、developer portal、management plane 和各类 gateway 的用户、网络与数据已经分开建模。Product 发布状态、Subscription scope、Policy 继承和 Revision current 状态都有正反请求,不用“门户不可见”证明 runtime 不可达。<base/>、inbound/backend/outbound/on-error 的执行结果能在 request ID、policy error 和 backend 日志中关联。
Managed、workspace 与 self-hosted gateway 的能力按目标套餐复核;self-hosted 的固定 digest、失联、backup 冷启动和滚动回退已经演练。Inbound、outbound、management、developer portal 与 Azure 配置出站分别验证 DNS、TLS、NSG/UDR 和公网拒绝。Subscription Key 不承担终端用户身份,主备轮换、JWT/证书过期、gateway 到 backend 身份与撤权时延都有负责人。
容量模型包含 policy、backend、遥测和自托管资源;价格、限制、区域和功能不写成静态承诺。Revision 回滚后重跑过拒绝与成功链路;Key、API、Product、网络、日志、PVC 和费用均完成核销。
