通过Headless类型多集群Service跨集群访问有状态服务的指定实例

通过多集群服务,您可以无需创建负载均衡,即可实现Kubernetes服务的跨集群访问。其中,Headless类型服务不仅支持跨集群服务访问,还可以根据域名选择服务中的指定Pod实例进行访问,一般用于有状态服务(StatefulSets)的应用访问,例如分布式数据库、消息队列等。本文以MySQL服务为例,为您介绍如何通过Headless类型的多集群Service,实现跨集服务的访问。

背景信息

Headless类型的服务在Kubernetes中是一种特殊的服务(Service)配置,其主要特点是没有分配ClusterIP。Kubernetes的DNS系统会为服务生成一条特殊的DNS记录,该记录直接指向所有匹配该服务 Selector的Pod的IP地址列表,而不是一个统一的Service IP。

客户端可通过域名跨集群访问服务中的指定实例,常用于有状态服务(StatefulSets)的应用访问,例如分布式数据库、消息队列等。

对于MySQL服务来说,可以使用该能力可以实现MySQL主从集群的读写分离,以提升性能和吞吐量,并提升系统的可靠性和容错性。

方案介绍

本文以有状态服务MySQL为例,介绍如何通过Headless类型服务实现跨集群访问MySQL服务中指定的Pod实例。

  • ACK One多集群舰队Fleet实例管理关联集群(ACK Cluster 1、ACK Cluster 2)服务的开放与注入。

  • ACK Cluster 1作为服务提供者集群,在ACK Cluster 1上创建MySQL服务和ServiceExport,将MySQL服务设置为允许集群外访问。

  • ACK Cluster 2作为服务消费者集群,在ACK Cluster 2上创建ServiceImport对象和Client Pod以做验证。

  • 在ACK Cluster 2中,Client Pod可通过域名访问ACK Cluster 1中的MySQL服务,并支持指定具体需要访问哪一个Pod实例。

image

前提条件

  • 已开启舰队管理功能。具体操作,请参见开启舰队管理功能

  • 舰队的Fleet实例已添加2个关联集群(服务提供者集群、服务消费者集群)。具体操作,请参见添加关联集群

  • 确保关联集群的版本为1.22及以上版本。

  • 获取服务提供者集群和服务消费者集群的Kubeconfig,并通过kubectl连接集群。具体操作,请参见获取集群KubeConfig并通过kubectl工具连接集群

  • 确保服务提供者集群和服务消费者集群的网络已连通。

    • 两个集群处于同一个VPC下。

    • 两个集群处于不同VPC下,已通过云企业网CEN打通网络。具体操作,请参见创建VPC连接

步骤一:在服务提供者集群Cluster 1上创建MySQL服务和ServiceExport

  1. 执行如下命令,在ACK Cluster 1中创建命名空间。本示例的命名空间为provider-ns

    kubectl create ns provider-ns
  2. 在ACK Cluster 1中创建MySQL服务。

    1. 使用如下内容创建一个mysql.yaml文件。

      apiVersion: v1
      kind: Secret
      metadata: 
          name: mysecret
      type: Opaque
      data:
         ROOT_PASSWORD: cGFzc3dvcmQ=
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: mysql
        labels:
          app: mysql
      spec:
        clusterIP: None
        selector:
          app: mysql
        ports:
          - name: tcp
            protocol: TCP
            port: 3306
      ---
      apiVersion: apps/v1
      kind: StatefulSet
      metadata:
        name: mysql
      spec:
        replicas: 2  # 本示例为部署2个Pod副本
        serviceName: mysql
        selector:
          matchLabels:
            app: mysql
        template:
          metadata:
            labels:
              app: mysql
          spec:
            terminationGracePeriodSeconds: 10
            containers:
              - name: mysql
                image: mysql:5.6
                ports:
                  - name: tpc
                    protocol: TCP
                    containerPort: 3306
                env:
                  - name: MYSQL_ROOT_PASSWORD
                    valueFrom: 
                     secretKeyRef: 
                      key: ROOT_PASSWORD
                      name: mysecret
                volumeMounts:
                  - name: data
                    mountPath: /var/lib/mysql
        volumeClaimTemplates:
          - metadata:
              name: data
            spec:
              storageClassName: standard
              accessModes:
                - ReadWriteOnce
              resources:
                requests:
                  storage: 50Gi
              storageClassName: alicloud-disk-topology-alltype
    2. 执行如下命令创建MySQL服务。

      kubectl -n provider-ns create -f mysql.yaml

      预期输出如下:

      secret/mysecret created
      service/mysql created
      statefulset.apps/mysql created
  3. 在ACK Cluster 1中创建ServiceExport资源,指定需要开放多集群访问的Kubernetes服务。本示例为mysql服务。

    1. 使用如下内容创建一个serviceexport.yaml文件。

      apiVersion: multicluster.x-k8s.io/v1alpha1
      kind: ServiceExport
      metadata:
        name: mysql           # 名称与待开放的Kubernetes服务名称相同。
    2. 执行如下命令创建ServiceExport。

      kubectl -n provider-ns create -f serviceexport.yaml

      预期输出如下:

      serviceexport.multicluster.x-k8s.io/mysql created

