☰
EKS Gateway API 实战:用 LBC 创建负载均衡器与扩展配置
2026/10/4 3:38:34 网站建设 项目流程

在EKS上做入口流量,很多团队还停留在Ingress + AWS Load Balancer Controller(LBC)的组合。坦白说这套方案用了好几年,大部分常规场景都能跑,但如果新起项目或者想彻底捋清楚Kubernetes入口流量的演进方向,我更推荐直接上Gateway API。这篇内容就是把“在EKS上使用LBC的GatewayAPI创建负载均衡器和扩展配置”这个场景完整拆开,从为什么选这条路、前置环境怎么搭、核心YAML怎么写,到注解(annotations)扩展和线上排查,一次性讲清楚。

适合谁看?一类是正在用Ingress、准备往Gateway API迁移的K8s运维和平台工程师;另一类是刚接触EKS,想知道云上负载均衡器到底是怎么被Kubernetes“管”起来的新手。看完之后,你至少能自己动手跑通一套“GatewayClass + Gateway + HTTPRoute”的完整链路,并且知道哪些参数是真正影响线上行为的,哪些坑是文档不会明说的。我把实际踩过的经验放在最后,直接照着抄能省不少时间。

1. 整体设计与思路拆解:为什么在EKS上优先选择Gateway API

1.1 Ingress的局限性与Gateway API的演进逻辑

Kubernetes的Ingress资源,说句公道话,设计得不算差,但它有个先天问题:路由规则和负载均衡器配置耦合在同一个对象里,而且不同厂商的Ingress Controller对字段的解释完全不同。你在EKS上写的alb.ingress.kubernetes.io注解,拿到NGINX上就是一堆废配置;反过来也一样。这种“一个规范,各自实现”的状态,直接导致两个后果:一是迁移成本高,换Controller等于重写资源;二是能力边界模糊,Ingress想表达HTTP之外的流量(比如TCP、TLS直通、gRPC)就非常吃力。

Gateway API这套规范就是冲着解决这些痛点来的。它把入口流量拆分成三层独立的资源:GatewayClass负责声明“用哪种Controller实现”,Gateway负责声明“在哪个监听端口上接收什么协议”,HTTPRoute负责声明“路由规则怎么匹配、转发到哪个后端”。三层各管各事,互相解耦。这意味着你在某个云厂商的EKS上写了一套Gateway资源,换到另一个支持Gateway API的集群,大部分资源可以原样平移,只需要改GatewayClass的引用。

网格网关、东西向流量、跨命名空间路由,这些在Ingress时代需要用各种外部插件和Controller自定义CRD才能做的事情,在Gateway API里都变成了规范内置能力。虽然整个规范还在演进,但方向很明确:Ingress就是“能用”,Gateway API是“好用且能扩展”。

1.2 LBC在Gateway API链路中的角色与工作流程

那AWS Load Balancer Controller(LBC)在这个链路里扮演什么角色?一句话:它是Gateway API规范在EKS上的落地实现。你需要安装LBC并让它注册对应的GatewayClass,之后你在集群里创建Gateway和HTTPRoute,LBC就会通过AWS SDK去调用Elastic Load Balancing API,把用户期望的负载均衡器资源创建出来,并在后台持续同步路由规则变化。

我理解这个工作流程的时候,特别喜欢把它类比成“Kubernetes里的一等公民和云资源的翻译官”。用户只关心HTTPRoute里写了什么路径规则,不关心ALB的Target Group、Listener Rule这些云上概念;LBC负责把Kubernetes声明翻译成AWS API调用,并把最终状态写回Gateway的status字段。你观察kubectl get gateway看到的Programmed状态,实际上就是LBC和AWS之间同步成功的信号。这个设计的好处是,你可以在完全不熟悉AWS控制台操作的情况下,通过Kubernetes声明获得一个生产可用的负载均衡器。

不过在动手之前要先想清楚:LBC对Gateway API的支持是从v2.4版本开始的,不同版本支持的功能范围差距很大。比如早期版本对TLS、GRPCRoute的支持不完整,最新版本已经能覆盖绝大多数生产需求。所以版本选错,后面排查起来会非常痛苦。我在下一节详细说说环境和组件的选择。

2. 前置条件与环境准备:EKS集群、IAM与LBC的安装

2.1 集群与组件版本要求

先整理一个版本基线,避免你上来就卡在兼容性上。我实际测下来比较稳的组合是:EKS集群版本1.27及以上,LBC版本v2.7以上,Gateway API的CRD版本用v1.0.0(对应gateway.networking.k8s.io/v1)。如果还想用GRPCRoute这类扩展资源,CRD需要装experimental-install.yaml,这个后面按需选择。

