亚马逊AWS官方博客

使用 AWS ALB ingress controller 的 TargetGroupBinding 进行 EKS 集群的蓝绿升级

Kubernetes 作为现代化应用部署中最为受欢迎的编排工具。从 2015 年开始,经过了快速的迭代,每年以 3 个版本(minor version)的速度进行迭代。越来越多新的特性和功能在新版本中涌现,状态会从 alpha 到 beta,再到最后的 stable 阶段。

Amazon EKS(Elastic Kubernetes Service)是和上游 Kubernetes 完全兼容的托管容器编排服务,在 AWS 上,提供至少 4 个版本的支持,迭代速度和开源社区大致同步。

因为 Amazon EKS 的控制平面是由 AWS 托管的,因此用户可以无需担心在版本升级过程中的控制平面组件的版本兼容问题。但是数据平面和控制平面的兼容问题取决于用户的节点组(托管节点组或者非托管节点组)内的 kubelet 版本。kubelet 版本不能高于 api server 的版本,而且最多只能落后于 api server 两个次要版本。

在升级 EKS 的过程中,我们推荐先升级控制平面再升级数据平面。升级有原地升级和新建集群升级两种方案。

  • 原地升级:先升级控制平面再原地升级数据平面,并升级应用和插件;或者先升级控制平面,再创建新的节点组替代旧节点组。
  • 新建集群升级:创建新版本 EKS 集群,将应用部署到新集群,再进行流量的灰度或者一次性割接。

在以上两种方案中,新建集群进行升级,再进行流量切换的形式会带来最少的 down 机时间,并给予我们最充分的新版本集群兼容性测试的时间。

但是在很多的场景里面,用户在 EKS 中用来承接用户流量的七层负载均衡器 ALB 和四层负载均衡器 NLB,往往非常难更改,因为它们可能已经被外部 DNS 服务所指定为 CNAME 解析,一旦更改 EKS 流量入口的负载均衡器将会对终端用户造成巨大的影响。

本文将从如何利用 AWS Load Balancer Controller 的 TargetGroupBinding 这个 Custom Resource 来将现有集群的流量割接或者灰度到新的集群上。

我们假设您已经有一定的 AWS EKS 经验,并对 AWS Application/Network LoadBalancer,目标组-TargetGroup 以及 AWS Load Balancer Controller 有一定的理解。

AWS Load Balancer Controller

AWS LoadBalancer Controller 是一个为管理 AWS 云上的 Elastic LoadBalancer,包括 Application LoadBalancer和Network LoadBalancer 而出现的一个控制器。当我们在 EKS 集群里面部署和创建 ALB Ingress 或者是 Type 为 LoadBalancer 的 Service 时,这个控制器会自动创建对应的 ALB/NLB。

TargetGroup

AWS TargetGroup -目标组是一个用于路由流量的对象,它将流量路由到注册的 EC2 实例、容器、IP 地址或 AWS Lambda 函数。您可以使用目标组来实现以下目标:

  • 负载均衡:将流量路由到多个目标以实现负载均衡。
  • 自动扩展:将流量路由到自动扩展组中的实例。
  • 容器服务:将流量路由到 Amazon ECS/EKS 服务中的容器。
  • 灰度发布:将流量路由到新版本的应用程序,以便在生产环境中进行测试。
  • 蓝绿部署:将流量路由到新版本的应用程序,以便在生产环境中进行测试,然后将所有流量切换到新版本。

TargetGroupBinding

TargetGroupBinding 是 AWS LoadBalancer 控制器内部使用的对象,用于支持 Ingress 和 Service 资源的功能。它会在使用的 Service 相同的命名空间中自动创建 TargetGroupBinding,用以连接 AWS targetgroup 和对应的 EKS services。

在本文中,我们利用如下架构来示例如何使用 TargetGroupBinding 来进行新建集群升级过程中的流量迅速切换。

  1. 两个 EKS 集群:一个用作旧的集群(Blue),一个用作升级之后的集群(Green);
  2. 在两个 EKS 集群均部署相同应用;
  3. 两种服务队外暴露方式 – ALB 和 NLB;
  4. 创建两个新的目标组 – targetgroup,用以绑定到新集群的 services。

假设:我们假设应用的部署,以及 PV/PVC 已经通过类似 Velero 这样的工具在旧的集群进行备份,在新的集群进行恢复,本文重点描述流量的线上切换。

新旧集群概况

旧集群 – 用以演示的两个服务:nginx-server 和 nginx-service-nlb,其中 nginx-service 使用 ALB 进行对外发布服务,nginx-service-nlb 使用 NLB 进行队外发布服务。

