1. 引言
当微服务体系接入 Service Mesh(例如 Istio)后,东西向流量可以实现统一治理,但体系外的南北向流量依然需要 API 网关。
我们团队目前在用的 API 网关是 Apache APISIX。
问题在于:
- APISIX 本身不是基于 Envoy 架构,对 Istio 的原生支持有限;
- 业内也有替代方案,例如阿里开源的 Higress(基于 Envoy + Istio),能无缝融入 Istio,但迁移成本大,会引入风险。
因此,我们的目标是:保持 APISIX 作为前置反向代理不变,同时让其具备接入 Istio Mesh 的能力,从而获得 Istio 的治理和 mTLS 安全能力。
2. 问题与挑战
2.1 为什么不是直接换 Higress?
- Higress 优点:与 Istio 原生无缝融合,生态一致;
- 问题:现有大量 APISIX 插件和路由规则需要迁移,成本和风险太高。
2.2 APISIX 与 Istio 的脱节
- APISIX 自身具备路由、负载均衡等功能,但这些能力与 Istio 重叠;
- 如果两者叠加,可能出现“双重治理”,规则冲突,带来运维复杂性。
2.3 两种可选方案
-
在 APISIX 与服务之间增加 Istio IngressGateway:
- 流量走 APISIX → IngressGateway → 服务网格;
- 缺点:多一跳流量转发,链路更长,架构复杂度增加。
-
在 APISIX Pod 注入 Istio Sidecar:
- Envoy 接管 APISIX 出站流量;
- APISIX 仅负责入口代理,放弃上游治理;
- 流量在 APISIX → Envoy 出站时进入 Istio Mesh;
- 可直接启用 mTLS,统一安全。
📌 最终选择了 方案 2,因为它能实现零信任安全(mTLS),并与现有架构改动最小。
3. 技术选型与设计思路
3.1 APISIX 注入 Sidecar 的差异
普通微服务注入 Sidecar 时,会让 Envoy 劫持入站 + 出站流量。
但 APISIX 作为网关入口,需要排除自身监听的 80/443 端口,避免入站流量被二次代理。
annotations:
traffic.sidecar.istio.io/excludeInboundPorts: "80,443"
同样,部分端口或 IP 范围(如 OTel 上报端口 4317/4318)也需要排除。
3.2 Service 与 FQDN 对齐
- 微服务必须暴露为 ClusterIP Service;
- APISIX 的 upstream 使用该 Service 的 FQDN;
- Envoy 基于 RDS(Route Discovery Service)匹配 host,才能触发 VirtualService 规则。
3.3 APISIX 与 Envoy 职责划分
- APISIX:只做入口代理、前置 header 注入、简单重写。
- Envoy:负责服务发现、路由、熔断、mTLS、安全策略。
这样可以避免功能重叠,让 APISIX 专注在 API 层,Envoy 专注在 Mesh 治理层。
4. 实施步骤
4.1 APISIX Pod 注入 Sidecar
配置 Pod 注解,排除入口端口:
traffic.sidecar.istio.io/excludeInboundPorts: 入站流量劫持排除端口traffic.sidecar.istio.io/excludeOutboundIPRanges: 出站流量劫持排除IPtraffic.sidecar.istio.io/excludeOutboundPorts: 出站流量劫持排除端口istio.io/rev: default: 开启sidecar注入
metadata:
annotations:
traffic.sidecar.istio.io/excludeInboundPorts: "80,443"
traffic.sidecar.istio.io/excludeOutboundPorts: "4317,4318"
traffic.sidecar.istio.io/excludeOutboundIPRanges: "192.168.49.18/32"
labels:
istio.io/rev: default
4.2 微服务 ClusterIP Service
apiVersion: v1
kind: Service
metadata:
labels:
app: seller
run_env: test1
svc_type: tourism
name: tourism-seller
namespace: test1-microsvc
spec:
type: ClusterIP
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: service
port: 8080
protocol: TCP
targetPort: 8080
selector:
app: seller
run_env: test1
svc_type: tourism
4.3 APISIX Upstream 配置
将APISIX的upstream从基于k8s的服务注册与发现切换成基于节点的域名解析, upstream 指向微服务的 FQDN,并在 header 中注入 baggage:
{
"uri": "/api/*",
"name": "xxxx",
"methods": [
"GET",
"POST"
],
"host": "istio.example.com",
"plugins": {
"proxy-rewrite": {
"headers": {
"trace_id": "$http_traceparent",
"baggage": "canary=true,tenant=foo"
},
"scheme": "http"
},
"redirect": {
"http_to_https": true
}
},
"upstream": {
"nodes": [
{
"host": "tourism-seller.test1-microsvc.svc.cluster.local",
"port": 8080,
"weight": 1
}
],
"retries": 0, // 把重试交给 Envoy(若走 Ingress 或入网格)
"timeout": {
"connect": 2, // 内网直连 2s 足够
"send": 10,
"read": 70 // >= VS 总预算(例如 60s)稍加余量
},
"type": "roundrobin",
"scheme": "http",
"pass_host": "rewrite",
"upstream_host": "tourism-seller.test1-microsvc.svc.cluster.local",
"keepalive_pool": {
"idle_timeout": 60,
"requests": 1000,
"size": 320
}
}
}
📌 注意:
- 重试交给 Envoy,不再由 APISIX 处理;
baggageheader 用于后续 Istio VirtualService 做流量染色。
4.4 Istio 路由配置
DestinationRule:定义负载均衡、连接池、熔断策略。
VirtualService:基于 baggage header 分流。
4.4.1 创建 DestinationRule 和 VirtualService
DestinationRule和VirtualService的host要和APISIX的upstream配置对齐
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: tourism-seller
namespace: test1-microsvc
spec:
host: tourism-seller.test1-microsvc.svc.cluster.local
trafficPolicy:
# 端口级:8080 明文(HTTP/1.1),按照是否使用 mTLS 进行放开
#portLevelSettings:
# - port:
# number: 8080
# tls:
# mode: DISABLE
# 负载均衡(HTTP/1.1 下推荐,减轻排队热点)
loadBalancer:
simple: LEAST_REQUEST # 如需最简单可改为 ROUND_ROBIN
# 连接池(HTTP/1.1 特化;显式禁用 h2 升级)
connectionPool:
tcp:
connectTimeout: 3s
maxConnections: 1024
tcpKeepalive:
time: 60s
interval: 30s
probes: 3
http:
h2UpgradePolicy: DO_NOT_UPGRADE # 确认走 HTTP/1.1
http1MaxPendingRequests: 1000 # 入队上限,防止过度排队
maxRequestsPerConnection: 100 # 合理复用,避免长连接过大
idleTimeout: 65s # 空闲断开时间,略大于常用 perTryTimeout
# 熔断/逐出(使用新字段)
outlierDetection:
consecutive5xxErrors: 5
consecutiveGatewayErrors: 5
interval: 5s
baseEjectionTime: 30s
maxEjectionPercent: 50
subsets:
- name: stable
labels:
traffic.microsvc.io/track: stable
- name: canary
labels:
traffic.microsvc.io/track: canary
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: tourism-seller
namespace: test1-microsvc
spec:
hosts:
- tourism-seller
- tourism-seller.test1-microsvc.svc
- tourism-seller.test1-microsvc.svc.cluster.local
gateways:
- mesh
http:
- name: canary-by-baggage
match:
- headers:
baggage:
regex: '.*(^|[,])\s*canary=true(\s*(;[^,]+)?)(,|$).*'
timeout: 60s
retries:
attempts: 2
perTryTimeout: 5s
retryOn: "5xx,gateway-error,connect-failure,refused-stream,reset"
route:
- destination:
host: tourism-seller.test1-microsvc.svc.cluster.local
subset: canary
weight: 100
- name: default-stable
timeout: 60s
retries:
attempts: 2
perTryTimeout: 5s
retryOn: "5xx,gateway-error,connect-failure,refused-stream,reset"
route:
- destination:
host: tourism-seller.test1-microsvc.svc.cluster.local
subset: stable
weight: 100
5. 流量治理与测试
5.1 流量分流
- 携带
baggage=canary=true→ 路由到 canary 子集; - 无 baggage → 路由到 stable 子集。
5.2 mTLS 验证
- 确认 APISIX → Envoy → 服务链路使用双向 TLS;
- Istio
PeerAuthentication策略生效,流量拒绝非 mTLS。
5.3 熔断与重试
- 模拟 canary Pod 异常,Envoy 会逐出实例并重试请求。
6. 拓展与思考
6.1 APISIX vs Higress
- Higress 更贴合 Istio,但迁移成本高;
- APISIX + Sidecar 是 平滑演进方案。
6.2 南北向与东西向流量统一
- APISIX 管理南北向流量;
- Istio 管理东西向流量;
- 双方通过 Sidecar 集成,形成统一治理面。
6.3 后续演进
- 可以逐步把部分 APISIX 插件迁移到 Istio EnvoyFilter;
- 最终形成 API 网关 + Service Mesh 一体化治理。
7. 总结
通过在 APISIX Pod 注入 Istio Sidecar,我们实现了:
- 让 APISIX 保持网关角色不变;
- 让出站流量进入 Istio Mesh;
- 统一应用了 Istio 的 mTLS、熔断、路由、流量染色等能力。
这是一个 低成本、低风险的演进方案:
- 短期:保留现有 APISIX 生态,快速接入 Istio;
- 长期:可以平滑过渡到更深度的 Istio 网关方案(如 Higress)。
📌 总结一句:我们没有替换 APISIX,而是让它“入网格”,成为 Mesh 的一部分。
评论区