Multi-cluster Services (MCS) let pods in one cluster reach specific pods in another cluster by domain name—without a load balancer. Headless Services make this possible for stateful workloads managed by StatefulSet, such as distributed databases and message queues, where targeting a specific pod instance matters.
This topic walks through how to expose a MySQL StatefulSet from one ACK cluster and access individual pods from a second cluster using a headless MCS.
How it works
A headless Service has no ClusterIP (clusterIP: None). Instead of resolving to a single Service IP, its DNS record resolves directly to the IP addresses of all matching pods. When you export a headless Service with a ServiceExport and import it with a ServiceImport of type Headless, ACK One extends this resolution across clusters.
In this example:
ACK Cluster 1 is the service provider. It runs a MySQL StatefulSet and a ServiceExport that makes the
mysqlService available to other clusters.ACK Cluster 2 is the service consumer. It has a ServiceImport that maps to the exported
mysqlService, plus a client pod that connects to specific MySQL pods by domain name.
Prerequisites
Before you begin, ensure that you have:
Fleet-level requirements:
Fleet management enabled
ACK Cluster 1 and ACK Cluster 2 both associated with the Fleet instance
Cluster-level requirements:
Kubernetes 1.22 or later on both clusters
kubeconfig files obtained and kubectl connected to each cluster. For details, see Get a cluster kubeconfig and connect to the cluster using kubectl
Network connectivity between the two clusters:
Same Virtual Private Cloud (VPC): no additional configuration needed
Different VPCs: inter-VPC communication enabled via Cloud Enterprise Network (CEN). For setup instructions, see Create a VPC connection
Step 1: Deploy a MySQL StatefulSet and create a ServiceExport in ACK Cluster 1
Create the
provider-nsnamespace:kubectl create ns provider-nsCreate a file named
mysql.yamlwith the following content: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 # Two replicated pods are provisioned for the MySQL database. serviceName: mysql selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: terminationGracePeriodSeconds: 10 containers: - name: mysql image: mysql:5.6 ports: - name: tcp 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-alltypeDeploy the MySQL StatefulSet:
kubectl -n provider-ns create -f mysql.yamlExpected output:
secret/mysecret created service/mysql created statefulset.apps/mysql createdCreate a file named
serviceexport.yamlwith the following content:apiVersion: multicluster.x-k8s.io/v1alpha1 kind: ServiceExport metadata: name: mysql # Specify the name of the Service to export.Create the ServiceExport:
kubectl -n provider-ns create -f serviceexport.yamlExpected output:
serviceexport.multicluster.x-k8s.io/mysql created
Step 2: Create a ServiceImport in ACK Cluster 2
Create the
provider-nsnamespace in ACK Cluster 2:kubectl create ns provider-nsCreate a file named
serviceimport.yamlwith the following content:ImportantSet
typetoHeadless. This tells ACK One to resolve the import to individual pod IPs rather than a virtual Service IP.apiVersion: multicluster.x-k8s.io/v1alpha1 kind: ServiceImport metadata: name: mysql # Must match the ServiceExport name. spec: ports: - name: tcp port: 3306 protocol: TCP type: HeadlessCreate the ServiceImport:
kubectl -n provider-ns create -f serviceimport.yamlExpected output:
serviceimport.multicluster.x-k8s.io/mysql created
Step 3: Access a specific MySQL pod from ACK Cluster 2
Within a single cluster, a StatefulSet pod is reachable at <pod-name>.<svc-name>.<namespace>.svc.cluster.local. Across clusters, ACK One extends this pattern with two domain name formats:
| Format | Domain name pattern | CoreDNS change required |
|---|---|---|
| Method 1 | <pod-name>.amcs-<svc-name>.<namespace>.svc.cluster.local | No |
| Method 2 | <pod-name>.<cluster-id>.<svc-name>.<namespace>.svc.clusterset.local | Yes |
Method 1 uses the cluster's existing DNS and requires no extra configuration—the amcs- prefix routes cross-cluster pod resolution through the local DNS server. Method 2 uses the clusterset.local domain, which requires the CoreDNS multicluster plugin. Install or update CoreDNS to version 1.9.3 or later and add a plugin entry before using it.
Use Method 1 for simplicity. Use Method 2 when you need the cluster ID embedded in the domain, for example, to target pods in a specific member cluster of the Fleet.
Both methods require a client pod in ACK Cluster 2. Create a file named mysqlclient.yaml:
apiVersion: v1
kind: Pod
metadata:
name: mysql-client
spec:
containers:
- name: mysql-client
image: mysql:5.6
command: ["sh", "-c", "sleep 12000"]Deploy the client pod:
kubectl create -f mysqlclient.yamlExpected output:
pod/mysql-client createdMethod 1: Use the amcs- domain (no CoreDNS changes)
Access mysql-0:
kubectl exec -it mysql-client -- mysql -h mysql-0.amcs-mysql.provider-ns.svc.cluster.local -P3306 -uroot -ppasswordAccess mysql-1:
kubectl exec -it mysql-client -- mysql -h mysql-1.amcs-mysql.provider-ns.svc.cluster.local -P3306 -uroot -ppasswordExpected output:
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.Method 2: Use the clusterset.local domain (requires CoreDNS update)
Before using this method, install or update CoreDNS in ACK Cluster 2 to version 1.9.3 or later. For instructions, see CoreDNS and Manage components.
The clusterset.local domain requires the CoreDNS multicluster plugin, which is not enabled by default. Add it to the Corefile:
Open the CoreDNS ConfigMap for editing:
kubectl edit configmap coredns -n kube-systemIn the
Corefilefield, addmulticluster clusterset.localto enable DNS resolution for multi-cluster Services:apiVersion: v1 data: Corefile: | .:53 { errors health { lameduck 15s } ready multicluster clusterset.local # Add this line. 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-systemAccess mysql-0, replacing
${clusterid}with the cluster ID of ACK Cluster 1:kubectl exec -it mysql-client -- mysql -h mysql-0.${clusterid}.mysql.provider-ns.svc.clusterset.local -P3306 -uroot -ppasswordAccess mysql-1:
kubectl exec -it mysql-client -- mysql -h mysql-1.${clusterid}.mysql.provider-ns.svc.clusterset.local -P3306 -uroot -ppasswordExpected output:
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.