亚马逊AWS官方博客

使用 AWS Load Balancer Controller 的 TargetGroupBinding 功能实现灵活的负载均衡器管理

AWS Load Balancer Controller 是一个控制器,可帮助管理 Kubernetes 集群的 Elastic Load Balancer。

  • 它通过部署和配置 Application Load Balancers – ALB 来提供 Kubernetes Ingress 资源。
  • 它通过部署和配置 Network Load Balancers – NLB 来提供 Kubernetes Service 资源。

我们在之前的博客中介绍了 如何通过 AWS Load Balancer Controller 实现在 AWS EKS 平台上发布 K8S 服务,包括通过新的Annotation: alb.ingress.kubernetes.io/group.name注释来支持在不同的Ingress对象上共享同一个ALB;以及通过使用Network Load Balancer + IP模式实现南北向 HTTP(S) 流量的导入等。

通过 AWS Load Balancer Controller,在创建 Kubernetes Service 和 Ingress 资源时,Controller 会自动创建 AWS NLB 或 ALB 资源以及相应的 Target Group,将 Pod 注册到 Target Group 中(通过 instance 模式或 IP 模式),并配置侦听器和相应的转发规则以便将流量发送给 Pod。当 Kubernetes Service 或 Ingress 删除时,对应的 NLB/ALB 和 Target Group 资源也一并被删除。另外,如果客户手工在 NLB/ALB 上添加了其他侦听器或转发规则,在 Controller Reconcile 时会被删除掉,Controller 会使所有的侦听器和规则与 Service/Ingress yaml 中的定义保持一致。

在一些实际场景中,客户希望在 Amazon Elastic Kubernetes Service (Amazon EKS) 集群之外统一管理 ALB/NLB,由客户自己而不是 AWS Load Balancer Controller 管理 ALB/NLB 和 Target Group 的生命周期;或者,需要 EKS Service 和 集群外的服务共享同一个 ALB/NLB,需要在 ALB/NLB 上手工配置其他转发规则且不希望 Controller Reconcile 时被删除。

在上面的两种场景中,我们可以使用 AWS Load Balancer Controller 中的新功能 TargetGroupBinding,它允许客户在 EKS 集群之外自己创建和管理 NLB/ALB 和 TargetGroup,或者在 NLB/ALB 上自己创建和管理其他的侦听器和规则。通过 TargetGroupBinding 可以将 Kubnernetes Service和指定的 Target Group 进行关联,帮助客户管理Pod 到 Target Group 的注册和注销;同时,它也只会管理与 Service 关联的特定 Target Group,而不会修改或删除同一个 NLB/ALB 上的任何其他内容。

本文通过实验演示 TargetGroupBinding 的创建和功能,实验以 ALB 为例,使用 NLB 时的配置和步骤一样。实验包括以下几个步骤:

  1. 在 EKS 集群之外创建 ALB,Target Group(IP 模式和 instance 模式)
  2. IP 模式下,在 EKS 集群内创建 Service,然后创建 TargetGroupBinding 将 Service 关联到 Target Group;验证 Target Group 中自动注册了 Target(Pod),且通过 ALB 相应端口可正常访问 Pod 内服务
  3. Instance 模式下,在 EKS 集群内创建 Service,然后创建 TargetGroupBinding 将 Service 关联到 Target Group;验证 Target Group 中自动注册了 Target(EC2),且通过 ALB 相应端口可正常访问 Pod 内服务
  4. (以 IP 模式为例)扩容 Service 中的副本数,验证关联的 Target Group 中 Target 数也相应扩容;删除 TargetGroupBinding(即取消 Service 与 Target Group 的关联),验证Target Group 中的 Target 也相应注销

创建 ALB, Target Group Listener

在 EKS 集群外,我们通过命令行创建一个 internet-facing 的 ALB,一个 IP 模式的 Target Group 和 一个 instance 模式的 Target Group,并在这个 ALB 上为两个 Target Group 分别创建两个 Listener。

创建 ALB

将下面的 子网、安全组 等内容替换为您自己环境中的实际值。创建完成后,记录下返回的 ALB ARN 值以便后续使用。

$ aws elbv2 create-load-balancer --name test-alb --subnets <SUBNET-ID-1> <SUBNET-ID-2> --security-groups <SG-ID> --scheme internet-facing --type application --ip-address-type ipv4

