AWS

本章介绍如何将现有IPv4 VPC转变为具有 IPv6 地址空间的双栈 VPC,并在该 VPC 中部署支持 IPv6 的EKS集群。

注意

  • 默认情况下,K8s向pod和service分配IPv4地址,EKS上可以配置向pod和service分配IPv6地址。

  • 但是EKS上只支持single stack, 要么只用IPv6, 要么只用IPv4(实际上官方k8s可以dual stack, 但是EKS上不行)

  • 在创建EKS的集群的时候,指定IP类型,在创建完成后不能再进行更改。

  • EKS所在VPC既要有IPv4 CIDR,也要绑定IPv6 CIDR。所有节点既分配 ipv6地址,也分配了ipv4地址。EC2级别不能关闭IPv4

  • 创建EKS IPv6集群时,所在IPv6子网必须要开启auto assign IPv6(注意默认这个设置是关闭的)

  • 子网所在的路由表必须有对IPv6地址的路由;安全组必须允许IPv6地址

  • Pod与外界公网交流时,用的是自己的IPv6地址,而不是node的,它是走IGW或EIGW(egress only)出去的

  • 通过Pod所在实例的NAT,IPv6 Pod可以与IPv4的地址进行访问

eksctl创建IPv6 cluster

我们先使用eksctl创建一个IPv6的EKS集群,它会自动创建一个新的VPC,并配置好VPC的ipv6配置

创建ipv6 EKS集群

创建ipv6-cluster.yamlkubernetesNetworkConfig部分定义了使用IPv6:

cat >ipv6-cluster.yaml <<EOF
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: ipv6-cluster
  region: us-east-2
  version: "1.28"

kubernetesNetworkConfig:
  ipFamily: IPv6

addons:
  - name: vpc-cni
    version: latest
  - name: coredns
    version: latest
  - name: kube-proxy
    version: latest

iam:
  withOIDC: true

managedNodeGroups:
  - name: ng-1
    instanceType: t3.large
EOF

创建集群:

eksctl create cluster -f ipv6-cluster.yaml

创建完成后,查询pod,上面分配了IPv6地址:

kubectl get pods -n kube-system -o wide

image-20240319083300558

查看service,上面也分配了IPv6地址:

kubectl get services -n kube-system -o wide

image-20240319083313174

查看集群,它的Cluster IP address familyIPv6

image-20240319083758358

从上面的页面打开任意子网,上面分配了IPv6 CIDR,并且开启了Auto-assign IPv6 address:

image-20240319083942618

查看子网的路由表,有一条到::0的路由,指向IGW:

image-20240319083956299

查看EKS的addon,VPC CNI上绑定了一个role,role上的policy具有AssignIPv6Addresses的权限:

image-20240319084228273

VPC开启IPv6

上一节创建eks集群时,vpc内的配置由eksctl自动处理。如果想在已有vpc中创建IPv6 EKS集群,需要提前对vpc进行一些配置,包括:

  • 将 IPv4 VPC转成dual stack VPC
  • 为子网分配IPv6 CIDR,并开启自动分配IPv6
  • 为子网路由表加上到::0的路由

将IPv6关联到现有vpc

进入vpc页面,选择VPC ,从右上角的“操作”菜单中,选择“Edit CIDR”:

image-20240316094337679

单击IPv6 CIDR 下的Add new IPv6 CIDR:

AWS

然后选择Amazon provided IPv6 CIDR block:

image-20240316094540225

添加完成后,新的IPv6段会显示出来:

image-20240316094414106

配置Dual Stack Subnet

我们将现有的 IPv4 子网设置为双栈,使其同时具有 IPv4 CIDR 和 IPv6 CIDR,并将使用它来启动支持 IPv6 的EKS 集群。

  1. 选择vpc下的子网,然后选择Edit IPv6 CIDRs:

image-20240318093737473

编辑 IPv6 CIDR页面上,添加一个IPv6子网:

