EKS上从Ingress到GatewayAPI:LBC负载均衡器迁移实践 在EKS里给业务接进负载均衡器以前我闭着眼都能念出一套流程Ingress挂到ALB需要什么高级能力就往注解里堆遇到特殊需求再叠加注解。直到上个月我把一条核心业务链路完整切到LBC的GatewayAPI之后才真正理解为什么这几年社区一直在重新设计流量入口的整个模型。这篇文章想把我在EKS上使用LBC配合GatewayAPI创建负载均衡器、以及做各种扩展配置的完整过程写出来给正准备从Ingress迁移或者打算直接用GatewayAPI落地负载均衡器的团队做个参考。1. 为什么在EKS上把流量入口从Ingress切到GatewayAPI1.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真正认识GatewayAPI2.1 安装GatewayAPI标准CRDGatewayAPI折腾人的第一步往往不是理解概念而是安装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如果集群里之前装过旧版GatewayAPICRD升级也要做一遍。我遇到过升级过程中因为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 enableGatewayApitrue \ --set clusterNamemy-eks-cluster如果是直接命令行启动的LBC对应的参数是--enable-gateway-api。不同版本的LBC对参数命名可能有差异建议安装前先查看当前版本的LBC启动参数避免写错。安装完后检查一下controller运行状态kubectl -n kube-system get pod -l app.kubernetes.io/nameaws-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 targetTypeALB会把流量转发到节点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: All3.2 用HTTPRoute把流量导入ServiceGateway只是入口真正决定流量怎么走的是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显示ProgrammedTrueHTTPRoute显示AcceptedTrue时负载均衡器流量通道就已经完全打通了。此时到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-idHTTPS监听器建好之后如果还想强制HTTP跳转HTTPS可以用HTTPRoute的RequestRedirect过滤器实现属于标准能力不需要再写一堆注解。灰度场景同样好做。比如你想把10%流量切到v2版本服务只需要在HTTPRoute的backendRefs里加权重backendRefs: - name: web-v1 port: 8080 weight: 90 - name: web-v2 port: 8080 weight: 10LBC会把这份配置同步为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注解直接用URLRewriterules: - matches: - path: type: PathPrefix value: /old-path filters: - type: URLRewrite urlRewrite: path: type: ReplacePrefixMatch replacePrefixMatch: /new-path backendRefs: - name: web-svc port: 8080header修改也能直接表达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 --tail200LBC启动时会打印它监听了哪些API处理GatewayAPI事件时也会输出对应的资源名和状态。如果状态条件显示正常但AWS侧没有资源这一步基本能发现真正卡点。第三步看AWS控制台。有时候LBC已经把请求发出去了但ALB状态不对比如子网缺失、证书无法验证、目标组异常等。到EC2控制台看一下ALB、监听器、目标组的状态往往能发现Kubernetes侧看不到的AWS侧报错。5.2 我遇到过的几个典型案例情况一GatewayClass一直NotAccepted。原因基本集中在controllerName写错或者LBC没有开启GatewayAPI支持。解决方法是核对controllerName是否为LBC官方要求的值检查Helm参数里enableGatewayApi是否生效然后看LBC启动日志中关于GatewayAPI的描述信息。情况二Gateway一直PendingALB没有创建。这个我遇到过两次。一次是子网标签缺失LBC在发现子网阶段失败了另一次是因为没有绑定任何HTTPRouteLBC等待路由就绪。先确认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侧使用一个独立ALBGatewayAPI侧又创建了另一个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上并不是什么炫技的新玩具而是一个值得投入流量入口重构的明确方向。