聊MCP落地的人挺多,但真正把一堆MCP服务推到生产环境以后,你会发现最头疼的不是协议调通,而是入口怎么收敛、流量怎么分、权限怎么控。Higress网关在这个场景里刚好补上这块短板。它是云原生API网关,底层是Envoy,原生跑在Kubernetes上,又提供Wasm插件扩展机制,用来承载MCP服务的高级路由和安全策略,是目前工程化落地比较顺的一条路。这篇文章就基于Higress网关环境,完整走一遍MCP服务的路由配置与安全体系搭建,适合已经在做MCP服务接入、正想统一纳管入口的团队参考。
1. MCP服务入网关:先搞懂链路里的角色分工
1.1 网关层看到的MCP流量本质是什么
MCP协议在服务端有stdio、SSE(Server-Sent Events)、Streamable HTTP等不同传输方式。部署到远程环境时,真正要过网关的主要是HTTP语义的SSE和Streamable HTTP。SSE方式建立长连接后持续推送事件;Streamable HTTP则更接近普通RESTful接口,支持普通响应,也支持流式响应。
两种方式在Higress眼中都被当作普通HTTP请求处理。这意味着网关可以复用传统API网关的路由、鉴权、限流能力。这个点很关键,因为很多团队一开始以为MCP是什么神秘的新协议,需要在接入层做特殊处理。其实Higress只需要看到“这是一个到路径X的HTTP请求,请求头里带了认证信息,响应可能是流式的”就足够了。
Streamable HTTP在网关层处理起来更顺滑,连接状态少,异常恢复也简单。如果服务端有能力选择,我建议新服务优先用Streamable HTTP;存量SSE服务不必急着改,Higress对SSE的支持也很稳,主要注意第5章会讲的超时和缓冲问题。
1.2 Higress在MCP链路里具体承担什么
简单说就四件事:统一入口、精准分流、统一安全、全链路可观测。
统一入口解决“客户端到底该连谁”的问题。没有网关之前,每个MCP服务都有自己的地址和端口,客户端配置里一大串URL,管理混乱不说,安全策略只能各家各写。有了Higress,所有MCP服务都挂在同一个域名或网关地址后面,客户端只认一个入口,后端的扩缩容、版本升级对调用方完全透明。
精准分流解决“请求到底该发给谁”的问题。MCP服务通常按业务域拆分,比如文件处理、数据库查询、搜索增强。网关根据请求路径、请求头、方法甚至来源IP把流量分发到对应的后端服务。这一点和第3章的高级路由直接相关。
统一安全比较好理解。认证、鉴权、限流、审计全部收敛到网关层做,后端MCP服务不用自己重复实现。因为MCP服务经常会被多个AI应用共享,安全策略在网关层做一次,覆盖所有调用方,不会出现漏配。
可观测性则是上线之后最刚需的能力。Higress内置了Prometheus指标、访问日志、链路追踪等能力。MCP服务调用失败、流量突增、某个客户端异常,都能在网关层第一时间看到,省去了到处抓包的痛苦。
1.3 一个典型生产链路的职责分层
我们拿某跨平台系统的实际方案来看,整个链路一般长这样:
| 层级 | 角色 | 核心职责 |
|---|---|---|
| 客户端层 | AI应用、Agent框架、自动化脚本 | 发起MCP调用,携带认证凭证,消费工具返回结果 |
| 接入层 | Higress网关 | 域名接入、TLS终止、路由匹配、认证鉴权、限流、审计、流量灰度 |
| 服务层 | 文件处理MCP服务、数据库MCP服务等 | 执行具体工具逻辑,返回MCP协议格式的结果 |
| 数据与基础设施层 | 对象存储、关系型数据库、向量库 | 提供底层数据能力,服务端在此完成真实读写 |
分工原则很清晰:网关层只做流量治理,不碰业务逻辑;MCP服务端只做工具执行,不关心调用方是哪来的。这套分层的好处是,任何一个MCP服务出问题,都可以在网关层快速摘除或灰度切换,不用改客户端代码。
提示:我见过不少团队把认证逻辑写在MCP服务端,每个服务都对着同一个用户体系实现一遍。等接了三五个服务以后,光维护这些重复代码就足够让人崩溃。尽早把认证上收到网关,能省掉大量重复劳动。
2. 环境准备:把Higress和MCP服务跑起来
2.1 Higress部署:K8s环境用Helm最省心
在已经具备Kubernetes集群的前提下,Higress的安装我建议直接用Helm走。命令大致是这样的形态:
helm repo add higress https://higress.io/helm-charts helm install higress -n higress-system --create-namespace higress/higress装完之后确认Pod全部Running,并且能看到Higress对应的Gateway资源被创建。Higress的版本迭代不算慢,装完以后第一时间看一眼当前版本支持的CRD版本,避免后面写配置时用错apiVersion。如果你用的K8s发行版自带Ingress Controller,也不冲突,Higress可以独立运行,只需要把外部流量入口的DNS解析到Higress网关的负载均衡器地址即可。
2.2 把MCP服务部署到集群并暴露Service
这次我以两个MCP服务为例,一个负责文件处理,一个负责数据库查询。它们都是标准的HTTP服务,部署形态就是普通Deployment加Service。
apiVersion: apps/v1 kind: Deployment metadata: name: file-mcp-server namespace: mcp-services spec: replicas: 2 selector: matchLabels: app: file-mcp-server template: metadata: labels: app: file-mcp-server spec: containers: - name: server image: registry.internal/mcp/file-server:1.4.3 ports: - containerPort: 8080 env: - name: MCP_TRANSPORT value: streamable-http对应的Service指向这个Deployment的Pod,端口用8080。数据库查询MCP服务同理,只是镜像和Service名字不同。这里有一个细节:MCP服务如果有多副本,务必确认服务是无状态的,或者至少把会话状态放到共享存储里,否则网关做水平扩展时,同一个客户端的连续请求可能落到不同Pod上,导致MCP会话上下文丢失。
2.3 打通网关入口:Gateway与域名
Higress的流量入口通过Gateway资源来定义。我们需要给MCP业务准备一个域名和对应的TLS证书,然后创建Gateway。
apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: higress-gateway namespace: higress-system spec: gatewayClassName: higress listeners: - name: mcp-https port: 443 protocol: HTTPS tls: certificateRefs: - name: mcp-tls-cert hostname: mcp.example.com这个资源创建好以后,网关就有了对外监听的HTTPS入口。接下来所有MCP服务的路由规则都以这个Gateway作为parentRefs来挂载。证书建议用正规CA签发的证书,内部测试可以用自签名证书,但客户端侧要提前导入信任链,否则AI应用调用时会出现证书校验失败,排查起来很绕。
3. 高级路由配置:一个入口,精准分发所有MCP流量
3.1 场景拆解:一套网关代理多个MCP服务
实际生产里,一个AI应用往往要调用多个MCP服务来完成一个复杂任务。比如用户上传一份Excel,AI应用先用文件处理MCP服务解析内容,再用数据库MCP服务查询关联数据,最后可能还要调搜索MCP服务做增强。如果每个MCP服务都暴露独立域名,客户端配置会越来越臃肿。更合理的方式是所有服务共用同一个域名,通过路径区分。
路径规划上,我习惯直接用业务域做前缀,比如/file、/db、/search。这样路由规则一目了然,后端服务可以通过替换前缀的方式拿到原始路径,避免MCP服务内部感知网关注入的前缀。
3.2 路径前缀路由:一套配置管所有工具
在Higress下,HTTPRoute是我们最常用的路由资源。下面这个配置把mcp.example.com/file前缀的请求全部转发到file-mcp-svc服务。
apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: mcp-route-file namespace: higress-system spec: parentRefs: - name: higress-gateway hostnames: - mcp.example.com rules: - name: file-tools matches: - path: type: PathPrefix value: /file filters: - type: URLRewrite urlRewrite: path: type: ReplacePrefix replacePrefix: / backendRefs: - name: file-mcp-svc namespace: mcp-services port: 8080这里我加了URLRewrite过滤器,把/file前缀替换成/。原因是MCP服务端自身路由可能已经固定了/mcp这类路径,网关前缀如果不重写,请求到后端就会变成/file/mcp,容易出现404。重写之后,后端只需要关注MCP协议路径本身。
数据库MCP服务只要复制一份,把路径改成/db,backendRefs指到db-mcp-svc即可。多服务接入时,这种模板化配置维护成本很低,新增服务就是复制改三个字段的事。
3.3 请求头路由:按客户端类型或租户分流
路径路由解决了服务分发,但同一个MCP服务给不同客户端使用时,经常需要按租户或客户端类型做差异化处理。比如移动端和Web端走不同的MCP配置,或者某些高等级租户走专属实例。这时用请求头路由更灵活。
spec: rules: - name: file-tools-mobile matches: - path: type: PathPrefix value: /file headers: - name: X-Client-Type type: Exact value: mobile backendRefs: - name: file-mcp-svc-mobile namespace: mcp-services port: 8080 - name: file-tools-default matches: - path: type: PathPrefix value: /file backendRefs: - name: file-mcp-svc namespace: mcp-services port: 8080注意HTTPRoute的匹配规则语义:同一条rule里的多个条件是AND关系,多条rule之间是OR关系。上面这个配置里,带X-Client-Type: mobile的请求进mobile实例,其余请求落到默认实例。实测下来这个模式很实用,特别是做渠道A/B测试时,不用改客户端代码,只靠网关侧加请求头就能切换。
注意:请求头匹配是大小写敏感的。客户端在设置自定义请求头时务必保持统一规范。如果有些客户端发的是小写key,有些发的是大写key,路由就可能出现间歇性不匹配,这种问题在日志里特别难查。
3.4 权重路由:MCP服务升级的灰度实践
MCP服务升级不能像内部管理后台那样一把梭。AI应用对工具调用的稳定性要求很高,新版MCP服务如果某个工具返回格式变了,直接影响上层Agent的决策。权重路由就是为这种场景准备的。
spec: rules: - name: file-tools-canary matches: - path: type: PathPrefix value: /file backendRefs: - name: file-mcp-svc-canary namespace: mcp-services port: 8080 weight: 10 - name: file-mcp-svc namespace: mcp-services port: 8080 weight: 90这个配置把10%的/file流量切到canary版本,90%保持在线版本。我建议灰度比例从5%开始,观察网关指标和MCP服务端错误率,确认没问题再逐步调高。Higress的权重路由是按请求维度分布的,不会出现同一个请求被拆到两个版本的情况。
还有一个容易忽略的点:权重路由切换后,已建立的HTTP长连接不会立刻消失。如果MCP服务用的是SSE长连接,存量连接可能还挂在旧版本Pod上,需要等连接自然断开或者下游主动重连。所以做MCP服务版本升级时,除了配权重,还要给后端Pod设置优雅终止周期,给存量请求留足完成时间。
3.5 路由优先级与匹配细节
HTTPRoute的路由匹配遵循“最长前缀优先”的规则。比如/file和/file/render两个前缀同时存在,请求/file/render/123会命中前缀更长的那条规则。这个设计很合理,但配置时容易搞混,尤其是新加一条更具体的路径时,一定要检查是否和已有路径存在重叠。
另外要注意HTTPRoute的namespace访问权限。网关在higress-system命名空间,而后端Service在mcp-services命名空间,HTTPRoute里必须显式写清楚后端资源的namespace。如果路由配置看起来没问题但一直转发失败,十有八九是namespace没写对,或者后端服务名拼错了。我自己排查时,会先在集群内用kubectl get svc -n mcp-services确认名字,再复制进配置文件,减少手误概率。
4. MCP安全体系统一建设
4.1 先立一个安全模型
MCP服务的安全模型,我建议按“认证在网关、授权在网关、业务在服务端”来设计。网关负责确认“你是谁、你能不能用这个工具”,MCP服务端只负责执行工具逻辑。如果服务端还要自己判断调用方权限,就会退回各自为政的老路。
这个模型下,安全配置分成四层:身份认证(确认调用方身份)、访问授权(确认身份有哪些工具权限)、流量保护(限流、熔断)、传输加密(TLS/mTLS)。下面逐一展开。
4.2 API Key认证:接入MCP的第一道门槛
API Key是最简单直接的认证方式。Higress可以通过内置的Wasm插件实现API Key校验,插件通用配置形态大致如下:
apiVersion: higress.io/v1 kind: WasmPlugin metadata: name: api-key-auth namespace: higress-system spec: defaultConfig: consumers: - name: dev-application credential: sk-xxxxxxxxxxxx配置好以后,所有到达网关的MCP请求必须带请求头X-API-Key: sk-xxxxxxxxxxxx,不带或者带错会直接返回401。这个凭证只在网关注册,后端MCP服务完全不知道API Key的存在。
我在实际项目里通常给每个调用方分配独立的Key,而不是所有应用共用一把。这样某个应用泄露了Key,可以在网关侧单独吊销,不影响其他应用。如果你已经上了Higress的消费者体系,还能和用量统计、限流配额联动,后面讲限流时会提到。
4.3 JWT认证与租户隔离
API Key适合机器与机器之间的固定调用,但AI应用内部通常已经有自己的用户体系,这时候JWT更适合。JWT里可以带用户的身份信息、租户ID、角色权限,网关校验签名后直接提取这些字段,传给后端做进一步逻辑处理。
Higress的JWT认证插件配置大致长这样:
apiVersion: higress.io/v1 kind: WasmPlugin metadata: name: jwt-auth namespace: higress-system spec: defaultConfig: consumers: - name: tenant-a issuer: https://idp.internal.example/tenant-a jwks: | { "keys": [ ... ] }核心逻辑就是把可信的Issuer和JWKS配置到网关上。客户端带着JWT请求,网关先验签名、验过期时间,再把解析出的租户信息放进请求头,比如X-MCP-Tenant: tenant-a。后端的MCP服务如果要做租户级数据隔离,直接读这个请求头即可。
租户隔离是我特别想强调的。两个租户共用同一个MCP服务时,最怕的就是A租户的请求查到了B租户的数据。网关在把请求转发到MCP服务之前,强制注入租户标识请求头,服务端基于这个标识做数据过滤,比服务端自己去猜调用方是谁要可靠得多。
4.4 限流与并发保护
MCP服务被多个AI应用调用时,最容易出现的问题是某个应用异常循环调用,把MCP服务打垮。比如Agent在调试阶段反复调用同一个工具,或者某个自动化任务写了个死循环。这种场景下,限流就是保命配置。
Higress的限流插件支持多种维度:全局限流、按消费者限流、按路由限流。我建议至少配置两层:
第一层是全局限流,保护MCP服务整体不会被打爆,配置形态类似:
apiVersion: higress.io/v1 kind: WasmPlugin metadata: name: rate-limit namespace: higress-system spec: defaultConfig: global: capacity: 5000 fillRate: 200第二层是按消费者限流,给不同的API Key或JWT消费者设置不同的配额。比如开发环境的应用可以给100QPS,生产核心应用给1000QPS,测试账号20QPS。这样某个应用出问题只会影响自己,不会拖垮其他租户。
注意:限流插件通常会缓存消费者和流量桶状态。如果MCP服务的流量峰值远高于配额,网关会快速触发429。AI应用收到429以后能不能正确退避重试,取决于客户端代码怎么写。我在测试时踩过坑,客户端对429不做处理,疯狂重试,反而把后端打到更惨。限流策略要和客户端重试策略一起设计,两边都要留退避机制。
4.5 审计日志与全链路观测
MCP服务“被谁调用了、调用了哪个工具、结果成功还是失败”,这些信息在生产环境一定要留痕。Higress的访问日志默认记录了请求时间、来源IP、请求路径、状态码、响应时长。把这些日志接到统一日志平台,按消费者维度聚合查询,能快速回答“这个应用今天调了多少次文件处理工具”这类问题。
全链路追踪方面,Higress支持标准链路追踪协议注入。MCP服务端如果也接入了追踪的SDK,从AI应用发起请求到MCP工具执行完毕,整条链路可以在追踪系统里串起来。排查问题的时候,先看网关日志定位是哪一跳慢,再看服务端链路追踪定位具体是哪个工具卡住了,效率比盲猜高很多。
4.6 链路加密与mTLS
网关对外已经通过HTTPS提供了加密,服务端和网关之间则在同一个K8s集群内。如果集群本身是可信环境,内部流量通常走普通HTTP就够了。但如果安全要求比较高,比如MCP服务处理的是用户隐私数据,建议把网关到后端的链路也升级到双向TLS认证。
在Higress的体系里,可以通过DestinationRule配置mTLS:
apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: mcp-mtls namespace: mcp-services spec: host: file-mcp-svc trafficPolicy: tls: mode: ISTIO_MUTUAL配置成ISTIO_MUTUAL模式后,网关和后端服务之间会使用K8s集群内的自动生成的证书做双向认证。后端Service可以拒绝任何非mTLS的请求,这样即使有内部Pod被攻破,也无法直接伪装客户端调用MCP服务。这个能力在等保或者内部安全审计时很有说服力。
要注意的是,开启mTLS后,如果MCP服务端没启用Sidecar注入,流量会直接不通。所以先确认MCP服务所在的Namespace开启了Sidecar自动注入,再改DestinationRule配置,否则会人为制造一次事故。
5. 踩坑实录:Higress加MCP常见问题排查
5.1 路由配置不生效
我遇到最多的是三类原因。第一,Hostname不匹配,客户端访问的域名和HTTPRoute里配置的hostnames不一致,导致请求没有匹配到任何规则。第二,HTTPRoute的命名空间权限问题,后端Service的namespace写错或没写。第三,Gateway资源本身没有Ready,证书有问题,监听器没起来。
排查顺序建议是:先在Higress网关的Pod日志里看请求是否到达网关,再到网关的访问日志里看请求匹配到了哪条路由。如果日志里直接显示没有匹配的路由,就从Hostname和Path入手检查。如果请求根本到不了网关,检查负载均衡器、DNS解析和证书。
5.2 全链路401但不清楚卡在哪
加了认证插件以后,请求一直401。排查路径第一步是确认客户端确实带了对应的请求头,很多情况是请求头发送时被代理层剥掉了。第二步是确认插件配置里的消费者凭证和客户端发送的Key完全一致,包括前后空格。
还有一个很隐蔽的问题:多个Wasm插件叠加时,执行顺序会影响结果。比如同时配置了JWT认证和自定义请求头改写插件,改写插件如果先执行并且覆盖了认证插件要读取的请求头,认证就会失败。碰到这种问题,把插件配置拆开一个一个验证,先只开认证插件,确认通过后再加其他插件,这样能快速定位组合冲突。
5.3 SSE流式响应被中断或缓冲
SSE长连接在网关层最容易遇到两个问题:响应头被缓冲、连接被空闲超时断开。
MCP的SSE响应是持续推数据,网关不能在响应体全部收齐后再发给客户端。我建议在MCP服务的HTTP响应头里显式设置Cache-Control: no-cache和X-Accel-Buffering: no,这两个头在很多网关和中间件里都会被识别,从而跳过缓冲逻辑。Higress侧也需要合理配置IdleTimeout,SSE连接如果长时间没有数据推送,会被网关认为是空闲连接而断开,导致客户端收不到后续消息。如果MCP服务端有心跳或者注释事件,要确保这些事件在超时时间内发出。
5.4 工具调用超时与重试
MCP工具不一定都是快接口,有些工具要执行几秒甚至更久。网关默认的超时时间可能不够用,导致工具调用被切断。这时需要在HTTPRoute上显式配置超时时间。如果工具本身支持幂等重试,还可以配置重试策略,让网关在碰到连接异常时自动重试一次。但如果工具不是幂等的,比如“创建订单”“删除文件”这类,重试可能带来重复执行,务必谨慎,宁可超时报错让上层Agent做决策,也不要盲目重试造成业务副作用。
结语:这套体系实际跑下来的感受
我在模拟项目X里按这套方案把三个MCP服务统一挂到Higress网关后面,前后跑了两个多月,整体感受是路由配置的模板化程度很高,新增一个MCP服务只需要复制改几个字段,加上对应的API Key和限流配置,半小时就能上线。安全策略在网关层统一管理,后面审计时只需要导网关日志,不用一个服务一个服务去查。
最想提醒的还是那个原则:网关层做流量治理,服务端做业务逻辑。MCP服务本身可以轻一点,把认证、限流、灰度这些通用能力都交给网关,长期来看团队的维护成本是最低的。如果你正在做MCP服务收敛,或者公司内部AI应用数量增加,Higress这套路线的性价比会越用越明显。