☰
EKS上从Ingress到GatewayAPI:LBC负载均衡器迁移实践
2026/10/5 10:57:42 网站建设 项目流程

在EKS里给业务接进负载均衡器,以前我闭着眼都能念出一套流程:Ingress挂到ALB,需要什么高级能力就往注解里堆,遇到特殊需求再叠加注解。直到上个月,我把一条核心业务链路完整切到LBC的GatewayAPI之后,才真正理解为什么这几年社区一直在重新设计流量入口的整个模型。这篇文章想把我在EKS上使用LBC配合GatewayAPI创建负载均衡器、以及做各种扩展配置的完整过程写出来,给正准备从Ingress迁移,或者打算直接用GatewayAPI落地负载均衡器的团队做个参考。

1. 为什么在EKS上把流量入口从Ingress切到GatewayAPI

1.1 Ingress在EKS上逐渐暴露的几个问题

Ingress不是不能用,它确实是过去几年Kubernetes南北向流量的绝对主流。但在EKS这种多团队、多业务、多环境共存的集群里,长期使用Ingress套ALB,你迟早会遇到这几类场景。

第一个是权限边界模糊。一个Ingress对象里既有路由规则,又有负载均衡器参数,还有各种controller专属注解。基础设施团队想统一管ALB的scheme、证书、日志,业务团队只想改自己的host和path,但所有人都挤在同一个Ingress对象上。最后只能靠口头约定加代码Review,久而久之就变成了注解大杂烩。

第二个是迁移成本高。Ingress的很多扩展能力是靠alb.ingress.kubernetes.io这类注解实现的,一旦你想把LBC换成别的controller,或者从ALB切到别的实现,这些注解全部作废。这本质上是把Kubernetes的流量API和某个厂商的特定实现绑死了,不符合云原生的开放性。

第三个是路由能力有限。Ingress对匹配规则、灰度权重、URL重写、Header改写这些能力的表达都比较弱,要么依赖注解,要么依赖第三方扩展。应用团队想要做一次简单的金丝雀发布,都得找平台组来调。

第四个是缺乏多协议规划。Ingress主要还是面向HTTP/HTTPS,想要表达TCP、UDP或者未来的gRPC流量,就需要另起一套机制。而GatewayAPI从一开始就把这些纳入同一个资源模型。

1.2 GatewayAPI的三层模型如何改变协作方式

GatewayAPI的核心设计,是把原来混在Ingress里的职责拆成三层独立资源,每一层对应一个明确的角色。

  • GatewayClass由基础设施管理员定义,描述负载均衡器的类型、提供方以及能力参数。
  • Gateway由集群管理员或者平台团队定义,描述具体的端口、协议、TLS配置等监听能力。
  • HTTPRoute由应用开发者定义,描述业务路由规则,可以干净地绑定到某个Gateway上。

这个分层直接解决了我在EKS上最头疼的问题:基础设施配置和业务规则终于可以分开维护了。应用团队提交一个HTTPRoute,平台团队只需要负责审核它绑定的Gateway是否合理,负载均衡器层面的参数不需要业务感知。权限模型配合Kubernetes RBAC做起来也会顺畅很多,不再需要在同一个对象上强行分权。

1.3 LBC在GatewayAPI中的定位

在EKS上,AWS Load Balancer Controller是目前最主流的负载均衡器控制器之一。前面叫ALB Ingress Controller的时候,它主要通过Ingress资源创建与管理ALB。后来官方把它演进为支持多种负载均衡器场景的控制器,再往后,LBC开始接入GatewayAPI,承担GatewayClass里指定的controller角色。

只要GatewayClass里的controllerName正确指向LBC,它就会持续监听Gateway和HTTPRoute的变化,自动完成ARN级别的资源创建、同步和删除。整个控制面逻辑和以前Ingress Controller非常相似,但数据模型变了,使用者能感受到的治理边界清晰了很多。

提示:GatewayAPI不是要立刻淘汰Ingress,很多团队在很长一段时间内会两个模型并存。先把非核心服务切过去,验证团队协作方式是否更顺,再逐步扩大范围,是比较稳妥的路径。

2. 环境准备:让LBC真正认识GatewayAPI

2.1 安装GatewayAPI标准CRD