image-20240318100630478

后面的单位是18.4Q,其实等于2^64:

image-20240318100734184

Q是科学计数法:

image-20240318100801773

再次点击子网的Edit subnet settings:

image-20240318131453373

启用自动分配IPv6地址以及DNS64:

image-20240318131433156

对于另外几个子网,重复上面的过程:

image-20240318131819988

更新路由表

在给vpc添加完IPv6 CIDR后,会自动添加一个local路由,这样vpc下的实例之间能用ipv6地址进行通讯:

image-20240319073818848

但此时IPv6还没有到公网的路由,需要进行手动添加:

  • 如果是public子网,添加一条到IGW的路由
  • 如果是private子网,创建一个Egress-only IGW(IPv6 不能使用NAT)

image-20240319074057619

这里以给public子网添加IGW为例,打开子网使用的route table:

image-20240319074304827

编辑路由表,添加一条::0的路由:

image-20240319075153746


参考: https://docs.aws.amazon.com/vpc/latest/userguide/vpc-migrate-ipv6.html

创建IPv6 EKS

使用eksctl创建新的IPv6, 里面指定了使用已有VPC:

cat >ipv6-cluster2.yaml <<EOF
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: ipv6-cluster
  region: us-west-2
  version: "1.28"

vpc:
  subnets:
    public:
      us-west-2a: 
        id: subnet-3221874a 
      us-west-2b:  
        id: subnet-22ff3c68 
      us-west-2c:
        id: subnet-2ab30377 

kubernetesNetworkConfig:
  ipFamily: IPv6

addons:
  - name: vpc-cni
    version: latest
  - name: coredns
    version: latest
  - name: kube-proxy
    version: latest

iam:
  withOIDC: true

managedNodeGroups:
  - name: ng-1
    instanceType: t3.large
EOF

创建集群:

eksctl create cluster -f ipv6-cluster2.yaml

查看pod上是否具有IPv6地址:

kubectl get pods -n kube-system -o wide

image-20240319093336335

查看service上是否具有IPv6地址:

kubectl get services -n kube-system -o wide

image-20240319093424437

部署应用

接下来创建AWS Load balancer controller,并部署一个service暴露EKS应用。

给subnet打tag

AWS Load balancer controller根据子网的标签来找到它,所以提前要给几个子网打上以下标签:

kubernetes.io/role/elb=1

image-20240319103646487

创建AWS Load balancer controller

eksctl utils associate-iam-oidc-provider --cluster ipv6-cluster --region $AWS_REGION --approve # 开启OIDC provider

curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.5.4/docs/install/iam_policy.json
aws iam create-policy \
--policy-name AWSLoadBalancerControllerIAMPolicy2 \
--policy-document file://iam_policy.json   # 创建policy

eksctl create iamserviceaccount \
--cluster=ipv6-cluster \
--namespace=kube-system \
--name=aws-load-balancer-controller \
--role-name AmazonEKSLoadBalancerControllerRole2 \
--region=$AWS_REGION \
--attach-policy-arn=arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy2 \
--approve

使用helm安装aws load balancer controller:

helm repo add eks https://aws.github.io/eks-charts

helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=ipv6-cluster \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller \
--set createIngressClassResource=true

查看aws-load-balancer-controller状态:

kubectl get pods -A -o wide

image-20240319102347647

部署应用

部署game 2048测试应用:

kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/main/docs/examples/2048/2048_full_dualstack.yaml

上述命令将在双栈模式下配置ALB。ALB 将接受来自支持 IPv4 和 IPv6 的客户端的传入连接请求。当将注释service.beta.kubernetes.io/aws-load-balancer-ip-address-type: Dualstack添加后,EKS IPv6 集群会在双栈模式下配置 ALB 和 NLB :

image-20240319102729100

几分钟后,使用以下命令验证是否已创建alb:

