通过多集群服务,您可以无需创建负载均衡,即可实现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实例。
前提条件
已开启舰队管理功能。具体操作,请参见开启舰队管理功能。
舰队的Fleet实例已添加2个关联集群(服务提供者集群、服务消费者集群)。具体操作,请参见添加关联集群。
确保关联集群的版本为1.22及以上版本。
获取服务提供者集群和服务消费者集群的Kubeconfig,并通过kubectl连接集群。具体操作,请参见获取集群KubeConfig并通过kubectl工具连接集群。
确保服务提供者集群和服务消费者集群的网络已连通。
步骤一:在服务提供者集群Cluster 1上创建MySQL服务和ServiceExport
执行如下命令,在ACK Cluster 1中创建命名空间。本示例的命名空间为
provider-ns
。kubectl create ns provider-ns
在ACK Cluster 1中创建MySQL服务。
使用如下内容创建一个
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
执行如下命令创建MySQL服务。
kubectl -n provider-ns create -f mysql.yaml
预期输出如下:
secret/mysecret created service/mysql created statefulset.apps/mysql created
在ACK Cluster 1中创建ServiceExport资源,指定需要开放多集群访问的Kubernetes服务。本示例为mysql服务。
使用如下内容创建一个
serviceexport.yaml
文件。apiVersion: multicluster.x-k8s.io/v1alpha1 kind: ServiceExport metadata: name: mysql # 名称与待开放的Kubernetes服务名称相同。
执行如下命令创建ServiceExport。
kubectl -n provider-ns create -f serviceexport.yaml
预期输出如下:
serviceexport.multicluster.x-k8s.io/mysql created
步骤二:在服务消费者集群Cluster 2上创建Namespace和ServiceImport
执行如下命令在ACK Cluster 2创建服务提供者命名空间。本示例的命名空间为
provider-ns
。kubectl create ns provider-ns
在ACK Cluster 2中创建ServiceImport。
使用如下内容创建一个
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
执行如下命令创建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
域名访问
在ACK Cluster 2中创建Client Pod。
使用以下内容创建一个
mysqlclient.yaml
文件。apiVersion: v1 kind: Pod metadata: name: mysql-client spec: containers: - name: mysql-client image: mysql:5.6 command: ["sh", "-c", "sleep 12000"]
执行如下命令创建Client Pod。
kubectl create -f mysqlclient.yaml
预期输出如下:
pod/mysql-client created
在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
域名访问
在ACK Cluster 2中创建Client Pod。
使用以下内容创建一个
mysqlclient.yaml
文件。apiVersion: v1 kind: Pod metadata: name: mysql-client spec: containers: - name: mysql-client image: mysql:5.6 command: ["sh", "-c", "sleep 12000"]
执行如下命令创建Client Pod。
kubectl create -f mysqlclient.yaml
预期输出如下:
pod/mysql-client created
在服务消费者集群Cluster 2中安装或升级CoreDNS,版本要求1.9.3及以上。具体操作,请参见CoreDNS和管理组件。
修改CoreDNS组件的配置Corefile。
执行如下命令,修改CoreDNS组件的ConfigMap文件。
kubectl edit configmap coredns -n kube-system
在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
在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.