GatewayAPI折腾人的第一步,往往不是理解概念,而是安装CRD。因为没有CRD,你写的GatewayClass根本没有API能接收。

在EKS上,我推荐直接用GatewayAPI的standard-install.yaml来安装标准CRD。这个清单包含了GatewayClass、Gateway、HTTPRoute等核心资源的CRD定义。选择版本时有一点要特别注意:LBC支持GatewayAPI是从早期版本开始逐步演进的,比较新的版本对v1的CRD支持更稳定,建议参考LBC官方Release Notes确定你当前LBC版本支持哪一版CRD。

安装命令很简单:

kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml

装完后验证一下:

kubectl get crd | grep gateway.networking.k8s.io

如果集群里之前装过旧版GatewayAPI,CRD升级也要做一遍。我遇到过升级过程中因为CRD版本不兼容导致LBC一直报错的情况,后来把CRD重新apply一次就正常了。这个步骤最容易忽略,但又最关键。

2.2 启用LBC的GatewayAPI能力

LBC默认并不一定启用GatewayAPI,需要显式打开。通过Helm安装或者升级时,有一个对应的参数开关。例如:

helm upgrade aws-load-balancer-controller eks/aws-load-balancer-controller \ -n kube-system \ --set enableGatewayApi=true \ --set clusterName=my-eks-cluster

如果是直接命令行启动的LBC,对应的参数是--enable-gateway-api。不同版本的LBC对参数命名可能有差异,建议安装前先查看当前版本的LBC启动参数,避免写错。

安装完后检查一下controller运行状态:

kubectl -n kube-system get pod -l app.kubernetes.io/name=aws-load-balancer-controller

如果pod正常Running,但创建GatewayClass后一直不被接受,大概率就是参数没开对,或者CRD版本不匹配。

2.3 网络与权限检查清单

这部分我建议在动手创建负载均衡器之前就过一遍,不然很容易把问题埋在后面。

LBC要通过IRSA方式调用AWS API,所以ServiceAccount需要绑定IAM角色。官方IAM Policy里已经包含了ALB/NLB相关的必要权限,最好用官方最新policy,不要自己精简,否则创建负载均衡器时会遇到InsufficientPermissions之类的异常。

子网标签是另一个高频踩坑点。ALB所在的子网需要打上kubernetes.io/cluster/<集群名>: owned或kubernetes.io/cluster/<集群名>: shared标签,并且至少覆盖两个可用区。如果没有这个标签,LBC在遍历子网的时候会发现节点一模一样,然后报错SubnetNotFound,最后Gateway一直Pending。

安全组方面,如果后端服务采用instance targetType,ALB会把流量转发到节点NodePort,因此节点安全组需要放行对应端口。如果用ip targetType,目标组直接注册Pod IP,同样需要确保ALB和Pod之间网络可达。

提示:这些前置条件和Ingress时代几乎一致,不完全是因为换GatewayAPI才引入的新门槛,而是LBC本身在创建负载均衡器时的必要前提。所以先排查网络和权限,再查yaml,效率会高很多。

3. 用GatewayAPI创建负载均衡器的完整实操

3.1 先建GatewayClass和Gateway

基础概念看完,我直接给出能跑的yaml。第一步是定义GatewayClass,告诉集群采用哪个控制器来提供负载均衡器能力。

apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eks-lbc-alb spec: controllerName: eks.amazonaws.com/gateway-controller description: "AWS Load Balancer Controller as gateway provider"

这里的controllerName必须写成LBC注册的固定名称,不能随便填。如果写错,GatewayClass的Accepted条件会一直为False。我一开始在这个字段上犹豫过,后来直接参考LBC官方文档确认了对应值,后面所有问题都顺利了。

第二步是创建Gateway。Gateway对应负载均衡器和监听器的抽象。以下示例会让LBC创建一个面向公网的ALB,监听80端口HTTP。

apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: app-gateway namespace: app annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip spec: gatewayClassName: eks-lbc-alb listeners: - name: http port: 80 protocol: HTTP allowedRoutes: kinds: - kind: HTTPRoute

把Gateway提交到集群后,LBC会开始准备ALB,但通常不会立刻把ALB创建完。LBC的reconcile逻辑需要看到至少一个可用的路由绑定到该Gateway,才会真正把负载均衡器落地上。这个行为和Ingress有一点差异,很多新手在第一次操作的时候会等半天发现没创建ALB,其实只是少写了一个HTTPRoute。

