在 EKS 上用 AWS Load Balancer Controller(下称 LBC)暴露服务,默认每个 Ingress 都会得到一个独立的 ALB。服务一多,ALB 的个数和费用跟着涨。LBC 有三种共用 ALB 的办法:IngressGroup 把多个 Ingress 的规则合并到一个 ALB 上;TargetGroupBinding 让 ALB 和 Target Group 在集群外建好,LBC 只负责把 Pod 注册进去;Gateway API 用一个 Gateway 对应一个 ALB,各团队在自己的 namespace 里挂 HTTPRoute。文中的行为以 LBC v3.6.2(2026-10-25 发布)对应的文档为准。

默认一个 Ingress 一个 ALB#

按 LBC 的文档,Ingress 默认不属于任何 IngressGroup,每个 Ingress 自成一个「implicit IngressGroup」,对应一个 ALB。下面这个 Ingress 会让 LBC 在 AWS 上建一个新的 internet-facing ALB:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: order-ingress
  namespace: order-ns
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec:
  ingressClassName: alb
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /order
            pathType: Prefix
            backend:
              service:
                name: order-service
                port:
                  number: 8080

ALB 按小时计费,不足一小时按一小时算,另外按 LCU 计费(ELB 价格页)。AWS Price List 里 us-east-1 的 ALB 是每小时 0.0225 美元,ap-southeast-1 是 0.0252 美元,LCU 两个区域都是每 LCU 小时 0.008 美元。按一个月 730 小时算,us-east-1 每个 ALB 光小时费约 16.4 美元,20 个服务各占一个 ALB 就是每月约 330 美元,LCU 另算。数量上也有上限:每个区域默认最多 50 个 ALB(ALB quotas)。

IngressGroup:多个 Ingress 合用一个 ALB#

给 Ingress 加上同一个 alb.ingress.kubernetes.io/group.name,它们就组成一个 explicit IngressGroup,LBC 把组里所有 Ingress 的规则合并到同一个 ALB 上。组按名字认,两个 Ingress 可以在不同 namespace:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: order-ingress
  namespace: order-ns
  annotations:
    alb.ingress.kubernetes.io/group.name: "shared-api-alb"
    alb.ingress.kubernetes.io/group.order: "10"
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec:
  ingressClassName: alb
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /order
            pathType: Prefix
            backend:
              service:
                name: order-service
                port:
                  number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: cart-ingress
  namespace: cart-ns
  annotations:
    alb.ingress.kubernetes.io/group.name: "shared-api-alb"
    alb.ingress.kubernetes.io/group.order: "20"
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec:
  ingressClassName: alb
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /cart
            pathType: Prefix
            backend:
              service:
                name: cart-service
                port:
                  number: 8080

合并时有两条规则要知道:

  • 规则的先后看 group.order,取值 -1000 到 1000,不写时是 0,数字小的先匹配;order 相同时按 Ingress 的 namespace/name 字典序排。
  • 注解分两类合并方式。scheme、subnets 这类负载均衡器级别的注解是 Exclusive:整个组里只能在一个 Ingress 上写,或者每个 Ingress 都写同一个值。只作用于路径的注解只影响写它的那个 Ingress。

这样多个服务只付一个 ALB 的小时费,各团队在自己的 namespace 里维护自己的 Ingress,不用合成一个包含所有路径的大 Ingress。代价有三个。

第一是信任边界。LBC 文档在 IngressGroup 一节专门标了 Security Risk:有权限创建或修改 Ingress 的人,都能把自己的 Ingress 加进同一个组,往这个 ALB 上加规则,或者用更高的优先级盖掉已有规则。所以 IngressGroup 只适合有 Ingress 写权限的人都在同一个信任边界内的集群。

LBC 在 IngressClass 引用的 IngressClassParams 上留了两个收紧的开关:spec.group.name 让用这个 IngressClass 的 Ingress 全部进同一个组,组名由集群管理员定;spec.namespaceSelector 限定哪些 namespace 的 Ingress 能用这个 IngressClass,范围外的 Ingress,LBC 拒绝调和。

apiVersion: elbv2.k8s.aws/v1beta1
kind: IngressClassParams
metadata:
  name: shared-api
spec:
  group:
    name: shared-api-alb
  namespaceSelector:
    matchLabels:
      alb-group: shared-api
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: shared-api
spec:
  controller: ingress.k8s.aws/alb
  parameters:
    apiGroup: elbv2.k8s.aws
    kind: IngressClassParams
    name: shared-api

