月度归档: 2021年2月

在AWS EKS上发布K8S服务(下)

本文将介绍在AWS Load Balancer Controller(V2)中发布的其他几项重要功能:通过在上篇中创建好的EKS集群中实验通过使用Network Load Balancer (NLB)+ IP模式实现南北向HTTP(S)流量的导入,以及演示通过引入Target Group Bingding CRD来实现将EKS中的服务灵活地绑定到已有Target Group上的新功能。

操作步骤

使用Network Load Balancer(NLB) + IP模式实现南北向流量的导入

除了上篇中介绍的使用Application Load Balancer的基于七层的HTTP流量导入,我们很多时候也会有基于四层TCP流量的导入需求,在EKS平台我们可以使用NLB来实现这个需求。下面的实验分别演示使用NLB的几种方法。

部署示例服务nginx,并通过Classic Load Balancer(CLB)发布

运行下列命令生成样例程序nginx的K8S部署和服务,注意通过设置type: LoadBalancer将服务通过AWS负载均衡器(默认类型为CLB)发布到互联网

cat <<EoF > ~/environment/nginx.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: "nginx"
spec:
  selector:
    app: nginx
  type: LoadBalancer
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
EoF
kubectl apply -f ~/environment/nginx.yaml

通过Kubectl命令查看新生成的ningx服务,运行下列命令可以看到除了默认的kubernetes服务之外新增加了一个nginx服务,这个服务的EXTERNAL-IP指向一个负载均衡器的域名

kubectl get svc

在EC2服务控制台查看Load Balancer,输入上一条命令生成的EXTERNAL-IP的值作为过滤条件就可以查验刚刚生成的nginx服务对应的CLB,验证列表中负载均衡器的Type为classic

通过Network Load Balancer(实例模式)发布

接下来,我们运行下列命令生成nginx-nlb-instance服务,其中绝大部分设置都和CLB实验中的服务相同,不同之处就是这里通过设置annotations的service.beta.kubernetes.io/aws-load-balancer-type: nlb将负载均衡器的类型更改为Network Load Balancer (NLB)

cat <<EoF > ~/environment/nginx-nlb-instance.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: "nginx-nlb-instance"
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: nlb
spec:
  selector:
    app: nginx
  type: LoadBalancer
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
EoF
kubectl apply -f ~/environment/nginx-nlb-instance.yaml

类似的,查看更改生成的nginx-nlb-instance服务

kubectl get svc nginx-nlb-instance

在EC2服务控制台查看Load Balancer,输入上一条命令生成的EXTERNAL-IP的值作为过滤条件就可以查验刚刚生成的nginx-nlb-instance服务对应的NLB,验证列表中的Type为network

 

待该NLB的状态从provisioning变为active后,选中该NLB后点击Listeners选项页查看该NLB对应的Listener,查看默认行为中的转发目标:Target Group的值(类似k8s-default-nginxnlb-xxxxx),点击该Target Group跳转到Target Group页面,选择Targets选项页,可以看到已经注册到该Target Group中的两个Targets为EC2 Instance类型,即在EKS集群中创建好的两台EC2主机节点

图1 – NLB Target Group中Targets为EC2 Instance类型

 

通过Network Load Balancer(IP模式)发布

接下来,我们运行下列命令生成nginx-nlb-ip服务,并通过设置annotations的service.beta.kubernetes.io/aws-load-balancer-type: nlb-ip 将负载均衡器的类型更改为NLB + IP模式

cat <<EoF > ~/environment/nginx-nlb-ip.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: "nginx-nlb-ip"
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: nlb-ip
spec:
  selector:
    app: nginx
  type: LoadBalancer
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
EoF
kubectl apply -f ~/environment/nginx-nlb-ip.yaml

运行下列命令查看更改生成的nginx-nlb-ip服务

kubectl get svc nginx-nlb-ip

类似的,根据nginx-nlb-ip服务的EXTERNAL-IP值在EC2的负载均衡器页面查看对应该服务的NLB。可以看到和上述nginx-nlb-instance服务所不同的是,service.beta.kubernetes.io/aws-load-balancer-type 设置为nlp-ip后NLB将Target类型有EC2 Instance类型变更为IP地址类型(192.168.x.x),如下图所示

 