这里特别提醒一个细节:Gateway的allowedRoutes字段如果不写,默认只允许同namespace的HTTPRoute绑定到它。这是GatewayAPI安全模型的一部分,容易和Ingress的使用习惯搞混。如果业务路由分布在多个namespace,这里要显式放行:

allowedRoutes: kinds: - kind: HTTPRoute namespaces: from: All

3.2 用HTTPRoute把流量导入Service

Gateway只是入口,真正决定流量怎么走的是HTTPRoute。下面这个例子把app.example.com这个域名的所有请求,从 app-gateway 路由到 web-svc 服务。

apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: homepage namespace: app spec: parentRefs: - name: app-gateway namespace: app hostnames: - app.example.com rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: web-svc port: 8080

这里我要特别解释整个流量路径的逻辑:

首先是ALB监听器。Gateway的每一条listener,都会对应ALB上的一个监听器,比如80端口就对应HTTP:80监听器。然后是主机名匹配,HTTPRoute里的hostnames,会在ALB监听器上生成按Host转发规则。之后是路径匹配,/前缀会被映射成ALB规则里的path pattern。最后是后端转发,backendRefs引用的Service,对应ALB实际的后端目标组。

创建完成后,可以通过以下命令观察状态:

kubectl get gateway -n app -w kubectl get httproute -n app

当Gateway显示Programmed=True,HTTPRoute显示Accepted=True时,负载均衡器流量通道就已经完全打通了。此时到AWS控制台也能看到ALB、监听器、目标组等资源都已经创建出来。

3.3 多监听器与灰度分流示例

生产环境通常不止80端口一件事。假设你的业务要同时开放HTTP和HTTPS,那就在Gateway里加一条443监听器,并配合SSL证书注解:

spec: listeners: - name: http port: 80 protocol: HTTP - name: https port: 443 protocol: HTTPS allowedRoutes: kinds: - kind: HTTPRoute

把证书ARN放在Gateway或HTTPRoute的注解上,LBC会自动把它绑定到443监听器:

annotations: alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:region:account:certificate/cert-id

HTTPS监听器建好之后,如果还想强制HTTP跳转HTTPS,可以用HTTPRoute的RequestRedirect过滤器实现,属于标准能力,不需要再写一堆注解。

灰度场景同样好做。比如你想把10%流量切到v2版本服务,只需要在HTTPRoute的backendRefs里加权重:

backendRefs: - name: web-v1 port: 8080 weight: 90 - name: web-v2 port: 8080 weight: 10

LBC会把这份配置同步为ALB目标组上的流量权重,这种表达方式比我在Ingress里做金丝雀时用注解搞一长串配置要直观得多。

4. 负载均衡器扩展配置:三个层次的写法

4.1 GatewayClass ParametersRef做全局级参数

GatewayAPI标准的CRD里并没有为负载均衡器的名称前缀、IP地址类型、子网选择、tags这类云厂商特有参数预留字段。为此,LBC扩展了一个自定义资源,通过GatewayClass的parametersRef字段引用,用于承载这类负载均衡器级别的配置。

看一个实际例子:

apiVersion: elbv2.k8s.aws/v1beta1 kind: AWSLoadBalancerControllerGatewayClassParams metadata: name: alb-params namespace: app spec: namePrefix: myapp scheme: internet-facing ipAddressType: ipv4 subnets: - subnet-xxx - subnet-yyy tags: team: platform env: staging

然后把它挂到GatewayClass上:

spec: controllerName: eks.amazonaws.com/gateway-controller parametersRef: group: elbv2.k8s.aws kind: AWSLoadBalancerControllerGatewayClassParams name: alb-params

这个层次适合做全局统一配置。平台团队把绝大多数负载均衡器底层参数收敛到一份对象里,业务侧创建同一个GatewayClass的Gateway都会继承这些配置。要调整全集群负载均衡器的tag、名称前缀或子网选择,改一个CRD就能生效,比用Ingress注解逐个改要高效得多。

提示:这个CRD的apiVersion和可用字段会随LBC版本演进而变化,实际配置前先看一下当前环境里这个CRD的schema,比如执行kubectl describe crd awsloadbalancercontrollergatewayclassparams来确认字段名和拼写。

