构建多个网络互通的 AWS EKS 集群

本文以构建 3 个集群为例,介绍了如何构建多个 AWS EKS 集群,并配置集群之间的网络互通,为跨 Kubernetes 集群部署 TiDB 集群作准备。

如果仅需要部署 TiDB 集群到一个 AWS EKS 集群,请参考在 AWS EKS 上部署 TiDB 集群文档。

环境准备

部署前,请确认已完成以下环境准备:

要验证 AWS CLI 的配置是否正确,请运行 aws configure list 命令。如果此命令的输出显示了 access_keysecret_key 的值,则 AWS CLI 的配置是正确的。否则,你需要重新配置 AWS CLI。

第 1 步:启动 Kubernetes 集群

定义三个 EKS 集群的配置文件分别为 cluster_1.yamlcluster_2.yamlcluster_3.yaml,并使用 eksctl 命令创建三个 EKS 集群。

  1. 定义集群 1 的配置文件,并创建集群 1。

    将如下配置保存为 cluster_1.yaml 文件,其中 ${cluster_1} 为 EKS 集群的名字,${region_1} 为部署 EKS 集群到的 Region,${cidr_block_1} 为 EKS 集群所属的 VPC 的 CIDR block。

    apiVersion: eksctl.io/v1alpha5
    kind: ClusterConfig
    
    metadata:
      name: ${cluster_1}
      region: ${region_1}
    
    # nodeGroups ...
    
    vpc:
      cidr: ${cidr_block_1}
    

    节点池 nodeGroups 字段的配置可以参考创建 EKS 集群和节点池一节。

    执行以下命令创建集群 1 :

    eksctl create cluster -f cluster_1.yaml
    

    执行上述命令后,等待 EKS 集群创建完成,以及节点组创建完成并加入进去,耗时约 5~20 分钟。可参考 eksctl 文档了解更多集群配置选项。

  2. 以集群 1 的配置文件为例,定义集群 2 与集群 3 的配置文件,并通过 eksctl 命令创建集群 2 与集群 3。

    需要注意,每个集群所属的 VPC 的 CIDR block 必须 与其他集群不重叠。

    后文中:

    • ${cluster_1}${cluster_2}${cluster_3} 分别代表三个集群的名字。
    • ${region_1}${region_2}${region_3} 分别代表三个集群所处的 Region。
    • ${cidr_block_1}${cidr_block_2}${cidr_block_3} 分别代表三个集群所属的 VPC 的 CIDR block。
  3. 在所有集群创建完毕后,你需要获取每个集群的 Kubernetes Context,以方便后续使用 kubectl 命令操作每个集群。

    kubectl config get-contexts
    
    点击查看输出,其中的 `NAME` 项就是你需要使用的 context 。
    CURRENT   NAME                                 CLUSTER                      AUTHINFO                            NAMESPACE *         pingcap@tidb-1.us-west-1.eksctl.io   tidb-1.us-west-1.eksctl.io   pingcap@tidb-1.us-west-1.eksctl.io pingcap@tidb-2.us-west-2.eksctl.io   tidb-2.us-west-2.eksctl.io   pingcap@tidb-2.us-west-2.eksctl.io pingcap@tidb-3.us-east-1.eksctl.io   tidb-3.us-east-1.eksctl.io   pingcap@tidb-3.us-east-1.eksctl.io

    后文中,${context_1}${context_2}${context_3} 分别代表各个集群的 context。

第 2 步:配置网络

设置 VPC peering

为了联通三个集群的网络,你需要为每两个集群所在的 VPC 创建一个 VPC peering。关于 VPC peering,可以参考 AWS 官方文档

  1. 通过 eksctl 命令,得到每个集群所在的 VPC 的 ID。以集群 1 为例:

    eksctl get cluster ${cluster_1} --region ${region_1}
    
    点击查看示例输出,其中 `VPC` 项就是该集群所在 VPC 的 ID。
    NAME          VERSION STATUS  CREATED                 VPC                       SUBNETS                                                                                                                   SECURITYGROUPS tidb-1        1.20    ACTIVE  2021-11-22T06:40:20Z    vpc-0b15ed35c02af5288   subnet-058777d55881c4095, subnet-06def2041b6fa3fa0,subnet-0869c7e73e09c3174,subnet-099d10845f6cbaf82,subnet-0a1a58db5cb087fed, subnet-0f68b302678c4d36b     sg-0cb299e7ec153c595

    后文中,${vpc_id_1}${vpc_id_2}${vpc_id_3} 分别代表各个集群所在的 VPC 的 ID。

  2. 构建集群 1 与集群 2 的 VPC peering。

    1. 按照 AWS VPC peering 文档创建 VPC peering。${vpc_id_1} 作为 requester VPC,${vpc_id_2} 作为 accepter VPC 。

    2. 按照 AWS VPC Peering 文档完成 VPC peering 的构建。

  3. 以步骤 2 为例,构建集群 1 与集群 3 的 VPC peering,以及集群 2 与集群 3 的 VPC peering。

  4. 按照更新路由表文档,更新三个集群的路由表。

    你需要更新集群使用的所有 Subnet 的路由表。每个路由表需要添加两个路由项,以集群 1 的某个路由表为例:

    DestinationTargetStatusPropagated
    ${cidr_block_2}${vpc_peering_id_12}ActiveNo
    ${cidr_block_3}${vpc_peering_id_13}ActiveNo

    每个路由项的 Destination 为另一个集群的 CIDR block,Target 为这两个集群的 VPC peering ID。