创建 IP 模式的 Target Group 和对应的 Listener

创建 Target Group:将下面 VPC ID 替换成您自己环境中的实际值(EKS 集群所在 VPC)。创建完成后,记录下返回的 Target Group ARN 以便后续使用。

$ aws elbv2 create-target-group --name test-tg-ip --protocol HTTP --port 80 --vpc-id <VPC_ID> --target-type ip

创建 Listener:将下面 ALB ARN 和 Target Group ARN 替换成您自己环境中的实际值(刚才创建返回的值)。

$ aws elbv2 create-listener --load-balancer-arn <ALB_ARN> --protocol HTTP --port 28080 --default-actions Type=forward,TargetGroupArn=<TG_IP_ARN>

创建 instance 模式的 Target Group 和对应的 Listener

创建 Target Group:将下面 VPC ID 替换成您自己环境中的实际值(EKS 集群所在 VPC)。创建完成后,记录下返回的 Target Group ARN 以便后续使用。

在 instance 模式下,ALB 会通过 NodePort 将流量转发到 Pod,因此我们指定了 30001 作为 Target Group 的端口,后面创建 K8S Service 时也会使用对应的这个端口。

$ aws elbv2 create-target-group --name test-tg-instance --protocol HTTP --port 30001 --vpc-id <VPC_ID> --target-type instance

创建 Listener:将下面 ALB ARN 和 Target Group ARN 替换成您自己环境中的实际值(刚才创建返回的值)。

$ aws elbv2 create-listener --load-balancer-arn <ALB_ARN> --protocol HTTP --port 38080 --default-actions Type=forward,TargetGroupArn=<TG_INSTANCE_ARN>

ALB 和两个 Target Group 及对应的 Listener 创建完成后,我们可以通过控制台进行查看。

TargetGroupBinding 参数介绍

TargetGroupBinding Specification

apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: <YOUR_TGB_NAME> 
spec:
serviceRef:
  name: <YOUR_K8S_SERVICE_NAME> # K8S Service you need to route traffic to
  port: <YOUR_K8S_SERVICE_PORT> # K8S Service port you need to route traffic to
targetGroupARN: <YOUR_TARGET_GROUP_ARN>  # Your existing ALB and TG created outside of EKS
networking: # Networking defines the networking rules to allow ELBV2 LoadBalancer to access targets in TargetGroup
  ingress:
  - from:
    - securityGroup:
        groupID: <YOUR_SG_ID> # Security Group allowed to access targets in TargetGroup
    ports:
    - protocol: <YOUR_PROTOCOL> # Protocol which should be made accessible on the targets in TargetGroup
      port: <YOUR_PORT> # Port which should be made accessible on the targets in TargetGroup

TargetGroupBinding Specification 包括:

  1. targetGroupARN

String. 要关联到 Kubnernetes Service 的 Target Group ARN。Target Group 需要在 EKS 之外创建。

  1. serviceRef

name: string. 要关联的 Service 名称。

port: IntOrString. 可选。要关联的 Service 的端口,如果没定义,则会根据关联 Service 的端口自动填充。

  1. NetworkingIngressRule

NetworkingIngressRule 定义了允许 ALB/NLB 访问 Target Group 中 Targets 的网络规则。如果没有在 TargetGroupBinding 中定义规则,则需要手工配置 EKS Worker Node 的安全组放行相应端口。其中,

from 可定义为:

  • securityGroup: string,针对 ALB 使用,定义可访问 Targets 的安全组 ID。一般我们可写为 ALB 的安全组 ID。
  • ipBlock: string,针对 NLB 使用,定义可访问 Targets 的IP CIDR 段。由于 NLB 没有安全组,并且当 Target Group 以 IP 方式注册时,会将访问 Targets 的 IP 替换成 NLB的私有 IP。因此我们可写为 NLB 所在子网的 CIDR 段。

port可定义为:

可选。允许ALB/NLB 访问Targets 的网络端口。如果定义了端口,则会在 EKS Worker Node 安全组上放行相应端口;如果没有定义,则默认为放行所有端口。

TargetGroupBinding 示例

ALB TargetGroupBinding yaml 文件示例:

apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: my-tgb-alb-ip
spec:
serviceRef:
  name: nginx-tgb-svc-alb-ip # route traffic to nginx-tgb-svc-alb-ip
  port: 80 # service port
targetGroupARN: arn:aws-cn:elasticloadbalancing:cn-north-1:<account_id>:targetgroup/test-tg-ip/1ce5b7da835c9ffb # TG ARN
networking:
  ingress:
  - from:
    - securityGroup:
        groupID: sg-XXX # ALB SG ID
    ports:
    - protocol: TCP # Allow all TCP traffic from ALB SG

NLB TargetGroupBinding yaml 文件示例:

apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: my-tgb-nlb-ip
spec:
serviceRef:
  name: nginx-tgb-svc-nlb-ip # srvice name
  port: 9090 # service port
targetGroupARN: arn:aws-cn:elasticloadbalancing:cn-north-1:<account_id>:targetgroup/test-tg/9f1777b2c36e0c8c # TG ARN
networking:
  ingress:
  - from:
    - ipBlock:
        cidr: 192.168.32.0/19 # subnet-1 cidr
    - ipBlock:
        cidr: 192.168.64.0/19 # subnet-2 cidr
    ports:
    - protocol: TCP
      port: 9090 # Only allow TCP 9090 from subnet cidr

IP 模式下的 TargetGroupBinding

EKS 集群中创建 Service

创建一个 Headless Service 用于关联到 IP 模式的 Target Group。

Service yaml 文件如下:

apiVersion: v1
kind: Service
metadata:
name: "nginx-tgb-svc-alb-ip"
spec:
selector:
  app: nginx
clusterIP: None
ports:
- protocol: TCP
  port: 80
  targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
  app: nginx
name: nginx-deployment
spec:
replicas: 1
selector:
  matchLabels:
    app: nginx
template:
  metadata:
    labels:
      app: nginx
  spec:
    containers:
    - image: nginx
      name: nginx
      ports:
      - containerPort: 80

创建 Service:

$ kubectl apply -f nginx-tgb-svc-alb-ip.yaml

创建 TargetGroupBinding

TargetGroupBinding yaml 文件如下,其中,将 targetGroupARN 替换成您自己刚才创建的 IP 模式的 Target Group ARN 值,ALB_SG_ID 替换成您的 ALB 安全组 ID。

apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: my-tgb-alb-ip
spec:
serviceRef:
  name: nginx-tgb-svc-alb-ip # route traffic to nginx-tgb-svc-alb-ip (ClusterIP=None)
  port: 80
targetGroupARN: <TG_IP_ARN> # ALB and TG are created outside of EKS
networking:
  ingress:
  - from:
    - securityGroup:
        groupID: <ALB_SG_ID> # ALB SG ID
    ports:
    - protocol: TCP # Allow all TCP traffic from ALB SG

创建 TargetGroupBinding:

$ kubectl apply -f tgb-alb-ip.yaml

验证 Pod 已成功注册到 Target Group 中并可正常访问

通过控制台可以看到,当 TargetGroupBinding 创建完成后,Service 对应的 Pod 就成功注册到 Target Group 中。

与 Service 对应的 Pod 是一致的:

$ kubectl describe svc nginx-tgb-svc-alb-ip
Name:             nginx-tgb-svc-alb-ip
Namespace:         default
Labels:           <none>
Annotations:       Selector:  app=nginx
Type:             ClusterIP
IP:               None
Port:             <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         192.168.68.252:80
Session Affinity: None
Events:           <none>

查看 EKS Worker Node 所在安全组,可以看到TargetGroupBinding 增加了一条安全组规则(在描述中有 elbv2.k8s.aws/targetGroupBinding=shared 字样),允许来自 ALB SG 的所有 TCP 流量,与我们在 yaml 文件中定义的一致。

通过 ALB 及对应的端口(28080)访问该 Service,注意要为 ALB 的安全组放行 28080 端口。

Instance 模式下的 TargetGroupBinding

EKS 集群中创建 Service

创建一个 NodePort 类型的 Service 用于关联到 instance 模式的 Target Group,nodePort 指定为 30001 与 Target Group 一致。

Service yaml 文件如下:

apiVersion: v1
kind: Service
metadata:
name: "nginx-tgb-svc-alb-instance"
spec:
selector:
  app: nginx