检查集群里是否已经安装Gateway API的CRD,用一条命令就能看出来:

kubectl get crd gatewayclasses.gateway.networking.k8s.io

如果返回NotFound,就需要手动安装。我习惯从GitHub Release页面找标准版安装文件:

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

这里有个细节非常容易被忽略:CRD版本必须和LBC支持的API版本匹配。比如LBC v2.7虽然兼容v1beta1和v1,但如果你装了比LBC支持的更新CRD,可能出现LBC不识别的情况;反之CRD太老,新的Gateway API字段也会被忽略。我自己的做法是在安装LBC之前先装好CRD,然后查LBC的日志确认它成功注册了GatewayClass。

2.2 IAM角色配置:IRSA与Pod Identity选择

LBC要操作AWS的ELB API,必须给它所属的Pod挂一个IAM角色。现在EKS上有两种方式:传统的IAM Roles for Service Accounts(IRSA)和更新的EKS Pod Identity。我在新项目里已经全面转向Pod Identity,因为它的使用体验比IRSA顺滑太多:不需要给ServiceAccount添加annotation,也不需要维护OIDC provider的信任关系,直接在EKS控制台或eksctl里配置Pod Identity关联即可。

如果沿用IRSA方式,你需要先创建IAM OIDC provider,再创建一个带有如下信任策略的角色,允许system:serviceaccount:kube-system:aws-load-balancer-controller这个ServiceAccount扮演它。角色至少需要这些权限:elasticloadbalancing:*、ec2:Describe*、ec2:AuthorizeSecurityGroupIngress、ec2:RevokeSecurityGroupIngress、waf-regional:GetWebACL、acm:DescribeCertificate等。如果你不确定策略细节,直接使用AWS官方文档里提供的示例策略,别自己精简,否则线上经常会报权限不足。

用eksctl创建集群加配置角色的命令大概是这样的:

eksctl create cluster \ --name demo-eks \ --version 1.29 \ --region us-east-1 \ --nodegroup-name core-linux \ --node-type m5.large \ --managed

然后单独创建Pod Identity角色并关联到kube-system/aws-load-balancer-controller的ServiceAccount上,这一步在IAM控制台和EKS控制台配合操作即可完成。整体而言,Pod Identity省掉了OIDC这把“容易被卡住的钥匙”,权限更直观,推荐优先用它。

2.3 安装和验证AWS Load Balancer Controller

安装LBC官方推荐用Helm chart。先添加仓库并更新:

helm repo add eks https://aws.github.io/eks-charts helm repo update

然后用Helm安装,注意把clusterName和region替换成你自己的环境,同时开启Pod Identity模式:

helm install aws-load-balancer-controller eks/aws-load-balancer-controller \ -n kube-system \ --set clusterName=demo-eks \ --set region=us-east-1 \ --set serviceAccount.create=false \ --set podIdentity.enabled=true

安装完成后,确认Pod状态和日志正常:

kubectl -n kube-system get po | grep aws-load-balancer-controller kubectl -n kube-system logs -l app.kubernetes.io/name=aws-load-balancer-controller | head -50

日志里如果能看到“successfully registered GatewayClass”这样的信息,说明LBC已经准备好接收Gateway API资源了。这一步经常翻车的原因有两个:一个是Helm参数里的podIdentity.enabled在没有Pod Identity环境时被设成true,导致LBC拿不到凭证;另一个是VPC的subnet没有打kubernetes.io/cluster/<cluster-name>标签,LBC找不到可用子网来给ALB划分可用区。Subnet标签的问题在第四节的排查里会详细展开,这里先埋个伏笔。

3. 用Gateway API创建负载均衡器:核心资源与YAML实操

3.1 先定义GatewayClass:声明使用哪种负载均衡器实现

Gateway API的第一步是定义GatewayClass,它就像面向对象语言里的“类”定义。你告诉集群:我要用哪个Controller来管理我的入口流量。对于LBC,控制器名字是固定的eks.amazonaws.com/awslb,不能写错,写错了LBC不会接管这个GatewayClass。

一个最小示例:

apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eks spec: controllerName: eks.amazonaws.com/awslb

创建后立刻查看状态:

kubectl get gatewayclass eks -o yaml

如果status.conditions里Accepted是True,说明LBC已经认领了它。有一个容易被忽略的点:GatewayClass是集群级资源,不受namespace限制;但一般建议在metadata里加个description字段,方便团队其他成员理解这个GatewayClass对应的是哪种负载均衡器或哪套环境。多个GatewayClass并存是很常见的,比如eks-internal和eks-public对应内部和公网入口,通过gatewayClassName在Gateway上引用即可。