图2 – NLB Target Group中Targets为IP.类型

通过运行下列系列命令查看nginx Pod的IP地址,可知上述IP地址列表中列出的IP正是Nginx Pod所使用的IP

kubectl get pod -o wide

 

Target Group Binding

TargetGroupBinding是在AWS Load Balancer Controller中引入的一种K8S的CRD(Custom Resource Definition),目的是用来将K8S中的服务绑定到已有的ALB或NLB的 Target Group上从而进行更加灵活的服务发布,实现南北向流量导入。有了TargetGroupBinding的支持,可以使你能够使用EKS集群之外的Load Balanacer和Target Group。

通过以下命令查验安装AWS Load Balancer Controller过程中同时安装的TargetGroupBinding的CRD

kubectl get crd targetgroupbindings.elbv2.k8s.aws

运行下面的命令创建nginx-tgb-svc服务

cat <<EoF > ~/environment/nginx-tgb-svc.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: "nginx-tgb-svc"
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: nlb-ip
spec:
  selector:
    app: nginx
  type: LoadBalancer
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 80
EoF
kubectl apply -f ~/environment/nginx-tgb-svc.yaml

运行下列命令在EKS集群所在的VPC中创建一个名称为my-ip-targets的Target Group

export VPC_ID=$(aws ec2 describe-vpcs --filters Name=tag:Name,Values=eksctl-eksworkshop-albc* | jq -r '.Vpcs[].VpcId')
aws elbv2 create-target-group --name my-ip-targets --protocol TCP --port 8080 --target-type ip --vpc-id $VPC_ID
export TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --name my-ip-targets --query 'TargetGroups[0].TargetGroupArn')

接下来我们将这个Target Group绑定到服务nginx-nlb-ip对应的NLB上面(便于演示),运行下列命令

kubectl get svc nginx-nlb-ip

在EC2服务的Load Balancer子项的Load Balancer列表中输入上面命令生成的EXTERNAL-IP作为过滤条件查看服务nginx-nlb-ip对应的NLB实例,同时将此NLB的ARN复制替换到下列命令

export LOAD_BALANCER_ARN=<Replace with Above NLB ARN >

在nginx-nlb-ip对应的NLB实例上创建新的Listener并绑定刚刚创建好的Target Group

aws elbv2 create-listener --load-balancer-arn $LOAD_BALANCER_ARN --protocol TCP --port 8080 --default-actions
Type=forward,TargetGroupArn=$TARGET_GROUP_ARN 

可以看到nginx-nlb-ip对应的NLB实例上新增加了一个Listener TCP:8080并与新建的Target Group:my-ip-targets成功关联

图3 – NLB 监听器中新绑定的Target Group

运行下列命令可以列出目前AWS Load Balancer Controller针对服务nginx-nlb-ip和nginx-tgb-svc创建的两个Target Group Binding

kubectl get targetgroupbindings

接下来运行下列命令创建一个名称为my-tgb-1的Target Group Binding,将Target Group:my-ip-targets的流量分发到服务nginx-tgb-svc上

cat <<EoF > ~/environment/tgb.yaml.template
---
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
  name: my-tgb-1
spec:
  serviceRef:
    name: nginx-tgb-svc # route traffic to the awesome-service
    port: 8080
  targetGroupARN: $TARGET_GROUP_ARN
EoF
envsubst < ~/environment/tgb.yaml.template > ~/environment/tgb.yaml
kubectl apply -f ~/environment/tgb.yaml

重新运行下列命令查看新创建的Target Group Binding:my-tgb-1

kubectl get targetgroupbindings

运行下列命令可以验证刚刚创建的Target Group Binding:my-tgb-1成功将流量从nginx-nlb-ip服务对应的NLB的8080端口的Listener成功的转发到了服务nginx-tgb-svc上,两条curl命令的输出的内容相同且都包含Welcome to nginx!