步骤二:在服务消费者集群Cluster 2上创建Namespace和ServiceImport

  1. 执行如下命令在ACK Cluster 2创建服务提供者命名空间。本示例的命名空间为provider-ns

    kubectl create ns provider-ns
  2. 在ACK Cluster 2中创建ServiceImport。

    1. 使用如下内容创建一个serviceimport.yaml文件。

      重要

      ServiceImport的类型,即type必须设置为Headless

      apiVersion: multicluster.x-k8s.io/v1alpha1
      kind: ServiceImport
      metadata:
        name: mysql              # 名称与待开放的Kubernetes服务名称相同。
      spec:
        ports:                   # 与待开放的Kubernetes服务ports相同。
        - name: tcp
          port: 3306
          protocol: TCP
        type: Headless
    2. 执行如下命令创建ServiceImport服务。

      kubectl -n provider-ns create -f serviceimport.yaml

      预期输出如下:

      serviceimport.multicluster.x-k8s.io/mysql created

步骤三:在服务消费者集群中通过域名访问服务提供者中的指定MySQL实例

在服务消费者集群ACK Cluster 2中,通过以下两种域名均可访问服务提供者集群ACK Cluster 1中MySQL服务的指定实例。

  • ${pod name}.amcs-${service name}.${namespace}.svc.cluster.local

  • ${pod name}.${clusterid}.${service name}.${namespace}.svc.clusterset.local

通过${pod name}.amcs-${service name}.${namespace}.svc.cluster.local域名访问

  1. 在ACK Cluster 2中创建Client Pod。

    1. 使用以下内容创建一个mysqlclient.yaml文件。

      apiVersion: v1
      kind: Pod
      metadata:
        name: mysql-client
      spec:
        containers:
        - name: mysql-client
          image: mysql:5.6
          command: ["sh", "-c", "sleep 12000"]
    2. 执行如下命令创建Client Pod。

      kubectl create -f mysqlclient.yaml

      预期输出如下:

      pod/mysql-client created
  2. 在ACK Cluster 2中访问ACK Cluster 1中的指定MySQL实例。

    • 执行如下命令访问指定的mysql-0实例

      kubectl exec -it mysql-client -- mysql -h mysql-0.amcs-mysql.provider-ns.svc.cluster.local -P3306 -uroot -ppassword
    • 执行如下命令访问指定的mysql-1实例

      kubectl exec -it mysql-client -- mysql -h mysql-1.amcs-mysql.provider-ns.svc.cluster.local -P3306 -uroot -ppassword

    预期输出如下:

    Warning: Using a password on the command line interface can be insecure.
    Welcome to the MySQL monitor.  Commands end with ; or \g.
    Your MySQL connection id is 1
    Server version: 5.6.51 MySQL Community Server (GPL)
    
    Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
    
    Oracle is a registered trademark of Oracle Corporation and/or its
    affiliates. Other names may be trademarks of their respective
    owners.
    
    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

通过${pod name}.${clusterid}.${service name}.${namespace}.svc.clusterset.local域名访问

  1. 在ACK Cluster 2中创建Client Pod。

    1. 使用以下内容创建一个mysqlclient.yaml文件。

      apiVersion: v1
      kind: Pod
      metadata:
        name: mysql-client
      spec:
        containers:
        - name: mysql-client
          image: mysql:5.6
          command: ["sh", "-c", "sleep 12000"]
    2. 执行如下命令创建Client Pod。

      kubectl create -f mysqlclient.yaml

      预期输出如下:

      pod/mysql-client created
  2. 在服务消费者集群Cluster 2中安装或升级CoreDNS,版本要求1.9.3及以上。具体操作,请参见CoreDNS管理组件

  3. 修改CoreDNS组件的配置Corefile。

    1. 执行如下命令,修改CoreDNS组件的ConfigMap文件。

      kubectl edit configmap coredns -n kube-system
    2. 在Corefile文件中新增一行配置项multicluster clusterset.local,支持多集群服务域名解析。

      apiVersion: v1
      data:
        Corefile: |
          .:53 {
              errors
              health {
                 lameduck 15s
              }
              ready
              multicluster clusterset.local    #增加配置项,支持多集群服务域名解析。
              kubernetes cluster.local in-addr.arpa ip6.arpa {
      
                pods verified
                ttl 30
                fallthrough in-addr.arpa ip6.arpa
              }
              ...
          }
      kind: ConfigMap
      metadata:
        name: coredns
        namespace: kube-system
                                      
  4. 在ACK Cluster 2中访问ACK Cluster 1中的指定MySQL实例。

    • 执行如下命令访问指定的mysql-0实例

      kubectl exec -it mysql-client -- mysql -h mysql-0.${clusterid}.mysql.provider-ns.svc.clusterset.local -P3306 -uroot -ppassword
    • 执行如下命令访问指定的mysql-1实例

      kubectl exec -it mysql-client -- mysql -h mysql-1.${clusterid}.mysql.provider-ns.svc.clusterset.local -P3306 -uroot -ppassword

    预期输出如下:

    Warning: Using a password on the command line interface can be insecure.
    Welcome to the MySQL monitor.  Commands end with ; or \g.
    Your MySQL connection id is 1
    Server version: 5.6.51 MySQL Community Server (GPL)
    
    Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
    
    Oracle is a registered trademark of Oracle Corporation and/or its
    affiliates. Other names may be trademarks of their respective
    owners.
    
    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.