ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

EKS Gateway API 实战:用 LBC 创建负载均衡器与扩展配置

EKS Gateway API 实战:用 LBC 创建负载均衡器与扩展配置 在EKS上做入口流量很多团队还停留在Ingress AWS Load Balancer ControllerLBC的组合。坦白说这套方案用了好几年大部分常规场景都能跑但如果新起项目或者想彻底捋清楚Kubernetes入口流量的演进方向我更推荐直接上Gateway API。这篇内容就是把“在EKS上使用LBC的GatewayAPI创建负载均衡器和扩展配置”这个场景完整拆开从为什么选这条路、前置环境怎么搭、核心YAML怎么写到注解annotations扩展和线上排查一次性讲清楚。适合谁看一类是正在用Ingress、准备往Gateway API迁移的K8s运维和平台工程师另一类是刚接触EKS想知道云上负载均衡器到底是怎么被Kubernetes“管”起来的新手。看完之后你至少能自己动手跑通一套“GatewayClass Gateway HTTPRoute”的完整链路并且知道哪些参数是真正影响线上行为的哪些坑是文档不会明说的。我把实际踩过的经验放在最后直接照着抄能省不少时间。1. 整体设计与思路拆解为什么在EKS上优先选择Gateway API1.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 ControllerLBC在这个链路里扮演什么角色一句话它是Gateway API规范在EKS上的落地实现。你需要安装LBC并让它注册对应的GatewayClass之后你在集群里创建Gateway和HTTPRouteLBC就会通过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 AccountsIRSA和更新的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 clusterNamedemo-eks \ --set regionus-east-1 \ --set serviceAccount.createfalse \ --set podIdentity.enabledtrue安装完成后确认Pod状态和日志正常kubectl -n kube-system get po | grep aws-load-balancer-controller kubectl -n kube-system logs -l app.kubernetes.io/nameaws-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 再定义GatewayListener的规划与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-tlsListener的名称在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控制台查看或者直接用命令看自动生成的DLBkubectl 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打标签用逗号分隔的keyvalueTarget 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-typeip要求后端Service的类型必须为NodePort或LoadBalancerALB才能拿到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状态显示AcceptedTrue但ProgrammedFalseAWS控制台里没有任何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健康检查必然失败。检查方法很简单进入Podkubectl exec -it pod-name -n app -- curl -s http://localhost:8080/healthz如果Pod内访问正常ALB还是失败那就看安全组。ALB节点安全组需要放通健康检查端口而Pod所在节点安全组需要允许来自ALB安全组的入站流量。很多团队把安全组管得很严忘了互相放通结果就是同样的路径在本机通、从外网通就是ALB健康检查不通。我的习惯是给ALB单独一个安全组在Pod节点的入站规则里允许sourcesg-alb的整个安全组访问业务端口这种绑定方式比写死CIDR要稳得多。5.3 清理与更新时容易踩的坑先说更新。你修改了Gateway的listener配置比如把80端口改成443LBC不一定像你想象的那样增量更新有时候它会直接创建一个新的Target Group并绑定过去旧的Target Group保留一段时间。这时候如果你想看当前规则是否已生效别只盯AWS控制台用这个命令更直接kubectl get gateway -o wide -n app kubectl get httproute -o wide -n app两个资源的status一致说明LBC和AWS已经同步完成。如果ResolvedRefsFalse最常见的原因是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那样被绑死在某种实现上。所以现在花时间把流量模型梳理清楚后面做灰度发布、流量镜像、故障注入都会顺手很多。
返回列表