4.2 用LBC注解做ALB行为扩展

别高兴太早,GatewayAPI虽然标准化了很多东西,但LBC对ALB行为的不少自定义能力还是通过注解暴露。好消息是,大部分在Ingress阶段用过的alb.ingress.kubernetes.io注解在GatewayAPI场景下依然可用,所以迁移成本其实没有想象中高。

我整理了一份在EKS上实践中比较常用的注解映射,遇到扩展配置需求时可以对照参考。

需求描述常用注解说明
负载均衡器访问级别alb.ingress.kubernetes.io/schemeinternet-facing或internal
目标组注册方式alb.ingress.kubernetes.io/target-typeip或instance
后端协议alb.ingress.kubernetes.io/backend-protocolHTTP或HTTPS
监听端口alb.ingress.kubernetes.io/listen-portsJSON数组,比如[{"HTTPS":443}]
TLS证书alb.ingress.kubernetes.io/certificate-arnARN值,多个证书逗号分隔
健康检查路径alb.ingress.kubernetes.io/healthcheck-path后端探活路径
丢弃不匹配流量alb.ingress.kubernetes.io/healthcheck-port探活端口,默认targetPort
访问日志alb.ingress.kubernetes.io/access-log-enabledtrue/false
负载均衡器属性alb.ingress.kubernetes.io/load-balancer-attributes逗号分隔的key/value对
HTTP跳转HTTPSalb.ingress.kubernetes.io/ssl-redirect值为数字监听端口

这些注解放在Gateway或者HTTPRoute上,LBC都能识别。不过我的经验是,涉及全局监听器、证书、访问日志级别的配置优先放Gateway层,涉及单个路由的特性放HTTPRoute层,避免一个Route的注解污染整个Gateway。虽然它们最终都作用在同一个ALB上,但多写一个细粒度提升可维护性。

4.3 用HTTPRoute标准字段做路由级分流

扩展配置分两种,一种是负载均衡器本身的行为,另一种是流量路由策略。前者适合用注解和ParametersRef,后者尽量用HTTPRoute自带的标准字段,这样才能真正享受GatewayAPI带来的跨实现兼容性。

举个例子,如果你想对/old-path做路径重写到/new-path,不需要依赖LBC注解,直接用URLRewrite:

rules: - matches: - path: type: PathPrefix value: /old-path filters: - type: URLRewrite urlRewrite: path: type: ReplacePrefixMatch replacePrefixMatch: /new-path backendRefs: - name: web-svc port: 8080

header修改也能直接表达:

filters: - type: RequestHeaderModifier requestHeaderModifier: set: - name: X-Env value: staging add: - name: X-Extra value: gateway-api

这类标准字段的好处是,以后即使把GatewayClass从LBC换成其他控制器,路由规则大概率还能复用。负载均衡器底层的差异被屏蔽在GatewayClass和Gateway这一层,业务侧不会感知太多。

需要重试策略和超时策略的话,较新版本的GatewayAPI也已经提供标准支持,不需要再靠注解魔改。总之我现在的原则是:能写的标准字段绝不进注解,注解只负责解决LBC真正特有的ALB参数问题。

5. 常见问题和排查技巧实录

5.1 三步定位法

在EKS上用LBC跑GatewayAPI,出现问题时的排查路径比Ingress要清晰,因为每一步都有明确的k8s状态对象可以查看。我总结了一套三步定位法,基本覆盖绝大多数问题。

第一步看状态条件:

kubectl describe gatewayclass eks-lbc-alb kubectl describe gateway -n app app-gateway kubectl describe httproute -n app homepage

这三个对象各自有对应的Conditions,比如Accepted、Programmed、Ready,每个条件里会给出具体的Reason和Message,比瞎翻日志快得多。

第二步看LBC日志:

kubectl -n kube-system logs deployment/aws-load-balancer-controller --tail=200

LBC启动时会打印它监听了哪些API,处理GatewayAPI事件时也会输出对应的资源名和状态。如果状态条件显示正常但AWS侧没有资源,这一步基本能发现真正卡点。

第三步看AWS控制台。

有时候LBC已经把请求发出去了,但ALB状态不对,比如子网缺失、证书无法验证、目标组异常等。到EC2控制台看一下ALB、监听器、目标组的状态,往往能发现Kubernetes侧看不到的AWS侧报错。

