Gravitee API Management:从 API 部署到订阅与分析闭环
一次 API 变更中,生产者在 Console 里保存了新的限流策略,又点击了 Publish,Developer Portal 随即出现了新版说明。测试人员却发现真实请求仍按旧额度通过。团队起初把问题归咎于 Gateway 缓存,后来才发现 Publish 改变的是 Portal 可见性,策略保存后还没有重新部署到 Gateway。三个绿色状态描述的是三个对象,不能合并成“发布成功”。
Gravitee APIM 的价值也恰恰在这些状态之间:Gateway 处理流量,Management API 保存和发布管理对象,APIM Console 面向 API 生产者,Developer Portal 面向消费者。真正可运营的链路要同时证明 API definition 已被目标 Gateway 加载、Plan 可以订阅、凭据按预期生效、分析数据可关联,并且控制面或存储故障时仍知道哪些动作会停、哪些存量流量还能继续。
四个组件承担四种责任
客户端只应把业务请求交给 Gateway。Gateway 根据 entrypoint 匹配 API,选择适用 Plan,执行 flow 中的 Policy,调用 endpoint,并在响应或消息阶段继续执行策略。Console 与 Management API 属于控制面,不应进入每次业务请求的同步依赖链;Portal 负责消费侧发现和订阅,也不是业务流量代理。
生产者通过 Console 或自动化调用 Management API,改变 API definition、Plan、Subscription 和 Portal 内容。Console 最终依赖 Management API,但这不意味着每个 UI 动作都能与某个固定 API 版本一一对应。目标实例可能同时暴露 Management API V1、Management API V2 与 Portal API;“Management API V2”和“业务 API definition v2”是两套版本维度。最可靠的接口契约是目标实例自己暴露的 OpenAPI 规格。
Core Concepts 给出了这些组件的职责;目标发行线的 Management API Reference 则用于确认实际规格入口。自动化脚本应写全 organization、environment 和 API 版本,不能依赖 Console 当前选中的上下文。
API definition 是 Gateway 运行所需代理、策略和 Plan 信息的 JSON 表示。v2 definition 面向 HTTP 请求/响应代理;v4 definition 把 entrypoint 与 endpoint 解耦,并扩展到消息和协议中介场景。v4 的能力模型不等于每一种协议、entrypoint、endpoint 和 Policy 组合都在当前版本与许可证中可用,接入前还要查询目标安装的插件清单。
在隔离项目中启动完整链路
第一次实验宜使用官方 self-hosted Docker quick start 的目标发行线,把它限定在个人工作站或专用测试主机。该拓扑用于学习组件关系,不是生产架构。操作前需要容器运行权限、足够磁盘、只绑定回环地址的管理端口,以及一组只用于本实验的虚构身份。先从 self-hosted installation guides 取得与目标版本匹配的 Compose bundle,核对仓库签名或发布校验值,再放入空目录。
export COMPOSE_PROJECT_NAME=gravitee40lab
mkdir -p gravitee40lab/evidence
cd gravitee40lab
# 将官方目标发行线的 compose.yaml 与环境文件放入此目录后再执行。
docker compose config -q
docker compose config --no-interpolate > evidence/compose-template.yaml
docker compose config --images | tee evidence/images.txt
docker compose pull
docker compose up -d
docker compose ps官方 quick start、Helm、RPM 与 ZIP 的 service 名、镜像 tag、端口和配置路径并不相同。docker compose config -q 先验证 Compose 模型,--no-interpolate 保存不展开变量的结构证据,避免把本机注入的密码或 Token 写进 evidence;实际展开值只能在受保护终端检查,并在脱敏后留档。镜像启动后还应记录 digest,而不是只保存浮动 tag。
docker compose ps --format json > evidence/compose-ps.json
docker compose images > evidence/compose-images.txt
docker inspect --format '{{.Name}} {{.Config.Image}} {{.RepoDigests}}' \
$(docker compose ps -q) > evidence/image-digests.txt不要沿用 quick start 的默认管理员凭据。首次登录后立即创建实验管理员和只读审计身份,替换默认口令,并确认 Console、Management API、Portal 只从 127.0.0.1 或受控反向代理访问。管理端口若暴露到共享网络,实验本身就已经突破安全边界。
平台管理权限不能只分成“管理员”和“普通用户”。Gravitee 的权限分别落在 Organization、Environment、API 与 Application scope;API 的 definition、gateway definition、Plan、Subscription、Analytics、Log 和 Audit 也是不同权限。流水线身份只应获得目标 Environment 中导入、更新、部署所需动作,订阅审批身份不应同时修改 Policy,分析人员也不应因为能查 Dashboard 就获得 endpoint、API Key 或敏感日志读取权。Identity Provider 的 group/role mapping 负责把外部身份映射进这些 scope,但映射条件本身也属于高风险配置,错误 claim 不能回退成 FULL_ADMIN。
权限配置完成后,用三个独立身份做反证:发布身份能够更新并部署 orders-lab,但不能管理 Organization 角色;订阅审批身份能够处理该 API 的 Subscription,但不能修改 definition;审计身份能够读取允许的 Audit/Analytics,却不能读取凭据或执行部署。每次拒绝都要保存 Management API 的状态码、目标资源和服务端审计事件,不能把 Console 中“按钮不可见”当成授权证据。人员退出或流水线退役后撤销 group、role 与 Token,再用旧身份重复部署请求并确认持续失败。
先证明实际生效的配置
Gravitee 的配置优先级是环境变量高于 JVM system property,高于 GRAVITEE_HOME/config/gravitee.yml。因此修改 YAML 后重启成功,并不能证明进程采用了新值:Compose、Helm 或进程管理器中残留的覆盖项可能继续生效。数组、大小写和环境变量映射还有专门规则,不能凭字段名猜测转换结果。
# 只保存变量名,不把值写入 evidence;输出中仍要人工检查自定义变量名是否泄密。
docker compose exec <service> sh -lc \
'env | sed -n "s/^\(GRAVITEE_[^=]*\)=.*/\1=<redacted>/p" | sort' \
> evidence/gravitee-env-names.txt
# 将 <config-path> 替换为目标镜像内真实配置路径;文件内容脱敏后才能留档。
docker compose exec <service> sh -lc \
'test -r <config-path> && sha256sum <config-path>' \
> evidence/config-digest.txt配置审查要沿影响面进行。HTTP 监听、context path、TLS 与反向代理设置决定入口是否暴露和 URL 是否变化;Management repository 决定 API、Application、Plan、Subscription 与 Key 的持久化和恢复;Analytics repository 与 reporter 决定 Dashboard 数据路径、积压和保留成本;Rate Limit repository 决定多节点计数能否共享;Distributed Sync 设置决定哪个 Gateway 读取管理库、其他节点从哪里取得已部署 definition。任何一组发生变化,都要同时记录旧值摘要、新值摘要、重启范围和对应的正反实验,不能只保留一张 YAML diff。
两个上游要返回可区分的正文,才能识别错路由。可在同一 Compose project 中增加这个 override;服务名称和网络名必须用 docker compose config 与实际 bundle 对齐。
# compose.override.yaml
services:
orders:
image: hashicorp/http-echo:1.0
command: ["-listen=:8080", "-text={\"service\":\"orders\",\"revision\":\"blue\"}"]
profile:
image: hashicorp/http-echo:1.0
command: ["-listen=:8080", "-text={\"service\":\"profile\",\"revision\":\"green\"}"]再次执行 docker compose up -d 后,先从 Gateway 容器所在网络直接访问两个上游,保存不经过网关的基线。不要在还没证明上游健康时创建 API,否则 502、503 和路由错误会缠在一起。
docker compose exec <gateway-service> sh -lc \
'wget -qO- http://orders:8080/ && wget -qO- http://profile:8080/'预期能分别看到 orders/blue 与 profile/green。若失败,第一证据是容器 DNS、网络成员和上游进程日志,而不是 Gravitee Policy。
先探测目标实例,再创建 API
接口探测必须指向目标 Compose 实际暴露的 TLS 或回环入口,并用短时环境变量注入凭据;Basic Auth、Token 或 Cookie 不得进入仓库、shell history 或 evidence。
export MGMT_BASE='https://127.0.0.1:<management-port>'
export GW_BASE='https://127.0.0.1:<gateway-port>'
curl -fsS "$MGMT_BASE/management/openapi.yaml" -o evidence/management-v1.yaml
curl -fsS "$MGMT_BASE/management/v2/openapi-apis.yaml" -o evidence/management-v2-apis.yaml
curl -fsS "$MGMT_BASE/management/v2/openapi-plugins.yaml" -o evidence/management-v2-plugins.yaml
curl -fsS "$MGMT_BASE/portal/openapi" -o evidence/portal-api.yaml若某个路径返回 401,说明入口存在但需要授权;404 则可能是 context path、发行线或组件布局不同。此时应回到 rendered Compose、Management API 日志和目标版本文档,不能从另一发行线复制 endpoint。取得规格后再据其生成客户端或用 curl 调用,明确超时、重试、crossId、organization/environment 与最小权限 principal。
在 Console 或 Management API 中按以下顺序创建 orders-lab:
建立私有 v4 Proxy API,context path 使用仅限实验的 /lab/orders,endpoint 指向 http://orders:8080。创建 API Key Plan,审批模式先选人工审批,限额使用很小的演示值;该值只服务实验,不是生产基线。发布 Plan,部署并启动 API,但暂不发布到 Developer Portal。
建立应用 consumer-a,提交订阅并审批,取得一次性显示的实验 API Key。分别保存 API、Plan、Subscription、deployment 与 Gateway 节点证据,再调用真实入口。
Plan 的安全类型创建后不能随意改成另一类型。Plan 会经历 staging、published、deprecated、closed 等状态;关闭会连带关闭订阅且不可逆,因此演练迁移时应创建同安全类型的新 Plan,再转移活跃订阅,而不是把 Close 当普通禁用按钮。具体行为应与目标发行线的 Plans 文档和实例 API 共同核对。
正向实验:证明部署、订阅与策略都生效
先调用不带凭据的请求,再调用正确凭据。不要只保存 HTTP 状态码,还要保存响应体、Gateway 生成或接受的 request ID、命中 API/Plan、Subscription、deployment 版本和上游标识。
curl -skD evidence/no-key.headers \
"$GW_BASE/lab/orders" -o evidence/no-key.body
curl -skD evidence/valid-key.headers \
-H "X-Gravitee-Api-Key: $LAB_API_KEY" \
-H 'X-Lab-Request: gravitee-positive-1' \
"$GW_BASE/lab/orders" -o evidence/valid-key.body预期无 Key 请求被 Gateway 拒绝,正确 Key 请求返回 orders/blue。如果两次都到达上游,优先核对 Plan 是否发布、API 是否 redeploy、请求是否命中预期 context path;如果正确 Key 仍被拒绝,则沿 Application、Subscription 状态、Key、Plan 选择和节点同步逐层检查。
接着给 request flow 增加一个可观察但无敏感数据的响应 Header Policy,例如写入 X-Policy-Revision: r2。先只保存,不 redeploy,再请求一次:预期仍看不到 r2。随后执行 redeploy,轮询每个 Gateway 节点并重复请求:只有所有目标节点均返回 r2,新 deployment 才算传播完成。
保存 Policy -> Management repository 出现新期望配置
未 redeploy 的请求 -> 仍执行旧 definition
redeploy -> 生成新的可运行部署
节点完成同步 -> 真实请求出现 X-Policy-Revision: r2
Portal publish -> 只改变消费者可见性,不改变上述请求结果最后把 API 与页面发布到 Portal,确认消费者能发现文档和 Plan。此时再取消 Portal 发布:Portal 应不再展示该产品,但已部署 Gateway 路由与现有订阅是否继续工作,要按目标状态单独验证。这个对照实验能稳定拆开“可见、可订阅、可调用”三种状态。
反向实验:错误凭据不能掉进 Keyless
为同一实验 API 增加 Keyless Plan 看似方便,却容易产生错误直觉:携带无效 Token 或 API Key 的请求不应被理解为“认证失败后自动按匿名访问”。Plan 解析顺序和失败语义是版本化行为,必须在目标 API 类型上实测。
curl -skD evidence/bad-key.headers \
-H 'X-Gravitee-Api-Key: definitely-invalid' \
-H 'X-Lab-Request: gravitee-negative-key' \
"$GW_BASE/lab/orders" -o evidence/bad-key.body预期错误凭据被拒绝且上游没有对应请求。若请求仍成功,先确认它究竟命中 Keyless Plan、错误 context path、旧 deployment,还是某个条件 flow 绕过了身份策略。需要把 Gateway 访问证据和上游日志按 request ID 对齐;仅凭一个 401 或 200 不能判断策略链在哪一步分支。
第二个反例把 endpoint 暂时改成隔离网络内不存在的 orders-missing:8080,redeploy 后调用正确 Key。预期身份识别成功但上游连接失败,证据应表现为已命中 API/Plan、无健康 endpoint 或连接错误、上游无请求。恢复 endpoint 并再次 redeploy,确认流量回到 orders/blue,这才完成故障注入后的回滚。
Policy 的阶段、顺序和许可都是运行边界
flow 决定 Policy 在什么阶段、什么条件下执行。v4 Proxy 常见 request/response 阶段;消息 API 还有 publish/subscribe,Kafka Native 场景还有 connect/interact 等阶段。把它们统称为一条 HTTP filter chain,会掩盖 body 是否可用、错误由谁生成以及重试在哪一侧发生。
Policy 顺序也属于安全设计。认证之后若动态路由、Header 或路径变换改变了授权输入,需要重新证明最终路由仍在授权范围内。HTTP Callout、动态路由、Groovy、自定义 Policy、缓存、重试和内容改写还会引入 SSRF、代码执行、数据泄露、body 缓冲、延迟放大或非幂等重放风险。每次只改变一个 Policy,分别保存正确输入、错误输入、Gateway 证据与上游证据。
Policy Studio 的保存不等于数据面生效,调试按钮也不能成为部署前提。v4 Policy Studio 与 Policy Reference 应按目标发行线核对阶段支持和插件要求。部分 Policy、Debug Mode 与扩展能力受 Enterprise 许可证约束;未带企业标记也不等于支持所有 API 类型和阶段,最终还要查目标安装的插件 endpoint 与许可证凭证。
四类 repository 不能互相代替
Gravitee 把状态按 scope 分开。Management repository 保存 API、用户、应用、Plan 与 API Key 等管理配置;Analytics repository 接收 Gateway 报表、指标和健康数据;Rate Limit repository 支撑多 Gateway 共享计数;Distributed Sync repository 保存集群同步状态。它们有不同一致性、容量、备份与恢复目标。
| 状态面 | 关键问题 | 故障时先验证什么 |
|---|---|---|
| Management | 新定义、订阅与凭据写到哪里 | 已部署流量是否继续;新变更是否停止传播;新节点能否启动 |
| Analytics | runtime data、查询与保留 | Gateway 是否仍代理;reporter 是否积压;恢复后是否追平或重复 |
| Rate Limit | 多节点计数和窗口 | 扩副本后总额度是否改变;分区时 fail-open 还是 fail-close |
| Distributed Sync | 哪个节点取定义、其他节点从哪里同步 | primary 退出后的接管;Redis 故障后的存量与重启行为 |
Repositories 给出目标发行线的兼容入口。MongoDB、JDBC、Redis 与 Elasticsearch/OpenSearch 不能任意换位;尤其不能让数据库品牌名代替 scope。JDBC 作为 rate-limit repository 还存在并发线程不共享计数而导致限额不准的官方风险,不应未经压测就成为生产默认。
启用 Redis distributed sync 后,primary Gateway 从 management repository 取得 definition 并写入 Redis,其他节点从 Redis 读取。这项能力要求 Enterprise License,目标 Redis 还必须启用 Search module;缺少任一前提时,不能把普通 Redis 连通性当成集群同步已经可用。它能改变管理库负载和某些启动故障行为,却不保证控制面断网时所有写操作继续,也不保证撤销凭据零延迟传播。需要测量 primary 退出、Redis 主从切换、单节点重启、全节点重启和断网期间的旧凭据窗口,并保存 primary 接管、最后成功同步时间、对象 deploy/undeploy 事件与真实请求结果。
容量规划要分别估算 management 对象增长、rate-limit counter 写放大、distributed-sync 事件与 payload、analytics 索引和 Gateway 本地内存,不能用“Redis 已高可用”概括四种状态面。尤其在 management repository 不可达时,新 Gateway 能否从 distributed sync 启动,与 Redis 自身故障时存量 Gateway 能否继续服务,是两个不同实验;恢复后还要验证撤销事件没有被旧 payload 覆盖。
Analytics 不是一张 Dashboard
Elasticsearch reporter 是 Gravitee UI 展示 runtime analytics 的常见数据来源,但请求分析、API runtime log、服务进程日志、Prometheus 组件指标与 OpenTelemetry trace 是不同信号面。Dashboard 绿色只说明查询到的聚合满足当前视图,不能证明 reporter 无丢失、日志已脱敏、trace 可关联或告警已经配置。
项目接入时至少固定这些关联字段:受信 request ID、API technical ID、deployment revision、Gateway node、Plan、Subscription/Application 的内部标识、上游标识、网关状态码、上游状态码与策略失败阶段。客户端自带的 request ID 不能直接作为审计可信标识;应另存网关生成的值并显式标记传播关系。
API logs、headers、body、callback URL、context attributes、Application 和 Subscription 都可能含敏感数据。默认只采允许列表字段,查询参数和凭据必须脱敏;verbose tracing 会扩大 trace 体积、网络和存储开销,应在隔离流量、短窗口和明确 owner 下开启。Data Logging Masking、特定 reporter 或 Alert 能力是否可用还受版本和许可影响,不能假定所有部署都有同一兜底策略,也不能把某个调试视图的掩码配置外推成日志、Analytics、trace 与上游 payload 都已经脱敏。
容量预算要把请求速率、策略耗时、body 大小、长连接、reporter 队列、采样率、索引副本、保留周期和查询负载同时放入模型。验收阈值来自目标 SLO 与压测基线,不照抄 sizing 表。analytics backend 故障实验的判据应包括“代理是否继续、队列是否有界、磁盘是否增长、恢复是否追平、重复量多少”,而不是只看 Gateway 进程存活。
把 Gravitee 变更接入项目流水线
Console 适合探索和紧急查看,团队发布应以可审查的 API definition、Portal 内容和自动化调用为准。仓库中保存去密后的源文件、目标 organization/environment、目标发行线、插件依赖、变更说明和回退制品;真实 Secret、API Key、许可证文件与管理 Token 由 Secret 系统短时注入。
一条稳健流水线按以下顺序工作:先从目标实例下载并锁定 OpenAPI 规格,再校验 definition 与引用,导出当前对象作为回退点,计算语义 diff,导入隔离环境,发布 Plan、redeploy API,等待每个目标 Gateway 同步,执行正确凭据和错误凭据测试,最后才改变 Portal 可见性。重试必须区分读取、幂等更新和可能重复创建的动作;crossId 可以参与跨环境映射,但不能替代导入合并规则、并发控制和备份演练。
故障时先确定停止层:规格调用失败属于管理入口;definition 已保存但节点未更新属于部署/同步;身份已识别但上游未到达属于 Policy 或 endpoint;请求成功但 Dashboard 无数据属于 reporter/analytics;Portal 不可见但凭据仍能调用属于发布可见性。分层后再看对应日志,避免把所有 4xx/5xx 都归到 Gateway。
架构选型看托管责任和状态故障域
self-hosted 由团队管理控制面、数据面与 repository;hybrid 通常由 Gravitee 托管控制面、团队运行 Gateway 数据面;managed 形态把更多组件交给服务方。具体 Cloud 代际、同步连接、Bridge 或 CloudGate 组件、Portal 能力、区域和数据驻留会演进,选型时应以目标合同、当前架构文档与网络验证为准,而不是把某张拓扑当永久定义。
选择 self-hosted,换来的是数据库、索引、Redis、证书、备份、扩容和升级的完整责任;选择 hybrid,要重点验证出站连接、失联窗口、配置与日志跨边界、控制面身份以及数据面自治程度;选择 managed,也仍需负责 API、消费者、策略、凭据、数据最小化和退出导出。托管减少组件维护,不会自动消除业务风险与治理责任。
容量较小且只有路由和少量策略时,完整 APIM 平台可能带来超出收益的状态和运维成本;需要消费者订阅、凭据生命周期、Portal、分析与多团队控制面时,平台化才更有价值。选型结论应来自目标策略链压测、存储增长、故障演练、权限模型、许可清单和退出成本,而不是功能数量。
成本模型至少拆成 Gateway 节点与故障余量、Management API/Console/Portal、四类 repository、日志与索引保留、备份恢复、跨区网络、证书与 Secret、企业插件许可和轮值人力。单次请求成本还要按 Policy 链、payload、长连接或消息会话、analytics 采样与索引副本校正。容量扩张前先判断瓶颈落在 Gateway CPU、上游连接、共享 counter、同步事件还是 analytics 写入;盲目增加 Gateway 副本可能同时放大 repository、Redis 和许可证成本。
升级必须同时处理组件、数据和插件
升级前记录 Gateway、Management API、Console、Portal、镜像 digest、Helm chart 或安装包、Java、Management/Analytics/Rate Limit/Sync repository、索引模板、自定义插件和许可证。逐个阅读跨越的每条发行说明与 breaking/deprecation,先完成管理库备份恢复、索引快照恢复和旧制品重建演练。
java -version
docker compose config --images
docker inspect --format '{{.Config.Image}} {{.RepoDigests}}' <container>
# Kubernetes 形态使用目标命名空间,不把命令指向共享默认空间。
kubectl -n <namespace> get deploy,statefulset,pod -o wide
helm -n <namespace> get values <release> -a
helm -n <namespace> get manifest <release> > rendered-before.yaml升级后不能停在容器 Ready:要重新验证 definition import/export、Plan/Subscription、Policy 插件加载、Gateway sync、Portal 登录与订阅、正确/错误凭据、analytics 新写入、Dashboard 查询、日志脱敏和 trace export。回滚也不只是换回旧镜像;数据库 migration、索引模板或 rollover、工作目录、插件 ABI 和许可证变化可能需要快照恢复或前滚修复。没有数据恢复点时,旧镜像通常不是可靠回退方案。
官方主仓库顶层采用 Apache License 2.0,只能说明相应仓库中的 Work,不能外推为平台全部能力免费开源。Gravitee 同时存在 OSS/Community 与 Enterprise 能力,Policy、reporter、审计、SSO、调试、同步和 Portal 扩展都可能受版本、插件包与许可约束。采购评审记录“需要的能力、部署形态、数据边界、插件来源、导出能力和退出动作”;价格、套餐、试用、区域和 SLA 属于采购时重新核对的动态合同事实,不应成为长期架构假设。
回滚与清理要消除每一类残留
策略回滚使用上一份已验证 definition 重新部署,并等待每个 Gateway 节点回到同一 revision;Portal 内容单独恢复;repository migration 则按演练过的快照或前滚方案处理。回滚后重复正确 Key、错误 Key、两个上游和 analytics 写入实验,不能用 Console 的成功提示代替流量证据。
清理顺序从消费关系向基础设施逆向执行:先撤销实验 API Key 与 Subscription,删除 Application;再取消 Portal 发布、deprecated/close 测试 Plan,停止并删除 API;随后删除 reporter 测试数据、短时 Token 和临时证书;最后停止 Compose project 并核对 volume、网络、端口和镜像残留。
# 先通过目标 Management API/Console 完成对象级删除并保存审计证据。
docker compose down --remove-orphans
docker volume ls --filter label=com.docker.compose.project=gravitee40lab
docker network ls --filter label=com.docker.compose.project=gravitee40lab
# 只有确认列表完全属于本实验后,才删除实验 volume。
docker compose down --volumes --remove-orphans不要在共享 MongoDB、Elasticsearch/OpenSearch 或 Redis 上使用全库删除、删索引通配符、FLUSHALL 或 KEYS *。若实验接入了共享测试存储,应使用专用数据库、索引前缀和 Redis 实例,并按事先记录的对象清单精确删除。最后复查管理入口已关闭、默认管理员已失效、Secret 系统中的短时凭据已撤销、日志和 trace 已按保留策略删除,成本侧也没有遗留托管实例、磁盘或公网入口。
当团队能从一次请求追到 API definition、deployment、Gateway node、Plan、Subscription、Policy、endpoint 和 analytics,并能在控制面、repository 或插件升级失败时恢复到已验证状态,Gravitee 才真正从“有 Console 的网关”变成可治理的 API 管理平台。
