While it is possible to deploy Multi-Cluster Ops Manager, Multi-Cluster Sharded Cluster, and Multi-Cluster Replica Set configurations without a service mesh, the recommended approach (presented here) is to leverage a service mesh to handle the networking across multiple Kubernetes clusters. To learn more, see How Does the Kubernetes Operator Establish Connectivity.
This page walks you through the process of deploying and validating an Istio service mesh across multiple Kubernetes clusters. Istio is only one of many options for deploying a service mesh, and it is not supported by MongoDB.
Note
Istio is not supported by MongoDB
Istio is not supported by MongoDB and is only one of many tools you can use to deploy a service mesh across your Kubernetes clusters.
Prerequisites
Before you begin, perform the following tasks:
Install
kubectl
.Set the
K8S_CLUSTER_*_CONTEXT_NAME
environment variables as explained in the GKE Clusters guide.
Source Code
You can find all included source code in the MongoDB Kubernetes Operator repository.
Procedure
Install Istio service mesh.
Install Istio service mesh to allow cross-cluster DNS resolution and network connectivity between Kubernetes clusters.
1 CTX_CLUSTER1=${K8S_CLUSTER_0_CONTEXT_NAME} \ 2 CTX_CLUSTER2=${K8S_CLUSTER_1_CONTEXT_NAME} \ 3 CTX_CLUSTER3=${K8S_CLUSTER_2_CONTEXT_NAME} \ 4 ISTIO_VERSION="1.20.2" \ 5 ./install_istio_separate_network.sh
Label the Kubernetes namespaces.
Label the Kubernetes namespaces in each cluster to enable Istio sidecar injection.
kubectl --context "${K8S_CLUSTER_0_CONTEXT_NAME}" label namespace "${OPERATOR_NAMESPACE}" istio-injection=enabled --overwrite kubectl --context "${K8S_CLUSTER_1_CONTEXT_NAME}" label namespace "${OPERATOR_NAMESPACE}" istio-injection=enabled --overwrite kubectl --context "${K8S_CLUSTER_2_CONTEXT_NAME}" label namespace "${OPERATOR_NAMESPACE}" istio-injection=enabled --overwrite kubectl --context "${K8S_CLUSTER_0_CONTEXT_NAME}" label namespace "${OM_NAMESPACE}" istio-injection=enabled --overwrite kubectl --context "${K8S_CLUSTER_1_CONTEXT_NAME}" label namespace "${OM_NAMESPACE}" istio-injection=enabled --overwrite kubectl --context "${K8S_CLUSTER_2_CONTEXT_NAME}" label namespace "${OM_NAMESPACE}" istio-injection=enabled --overwrite kubectl --context "${K8S_CLUSTER_0_CONTEXT_NAME}" label namespace "${MDB_NAMESPACE}" istio-injection=enabled --overwrite kubectl --context "${K8S_CLUSTER_1_CONTEXT_NAME}" label namespace "${MDB_NAMESPACE}" istio-injection=enabled --overwrite kubectl --context "${K8S_CLUSTER_2_CONTEXT_NAME}" label namespace "${MDB_NAMESPACE}" istio-injection=enabled --overwrite
Optional. Check cluster connectivity.
The following optional scripts verify whether the service mesh is configured correctly for cross-cluster DNS resolution and connectivity.
Create a Kubernetes namespace for the connectivity test.
1 kubectl --context "${K8S_CLUSTER_0_CONTEXT_NAME}" create namespace "connectivity-test" 2 kubectl --context "${K8S_CLUSTER_0_CONTEXT_NAME}" label namespace "connectivity-test" istio-injection=enabled --overwrite 3 4 kubectl --context "${K8S_CLUSTER_1_CONTEXT_NAME}" create namespace "connectivity-test" 5 kubectl --context "${K8S_CLUSTER_1_CONTEXT_NAME}" label namespace "connectivity-test" istio-injection=enabled --overwrite 6 7 kubectl --context "${K8S_CLUSTER_2_CONTEXT_NAME}" create namespace "connectivity-test" 8 kubectl --context "${K8S_CLUSTER_2_CONTEXT_NAME}" label namespace "connectivity-test" istio-injection=enabled --overwrite Run this script on cluster 0:
1 kubectl apply --context "${K8S_CLUSTER_0_CONTEXT_NAME}" -n "connectivity-test" -f - <<EOF 2 apiVersion: apps/v1 3 kind: StatefulSet 4 metadata: 5 name: echoserver0 6 spec: 7 replicas: 1 8 selector: 9 matchLabels: 10 app: echoserver0 11 template: 12 metadata: 13 labels: 14 app: echoserver0 15 spec: 16 containers: 17 - image: k8s.gcr.io/echoserver:1.10 18 imagePullPolicy: Always 19 name: echoserver0 20 ports: 21 - containerPort: 8080 22 EOF Run this script on cluster 1:
1 kubectl apply --context "${K8S_CLUSTER_1_CONTEXT_NAME}" -n "connectivity-test" -f - <<EOF 2 apiVersion: apps/v1 3 kind: StatefulSet 4 metadata: 5 name: echoserver1 6 spec: 7 replicas: 1 8 selector: 9 matchLabels: 10 app: echoserver1 11 template: 12 metadata: 13 labels: 14 app: echoserver1 15 spec: 16 containers: 17 - image: k8s.gcr.io/echoserver:1.10 18 imagePullPolicy: Always 19 name: echoserver1 20 ports: 21 - containerPort: 8080 22 EOF Run this script on cluster 2:
1 kubectl apply --context "${K8S_CLUSTER_2_CONTEXT_NAME}" -n "connectivity-test" -f - <<EOF 2 apiVersion: apps/v1 3 kind: StatefulSet 4 metadata: 5 name: echoserver2 6 spec: 7 replicas: 1 8 selector: 9 matchLabels: 10 app: echoserver2 11 template: 12 metadata: 13 labels: 14 app: echoserver2 15 spec: 16 containers: 17 - image: k8s.gcr.io/echoserver:1.10 18 imagePullPolicy: Always 19 name: echoserver2 20 ports: 21 - containerPort: 8080 22 EOF Run this script to wait for the creation of StatefulSets:
1 kubectl wait --context "${K8S_CLUSTER_0_CONTEXT_NAME}" -n "connectivity-test" --for=condition=ready pod -l statefulset.kubernetes.io/pod-name=echoserver0-0 --timeout=60s 2 kubectl wait --context "${K8S_CLUSTER_1_CONTEXT_NAME}" -n "connectivity-test" --for=condition=ready pod -l statefulset.kubernetes.io/pod-name=echoserver1-0 --timeout=60s 3 kubectl wait --context "${K8S_CLUSTER_2_CONTEXT_NAME}" -n "connectivity-test" --for=condition=ready pod -l statefulset.kubernetes.io/pod-name=echoserver2-0 --timeout=60s Create Pod service on cluster 0:
1 kubectl apply --context "${K8S_CLUSTER_0_CONTEXT_NAME}" -n "connectivity-test" -f - <<EOF 2 apiVersion: v1 3 kind: Service 4 metadata: 5 name: echoserver0-0 6 spec: 7 ports: 8 - port: 8080 9 targetPort: 8080 10 protocol: TCP 11 selector: 12 statefulset.kubernetes.io/pod-name: "echoserver0-0" 13 EOF Create Pod service on cluster 1:
1 kubectl apply --context "${K8S_CLUSTER_1_CONTEXT_NAME}" -n "connectivity-test" -f - <<EOF 2 apiVersion: v1 3 kind: Service 4 metadata: 5 name: echoserver1-0 6 spec: 7 ports: 8 - port: 8080 9 targetPort: 8080 10 protocol: TCP 11 selector: 12 statefulset.kubernetes.io/pod-name: "echoserver1-0" 13 EOF Create Pod service on cluster 2:
1 kubectl apply --context "${K8S_CLUSTER_2_CONTEXT_NAME}" -n "connectivity-test" -f - <<EOF 2 apiVersion: v1 3 kind: Service 4 metadata: 5 name: echoserver2-0 6 spec: 7 ports: 8 - port: 8080 9 targetPort: 8080 10 protocol: TCP 11 selector: 12 statefulset.kubernetes.io/pod-name: "echoserver2-0" 13 EOF Create round robin service on cluster 0:
1 kubectl apply --context "${K8S_CLUSTER_0_CONTEXT_NAME}" -n "connectivity-test" -f - <<EOF 2 apiVersion: v1 3 kind: Service 4 metadata: 5 name: echoserver 6 spec: 7 ports: 8 - port: 8080 9 targetPort: 8080 10 protocol: TCP 11 selector: 12 app: echoserver0 13 EOF Create round robin service on cluster 1:
1 kubectl apply --context "${K8S_CLUSTER_1_CONTEXT_NAME}" -n "connectivity-test" -f - <<EOF 2 apiVersion: v1 3 kind: Service 4 metadata: 5 name: echoserver 6 spec: 7 ports: 8 - port: 8080 9 targetPort: 8080 10 protocol: TCP 11 selector: 12 app: echoserver1 13 EOF Create round robin service on cluster 2:
1 kubectl apply --context "${K8S_CLUSTER_2_CONTEXT_NAME}" -n "connectivity-test" -f - <<EOF 2 apiVersion: v1 3 kind: Service 4 metadata: 5 name: echoserver 6 spec: 7 ports: 8 - port: 8080 9 targetPort: 8080 10 protocol: TCP 11 selector: 12 app: echoserver2 13 EOF Verify Pod 0 from cluster 1:
1 source_cluster=${K8S_CLUSTER_1_CONTEXT_NAME} 2 target_pod="echoserver0-0" 3 source_pod="echoserver1-0" 4 target_url="http://${target_pod}.connectivity-test.svc.cluster.local:8080" 5 echo "Checking cross-cluster DNS resolution and connectivity from ${source_pod} in ${source_cluster} to ${target_pod}" 6 out=$(kubectl exec --context "${source_cluster}" -n "connectivity-test" "${source_pod}" -- \ 7 /bin/bash -c "curl -v ${target_url}" 2>&1); 8 9 if grep "Hostname: ${target_pod}" &>/dev/null <<< "${out}" 10 then 11 echo "SUCCESS" 12 else 13 echo "ERROR: ${out}" 14 return 1 15 fi 1 Checking cross-cluster DNS resolution and connectivity from echoserver1-0 in gke_scratch-kubernetes-team_europe-central2-b_k8s-mdb-1-682f2df6e1745e000788a1d5-24552 to echoserver0-0 2 SUCCESS Verify Pod 1 from cluster 0:
1 source_cluster=${K8S_CLUSTER_0_CONTEXT_NAME} 2 target_pod="echoserver1-0" 3 source_pod="echoserver0-0" 4 target_url="http://${target_pod}.connectivity-test.svc.cluster.local:8080" 5 echo "Checking cross-cluster DNS resolution and connectivity from ${source_pod} in ${source_cluster} to ${target_pod}" 6 out=$(kubectl exec --context "${source_cluster}" -n "connectivity-test" "${source_pod}" -- \ 7 /bin/bash -c "curl -v ${target_url}" 2>&1); 8 9 if grep "Hostname: ${target_pod}" &>/dev/null <<< "${out}" 10 then 11 echo "SUCCESS" 12 else 13 echo "ERROR: ${out}" 14 return 1 15 fi 1 Checking cross-cluster DNS resolution and connectivity from echoserver0-0 in gke_scratch-kubernetes-team_europe-central2-a_k8s-mdb-0-682f2df6e1745e000788a1d5-24552 to echoserver1-0 2 SUCCESS Verify Pod 1 from cluster 2:
1 source_cluster=${K8S_CLUSTER_2_CONTEXT_NAME} 2 target_pod="echoserver1-0" 3 source_pod="echoserver2-0" 4 target_url="http://${target_pod}.connectivity-test.svc.cluster.local:8080" 5 echo "Checking cross-cluster DNS resolution and connectivity from ${source_pod} in ${source_cluster} to ${target_pod}" 6 out=$(kubectl exec --context "${source_cluster}" -n "connectivity-test" "${source_pod}" -- \ 7 /bin/bash -c "curl -v ${target_url}" 2>&1); 8 9 if grep "Hostname: ${target_pod}" &>/dev/null <<< "${out}" 10 then 11 echo "SUCCESS" 12 else 13 echo "ERROR: ${out}" 14 return 1 15 fi 1 Checking cross-cluster DNS resolution and connectivity from echoserver2-0 in gke_scratch-kubernetes-team_europe-central2-c_k8s-mdb-2-682f2df6e1745e000788a1d5-24552 to echoserver1-0 2 SUCCESS Verify Pod 2 from cluster 0:
1 source_cluster=${K8S_CLUSTER_0_CONTEXT_NAME} 2 target_pod="echoserver2-0" 3 source_pod="echoserver0-0" 4 target_url="http://${target_pod}.connectivity-test.svc.cluster.local:8080" 5 echo "Checking cross-cluster DNS resolution and connectivity from ${source_pod} in ${source_cluster} to ${target_pod}" 6 out=$(kubectl exec --context "${source_cluster}" -n "connectivity-test" "${source_pod}" -- \ 7 /bin/bash -c "curl -v ${target_url}" 2>&1); 8 9 if grep "Hostname: ${target_pod}" &>/dev/null <<< "${out}" 10 then 11 echo "SUCCESS" 12 else 13 echo "ERROR: ${out}" 14 return 1 15 fi 1 Checking cross-cluster DNS resolution and connectivity from echoserver0-0 in gke_scratch-kubernetes-team_europe-central2-a_k8s-mdb-0-682f2df6e1745e000788a1d5-24552 to echoserver2-0 2 SUCCESS Run the cleanup script:
1 kubectl --context "${K8S_CLUSTER_0_CONTEXT_NAME}" -n "connectivity-test" delete statefulset echoserver0 2 kubectl --context "${K8S_CLUSTER_1_CONTEXT_NAME}" -n "connectivity-test" delete statefulset echoserver1 3 kubectl --context "${K8S_CLUSTER_2_CONTEXT_NAME}" -n "connectivity-test" delete statefulset echoserver2 4 kubectl --context "${K8S_CLUSTER_0_CONTEXT_NAME}" -n "connectivity-test" delete service echoserver 5 kubectl --context "${K8S_CLUSTER_1_CONTEXT_NAME}" -n "connectivity-test" delete service echoserver 6 kubectl --context "${K8S_CLUSTER_2_CONTEXT_NAME}" -n "connectivity-test" delete service echoserver 7 kubectl --context "${K8S_CLUSTER_0_CONTEXT_NAME}" -n "connectivity-test" delete service echoserver0-0 8 kubectl --context "${K8S_CLUSTER_1_CONTEXT_NAME}" -n "connectivity-test" delete service echoserver1-0 9 kubectl --context "${K8S_CLUSTER_2_CONTEXT_NAME}" -n "connectivity-test" delete service echoserver2-0 10 kubectl --context "${K8S_CLUSTER_0_CONTEXT_NAME}" delete ns "connectivity-test" 11 kubectl --context "${K8S_CLUSTER_1_CONTEXT_NAME}" delete ns "connectivity-test" 12 kubectl --context "${K8S_CLUSTER_2_CONTEXT_NAME}" delete ns "connectivity-test"