新集群 – 通过 Velero 或者 argocd 之类的工具进行应用的恢复和部署。

Service/NLB

在旧集群上我们查看现有的 NLB 的情况:

现有的 service,对应的 NLB 后端目标组如下:

我们根据现有的 NLB 的情况创建一个新的 TargetGroup – NLB-EKS-Green-TG,协议,端口以及 VPC 保持一致,创建完成之后,我们观察到没有目标注册,也没有绑定任何的负载均衡器:

编写如下 TargetGroupBinding,并在新的集群进行部署:

apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
  name: nlb-eks-green-tg
  namespace: test
spec:
  serviceRef:kubect
    name: nginx-service-nlb # 目标对应service
    port: 80
  targetGroupARN: arn:aws:elasticloadbalancing:ap-east-1:460811381319:targetgroup/NLB-EKS-Green-TG/1d563c0b180440a6

部署完成之后,查看新集群内的 targetgroupbinding 情况:

这个时候我们可以看到,新建的目标组已经注册了新集群内的服务,这里我们使用 ip mode,因为还未进行负载均衡器绑定,目标的状态未未使用:

我们在旧集群暴露服务的 NLB 上进行监听器的目标组修改:

修改完成之后,我们可以看到新的目标组已经绑定负载均衡器,目标状态是 healthy:

在更改的过程中,我们使用 curl 命令持续进行服务的调用,可以看到服务在切换过程中并无中断:

ALB Ingress

和 NLB service 类似,我们在旧的集群已经使用 ALB ingress 暴露服务:

检查现有 ALB 的配置 – 监听器以及目标组:

我们将根据目标组来创建一个相同配置的新的目标组 – ALB-EKS-Green-TG:

ALB-EKS-Green-TG 和现有目标组配置保持一致:

编写如下的 TargetGroupBinding:

apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
  name: alb-eks-green-tg
  namespace: test
spec:
  serviceRef:
    name: nginx-service # 目标对应service
    port: 80
  targetGroupARN: arn:aws:elasticloadbalancing:ap-east-1:460811381319:targetgroup/k8s-test-nginxser-a45be1cb38/10e8248439069e72

部署 TargetGroupBinding:

部署完成之后,和 NLB 的例子相同,在未进行 ALB 监听器的目标组更改之前,目标组显示为未绑定负载均衡器,注册目标为未使用:

在持续调用服务的同时,我们进行目标组切换:

可以观察到,服务的调用并未因为目标组的切换而掉线。

注意:

  1. 如果您已经创建一个 TargetGroupBinding,在部署之后修改,然后重新部署,会出现如下错误——已有的 TargetGroupBinding 不能修改 targetGroupARN。
ljunje@5c52309ccaa6 eks % kubectl apply -f ALB-EKS-Green_TG.yaml
Error from server (TargetGroupBinding update may not change these fields: spec.targetGroupARN): error when applying patch:
xxxxxxxxx
xxxxxxxxx
xxxxxxxxx
to:
Resource: "elbv2.k8s.aws/v1beta1, Resource=targetgroupbindings", GroupVersionKind: "elbv2.k8s.aws/v1beta1, Kind=TargetGroupBinding"
Name: "alb-eks-green-tg", Namespace: "test"
for: "ALB-EKS-Green_TG.yaml": admission webhook "vtargetgroupbinding.elbv2.k8s.aws" denied the request: TargetGroupBinding update may not change these fields: spec.targetGroupARN
  1. 在修改现有的负载均衡器的监听器目标组时,我们建议您先停用 alb ingress controller,比如将该 controller 的副本调整为 0,因为如果客户手工在 NLB/ALB 上添加了其他侦听器或转发规则,在 Controller Reconcile 时会被删除掉,Controller 会使所有的侦听器和规则与 Service/Ingress yaml 中的定义保持一致。

总结

在 EKS 的升级,或者是服务的发布过程中,我们可以通过新建 EKS 集群,使用 TargetGroupBinding 的方式,进行流量的灰度或者切割,从而让我们可以有更多的时间来进行新集群和新服务的测试,并有足够安全的回滚方案。

参考资料

本篇作者

李俊杰

AWS 解决方案架构师,负责基于 AWS 的云计算方案的咨询与架构设计,同时致力于容器方面研究和推广。在加入 AWS 之前曾在金融行业 IT 部门负责传统金融系统的现代化改造,对传统应用的改造,容器化具有丰富经验。

林旭芳

AWS 解决方案架构师,主要负责 AWS 云技术和解决方案的推广工作,在 Container、容灾等方向有丰富实践经验。