type: NodePort
ports:
- protocol: TCP
  port: 80
  targetPort: 80
  nodePort: 30001

创建 Service:

$ kubectl apply -f nginx-tgb-svc-alb-instance.yaml

创建 TargetGroupBinding

TargetGroupBinding yaml 文件如下,其中,将 targetGroupARN 替换成您自己刚才创建的 instance 模式的 Target Group ARN 值,ALB_SG_ID 替换成您的 ALB 安全组 ID。

apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: my-tgb-alb-instance
spec:
serviceRef:
  name: nginx-tgb-svc-alb-instance # route traffic to nginx-tgb-svc-alb-instance (NodePort)
  port: 80
targetGroupARN: <TG_INSTANCE_ARN> # ALB and TG are created outside of EKS
networking:
  ingress:
  - from:
    - securityGroup:
        groupID: <ALB_SG_ID>  # ALB SG ID
    ports:
    - protocol: TCP
      port: 30001 # Allow 30001 TCP traffic from ALB SG

创建 TargetGroupBinding:

$ kubectl apply -f tgb-alb-instance.yaml

验证 EC2 Instance 已成功注册到 Target Group 中并可正常访问

通过控制台可以看到,当 TargetGroupBinding 创建完成后,EKS Node 所在的 EC2 instance 就成功注册到 Target Group 中。

查看 EKS Worker Node 所在安全组,可以看到TargetGroupBinding 增加了一条安全组规则(在描述中有 elbv2.k8s.aws/targetGroupBinding=shared 字样),允许来自 ALB SG 的 30001 端口的 TCP 流量,与我们在 yaml 文件中定义的一致。

通过 ALB 及对应的端口(38080)访问该 Service,注意要为ALB 的安全组放行 38080 端口。

Service 的更新与取消关联

我们以 IP 模式为例,验证 Service 在扩容和删除时,TargetGroupBinding 会自动更新对应 Target Group 中的 Target。

Service 扩容

将 Service 对应的 deployment 副本数从 1 个修改为 4 个,观察 Target Group 的变化。

$ kubectl scale deployment nginx-deployment --replicas 4
deployment.apps/nginx-deployment scaled

通过控制台可以看到,当 Service 扩容后,新创建的 Pod 成功注册到 Target Group 中。

与 Service 对应的 Pod 是一致的:

$ kubectl describe svc nginx-tgb-svc-alb-ip
Name:             nginx-tgb-svc-alb-ip
Namespace:         default
Labels:           <none>
Annotations:       Selector:  app=nginx
Type:             ClusterIP
IP:               None
Port:             <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         192.168.37.209:80,192.168.41.89:80,192.168.52.97:80 + 1 more...
Session Affinity: None

Service Target Group 取消关联

将 IP 模式对应的 TargetGroupBinding 删除,观察 Target Group 的变化。

查看现有的 TargetGroupBinding

$ kubectl get targetgroupbindings.elbv2.k8s.aws
NAME                         SERVICE-NAME                 SERVICE-PORT   TARGET-TYPE   AGE
my-tgb-alb-instance           nginx-tgb-svc-alb-instance   80             instance     22m
my-tgb-alb-ip                 nginx-tgb-svc-alb-ip         80             ip           34m

删除 my-tgb-alb-ip

$ kubectl delete targetgroupbindings.elbv2.k8s.aws my-tgb-alb-ip
targetgroupbinding.elbv2.k8s.aws "my-tgb-alb-ip" deleted

通过控制台可以看到,当 Service 删除后,对应的 Pod 从 Target Group 中注销。而我们自己创建的 ALB 和 Target Group 仍然保持。

 

通过上面的实验演示,我们介绍了如何通过AWS Load Balancer Controller 中的新功能 TargetGroupBinding,让客户更加灵活的实现在 Amazon EKS 集群之外自己创建和管理 NLB/ALB 和 TargetGroup,或者在 NLB/ALB 上自己创建和管理其他的侦听器和规则。希望本文的内容能帮助您在更多业务场景中更好的采用 Amazon EKS。

 

参考资料

本篇作者

郭勋

AWS 解决方案架构师,负责基于 AWS 的云计算方案的架构设计,同时致力于 AWS 云服务在移动应用与互联网行业的应用和推广,有十年的云计算和云管理平台解决方案架构经验。