在 Kubernetes 中运行多个 Ingress 控制器:ingress-nginx 的 IngressClass 隔离部署指南
【免费下载链接】ingress-nginxIngress NGINX Controller for Kubernetes项目地址: https://gitcode.com/GitHub_Trending/in/ingress-nginx
本篇技术指南聚焦于 ingress-nginx 项目中"多 Ingress 控制器共存"的经典难题,系统讲解通过 Kubernetes 原生IngressClass机制(推荐方案)与已弃用的kubernetes.io/ingress.class注解(兼容方案)将多个 Ingress 控制器(如内部/外部 ingress-nginx、GCE 等)隔离共存、各司其职的完整部署方法与参数配置。读完本文,你将掌握--controller-class、--ingress-class、--election-id等关键启动参数的作用原理,能够安全地在一个集群中同时运行两套甚至多套 ingress-nginx 控制器,并理解其底层判类逻辑(依据 pkg/flags/flags.go 与 internal/ingress/controller/ingressclass/ingressclass.go 的源码实现)。
问题的根源:多个控制器互相"抢" Ingress
在默认情况下,如果在一个集群中部署多个 Ingress 控制器(例如同时部署ingress-nginx与gce),由于它们都没有经过任何类隔离配置,所有控制器都会同时监听集群中的全部 Ingress 对象,并竞相更新 Ingress 的status.loadBalancer字段,产生令人困惑的互相覆盖、反复改写现象(例如 A 控制器写入自己的负载均衡地址,B 控制器随即又改写成自己的)。这是多控制器共存必须首先解决的问题。
为解决这一问题,官方推荐使用 Kubernetes 的IngressClass机制。而传统的kubernetes.io/ingress.class注解不再被推荐使用([IN DEPRECATION],见 pkg/flags/flags.go 中的参数注释),因为它未来可能被弃用;更优的做法是使用 Ingress 规范中的字段spec.ingressClassName。
需要注意一个例外场景:当通过 Helm 以scope.enabled(限制控制器只监听某个命名空间)方式部署时,IngressClass 资源中的spec.controller字段不会被用于判类,此时判定逻辑另走命名空间作用域路径(scope配置见 charts/ingress-nginx/values.yaml)。
方案一:使用 IngressClass 隔离多个控制器(推荐)
如果所有 Ingress 控制器都支持 IngressClass(例如多个 ingress-nginx v1.0+ 实例),可以部署两套控制器,分别授权它们管理两个不同的 IngressClass,然后通过ingressClassName字段在 Ingress 上显式选择由哪套控制器接管。
第一步:为每个控制器分配不同的启动参数
在每套控制器的 Deployment / StatefulSet 中,确保--controller-class与--ingress-class设置为互不相同的值。如果新部署的控制器所在命名空间已经存在一个或多个 ingress-nginx 控制器,还必须为新实例指定一个唯一的--election-id,避免多个实例共用同一个 Leader Election 标识导致状态更新异常。
# ingress-nginx Deployment/Statefulset spec: template: spec: containers: - name: ingress-nginx-internal-controller args: - /nginx-ingress-controller - '--election-id=ingress-controller-leader' - '--controller-class=k8s.io/internal-ingress-nginx' - '--ingress-class=k8s.io/internal-nginx' ...参数含义(默认值均可在 pkg/flags/flags.go 中确认):
| 启动参数 | 默认值 | 作用 |
|---|---|---|
--controller-class | k8s.io/ingress-nginx | 本控制器满足的 IngressClassspec.controller取值,用于匹配 Ingress 引用的 IngressClass 资源 |
--ingress-class | nginx | (已弃用路径)本控制器满足的注解类名,对应kubernetes.io/ingress.class注解值 |
--election-id | ingress-controller-leader | 用于 Ingress 状态更新的 Leader Election 标识,多实例共存时必须唯一 |
第二步:创建与--controller-class对应的 IngressClass 资源
IngressClass 的spec.controller字段值必须与控制器启动参数--controller-class完全一致,这样该控制器才会接管引用此 IngressClass 的 Ingress:
# ingress-nginx IngressClass apiVersion: networking.k8s.io/v1 kind: IngressClass metadata: name: internal-nginx spec: controller: k8s.io/internal-ingress-nginx ...第三步:在 Ingress 中通过ingressClassName引用目标类
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress spec: ingressClassName: internal-nginx ...使用 Helm 安装时的等价配置
如果通过 Helm Chart 安装(对应 Chart 位置 charts/ingress-nginx),使用以下 values 片段即可达到与上述手工配置相同的效果:
controller: electionID: ingress-controller-leader ingressClass: internal-nginx # default: nginx ingressClassResource: name: internal-nginx # default: nginx enabled: true default: false controllerValue: "k8s.io/internal-ingress-nginx" # default: k8s.io/ingress-nginx这里需要特别说明 Chart 中两个易混淆字段的关系(依据 charts/ingress-nginx/values.yaml 的注释):
ingressClassResource.controllerValue:创建 IngressClass 资源时写入spec.controller的值,同时也会作为--controller-class参数传入控制器;ingressClassResource.name:创建的 IngressClass 资源的名称;ingressClass:向后兼容ingress.class注解时使用的类名(默认nginx)。判类算法为:优先考虑ingressClassName字段,若不存在,再回退查找ingress.class注解;ingressClassResource.default: true:将此类标记为集群默认 IngressClass,使未指定ingressClassName的 Ingress 在创建时被自动分配;但若存在多个默认类,Ingress 的创建会被拒绝。
重要:无类注解 Ingress 的处理规则
在多控制器场景下,必须理解"未设置类"的 Ingress 会被谁处理(这一点直接决定了隔离是否生效):
- 当运行多个 ingress-nginx 控制器时,只有使用了默认
--controller-class(即k8s.io/ingress-nginx)的控制器才会处理未设置类注解的 Ingress,否则类注解/类字段变成必填项(对应internal/ingress/controller/ingressclass/ingressclass.go中的Configuration结构及其常量DefaultControllerName = "k8s.io/ingress-nginx"、DefaultAnnotationValue = "nginx"); - 当
--controller-class保持默认值k8s.io/ingress-nginx时,该控制器会同时监控无类注解的 Ingress与类注解设置为nginx的 Ingress; - 若希望控制器只接管特定类的 Ingress,就必须把
--controller-class设为非默认值(如上述示例中的k8s.io/internal-ingress-nginx),从而确保它仅满足指定类别的 Ingress。
从源码实现看,判类逻辑在 internal/ingress/controller/store/store.go 中收口:当启用--watch-ingress-without-class(对应 Helm 的controller.watchIngressWithoutClass,默认false,见 values.yaml)时,无类 Ingress 会以"_"作为通配符名称被接受;否则将返回"ingress does not contain a valid IngressClass"错误,即该 Ingress 不被任何严格按类隔离的控制器接管。
方案二:使用 kubernetes.io/ingress.class 注解(已弃用,仅作兼容)
如果同时运行的多个 Ingress 控制器中,有一个或多个尚不支持 IngressClass 机制(例如较老版本的 GCE 控制器),则必须退回到注解方式:在所有希望由 ingress-nginx 接管的 Ingress 上显式标注kubernetes.io/ingress.class: "nginx"。
例如,下面的 Ingress 会命中 GCE 控制器,从而被 Ingress-NGINX 控制器主动忽略:
metadata: name: foo annotations: kubernetes.io/ingress.class: "gce"而下面的 Ingress 则会命中 Ingress-NGINX 控制器,使 GCE 控制器忽略它:
metadata: name: foo annotations: kubernetes.io/ingress.class: "nginx"自定义注解类名
默认的类名"nginx"可以通过--ingress-class启动参数改为任意其他值:
spec: template: spec: containers: - name: ingress-nginx-internal-controller args: - /nginx-ingress-controller - --ingress-class=internal-nginx然后在 Ingress 上设置与之对应的注解:
metadata: name: foo annotations: kubernetes.io/ingress.class: "internal-nginx"注解方式的关键行为
- 注解值不匹配任何有效类 ⇒ 控制器忽略该 Ingress:将注解设置为任何与有效 ingress class 不匹配的值,都会强制 Ingress-NGINX 控制器忽略该 Ingress;
- 单控制器场景下的用法:如果你只运行一个 Ingress-NGINX 控制器,但仍希望与其他 Ingress 控制器同时使用,可将注解设置为除
"nginx"或空字符串以外的任意值,即可让 NGINX 控制器跳过该 Ingress; - 该注解方式本质上依赖 internal/ingress/controller/ingressclass/ingressclass.go 中定义的注解键
IngressKey = "kubernetes.io/ingress.class"与默认值DefaultAnnotationValue = "nginx",其注解参数在 pkg/flags/flags.go 中被明确标注为[IN DEPRECATION],且注解判定优先级低于--controller-class(源码注释原文:The parameter --controller-class has precedence over this.)。
补充:辅助判定参数与验证手段
相关启动参数速查
除上述三个核心参数外,多控制器隔离场景下还常涉及以下参数(完整参数表见 docs/user-guide/cli-arguments.md):
| 参数 | 默认值 | 说明 |
|---|---|---|
--watch-ingress-without-class | false | 是否额外监控不带 IngressClass / 注解的 Ingress |
--ingress-class-by-name | false | 是否除spec.controller外,再按 IngressClass 的.metadata.name进行匹配 |
--disable-leader-election | false | 禁用 Leader Election(多实例共存时谨慎使用) |
测试用例佐证
仓库在 test/e2e/settings/ingress_class.go 中提供了针对 IngressClass 行为的端到端测试(覆盖无类 Ingress 处理、WatchWithoutClass等场景),同时在 internal/ingress/controller/store/store_test.go 中通过单元测试验证了WatchWithoutClass: true/false两种配置下 Ingress 过滤逻辑的差异,可作为理解判类行为与回归验证的参考。
总结与最佳实践
| 场景 | 推荐做法 |
|---|---|
| 所有控制器均支持 IngressClass(如同为 ingress-nginx v1.0+) | 使用--controller-class+ingressClassName隔离,避免使用注解 |
| 同命名空间部署第二个 ingress-nginx 控制器 | 额外设置唯一的--election-id |
| 存在不支持 IngressClass 的旧控制器(如 GCE) | 使用kubernetes.io/ingress.class注解隔离 |
| 希望某个控制器只处理特定类 | 将--controller-class设为非默认值,并配套创建对应 IngressClass |
| 单控制器但想与其他控制器共存 | 在被忽略的 Ingress 上设置非nginx/ 非空字符串的类注解 |
| 限制控制器仅监听单个命名空间 | 开启scope.enabled(注意此时 IngressClass 的spec.controller不参与判类) |
核心结论可以概括为:多控制器共存的本质是"类隔离 + 选举隔离"——类隔离决定"哪个控制器接管哪个 Ingress"(IngressClass 优先、注解回退),选举隔离(唯一的--election-id)保证"同一状态更新不被多个控制器互相覆盖"。在部署时优先采用 IngressClass 机制,并始终为非默认控制器使用非默认的--controller-class值,即可获得清晰、可预期的多控制器行为。
【免费下载链接】ingress-nginxIngress NGINX Controller for Kubernetes项目地址: https://gitcode.com/GitHub_Trending/in/ingress-nginx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考