更新实例的安全组

  1. 更新集群 1 的安全组:

    1. 进入 AWS Security Groups 控制台,找到集群 1 的安全组。安全组命名类似于 eksctl-${cluster_1}-cluster/ClusterSharedNodeSecurityGroup

    2. 在安全组中添加 Inbound rules,以允许来自集群 2 和集群 3 的流量:

      TypeProtocolPort rangeSourceDescription
      All trafficAllAllCustom ${cidr_block_2}Allow cluster 2 to communicate with cluster 1
      All trafficAllAllCustom ${cidr_block_3}Allow cluster 3 to communicate with cluster 1
  2. 按照步骤 1,更新集群 2 与集群 3 的安全组。

配置负载均衡器

每个集群的 CoreDNS 服务需要通过一个网络负载均衡器暴露给其他集群。本节介绍如何配置负载均衡器。

  1. 创建 Load Balancer Service 定义文件 dns-lb.yaml,其文件内容如下:

    apiVersion: v1
    kind: Service
    metadata:
      labels:
        k8s-app: kube-dns
      name: across-cluster-dns-tcp
      namespace: kube-system
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
        service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
        service.beta.kubernetes.io/aws-load-balancer-internal: "true"
    spec:
      ports:
      - name: dns
        port: 53
        protocol: TCP
        targetPort: 53
      selector:
        k8s-app: kube-dns
      type: LoadBalancer
    
  2. 在每个集群中部署 Load Balancer Service。

    kubectl --context ${context_1} apply -f dns-lb.yaml
    
    kubectl --context ${context_2} apply -f dns-lb.yaml
    
    kubectl --context ${context_3} apply -f dns-lb.yaml
    
  3. 获取各集群的负载均衡器的名字,并等待所有集群的负载均衡器变为 Active 状态。

    使用以下命令来查询三个集群的负载均衡器的名字。

    lb_name_1=$(kubectl --context ${context_1} -n kube-system get svc across-cluster-dns-tcp -o jsonpath="{.status.loadBalancer. ingress[0].hostname}" | cut -d - -f 1)
    
    lb_name_2=$(kubectl --context ${context_2} -n kube-system get svc across-cluster-dns-tcp -o jsonpath="{.status.loadBalancer. ingress[0].hostname}" | cut -d - -f 1)
    
    lb_name_3=$(kubectl --context ${context_3} -n kube-system get svc across-cluster-dns-tcp -o jsonpath="{.status.loadBalancer. ingress[0].hostname}" | cut -d - -f 1)
    

    执行以下命名来查看三个集群的负载均衡器的状态,所有的命令输出结果都为 "active" 时,表明负载均衡器为 Active 状态。

    aws elbv2 describe-load-balancers --names ${lb_name_1} --region ${region_1} --query 'LoadBalancers[*].State' --output text
    
    aws elbv2 describe-load-balancers --names ${lb_name_2} --region ${region_2} --query 'LoadBalancers[*].State' --output text
    
    aws elbv2 describe-load-balancers --names ${lb_name_3} --region ${region_3} --query 'LoadBalancers[*].State' --output text
    
    点击查看期望输出
    active active active
  4. 查询各集群的负载均衡器关联的 IP 地址。

    以集群 1 为例,执行下面命令查询集群 1 的负载均衡器关联的所有 IP 地址。

    aws ec2 describe-network-interfaces --region ${region_1} --filters Name=description,Values="ELB net/${lb_name_1}*" --query  'NetworkInterfaces[*].PrivateIpAddress' --output text
    
    点击查看期望输出
    10.1.175.233 10.1.144.196

    后文中,将各集群的负载均衡器关联 IP 地址称为 ${lb_ip_list_1}${lb_ip_list_2}${lb_ip_list_3}

    不同 Region 的负载均衡器可能有着不同数量的 IP 地址。例如上述示例中,${lb_ip_list_1} 就是 10.1.175.233 10.1.144.196

配置 CoreDNS

为了让集群中的 Pod 能够访问其他集群的 Service,你需要配置每个集群的 CoreDNS 服务,使其能够转发 DNS 请求给其他集群的 CoreDNS 服务。