kubectl get ingress/ingress-2048 -n game-2048

创建出来的ALB是dual stack模式,解析出来同时具有ipv6和ipv4 地址:

image-20240319103943187

“IPv6验证 - I”

场景1 - 浏览器访问应用

在本部分中,我们将验证从 IPv4客户端 到IPv6 EKS 集群上的应用程序的连接。

ALB启用了dual stack模式,它允许 IPv4 和 IPv6 客户端进行连接。

获取上一步的ALB DNS地址,在浏览器中访问:

image-20240319104255214

验证ALB是否可以解析为 A 和 AAAA DNS 记录:

$ dig k8s-game2048-ingress2-xxxxxxxxx-yyyyyyyyyy.us-west-2.elb.amazonaws.com +short
$ dig k8s-game2048-ingress2-xxxxxxxxx-yyyyyyyyyy.us-west-2.elb.amazonaws.com AAAA +short

image-20240319104421262

场景2 - EKS Pod访问公网

在本部分中,我们将验证从 EKS Pod 到 Internet 上的 IPv4 / IPv6 的连接。

运行以下命令创建一个新的 pod,该 pod 允许执行 curl 命令并打开 pod 的 shell 终端。

kubectl run mycurlpod --image=curlimages/curl -i --tty -- sh

测试访问外界IPv4

在 Pod 终端上,运行以下命令来测试与 Internet 上的 IPv4 公共端点的连接:

curl 'https://api.ipify.org?format=json'

预期回应:

kongpingfan:~/environment/eksctl $ kubectl run mycurlpod --image=curlimages/curl -i --tty -- sh
If you don't see a command prompt, try pressing enter.
~ $ curl 'https://api.ipify.org?format=json'
{"ip":"54.202.37.175"}~ $ 

通过Pod所在实例的NAT,IPv6 Pod可以与IPv4的地址进行访问

这个返回的IP实际上是Pod所在EC2的IP:

image-20240319105734145

测试访问外界IPv6

image-20240319105308927

Pod与外界公网交流时,用的是自己的IPv6地址,它是走IGW或EIGW(egress only)出去的


上面两个url:

api.ipify.org
ip.gs

一个走ipv4,另一个走ipv6, 这是因为两个URL对IPv6的支持不一样:

image-20240319110144030

场景3 - EKS Pod 之间通过 IPv6 地址的连接

在本部分中,我们将验证 EKS Pod 之间通过 IPv6 地址的连接。

我们将在 EKS Pod 上部署一个前端(一个将代理用户请求的 nginx)和一个后端应用程序(一个用于返回hello world的简单微服务),并测试它们之间的连接。为简单起见,我们将在此场景中在两个 pod 之间执行 ping6 测试

运行以下命令来创建后端 Pod 和后端服务:

kubectl apply -f https://k8s.io/examples/service/access/backend-deployment.yaml
kubectl apply -f https://k8s.io/examples/service/access/backend-service.yaml

接下来,运行以下命令来验证服务是否已成功创建。注意到分配给服务的CLUSTER-IP将是 IPv6 地址。

kubectl get svc hello

image-20240319110259163

现在我们将创建前端,它接受用户请求并将其代理到后端服务:

kubectl apply -f https://k8s.io/examples/service/access/frontend-deployment.yaml
kubectl apply -f  https://k8s.io/examples/service/access/frontend-service.yaml

运行以下命令并从输出中复制任意一个 IPv6 地址。

kubectl describe svc hello

image-20240319110426037

接下来,我们需要进入前端pod的bash终端:

kubectl exec -it frontend-<podname> -- /bin/bash

从前端 pod 的 bash 终端,运行 ping6 命令。在以下命令中使用上一步查到的 IPv6 地址:

ping6 <IPV6ADDRESS>

会看到来自后端 Pod 的成功 ping 响应,这验证了 Pod 之间通过 IPv6 地址进行的通信。

image-20240319110605998