一、冲突的核心应用场景与原因分析

1.1 典型共存场景

在企业级Kubernetes集群的日常运维中,常会遇到这类实际场景:项目早期只部署了Kubernetes原生的Nginx Ingress Controller,用来处理老商城、后台管理等存量服务的流量;随着业务迭代,新项目需要做灰度发布、链路追踪、跨集群流量治理,于是引入Istio的Ingress Gateway来管理新业务(比如用户中心、订单查询等)的流量。两个Ingress控制器同时运行后,偶尔会出现请求跳转错误的情况——比如访问user.example.com/user路径时,部分请求会跑到老商城的/shop路径,这就是两个Ingress共存时的路由规则冲突问题。

1.2 冲突的本质原因

两个Ingress控制器发生冲突,核心有两个原因:一是没有明确指定每个Ingress对应的控制器,Kubernetes会随机分配请求给任意一个可用的Ingress控制器;二是两者监听了相同的网络端口(默认都是80/443),导致流量转发时无法准确识别管辖范围。另外如果两个Ingress规则的路径前缀完全一致,也会触发Kubernetes的优先匹配逻辑,把请求分给某一个控制器,从而导致路由错乱。

二、具体解决策略与示例

本次示例统一使用Kubernetes 1.25、Istio 1.18、Nginx Ingress Controller的单一K8s生态栈,每个策略都附带完整可运行的代码和注释。

2.1 策略一:通过IngressClass明确区分两个控制器

IngressClass是Kubernetes专门用来指定Ingress对应哪个控制器的资源,每个Ingress Class对应唯一的控制器标识,这样就能让K8s明确知道把请求分给谁,彻底避免混淆。

# 1. 创建Nginx原生Ingress Class,对应K8s自带的Nginx控制器
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx
spec:
  controller: k8s.io/ingress-nginx # 这是Nginx Inress Controller的固定标识
---
# 2. 创建Istio Ingress Gateway的Ingress Class,对应Istio的网关控制器
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: istio
spec:
  controller: istio.io/ingress-gateway # 这是Istio Ingress Gateway的固定标识
---
# 3. 老商城的原生Ingress,明确指定用Nginx控制器
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: old-shop-ingress
  namespace: default
spec:
  ingressClassName: nginx # 必须显式指定,不能依赖默认
  rules:
  - host: shop.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: old-shop-api
            port:
              number: 80
---
# 4. 新用户中心的Ingress,明确指定用Istio Ingress Gateway
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: user-center-ingress
  namespace: default
spec:
  ingressClassName: istio # 显式指定Istio控制器,避免分配错误
  rules:
  - host: user.example.com
    http:
      paths:
      - path: /user
        pathType: Prefix
        backend:
          service:
            name: user-center-svc
            port:
              number: 80

这个策略的优点是规范清晰,适合多Ingress控制器共存的稳定集群;缺点是需要给旧的Ingress资源添加ingressClassName字段,已经运行的存量服务需要做少量修改。

2.2 策略二:调整路由路径前缀做物理隔离

如果不想改动Ingress控制器的配置,也可以直接调整两个服务的路径前缀,让它们的路径完全不重叠,即使K8s分配错控制器,也不会跳到错误的服务。

# 老商城的Ingress调整路径,增加/shop前缀,避免和新服务冲突
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: old-shop-ingress
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: shop.example.com
    http:
      paths:
      - path: /shop/api # 新增/shop前缀,和新服务路径完全隔离
        pathType: Prefix
        backend:
          service:
            name: old-shop-api
            port:
              number: 80
---
# 新用户中心的Ingress保持原有路径,和老服务无重叠
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: user-center-ingress
  namespace: default
spec:
  ingressClassName: istio
  rules:
  - host: user.example.com
    http:
      paths:
      - path: /user # 路径和老服务的/shop/api完全不重叠,不会触发冲突
        pathType: Prefix
        backend:
          service:
            name: user-center-svc
            port:
              number: 80

这个策略的优点是不需要修改控制器或IngressClass,只需要调整路径规则,适合临时紧急解决问题;缺点是前端调用时需要同步修改请求路径,后期路径维护容易出现混乱,不适合长期使用。

2.3 策略三:修改Ingress Gateway的监听端口彻底隔离

如果两个控制器都要保留默认路径,也可以把Istio Ingress Gateway的监听端口改成非标准端口(比如8080),和Nginx控制器的80端口完全错开,从端口维度避免冲突。

# 1. 修改Istio Ingress Gateway的端口,把默认的80改成8080
apiVersion: apps/v1
kind: Deployment
metadata:
  name: istio-ingressgateway
  namespace: istio-system
spec:
  template:
    spec:
      containers:
      - name: istio-proxy
        ports:
        - containerPort: 8080 # 替换原来的80端口,避免和Nginx的80冲突
          name: http
---
# 2. 同步修改Gateway的Service,对外暴露8080端口
apiVersion: v1
kind: Service
metadata:
  name: istio-ingressgateway
  namespace: istio-system
spec:
  ports:
  - port: 8080 # 对外端口改成8080,内部转发到容器的8080
    targetPort: 8080
    name: http
---
# 3. 新用户中心的Ingress关联修改后的端口
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: user-center-ingress
  namespace: default
spec:
  ingressClassName: istio
  rules:
  - host: user.example.com
    http:
      paths:
      - path: /user
        pathType: Prefix
        backend:
          service:
            name: user-center-svc
            port:
              number: 80
  tls:
  - hosts:
    - user.example.com
    secretName: user-tls

这个策略的优点是从底层端口维度彻底隔离,不需要修改路径规则,适合需要保留原有路径的场景;缺点是对外暴露新端口,域名访问时需要配置反向代理或带端口,端口资源占用会增加。

三、技术优缺点与注意事项

3.1 各解决策略的优缺点对比

三种策略的适用场景差异明显:IngressClass区分的方式,适合长期运维的稳定集群,规范清晰但改动稍大;路径前缀隔离适合临时救急,改动小但维护麻烦;端口隔离适合需要完全保留路径的场景,彻底解决冲突但端口占用多。

3.2 关键注意事项

不管用哪种策略,都要注意这几点:一是所有Ingress资源必须显式指定ingressClassName,不要依赖K8s的默认IngressClass,避免新增控制器后出现混淆;二是路径匹配尽量用Implementation: Exact(精确匹配)而不是Prefix(前缀匹配),防止边缘路径被误匹配;三是如果用到TLS证书,两个Ingress的证书域名不要完全重叠,或者使用不同的证书,避免证书冲突;四是修改配置后必须做全量测试,覆盖核心路径、边缘路径和错误路径,确保所有请求都能正确转发到对应的服务。

四、总结

在Kubernetes集群中,Ingress Gateway和原生Ingress共存的路由冲突是很常见的问题,核心解决思路是明确每个Ingress控制器的管辖范围。优先选择IngressClass区分的方式,符合K8s的规范,也便于后续扩展;如果是临时调整可以用路径前缀隔离;如果需要彻底隔离端口,则选择修改Gateway端口的方式。无论用哪种策略,都要做好测试和监控,保障业务流量稳定转发,避免因为路由错误影响用户体验。