3.2 再定义Gateway:Listener的规划与annotations扩展

Gateway是实际描述负载均衡器入口的对象。你在Spec里定义监听器,监听器的组合直接决定了云上ALB/NLB的Listener行为和SSL配置。这里我建议先规划好域名和协议,不要反复改了再回填。一个同时支持HTTP和HTTPS的Gateway示例:

apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: app-gateway namespace: app annotations: alb.ingress.kubernetes.io/load-balancer-name: app-alb alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/ip-address-type: ipv4 alb.ingress.kubernetes.io/target-type: ip alb.ingress.kubernetes.io/healthcheck-path: /healthz spec: gatewayClassName: eks listeners: - name: http protocol: HTTP port: 80 - name: https protocol: HTTPS port: 443 tls: certificateRefs: - kind: Secret name: app-tls

Listener的名称在Gateway范围内必须唯一,协议和端口组合也不能重复。这里有个设计细节:HTTPS监听器需要引用证书,如果你不想用Kubernetes Secret,也可以直接引用AWS Certificate Manager证书,但需要在annotation里指定alb.ingress.kubernetes.io/certificate-arn,两种方式都可以,看团队管理习惯。我个人偏向用ACM直接管理证书,避免Secret的轮换问题。

注意,这里上面写的annotations都是放在Gateway的metadata里的,LBC会把它们翻译为创建ALB时的配置。alb.ingress.kubernetes.io/target-type: ip表示流量直接转发到Pod的IP地址,这是基于Fargate和IP模式EKS的常见选择;如果是EC2节点且启用了VPC直通,可以改成instance让它转发到节点IP再走kube-proxy。两种模式的差异在第四节详细说明。

3.3 HTTPRoute路由编写:路径匹配、权重与转发配置

现在到了写路由规则的阶段。HTTPRoute通过parentRefs指向刚才创建的Gateway,然后声明hostname和路径匹配规则。这里要理解一个逻辑:一个HTTPRoute并不一定要绑定整个Gateway,它可以只绑定某个Listener。比如只监听HTTPS、只对某个域名生效。

