前言
AWS 提供多种弹性负载均衡器,包括应用程序负载均衡器 (ALB)、网络负载均衡器 (NLB)、网关负载均衡器 (GWLB) 和经典负载均衡器 (CLB)。本文重点介绍 ALB 和 NLB,因为它们是 EKS 集群最相关的选项。
在确定合适的负载均衡器类型时,需要考虑的主要因素是工作负载的具体需求。
什么是应用程序负载均衡器 (ALB)
应用程序负载均衡器 (ALB) 的主要功能是将传入流量分配到多个目标,这些目标可以是:
- EC2 实例(例如,传统计算服务器、EKS 工作节点、ECS 工作节点)。
- IP 地址(例如,本地服务器、第三方服务、跨 VPC 实例)。
- Lambda 函数(例如,用于无服务器应用程序、API 网关的替代方案、混合架构)。
以上示例可视化了一个附加了三个目标组的 ALB,每个目标组代表一种特定的目标类型,例如 EC2 实例、IP 地址和 Lambda 函数。
流量可以分配到至少两个或更多可用区。创建 ALB 时,必须指定要在哪些可用区(每个可用区一个子网)中“启用”它。这意味着 ALB 只能将流量分配到已启用的可用区。
更详细地说,您为 ALB 启用的每个可用区实际上都会为该特定 ALB 创建一个实例。如下所示,我们在 3 个公有子网中启用了 ALB,以将流量分配到 EC2 实例,并且创建了 3 个网络接口(每个启用的子网中一个):
有趣的是,如果没有为特定 ALB 分配目标,那么最初只会创建一个网络接口;当然,这完全由 AWS 管理,不会以任何方式影响 ALB 的性能。
什么是网络负载均衡器 (NLB)
网络负载均衡器可以将传入流量分配到多个目标:
- EC2 实例(例如,传统计算服务器、EKS 工作节点、ECS 工作节点)。
- IP 地址(例如,本地服务器、第三方服务、跨 VPC 实例)。
- 应用程序负载均衡器(例如,将 TCP/UDP 流量分流到更高级的 HTTP/HTTPS 路由、混合架构)。
与 ALB 类似,当您创建 NLB 并在特定可用区中启用它时,每个可用区中都会创建一个负载均衡器实例。每个实例由一个网络接口表示:
负载均衡器如何与EKS集成
负载均衡器接收传入流量,并将其分发到 EKS 集群中托管的目标应用程序的目标上。这提高了应用程序的弹性。当部署到 EKS 集群中时,AWS 负载均衡器控制器将为该集群创建和管理 AWS 弹性负载均衡器。创建 LoadBalancer 类型的 Kubernetes 服务时,AWS 负载均衡器控制器会创建一个网络负载均衡器 (NLB),用于在 OSI 模型的第 4 层对接收到的流量进行负载均衡。而创建 Kubernetes Ingress 对象时,AWS 负载均衡器控制器会创建一个应用程序负载均衡器 (ALB),用于在 OSI 模型的第 7 层对流量进行负载均衡。
何时选择应用程序负载均衡器 (ALB)
对于通过 HTTP/HTTPS 运行的工作负载,请选择应用程序负载均衡器 (ALB)。
如果您的应用程序需要 OSI 模型中的第 7 层负载均衡,AWS 负载均衡器控制器可以预置 ALB,ALB 通过 Ingress 资源进行管理和配置,并将 HTTP 或 HTTPS 流量定向到集群内的各个 Pod。它提供了更改路由算法的灵活性,默认采用循环路由,而 LISTING 请求数算法则作为替代方案。
何时选择网络负载均衡器 (NLB)
如果您的工作负载基于 TCP 或需要为客户端保留源 IP,请选择网络负载均衡器 (NLB)。
NLB 运行在 OSI 模型的传输层(第 4 层),因此适用于使用 TCP 和 UDP 协议的工作负载。默认情况下,NLB 在将流量转发到 Pod 时会保留客户端的原始源 IP 地址。
使用 NLB 的另一个重要考虑因素是客户端无法使用 DNS 的情况。在这种情况下,NLB 通常是更合适的选择,因为它具有静态 IP 地址。虽然建议客户端使用 DNS 将域名解析为用于负载均衡器连接的 IP 地址,但如果客户端的应用程序仅接受硬编码 IP 地址,则 NLB 的静态 IP 地址将是一个更佳的选择。
在EKS中预置负载均衡器
目前有两种主要的 Kubernetes 控制器可用于管理 AWS 负载均衡器实例:
- 旧版 Kubernetes“云控制器管理器”
- “AWS 负载均衡器控制器”
旧版控制器
第一种控制器的代码库位于 Kubernetes 代码库中(也称为“in-tree”云控制器)。它已被弃用并迁移到新的外部代码库。
- in tree(弃用)
由于已经弃用,不在给出代码库和文档链接
- out of tree
后者需要一些额外的步骤才能启用。
全新 AWS 负载均衡器控制器
原名为 ALB 入口控制器,现已更名为 AWS 负载均衡器控制器,并新增了以下功能和特性:
- 适用于 Kubernetes 服务的网络负载均衡器 (NLB)
- 与多个 Kubernetes 入口规则共享 ALB
- 新增 TargetGroupBinding 自定义资源
- 支持完全私有集群
因此,它可以用作管理 NLB 实例的控制器,也可以用作 Ingress 控制器(如果您喜欢使用 Ingress 自定义资源)。
我将演示用于 NLB 的全新 AWS 负载均衡器控制器。
为此,我们将使用这个特殊的注解 service.beta.kubernetes.io/aws-load-balancer-type: "external",同时我们还将部署 AWS 负载均衡器控制器,以便在 NLB 上提供更多选项。
EKS 集群设置
如果您已经运行了 EKS 集群,请跳过此部分,直接进入下一部分。
eksctl create cluster \
--name ${CLUSTER_NAME} \
--version 1.21 \
--region ${REGION} \
--nodegroup-name linux-nodes \
--nodes 3 \
--nodes-min 2 \
--nodes-max 4 \
--with-oidc \
--spot \
--managed
AWS NLB 的 Kubernetes in-tree控制器
这当然不是本文的目的,但了解我们的出发点总是有益的。所以,让我们快速了解一下 AWS ELB 的树内控制器。
CLB
在尝试 NLB 之前,出于好奇,我们创建一个基本服务,类型为 LoadBalancer,不添加任何额外注解,看看 AWS 端会发生什么:
kubectl apply -f -<<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v1
template:
metadata:
labels:
app: httpbin
version: v1
spec:
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
env:
- name: GUNICORN_CMD_ARGS
value: "--capture-output --error-logfile - --access-logfile - --access-logformat '%(h)s %(t)s %(r)s %(s)s Host: %({Host}i)s}'"
ports:
- containerPort: 80
EOF
kubectl expose deploy httpbin --type LoadBalancer --port 8000 --target-port=80
结果是一个 Classic ELB (CLB) 实例。AWS 建议您迁移到 NLB 实例类型:
NLB
以下是注解的简单配置:
apiVersion: v1
kind: Service
metadata:
name: httpbin
namespace: default
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
ports:
- port: 8000
protocol: TCP
targetPort: 80
selector:
app: httpbin
version: v1
type: LoadBalancer
部署 AWS 负载均衡器控制器
因此,AWS 负载均衡器控制器现在是使用 NLB 的推荐方式。
完成IAM 绑定工作:
export CLUSTER_NAME="my-cluster"
export REGION="eu-central-1"
export AWS_ACCOUNT_ID=XXXXXXXXXX
export IAM_POLICY_NAME=AWSLoadBalancerControllerIAMPolicy
export IAM_SA=aws-load-balancer-controller
# Setup IAM OIDC provider for a cluster to enable IAM roles for pods
eksctl utils associate-iam-oidc-provider \
--region ${REGION} \
--cluster ${CLUSTER_NAME} \
--approve
# Fetch the IAM policy required for our Service-Account
curl -o iam-policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.2.4/docs/install/iam_policy.json
# Create the IAM policy
aws iam create-policy \
--policy-name ${IAM_POLICY_NAME} \
--policy-document file://iam-policy.json
# Create the k8s Service Account
eksctl create iamserviceaccount \
--cluster=${CLUSTER_NAME} \
--namespace=kube-system \
--name=${IAM_SA} \
--attach-policy-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:policy/${IAM_POLICY_NAME} \
--override-existing-serviceaccounts \
--approve \
--region ${REGION}
# Check out the new SA in your cluster for the AWS LB controller
kubectl -n kube-system get sa aws-load-balancer-controller -o yaml
安装AWS load balancer 控制器
kubectl apply -k "github.com/aws/eks-charts/stable/aws-load-balancer-controller/crds?ref=master"
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=${CLUSTER_NAME} \
--set serviceAccount.create=false \
--set serviceAccount.name=${IAM_SA}
使用 AWS 负载均衡器控制器创建NLB
首先,让我们告诉 Kubernetes 内置云控制器不要处理该服务。AWS 负载均衡器控制器现在会处理这个问题。开关是这个注解:service.beta.kubernetes.io/aws-load-balancer-type
此外,我们希望 NLB 公开可见(默认情况下,它是一个内部 NLB),并且我们希望它处于实例模式(有助于客户端 IP 保护)
注解更改前
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
更改后
# use the AWS LB Controller
service.beta.kubernetes.io/aws-load-balancer-type: "external"
# we want an internet-facing NLB
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
# use target groups in instance mode
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "instance"
为了启用backend config,访问日志和健康检查,需要添加更多的注解,下面是完整的注解
apiVersion: v1
kind: Service
metadata:
name: httpbin
namespace: default
annotations:
# NLB
service.beta.kubernetes.io/aws-load-balancer-type: "external"
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "instance"
# Backend
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http" # https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/legacy-cloud-providers/aws/aws.go#L182
# Access logs
service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "true"
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name: "${BUCKET_NAME}"
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix: "my-httpbin-app"
# service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "5" # not yet implemented
# LB attributes - you can concatenate values separated with comma signs ','
service.beta.kubernetes.io/aws-load-balancer-attributes: "load_balancing.cross_zone.enabled=false"
# Health checks
service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold: "2" # https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/legacy-cloud-providers/aws/aws.go#L209 2 to 20
service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold: "2" # 2-10
service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval: "10" # 10 or 30
service.beta.kubernetes.io/aws-load-balancer-healthcheck-path: "/status/200"
service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol: "HTTP"
service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "traffic-port"
service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout: "6" # 6 is the minimum
spec:
ports:
- port: 8000
protocol: TCP
targetPort: 80
selector:
app: httpbin
version: v1
type: LoadBalancer
使用 AWS 负载均衡器控制器创建ALB
每当在集群上使用 kubernetes.io/ingress.class: alb 注释创建 Kubernetes 入口资源时,AWS 负载均衡器控制器都会创建 ALB 及必要的 AWS 支持资源。入口资源会配置 ALB,以将 HTTP 或 HTTPS 流量路由到集群内的不同 Pod。为确保您的入口对象使用 AWS 负载均衡器控制器,请将以下注释添加到您的 Kubernetes 入口规范中。
annotations:
kubernetes.io/ingress.class: alb
下面一个完整的ingress 配置清单文件
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: generic-company-ingress
annotations:
kubernetes.io/ingress.class: alb //使用alb
alb.ingress.kubernetes.io/scheme: internet-facing。//external alb
alb.ingress.kubernetes.io/target-type: ip. //使用IP mode,alb 直接和pod通信
external-dns.alpha.kubernetes.io/hostname: mobile.generic123.net //绑定的域名
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]' //监听的端口
alb.ingress.kubernetes.io/group.name: "prod-external" //您可以使用 IngressGroups 在多个服务资源之间共享应用程序负载均衡器
spec:
rules:
- host: mobile.generic123.net
http:
paths:
- path: /apple
pathType: Exact
backend:
service:
name: ssl-redirect
port:
name: use-annotation
- path: /apple
pathType: Exact
backend:
service:
name: apple-service
port:
number: 3000
- path: /android
pathType: Exact
backend:
service:
name: ssl-redirect
port:
name: use-annotation
- path: /android
pathType: Exact
backend:
service:
name: android-service
port:
number: 3000
总结
AWS提供了多种弹性负载均衡器,包括应用程序负载均衡器(ALB)、网络负载均衡器(NLB)、网关负载均衡器(GWLB)和经典负载均衡器(CLB)。本文重点介绍ALB和NLB,因为它们是与EKS集群最相关的选项。ALB适用于HTTP/HTTPS工作负载,支持第7层负载均衡,而NLB适用于TCP/UDP工作负载,支持第4层负载均衡,并能保留客户端源IP。在EKS集群中,AWS负载均衡器控制器负责创建和管理这些负载均衡器。选择哪种负载均衡器取决于工作负载的具体需求。此外,AWS负载均衡器控制器还支持NLB的实例模式和ALB的Ingress资源管理,提供了更灵活的配置选项。