这两项把能进组的 namespace 收窄成一个白名单,白名单里的 Ingress 之间仍然能互相覆盖规则。EKS Auto Mode 内置的负载均衡只认这种写法:它的 IngressClass 用 eks.amazonaws.com/alb 控制器和 eks.amazonaws.com 组下的 IngressClassParams,Ingress 上的 group.name 注解不支持,组只能在 IngressClassParams 里指定(Auto Mode 文档)。

第二是配额(ALB quotas)。每个 ALB 默认最多 100 条规则(不含默认规则,可以申请提高),按 ALB 算,不按监听器算;证书每个 ALB 最多 25 张(不含默认证书,可以提高);Target Group 每个 ALB 最多 100 个,这一项不能提高。按 LBC 迁移文档的说法,Ingress 模式下同一个 Service 的所有规则共用一个 Target Group,所以组里的后端 Service 接近 100 个时,就只能拆成多个组。

第三是影响面。组里的服务共用一个 ALB,它的监听器、证书或配额出了问题,组里所有服务一起受影响。

TargetGroupBinding:ALB 在集群外建好#

Ingress 模式下,ALB、监听器和 Target Group 都由 LBC 根据 Ingress 创建,删掉 Ingress,这些云上资源也跟着删。TargetGroupBinding(下称 TGB)是 LBC 的一个 CRD,把方向反过来:ALB、监听器、规则和 Target Group 由 Terraform 这类工具在集群外建好,集群里只写一个 TGB,指明 Service 和 Target Group,LBC 跟着 Service 后面的 Pod 变化注册和注销目标。

Ingress 模式其实也在用 TGB。按 LBC 的迁移文档,LBC 为 Ingress、Service 和 Gateway 注册 Pod 用的就是它自己生成的 TGB,删掉 Ingress 时一起清掉;手写的 TGB 绑的是集群外的 Target Group,生命周期不跟着 Ingress 走。

apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
  name: payment-service-tgb
  namespace: payment-ns
spec:
  serviceRef:
    name: payment-service
    port: 8080
  targetGroupARN: arn:aws:elasticloadbalancing:ap-southeast-1:123456789012:targetgroup/payment-prod-tg/a1b2c3d4e5f6
  targetType: ip
  networking:
    ingress:
      - from:
          - securityGroup:
              groupID: sg-0123456789abcdef0
        ports:
          - port: 8080
            protocol: TCP
  • serviceRef:集群里的 Service 和端口。
  • targetGroupARN:集群外建好的 Target Group,也可以改用 targetGroupName。
  • targetType:ip 或 instance,要和 Target Group 本身的类型一致;不写的话,LBC 的 mutating webhook 会调 AWS API 查出来补上。ip 模式直接把 Pod IP 注册成目标,前提是 Pod 用的是 VPC 里的地址,比如 Amazon VPC CNI 分配的 ENI 辅助 IP(target-type 注解说明)。
  • networking:可选。按 TGB spec 的说法,它定义允许负载均衡器访问 Target Group 里这些目标的网络规则,上面写的是放行 ALB 所在安全组访问 8080 端口。

这样分工以后,ALB、WAF 规则和证书都在平台侧的 Terraform 里,业务团队只维护 Service 和 TGB,业务的 CI/CD 不需要任何 AWS 权限。卸载 Helm release 或者删错一个 Ingress,也不会把公网 ALB 一起带走。多个集群也能共用一个公网入口:每个集群绑自己的 Target Group,ALB 规则按 host 或路径分到不同的 Target Group。Target Group 在另一个 AWS 账号时,LBC 从 v2.12.0(2025-03-10 发布)起支持用 iamRoleArnToAssume 跨账号注册(AssumeRole 一节)。

跨集群迁移时,最直接的想法是新旧两个集群都绑同一个 Target Group,这里有一个默认值要改。TGB 的 multiClusterTargetGroup 是 LBC v2.10.0(2024-11-01 发布)加的,默认 false,按文档的说法,这时 LBC 认为自己独占这个 Target Group,会把不在本集群里的目标都注销掉。两个集群绑同一个 Target Group 又不开这个开关,两边的 LBC 会互相注销对方的 Pod。两边都设成 multiClusterTargetGroup: true 之后,流量按两边注册的目标分摊,切流靠调整两边的副本数。另一种做法是两个集群各绑一个 Target Group,在 ALB 的转发规则上给两个 Target Group 配权重,权重取 0 到 999(ALB rule action types),切流时改权重,不用碰两边的副本数。