5.2 我遇到过的几个典型案例

情况一:GatewayClass一直NotAccepted。

原因基本集中在controllerName写错,或者LBC没有开启GatewayAPI支持。解决方法是核对controllerName是否为LBC官方要求的值,检查Helm参数里enableGatewayApi是否生效,然后看LBC启动日志中关于GatewayAPI的描述信息。

情况二:Gateway一直Pending,ALB没有创建。

这个我遇到过两次。一次是子网标签缺失,LBC在发现子网阶段失败了;另一次是因为没有绑定任何HTTPRoute,LBC等待路由就绪。先确认Gateway的Conditions,再看LBC日志,基本能定位。不要急着怀疑yaml,大多数情况下是前置条件或者资源依赖没满足。

情况三:HTTPRoute绑定了,但流量访问不通。

先确认目标组是否健康。如果target-type是ip,检查后端Pod是否真的监听着你声明的端口;如果target-type是instance,检查节点安全组是否放行了NodePort。访问不通的问题大多数不是GatewayAPI本身,而是负载均衡器到后端之间网络链路没打通。

情况四:HTTPS证书不生效。

证书ARN在ACM里存在,但LBC绑定失败。可能原因是IAM角色没有ACM读取权限,或者证书不在集群所在Region。还有一个容易被忽略的坑:当你把certificate-arn注解放在HTTPRoute上时,一定要确认这条Route绑定的Gateway确实有443监听器,否则证书没有监听器可以挂载。

情况五:同一套GatewayAPI配置,在测试集群好了,在预发集群不行。

大部分时候是CRD版本不一致,或者LBC版本不一致。GatewayAPI的资源模型演进比较快,建议测试集群和预发集群尽量使用同一套LBC镜像和GatewayAPI CRD版本。如果版本差异过大,标准字段和注解的识别行为都会不同。

情况六:迁移过程中Ingress和GatewayAPI混跑出现规则冲突。

同一域名同时被Ingress和GatewayAPI管理会不会互相覆盖?答案是要看它们是否管理同一个负载均衡器。如果Ingress侧使用一个独立ALB,GatewayAPI侧又创建了另一个ALB,那两套流量入口同时存在但没有冲突。如果你的Ingress和Gateway最后指向同一个ALB资源,那就要小心了,LBC不一定负责同一ALB下的所有配置。我的做法是迁移期间直接让不同业务使用不同GatewayClass,彻底隔离两个入口。

5.3 迁移期可以参考的操作方式

如果团队决定从Ingress切到GatewayAPI,我建议不要搞一次性搬迁。先把一个非核心服务整体迁移过去,期间保留Ingress配置不删除,业务验证稳定后再清理旧Ingress。

迁移时注意对照:Ingress里的annotations哪些对应Gateway的parametersRef,哪些对应HTTPRoute注解。先把证书、scheme、target-type这类全局参数挪到GatewayClass或者Params对象,把路由规则改写成HTTPRoute的matches和filters,这样负载均衡器底层配置和业务配置自然分离。

等到新的GatewayAPI链路稳定运行一周以上,再删掉Ingress就行。回滚策略就是保留一段时间的Ingress资源,一旦新链路有异常,切换域名指向的负载均衡器或者改回Ingress规则即可。整体风险可控。

6. 最后聊两句

在EKS上把LBC接入GatewayAPI之后,我对这套模型最大的感受是:负载均衡器本身并没有变,ALB还是那个ALB,监听器、目标组、健康检查这些底层概念全都在。变的是Kubernetes暴露入口的方式和权限边界。过去业务侧写一个Ingress就要连带考虑一堆ALB参数,现在这些底层参数被收敛到GatewayClass和Gateway层,业务侧写HTTPRoute只需要关心自己的host、path和后端服务,这种认知上的简化,比省几行yaml更有价值。

我的建议是,如果你正在EKS上长期维护负载均衡器相关配置,可以先用一个非核心业务做试点,把GatewayAPI的CRD和LBC的GatewayAPI支持跑通,然后对比一下团队协作流程是否变顺。踩过几次坑之后你就会发现,GatewayAPI在EKS上并不是什么炫技的新玩具,而是一个值得投入流量入口重构的明确方向。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询