一个把/api/*转发到后端服务的示例:

apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: api-route namespace: app spec: parentRefs: - name: app-gateway namespace: app hostnames: - api.example.com rules: - matches: - path: type: PathPrefix value: /api backendRefs: - name: api-service port: 8080 weight: 100

这里PathPrefix的匹配逻辑比较简单,但要注意LBC在转换成ALB监听器规则时,默认使用精确路径还是前缀匹配,跟PathPrefix存在细微差异。我的经验是,如果你需要精确匹配,显式用Exact类型,别靠前缀加通配符去猜。backendRefs也支持多个服务,配合weight做灰度发布非常直观:一个路由下面配置两个backendRef,权重设为95和5,流量就会按比例分配,这在Ingress时代需要额外写annotation或者拆路由才能实现。

写好之后,验证绑定状态:

kubectl get httproute api-route -o yaml

观察status.parents里的ResolvedRefs是否True,以及Conditions里有没有异常。如果一切正常,此时LBC应该已经在后台创建了ALB。你可以登录AWS控制台查看,或者直接用命令看自动生成的DLB:

kubectl get gateway app-gateway -n app -o jsonpath='{.status.addresses}'

返回值里会出现ALB的DNS地址,这就代表负载均衡器创建成功了。

4. 扩展配置实战:从Listener到Target Group的精细化控制

4.1 常用annotations分类速查表

LBC提供了非常多的annotation,用来控制ALB的方方面面。这些参数既可以在Ingress资源上写,也可以在Gateway资源上写,但到了Gateway API语境下,需要区分该放在哪一层。我在实际使用中,把常用的注解分成三大类:负载均衡器级、Target Group级、安全和日志级。

下面是我整理的一份速查表,覆盖了日常使用频率最高的配置:

分类annotation作用说明
负载均衡器级alb.ingress.kubernetes.io/schemeinternet-facing还是internal默认internal,公网服务要显式写
负载均衡器级alb.ingress.kubernetes.io/ip-address-typeipv4或dualstack需要IPv6就写dualstack
负载均衡器级alb.ingress.kubernetes.io/load-balancer-name自定义ALB名称不用自动生成的随机名
负载均衡器级alb.ingress.kubernetes.io/tags给ALB打标签用逗号分隔的key=value
Target Group级alb.ingress.kubernetes.io/target-typeip还是instance决定后端寻址方式,极其关键
Target Group级alb.ingress.kubernetes.io/healthcheck-path健康检查路径默认/,很多服务没实现根路径会误判
Target Group级alb.ingress.kubernetes.io/healthcheck-interval-seconds健康检查间隔默认30秒,调小会增大API调用频率
Target Group级alb.ingress.kubernetes.io/healthcheck-timeout-seconds超时时间默认5秒
Target Group级alb.ingress.kubernetes.io/success-codes健康检查成功状态码默认200
安全和日志alb.ingress.kubernetes.io/ssl-policy自定义TLS策略默认ELBSecurityPolicy-2016-08
安全和日志alb.ingress.kubernetes.io/access-log-enabled是否开启访问日志true/false
安全和日志alb.ingress.kubernetes.io/access-log-s3-bucketS3桶名称需要和集群同区域
安全和日志alb.ingress.kubernetes.io/security-groups指定安全组ID逗号分隔,多个
安全和日志alb.ingress.kubernetes.io/wafv2-acl-arn关联WAFv2 ACL更安全、更合规的流量过滤

这里面有些参数是全局的,写在Gateway上;有些是按服务定的,写在HTTPRoute的backendRefs对应的Service上。区分方法很简单:凡是影响ALB本身的配置放Gateway,凡是影响某个后端服务的配置放Service。但不排除LBC实现里把某些参数同时支持在两个位置写,我的建议是:不要重复配置,否则优先级不确定,维护会很混乱。

4.2 核心参数详解:target-type、健康检查与日志开关

先讲target-type,这个参数在EKS上最容易混。ip模式表示ALB直接访问Pod的IP,这种模式需要Pod网络和VPC网络打通,适合使用AWS VPC CNI插件的标准EKS集群;instance模式表示ALB先把流量发到节点服务器的IP和NodePort,然后由节点上的kube-proxy转发到Pod。如果你的EKS集群开启了VPC直通,ip模式效率更高,省一层转发;如果集群是Fargate或者自建节点组,就得看网络插件的支持情况。

我实际遇到的案例:把一个老集群从Ingress迁移到Gateway API时,忘记同步target-type,结果ALB创建成功但健康检查永远失败。为什么?因为Service是ClusterIP模式,而target-type=ip要求后端Service的类型必须为NodePort或LoadBalancer,ALB才能拿到Pod的IP进行访问。LBC这里不报错,只是把流量转发成“看似正常但实际不通”的状态,排查非常头疼。

再看健康检查。默认情况下,LBC给每个Target Group配置的路径是/,超时5秒,间隔30秒,成功码200。如果你的后端服务根路径没有返回200,就会出现“负载均衡器在线、后端全红”的状态。我在实际项目中几乎总会给Gateway注释加上:

alb.ingress.kubernetes.io/healthcheck-path: /healthz alb.ingress.kubernetes.io/healthcheck-interval-seconds: "10" alb.ingress.kubernetes.io/healthcheck-timeout-seconds: "3" alb.ingress.kubernetes.io/success-codes: "200-299"

把这些参数挂到Gateway上,所有通过这个Gateway创建出来的Target Group就统一使用这些配置。这样后端只需要实现一个/healthz接口即可,不用每个服务各写各的路径。

日志开关也不要省。开启ALB访问日志后,把日志输出到S3桶,排障时能拿到最原始的request记录,还能配合Athena做查询。配置如下:

alb.ingress.kubernetes.io/access-log-enabled: "true" alb.ingress.kubernetes.io/access-log-s3-bucket: my-access-log-bucket

注意access-log-enabled的值要用字符串“true”,不能是布尔值——LBC的注解很多时候只认字符串,写错了会导致配置被静默丢弃。这个细节我踩过好多次,写在这里提醒你。

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

5.1 LoadBalancer状态卡在Provisioning

这是我遇到最多的问题:Gateway状态显示Accepted=True,但Programmed=False,AWS控制台里没有任何ALB被创建。排查思路是这样,第一下看LBC Pod日志,日志里往往会给出明确原因,比如无法找到可用子网。最常见的原因就是子网没有打标签,LBC默认只会选择带有以下标签的子网作为ALB的可用区:

kubernetes.io/cluster/<cluster-name>: shared kubernetes.io/cluster/<cluster-name>: owned

如果是公有子网,还需要额外加上kubernetes.io/role/elb: 1;私有子网则加上kubernetes.io/role/internal-elb: 1。用eksctl创建的集群,这些标签通常自动打好了;但如果是你自己手搓的VPC和子网,漏掉标签几乎必然让你卡在这个状态下。你可以用如下命令检查:

aws ec2 describe-subnets --region us-east-1 --query 'Subnets[*].[SubnetId,Tags]'

另一种常见情况是AWS权限不足。如果LBC日志里出现AccessDenied,那就回IAM角色配置那里去查,确认策略有没有elasticloadbalancing:CreateLoadBalancer等权限。记住:LBC是同步在Gateway.status里反馈给用户的,但这个反馈有延时,一般30秒到1分钟。观察状态时要有耐心,别改两下配置发现没变化就立刻去动代码。

5.2 健康检查失败的常见原因与排查命令

健康检查失败的表现是:LB创建成功,Target Group在控制台能看到,但实例全部显示unhealthy。这类问题的第一反应是打开Target Group的Targets页面,看失败原因里给出的端口和路径到底是什么。常见原因有几个。

首先检查target-type配置是否和当前集群模式匹配。如果你用的是instance模式,Target Group里显示的就是Node节点的IP和NodePort;如果是ip模式,显示的是Pod的IP。如果显示IP地址完全不对,比如显示的是节点IP但你在ip模式,优先检查Service类型。ip模式下,LBC要求后端Service的类型是NodePort或LoadBalancer,因为ALB需要依赖NodePort做分发。

其次,很多服务在容器内监听的端口和Service声明的端口不一致。比如Nginx容器默认80端口,但你在Service里用targetPort: 8080,如果healthcheck-path写的路径又恰好返回404,健康检查必然失败。检查方法很简单:进入Pod:

kubectl exec -it <pod-name> -n app -- curl -s http://localhost:8080/healthz

如果Pod内访问正常,ALB还是失败,那就看安全组。ALB节点安全组需要放通健康检查端口,而Pod所在节点安全组需要允许来自ALB安全组的入站流量。很多团队把安全组管得很严,忘了互相放通,结果就是同样的路径在本机通、从外网通,就是ALB健康检查不通。我的习惯是给ALB单独一个安全组,在Pod节点的入站规则里允许source=sg-alb的整个安全组访问业务端口,这种绑定方式比写死CIDR要稳得多。

5.3 清理与更新时容易踩的坑

先说更新。你修改了Gateway的listener配置(比如把80端口改成443),LBC不一定像你想象的那样增量更新,有时候它会直接创建一个新的Target Group并绑定过去,旧的Target Group保留一段时间。这时候如果你想看当前规则是否已生效,别只盯AWS控制台,用这个命令更直接:

kubectl get gateway -o wide -n app kubectl get httproute -o wide -n app

两个资源的status一致,说明LBC和AWS已经同步完成。如果ResolvedRefs=False,最常见的原因是parentRefs指向了不存在的Gateway,或者Gateway的listener没有匹配的protocol支持。

再说清理。当你删除一个Gateway时,LBC会尝试同步删除它创建的所有AWS资源,包括ALB、Target Group、Listener Removals等。但如果你在此之前手动在AWS控制台改了ALB的配置,或者改了Target Group的NameTag导致LBC识别不到归属,这个级联删除可能失败,留下孤儿ALB产生费用。为了避免这种情况,我在生产环境里的清理顺序一定是先删HTTPRoute,等几秒确认路由已经不再指向后端,再删Gateway,最后删GatewayClass。虽然LBC理论上能自动处理,但人工控制顺序后,控制台里出现残留资源的概率会小很多。

另外再提醒一句,LBC对Gateway API的支持虽然成熟,但并不是所有Ingress注解都适用于Gateway模式。有些注解(比如alb.ingress.kubernetes.io/actions.${service})是基于Ingress的特殊语义实现的,在Gateway API下不会生效。如果你在迁移时沿用了大量Ingress专属注解,务必逐个回归验证,不要盲目复制。

从我个人经验来看,EKS上做入口流量,Gateway API这条路已经具备生产化能力,尤其是LBC对它的支持力度越来越大,资源模型又比Ingress清晰。你要做的不是立刻推倒重来,而是挑一两个新服务,用GatewayClass + Gateway + HTTPRoute跑通一条端到端链路,然后在灰度环境上线观察。这套方案里有一个特别值得玩味的设计:路由和网关分离,意味着应用团队和平台团队可以各自维护自己的资源,不用在同一个Ingress对象里互相争抢权限,这个协作模式在多人团队里是真的省心。

最后再分享一个小技巧:如果你的团队将来打算接服务网格,Gateway API这套资源天然可以和Istio的入口网关、网格内部的服务路由统一模型,不会像Ingress那样被绑死在某种实现上。所以现在花时间把流量模型梳理清楚,后面做灰度发布、流量镜像、故障注入,都会顺手很多。

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

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

立即咨询