你可以通过修改 CoreDNS 对应的 ConfigMap 来进行配置。如需了解更多配置项,参考 Customizing DNS Service

  1. 修改集群 1 的 CoreDNS 配置。

    1. 备份当前的 CoreDNS 配置:

      kubectl --context ${context_1} -n kube-system get configmap coredns -o yaml > ${cluster_1}-coredns.yaml.bk
      
    2. 修改 ConfigMap:

      kubectl --context ${context_1} -n kube-system edit configmap coredns
      

      修改 data.Corefile 字段如下,其中 ${namespace_2}${namespace_3} 分别为集群 2 和集群 3 将要部署的 TidbCluster 所在的 namespace。

      apiVersion: v1
      kind: ConfigMap
      # ...
      data:
        Corefile: |
           .:53 {
              # ... 默认配置不修改
           }
           ${namespace_2}.svc.cluster.local:53 {
               errors
               cache 30
               forward . ${lb_ip_list_2} {
                   force_tcp
               }
           }
           ${namespace_3}.svc.cluster.local:53 {
               errors
               cache 30
               forward . ${lb_ip_list_3} {
                   force_tcp
               }
           }
      
    3. 等待 CoreDNS 重新加载配置,大约需要 30s 左右。

  2. 以步骤 1 为例,修改集群 2 和集群 3 的 CoreDNS 配置。

    对于每个集群的 CoreDNS 配置,你需要将 ${namespace_2}${namespace_3} 修改为另外两个集群将要部署 TidbCluster 的 namespace,将配置中的 IP 地址配置为另外两个集群的 Load Balancer 的 IP 地址。

后文中,使用 ${namespace_1}${namespace_2}${namespace_3} 分别代表各个集群的将要部署的 TidbCluster 所在的 namespace。

第 3 步:验证网络连通性

在部署 TiDB 集群之前,你需要先验证多个集群之间的网络连通性。

  1. 将下面定义保存到 sample-nginx.yaml 文件。

    apiVersion: v1
    kind: Pod
    metadata:
      name: sample-nginx
      labels:
        app: sample-nginx
    spec:
      hostname: sample-nginx
      subdomain: sample-nginx-peer
      containers:
      - image: nginx:1.21.5
        imagePullPolicy: IfNotPresent
        name: nginx
        ports:
          - name: http
            containerPort: 80
      restartPolicy: Always
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: sample-nginx-peer
    spec:
      ports:
        - port: 80
      selector:
        app: sample-nginx
      clusterIP: None
    
  2. 在三个集群对应的命名空间下部署 NGINX 服务。

    kubectl --context ${context_1} -n ${namespace_1} apply -f sample-nginx.yaml
    
    kubectl --context ${context_2} -n ${namespace_2} apply -f sample-nginx.yaml
    
    kubectl --context ${context_3} -n ${namespace_3} apply -f sample-nginx.yaml
    
  3. 访问其他集群的 NGINX 服务,验证网络是否连通。

    以验证集群 1 到集群 2 的网络连通性为例,执行以下命令。

    kubectl --context ${context_1} exec sample-nginx -- curl http://sample-nginx.sample-nginx-peer.${namespace_2}.svc.cluster.local:80
    

    如果输出为 NGINX 的欢迎页面,那么就表明网络是正常连通的。

  4. 验证完成后,执行以下命令删除 NGINX 服务。

    kubectl --context ${context_1} -n ${namespace_1} delete -f sample-nginx.yaml
    
    kubectl --context ${context_2} -n ${namespace_2} delete -f sample-nginx.yaml
    
    kubectl --context ${context_3} -n ${namespace_3} delete -f sample-nginx.yaml
    

第 4 步:部署 TiDB Operator

每个集群的 TidbCluster CR 由当前集群的 TiDB Operator 管理,因此每个集群都需要部署 TiDB Operator。

参考在 Kubernetes 上部署 TiDB Operator 部署 TiDB Operator 到每个 EKS 集群。区别在于,你需要通过命令 kubectl --context ${context}helm --kube-context ${context} 来为每个 EKS 集群部署 TiDB Operator。

第 5 步:部署 TiDB 集群

参考跨多个 Kubernetes 集群部署 TiDB 集群,为每个集群部署一个 TidbCluster CR。需要注意的是:

  • 必须将各集群的 TidbCluster 部署到配置 CoreDNS 一节中对应的 namespace 下,否则 TiDB 集群运行将会失败。
  • 各集群的 cluster domain 必须 设置为 "cluster.local"。

例如,部署初始集群的 TidbCluster CR 到集群 1 时,将 metadata.namespace 指定为 ${namespace_1}:

apiVersion: pingcap.com/v1alpha1
kind: TidbCluster
metadata:
   name: ${tc_name_1}
   namespace: ${namespace_1}
spec:
  # ...
  clusterDomain: "cluster.local"
  acrossK8s: true

探索更多

下载 PDF文档反馈社区交流
文档内容是否有帮助?
产品
TiDB
学习
客户案例
PingCAP Education
TiDB in Action
© 2022 PingCAP. All Rights Reserved.