export loadbalancer=$(kubectl get svc nginx-nlb-ip -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')
curl $loadbalancer
curl $loadbalancer:8080

删除上文中创建的K8S对象

按照与上述创建各种资源相反的顺序删除创建的各种K8S对象

kubectl delete -f ~/environment/tgb.yaml
kubectl delete -f ~/environment/nginx-tgb-svc.yaml
kubectl delete -f ~/environment/nginx-nlb-ip.yaml
kubectl delete -f ~/environment/nginx-nlb-instance.yaml
kubectl delete -f ~/environment/nginx.yaml

删除EKS集群和Cloud9环境

删除EKS托管与非托管节点组,最后删除EKS集群。

eksctl delete nodegroup --cluster $EKS_CLUSTER_NAME --name $EKS_NODEGROUP_NAME
eksctl delete nodegroup --cluster $EKS_CLUSTER_NAME --name $EKS_MANAGED_NODEGROUP_NAME
eksctl delete cluster --name $EKS_CLUSTER_NAME

最后,在AWS控制台的Cloud9服务的环境列表中删除eksworksohp演示环境。

 

总结

在EKS平台我们可以使用AWS Load Balancer Controller与NLB轻松的实现四层TCP流量的导入,并同时支持EC2 Instance与IP模式。同时因为有了NLB + IP模式的支持,使得在EKS Fargate模式下也可以应用NLB+IP模式来实现无服务器的EKS四层应用南北向流量的导入。另外新增的Target Group Binding功能不仅仅使得AWS Load Balancer Controller的内部实现解耦、更加高效,也可以满足将EKS集群内部的服务绑定到集群外部ALB/NLB的Target Group上的需求。

 

参考资料

  • Network load balancing on Amazon EKS

       https://docs.aws.amazon.com/eks/latest/userguide/load-balancing.html

  • AWS Load Balancer Controller 官方博客

       https://aws.amazon.com/blogs/containers/introducing-aws-load-balancer-controller/

  • AWS Load Balancer Controller 在线文档

       https://kubernetes-sigs.github.io/aws-load-balancer-controller/

  • EKS 工作坊

       https://www.eksworkshop.com/

在 AWS EKS 平台上发布 K8S 服务(上)

背景

目前已经有许多AWS EKS的客户通过使用ALB Ingress Controller来实现南北向七层流量的导入,在项目实施过程中碰到的一个比较集中的问题就是如何使用同一个Application Load Balancer(ALB)来实现对入口流量按照多个路由规则匹配来转发到EKS集群中运行的多个服务或者Pod内,避免因为多服务或者微服务架构中产生的大量服务对应生成几十个甚至数百个ALB的情况,进而避免增加运维管理负担以及资源使用上不必要的成本开销。

本文分为上下两篇,分别介绍在EKS上发布多个服务以及单个服务的不同方法,同时提供具体的操作步骤供大家动手实验。

上篇通过具体的示例来演示在同一个ALB Ingress Controller(V1) Ingress对象上通过扩展ALB Rule增加不同的URl路径映射来实现这个需求。同时随着AWS Load Balancer Controller在2020年10月份的发布,通过新的Annotation: alb.ingress.kubernetes.io/group.name注释来支持在不同的Ingress对象上共享同一个ALB,本文同样通过具体的示例介绍如何使用这个新功能。文中的实验会给读者一个相对直观的认识,从而理解和掌握在AWS EKS平台上灵活使用Ingress来实现HTTP(S)流量导入。通过同时介绍ALB Ingress Controller(V1)和AWS Load Balancer Controller(V2)Ingress的不同定义方法来实现绑定到同一个ALB的实现来对比两个版本的功能,进而更好的区别V1和V2中的异同点,避免在概念理解和项目实施过程中产生混淆。

下篇介绍在AWS Load Balancer Controller(V2)中发布的其他几项重要功能,包括对四层流量由Network Load Balancer转发下的IP模式支持,以及通过引入Target Group Binding CRD来实现将EKS中的服务灵活地绑定到已有Target Group上的新功能。

原理

下图介绍了AWS Load Balancer Controller创建的 AWS 组件,以及用户端流量经过 AWS Load Balancer Controller(图中为alb-ingress-controller组件)创建的ALB,然后通过URL路径匹配将不同路径的HTTP(S)请求转发到在K8S节点上Pod的过程,(mode instance)和(mode ip)分别代表了AWS Load Balancer Controller所支持的实例和IP两种模式,本文中的演示使用的均为IP模式,即将流量直接发送到Pod所绑定的ENI的IP。

操作步骤

前提:当前使用的用户具有Administrator Access权限

准备Cloud9实验环境

在AWS管理控制台中选择Cloud9服务,然后创建一个名称为:eksworkshop的环境,将Cost-saving setting选项设置为:After four hours,其他配置保持默认。创建完毕后关闭Welcome和底部的工作区页面,然后在主工作区中打开一个新的Terminal。

在IAM服务中,使用链接创建一个名称为:eksworkshop-admin的角色,确认:AWS service和EC2被选中,点击下一步,确认AdministratorAccess策略被选中,点击下一步,跳过Tag选项,点击下一步在Review页面中输入eksworkshop-admin作为新角色的名称,点击创建角色完成创建。

点击链接在EC2服务中查看刚刚创建的Cloud9环境对应的EC2实例,选中该实例,然后在菜单选择:Actions / Security / Modify IAM Role,在IAM Role的下拉列表中选择eksworkshop-admin的角色,点击保存。

返回刚刚创建好的Cloud9环境,点击页面右上角的齿轮,打开首选项设置页面,然后选择AWS SETTINGS,关闭AWS managed temporary credentials单选框,最后关闭首选项设置页面。

在打开的Terminal中运行以下命令确认临时的秘钥凭证已经被删除干净,并验证在返回结果 ARN 中包含eksworkshop-admin。

rm -vf ${HOME}/.aws/credentials
aws sts get-caller-identity

 运行下列脚本安装实验所需的Kubernetes 工具:eksctl,kubectl,helm,jq,aws cli

# create a folder for the scripts
mkdir ~/environment/scripts
# tools script
cat > ~/environment/scripts/install-tools <<-"EOF"
#!/bin/bash -ex
sudo yum install -y jq gettext bash-completion
sudo curl --silent --location "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/linux_64bit/session-manager-plugin.rpm" -o "session-manager-plugin.rpm"
sudo yum install -y session-manager-plugin.rpm
# install kubectl
sudo curl --silent --location -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.18.10/bin/linux/amd64/kubectl
sudo chmod +x /usr/local/bin/kubectl
echo 'source <(kubectl completion bash)' >>~/.bashrc
source ~/.bashrc
# install eksctl
curl --silent --location "https://github.com/weaveworks/eksctl/releases/download/latest_release/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv -v /tmp/eksctl /usr/local/bin
if ! [ -x "$(command -v jq)" ] || ! [ -x "$(command -v envsubst)" ] || ! [ -x "$(command -v kubectl)" ] || ! [ -x "$(command -v eksctl)" ] || ! [ -x "$(command -v ssm-cli)" ]; then
  echo 'ERROR: tools not installed.' >&2
  exit 1
fi
#pip install awscli --upgrade --user
# install aws cli v2
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
. ~/.bash_profile
# install helm 3
curl -sSL https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
helm version --short
helm repo add stable https://charts.helm.sh/stable
helm completion bash >> ~/.bash_completion
. /etc/profile.d/bash_completion.sh
. ~/.bash_completion
source <(helm completion bash)
helm repo update
EOF
chmod +x ~/environment/scripts/install-tools
~/environment/scripts/install-tools

创建EKS集群(版本:1.18)

配置创建集群需要的环境变量

export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
export AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region')
export EKS_CLUSTER_NAME=eksworkshop-albc
export EKS_NODEGROUP_NAME=ung-1
export EKS_MANAGED_NODEGROUP_NAME=mng-1
export AWS_DEFAULT_REGION=$AWS_REGION
echo "export ACCOUNT_ID=${ACCOUNT_ID}" >> ~/.bash_profile
echo "export AWS_REGION=${AWS_REGION}" >> ~/.bash_profile
echo "export AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION}" >> ~/.bashrc
echo "export EKS_CLUSTER_NAME=${EKS_CLUSTER_NAME}" >> ~/.bashrc
echo "export EKS_NODEGROUP_NAME=${EKS_NODEGROUP_NAME}" >> ~/.bashrc
aws configure set default.region ${AWS_REGION}
aws configure get default.region

运行下列命令创建EKS集群配置模板文件 eks-cluster.yml.template

cat > ~/environment/scripts/eks-cluster.yml.template <<-"EOF"
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: $EKS_CLUSTER_NAME
  region: $AWS_REGION
  version: "1.18"
nodeGroups:
  - name: $EKS_NODEGROUP_NAME
    minSize: 1
    maxSize: 3
    desiredCapacity: 1
managedNodeGroups:
  - name: $EKS_MANAGED_NODEGROUP_NAME
    minSize: 1
    maxSize: 3
    desiredCapacity: 1
    labels: {role: worker}
    tags:
      nodegroup-role: worker
    iam:
      withAddonPolicies:
        externalDNS: true
        certManager: true
        albIngress: true
        autoScaler: true
EOF

利用 eksctl 工具来创建 EKS 集群,运行下列命令创建一个 EKS 1.18 的集群,同时会创建一个新的 VPC,并且在该VPC中创建 一个1个节点的托管节点组(Managed Node Group)和一个1节点的非托管节点组(Unmanaged Node Group),整个过程大概需要 15分钟左右

envsubst < ~/environment/scripts/eks-cluster.yml.template > ~/environment/scripts/eks-cluster.yml
eksctl create cluster -f ~/environment/scripts/eks-cluster.yml

运行下列命令测试 EKS 集群是否正常工作

kubectl get nodes --show-labels

如果我们看到 2 个 m5.large 的EC2实例,说明 EKS 集群正确启动,并且相应的权限也是正确的。可以在管理控制台的EKS服务的集群列表中查看刚刚创建好的集群节点组(托管)、网络和其他配置。

同时,因为eksctl工具的底层实现是依赖CloudFormation服务的,所以可以再CloudFormation服务的管理界面查看为了创建集群而新建的3个CloudFormaiton模板:集群控制平面Stack、托管节点组Stack、非托管节点组Stack。

安装AWS Load Balancer Controller V2

因为AWS Load Balancer Controller对于版本在1.1.3以上的ALB Ingress Controller的Ingress定义能够完全兼容,所以本实验将仅安装AWS Load Balancer Controller V2而不会安装V1的ALB Ingress Controller,并在AWS Load Balancer Controller(V2)的环境中完成V1中Ingress定义的演示,有需求的读者可以使用同样的步骤在V1的环境中自行完成实验。

首先,为EKS集群创建IAM OIDC provider

eksctl utils associate-iam-oidc-provider --cluster=$EKS_CLUSTER_NAME --approve

接下来,下载AWS Load Balancer Controller需要的IAM Policy文件

curl -o iam-policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/main/docs/install/iam_policy.json

基于Policy文件创建新的IAM Policy,并记录生成Policy的ARN

aws iam create-policy 
    --policy-name AWSLoadBalancerControllerIAMPolicy 
    --policy-document file://iam-policy.json
export POLICY_ARN=$(aws iam list-policies | jq '.Policies | .[] | select(.PolicyName=="ALBIngressControllerIAMPolicy").Arn')

使用上面生产的ARN创建AWS Load Balancer Controller需要的IAM角色和ServiceAccount

eksctl create iamserviceaccount 
--cluster=$EKS_CLUSTER_NAME 
--namespace=kube-system 
--name=aws-load-balancer-controller 
--attach-policy-arn=$POLICY_ARN 
--approve

安装cert-manager

kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.0.2/cert-manager.yaml

运行下列命令下载、替换–your-cluster-name,并应用AWS Load Balancer Controller的Yaml定义

curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/main/docs/install/v2_0_0_full.yaml
sed -i 's/your-cluster-name/'"$EKS_CLUSTER_NAME"'/' v2_0_0_full.yaml
kubectl apply -f v2_0_0_full.yaml

使用下列命令查验load balancer controller服务安装是否成功

kubectl  get svc  aws-load-balancer-webhook-service -n kube-system

部署示例服务nginx-1与nginx-2和相应的Ingress

部署nginx-1

运行下列命令生成样例程序nginx-1的K8S部署、服务及Ingress对象的定义,注意的是下面Ingress Annotation配置中的alb.ingress.kubernetes.io/target-type设置为ip,也就是使用了mode ip模式,流量经由ALB直接跳转到K8S内部的Pod上,如果这个值为instance,流量会经过节点上的临时端口,根据kube-proxy的设置转发到Pod上。

cat <<EoF > ~/environment/nginx-1.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-1
  labels:
    app: nginx-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-1
  template:
    metadata:
      labels:
        app: nginx-1
    spec:
      containers:
      - name: nginx-1
        image: nginx
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: "nginx-1"
spec:
  selector:
    app: nginx-1
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: "nginx-1"
  namespace: "default"
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
  labels:
    app: nginx
spec:
  rules:
    - 52.83.57.155
        paths:
          - path: /*
            backend:
              serviceName: "nginx-1"
              servicePort: 80
EoF

部署nginx-1服务及Ingress对象

kubectl apply -f ~/environment/nginx-1.yaml

查看nginx-1服务及生成的Ingress对象

kubectl get svc nginx-1
kubectl get ing nginx-1

如图1所示,以上述命令输出的Ingress:nginx-1的ADDRESS为过滤条件在EC2服务控制台查找对应的ALB实例,查看基本信息,待该ALB的状态由provisioning转变为active后,访问上面命令输出的地址(即该ALB的DNS地址)信息,打开浏览器或者使用curl命令访问该地址,验证http请求可以成功返回

图1 – 查找Ingress:nginx-1对应的ALB实例

部署nginx-2

将上一节中出现的所有nginx-1改为nginx-2,应用所有步骤,发现系统会创建一个全新的ALB,访问生成的外部地址验证正常工作

cat <<EoF > ~/environment/nginx-2.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-2
  labels:
    app: nginx-2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-2
  template:
    metadata:
      labels:
        app: nginx-2
    spec:
      containers:
      - name: nginx-2
        image: nginx
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: "nginx-2"
spec:
  selector:
    app: nginx-2
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: "nginx-2"
  namespace: "default"
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
  labels:
    app: nginx
spec:
  rules:
    - 52.83.57.155
        paths:
          - path: /*
            backend:
              serviceName: "nginx-2"
              servicePort: 80
EoF

部署nginx-2服务及Ingress对象

kubectl apply -f ~/environment/nginx-2.yaml

查看nginx-1与nginx-2对应的Ingress对象

kubectl get ing

相应的可以在EC2服务的Load Balancer子项的Load Balancer列表中以每个Ingress对象对应的地址信息为过滤条件查看为每个Ingress对象创建的ALB

到目前为止,我们创建的每个Ingress对象都分别对应独立K8S服务和独立的ALB,接下来我们将分别利用AWS Load Balancer Controller V1和V2使用单个Ingress与单个ALB来实现映射多个K8S服务的实验。

 

使用ALB Ingress Controller(V1)Ingress定义实现发布多个K8S服务

执行下列命令在已经创建好的nginx-1的pod中创建新路径v1和index页面

# 生成新的nginx 1服务路径v1

# 生成新的nginx 1服务路径v1
export mypod=$(kubectl get pods  -o jsonpath='{.items[0].metadata.name}')
kubectl exec -it ${mypod} -- bash
mkdir -p /usr/share/nginx/html/v1 && echo "welcome to Nginx v1" > /usr/share/nginx/html/v1/index.html
exit

执行下列命令在已经创建好的nginx-2的pod中创建新路径v2和index页面

# 生成新的nginx 2服务路径v2
export mypod=$(kubectl get pods  -o jsonpath='{.items[1].metadata.name}')
kubectl exec -it ${mypod} -- bash
mkdir -p /usr/share/nginx/html/v2 && echo "welcome to Nginx v2" > /usr/share/nginx/html/v2/index.html
exit

创建一个新的ALB Ingress对象,将nginx-1的v1目录和nginx-2的v2目录对应到不同的HTTP路径上

cat <<EoF > ~/environment/multi-nginx-path-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: "multi-nginx-path"
  namespace: "default"
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
  labels:
    app: nginx
spec:
  rules:
    - 52.83.57.155
        paths:
          - path: /v1/*
            backend:
              serviceName: "nginx-1"
              servicePort: 80
          - path: /v2/*
            backend:
              serviceName: "nginx-2"
              servicePort: 80
EoF
kubectl apply -f ~/environment/multi-nginx-path-ingress.yaml

运行下列命令查看如下图所示的新创建好的multi-nginx-path Ingress对象,同上也可以在Load Balancer列表中查看对应multi-nginx-path的ALB

kubectl get ing multi-nginx-path

运行下面的命令得到新的ALB Ingress对应的外部DNS地址(同样需要等待ALB创建完成)

export loadbalancer=$(kubectl get ing multi-nginx-path -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')

等待ALB创建完成状态变为active后,分别访问下面两个URL来验证使用同一个Ingress对象定义来实现映射到两个K8S服务:nginx-1和nginx-2的实验结果,可以看到不同的HTTP路径请求会被转发到URL映射的后台服务并分别返回:welcome to Nginx v1与welcome to Nginx v2

curl $loadbalancer/v1/
curl $loadbalancer/v2/

这就是如何在ALB Ingress Controller(V1)中使用同一个Ingress定义生成一个ALB来实现发布多个K8S服务的例子,接下来我们看一下在V2中的实现方法。

 

使用AWS Load Balancer Controller(V2)Ingress定义实现发布多个K8S服务

AWS Load Balancer Controller(V2)通过Annotation: alb.ingress.kubernetes.io/group.name来支持在不同的Ingress对象上共享同一个ALB来实现多个K8S服务发布。

运行下列命令将alb.ingress.kubernetes.io/group.name: “share-alb”的注释分别添加到Ingress对象share-1和share-2当中,后端的K8S服务依然沿用上文创建好的nginx-1与nginx-2服务

cat <<EoF > ~/environment/share-1.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: "share-1"
  namespace: "default"
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/group.name: "share-alb"
  labels:
    app: nginx
spec:
  rules:
    - 52.83.57.155
        paths:
          - path: /v1/*
            backend:
              serviceName: "nginx-1"
              servicePort: 80
EoF
kubectl apply -f ~/environment/share-1.yaml
cat <<EoF > ~/environment/share-2.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: "share-2"
  namespace: "default"
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/group.name: "share-alb"
  labels:
    app: nginx
spec:
  rules:
    - 52.83.57.155
        paths:
          - path: /v2/*
            backend:
              serviceName: "nginx-2"
              servicePort: 80
EoF
kubectl apply -f ~/environment/share-2.yaml

运行下列命令查验Ingress对象share-1与share-2对应的ALB DNS名称,会得到如下图所示的结果:DNS Address是完全相同的,也就是说后台服务nginx-1与nginx-2虽然分别对应了一个Ingress对象share-1与share-2,但这两个服务和Ingress共享着一个ALB,也可以在ALB列表验证同样的实验结果

 kubectl get ing share-1
 kubectl get ing share-2

删除上文中创建的K8S对象

按照与上述创建各种资源相反的顺序删除创建的各种K8S对象

kubectl delete -f ~/environment/share-2.yaml
kubectl delete -f ~/environment/share-1.yaml
kubectl delete -f ~/environment/multi-nginx-path-ingress.yaml 
kubectl delete -f ~/environment/nginx-2.yaml
kubectl delete -f ~/environment/nginx-1.yaml

删除EKS集群和Cloud9环境(如需进行本文下篇中的实验内容请跳过此步)

删除EKS托管与非托管节点组,最后删除EKS集群。

eksctl delete nodegroup --cluster $EKS_CLUSTER_NAME --name $EKS_NODEGROUP_NAME
eksctl delete nodegroup --cluster $EKS_CLUSTER_NAME --name $EKS_MANAGED_NODEGROUP_NAME
eksctl delete cluster --name $EKS_CLUSTER_NAME

最后,在AWS控制台的Cloud9服务的环境列表中删除eksworksohp演示环境。

兼容性

如上面示例中演示,AWS Load Balancer Controller对于版本在1.1.3以上的ALB Ingress Controller的Ingress定义能够完全兼容。

从AWSALBIngressController(v1) 到 AWSLoadBalancerController(v2) 的升级可以参考 https://kubernetes-sigs.github.io/aws-load-balancer-controller/guide/upgrade/migratev1v2/。

总结

在EKS平台上无论您是使用ALB Ingress Controller(V1)还是使用最新发布的AWS Load Balancer Controller(V2)都能够很轻松的实现在单个Application Load Balancer上发布多个K8S容器服务。这两个版本的Ingress Controller除了以上演示的功能之外,还支持在Ingress定义中启用健康检查、开启ALB访问日志、启用证书实现HTTPS通讯等诸多功能,有兴趣的读者可以参考AWS Load Balancer Controller 在线文档中相应的Annotation章节中的介绍。

参考资料