Deploying Istio Multi-Cluster Service Mesh with Multi-Control-Plane Topology
This guide explains how to set up Istio across multiple Kubernetes clusters using a multi‑control‑plane topology, covering CA secret creation, control‑plane installation, DNS configuration, sample application deployment, ServiceEntry and DestinationRule creation, and cross‑cluster traffic routing verification.
Overview
Istio can manage services across multiple Kubernetes clusters without a shared network by using a shared root CA, ServiceEntry resources, and a multi‑control‑plane topology (one Istio control plane per cluster). Traffic between clusters is secured with mutual TLS and appears as a single logical mesh.
Deploy an Istio control plane in each cluster
Create the istio-system namespace and a secret with the sample CA certificates.
Install Istio CRDs and wait for the API server to accept them.
Generate and apply the Istio control‑plane manifest using the values-istio-multicluster-gateways.yaml values file.
kubectl create namespace istio-system
kubectl create secret generic cacerts -n istio-system \
--from-file=samples/certs/ca-cert.pem \
--from-file=samples/certs/ca-key.pem \
--from-file=samples/certs/root-cert.pem \
--from-file=samples/certs/cert-chain.pem
for i in install/kubernetes/helm/istio-init/files/crd*.yaml; do
kubectl apply -f $i
done
helm template install/kubernetes/helm/istio \
--name istio --namespace istio-system \
-f install/kubernetes/helm/istio/values-istio-multicluster-gateways.yaml > istio.yaml
kubectl apply -f istio.yamlRun these steps on every cluster; the Helm command only needs to be executed once to produce the manifest.
Configure DNS for cross‑cluster service names
Remote services are addressed with the <name>.<namespace>.global suffix. Istio provides a CoreDNS server that resolves the .global domain.
For clusters using kube-dns, apply a ConfigMap that forwards .global queries to the Istio CoreDNS service:
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-dns
namespace: kube-system
data:
stubDomains: |
{"global": ["$(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP})"]}For clusters that already run CoreDNS, update the Corefile to add a .global server:
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
upstream
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
proxy . /etc/resolv.conf
cache 30
reload
loadbalance
}
global:53 {
errors
cache 30
proxy . $(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP})
}Deploy sample applications
In cluster 1 deploy the sleep demo app:
kubectl create namespace app1
kubectl label namespace app1 istio-injection=enabled
kubectl apply -n app1 -f samples/sleep/sleep.yaml
export SLEEP_POD=$(kubectl get -n app1 pod -l app=sleep -o jsonpath={.items..metadata.name})In cluster 2 deploy the httpbin demo app:
kubectl create namespace app2
kubectl label namespace app2 istio-injection=enabled
kubectl apply -n app2 -f samples/httpbin/httpbin.yamlObtain the ingress‑gateway address of cluster 2:
export CLUSTER2_GW_ADDR=$(kubectl get svc --selector=app=istio-ingressgateway -n istio-system -o jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}")Create a ServiceEntry for the remote service
In cluster 1, expose httpbin.app2.global via a ServiceEntry that points to the ingress gateway of cluster 2:
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: httpbin-app2
spec:
hosts:
- httpbin.app2.global
location: MESH_INTERNAL
ports:
- name: http1
number: 8000
protocol: http
resolution: DNS
addresses:
- 127.255.0.2
endpoints:
- address: ${CLUSTER2_GW_ADDR}
ports:
http1: 15443Verify connectivity from the sleep pod:
kubectl exec $SLEEP_POD -n app1 -c sleep -- curl httpbin.app2.global:8000/headersCross‑cluster version routing example
Deploy three versions of a helloworld service: v1 in cluster 1, v2 and v3 in cluster 2.
# Cluster 1 – v1
kubectl create namespace hello
kubectl label namespace hello istio-injection=enabled
kubectl apply -n hello -f samples/helloworld/service.yaml
kubectl apply -n hello -f samples/helloworld/helloworld.yaml -l version=v1
# Cluster 2 – v2 and v3
kubectl create namespace hello
kubectl label namespace hello istio-injection=enabled
kubectl apply -n hello -f samples/helloworld/service.yaml
kubectl apply -n hello -f samples/helloworld/helloworld.yaml -l version=v2
kubectl apply -n hello -f samples/helloworld/helloworld.yaml -l version=v3Create a ServiceEntry and a DestinationRule in cluster 1 that reference the remote .global host:
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: helloworld
spec:
hosts:
- helloworld.hello.global
location: MESH_INTERNAL
ports:
- name: http1
number: 5000
protocol: http
resolution: DNS
addresses:
- 127.255.0.8
endpoints:
- address: ${CLUSTER2_GW_ADDR}
ports:
http1: 15443
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: helloworld-global
spec:
host: helloworld.hello.global
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
subsets:
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3Create local DestinationRules for each cluster’s subsets:
# Cluster 1 – v1
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: helloworld
spec:
host: helloworld.hello.svc.cluster.local
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
subsets:
- name: v1
labels:
version: v1
---
# Cluster 2 – v2 & v3
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: helloworld
spec:
host: helloworld.hello.svc.cluster.local
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
subsets:
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3Define a VirtualService that routes 70 % of traffic from user jason to v2, 30 % to v3, and all other traffic to the local v1 version:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: helloworld
spec:
hosts:
- helloworld.hello.svc.cluster.local
- helloworld.hello.global
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: helloworld.hello.global
subset: v2
weight: 70
- destination:
host: helloworld.hello.global
subset: v3
weight: 30
- route:
- destination:
host: helloworld.hello.svc.cluster.local
subset: v1Repeated calls from the jason user show the expected 70/30 split, confirming that traffic routing works identically in a multi‑control‑plane mesh as in a single‑cluster deployment.
Conclusion
The multi‑control‑plane topology is the simplest way to build a multi‑cluster Istio mesh because it imposes no special network requirements. All Istio routing resources—ServiceEntry, DestinationRule, VirtualService—operate the same way across clusters, enabling seamless traffic management in multi‑region environments.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Alibaba Cloud Native
We publish cloud-native tech news, curate in-depth content, host regular events and live streams, and share Alibaba product and user case studies. Join us to explore and share the cloud-native insights you need.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