EKS Auto Mode 里的 TGB 规则不一样(Auto Mode 文档):API 组换成 eks.amazonaws.com/v1,targetType 必填,不支持 spec.networking.ingress.from;Target Group 要打上 eks:eks-cluster-name 标签,控制器才有权限操作它;删掉 TGB 或者整个集群时,控制器会把这个 Target Group 一起删掉。在 Auto Mode 下用 TGB,Target Group 的生命周期就跟着 TGB 走了。

Gateway API:一个 Gateway 对应一个 ALB#

Kubernetes 的 Ingress 文档写明 Ingress API 已经冻结,项目推荐改用 Gateway API。LBC 这边,Gateway API 在 v2.13.0(2025-05-06 发布)加入基础支持,ALB 用的 L7 路由在 v2.14.0(2025-10-02 发布)进入 beta,v3.0.0(2026-01-23 发布)GA;v3.6.0(2026-10-05 发布)的发布说明写的是仍按 Gateway API v1.6.0 的 CRD 构建(LBC Gateway API 文档)。

在 Gateway API 里,共用 ALB 的方式是一个 Gateway 对应一个 ALB。controllerName 为 gateway.k8s.aws/alb 的 GatewayClass 下,LBC 给每个 Gateway 建一个 ALB(L7 文档),各团队在自己的 namespace 里写 HTTPRoute 挂上去。IngressGroup 的信任问题在这里交给 Gateway 监听器上的 allowedRoutes:默认 Same,只有 Gateway 所在 namespace 的路由能挂;Selector 只放行带指定标签的 namespace;All 放行全部 namespace。LBC 文档对多租户的建议是用 Selector,All 只在所有 namespace 同样可信、或者另有 RBAC 限制谁能建 HTTPRoute 时再用(Security: Multi-Tenant Route Attachment)。

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: aws-alb
spec:
  controllerName: gateway.k8s.aws/alb
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shared-api
  namespace: platform
spec:
  gatewayClassName: aws-alb
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: "trusted"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: order
  namespace: order-ns
spec:
  parentRefs:
    - name: shared-api
      namespace: platform
  hostnames: ["api.example.com"]
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /order
      backendRefs:
        - name: order-service
          port: 8080

order-ns 要带上 gateway-access: "trusted" 标签,这个 HTTPRoute 才能挂到 Gateway 上。Gateway API 的安全模型按资源分了写权限:应用开发者只写 Route,Gateway 归集群运维,GatewayClass 归基础设施提供方,各自用 RBAC 控制。

从 IngressGroup 迁过来有现成工具。LBC 的 lbc-migrate 把每个 IngressGroup 转成一个共用的 Gateway 加一份 LoadBalancerConfiguration,组里每个 Ingress 各生成一个 HTTPRoute,留在原来的 namespace。跨 namespace 的组,生成的 Gateway 设的是 allowedRoutes.namespaces.from: All,原样保留了 IngressGroup 的信任模型,迁完以后可以按上面的建议收紧成 Selector。两边有两处行为差别:group.order 在 Gateway API 里没有对应字段,规则先后按 Gateway API 规范里的主机名、路径匹配类型、路径长度等排序(Rule Priority and group.order);同样的路由在 Gateway 下可能生成更多条 ALB 规则。手写的 TGB 和 Ingress、Gateway 都无关,迁移时不用动。迁移指南的做法是新建一套 Gateway 的 ALB,和原来的并存,验证以后再切流。

小结#

LBC 默认一个 Ingress 一个 ALB,us-east-1 每个 ALB 每月光小时费约 16 美元。共用 ALB 有三种做法。IngressGroup 用 group.name 合并规则,ALB 的生命周期仍归 LBC 管;同组的 Ingress 能改彼此的规则,IngressClassParams 的 group 和 namespaceSelector 能把能进组的 namespace 收成白名单,另外要留意每个 ALB 100 个 Target Group 的硬上限。TargetGroupBinding 把 ALB 留给集群外的 Terraform,集群只同步 Pod,适合把 AWS 权限收在平台侧、或者多个集群共用入口;跨集群共用同一个 Target Group 要打开 multiClusterTargetGroup,EKS Auto Mode 下删掉 TGB 会连 Target Group 一起删。Gateway API 在 LBC v3.0.0 GA,Gateway 对应 ALB,HTTPRoute 留在各团队的 namespace,allowedRoutes 管谁能挂路由;Ingress API 已经冻结,新建的共享入口可以直接从 Gateway API 起步。

参考资料#