VOOZH about

URL: https://dzone.com/articles/multi-cluster-networking-with-kubernetes-docker

⇱ Docker and Kubernetes Multi-Cluster Networking Guide


Related

  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. Multi-Cluster Networking With Kubernetes and Docker: Connecting Your Containerized Environment

Multi-Cluster Networking With Kubernetes and Docker: Connecting Your Containerized Environment

Simplify your complex Kubernetes multi-cluster docker environments for better efficiency and traceability by following the steps in this tutorial.

Likes
Comment
Save
2.9K Views

Join the DZone community and get the full member experience.

Join For Free

Ever found yourself managing multiple Kubernetes clusters across different environments, and wondering how to get them all talking to each other without losing your mind? You're not alone. Multi-cluster networking has become a necessity as organizations expand their container footprints.

I've spent years watching teams combatting this similar problem, and what I've found is that with the right approach, multi-cluster networking doesn't have to be complicated. Let's explore how to connect your containerized universe effectively and securely.

Why Multi-Cluster Networking Matters

Remember when running a single Kubernetes cluster felt like a huge achievement? Those were simpler times. Now, organizations routinely run multiple clusters across different regions, clouds, and even on-premises environments.

There are plenty of good reasons for this complexity:

  • Geographic distribution for lower latency and higher availability
  • Regulatory requirements that mandate data residency in specific regions
  • Isolation between development, testing, and production environments
  • Multicloud strategies to avoid vendor lock-in
  • Edge computing bringing compute closer to users

Network Topology Options: Finding Your Path

When connecting multiple Kubernetes clusters, you've got several architectural patterns to consider. 

VPN-Based Connectivity

The most straightforward approach is establishing VPN tunnels between your clusters. This sets up a secure, encrypted connection for communication between clusters.

YAML
# Example Wireguard VPN configuration for Kubernetes
apiVersion: apps/v1
kind: DaemonSet
metadata:
 name: wireguard-node
 namespace: kube-system
spec:
 selector:
 matchLabels:
 app: wireguard-node
 template:
 metadata:
 labels:
 app: wireguard-node
 spec:
 hostNetwork: true
 containers:
 - name: wireguard
 image: linuxkit/wireguard:v0.0.2
 securityContext:
 privileged: true
 volumeMounts:
 - name: config
 mountPath: /etc/wireguard
 - name: modules
 mountPath: /lib/modules
 volumes:
 - name: config
 configMap:
 name: wireguard-config
 - name: modules
 hostPath:
          path: /lib/modules



VPN connections work well for smaller deployments but can become challenging to manage at scale. They also typically require privileged container access, which isn't always acceptable from a security standpoint.

Service Mesh Approach

Service meshes like Istio and Linkerd have emerged as powerful solutions for multi-cluster networking. They take care of most of the complexity involved in service discovery and managing traffic.

YAML
# Example Istio multi-cluster configuration
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
 name: istio-control-plane
spec:
 profile: default
 meshConfig:
 accessLogFile: /dev/stdout
 enableTracing: true
 components:
 pilot:
 k8s:
 env:
 - name: PILOT_SCOPE_GATEWAY_TO_NAMESPACE
 value: "true"
 values:
 global:
 meshID: mesh1
 multiCluster:
 clusterName: cluster1
      network: network1



A service mesh provides unified service discovery, load balancing, security, and observability all together. But they do add overhead both in terms of performance and operational complexity.

Kubernetes Federation

Kubernetes Federation (KubeFed) lets you manage and sync configurations across multiple Kubernetes clusters using just one API endpoint.

YAML
# Example KubeFed configuration
apiVersion: core.kubefed.io/v1beta1
kind: KubeFedConfig
metadata:
 name: kubefed
 namespace: kube-federation-system
spec:
 featureGates:
 - name: SchedulerPreferences
 configuration: "Enabled"
 - name: PushReconciler
 configuration: "Enabled"
 - name: RawResourceStatusCollection
 configuration: "Enabled"
 leaderElect:
 leaseDuration: 15s
 renewDeadline: 10s
    retryPeriod: 5s



Federation is still maturing, but it's a promising approach for organizations that want to manage resources across clusters in a unified way. However, it doesn't solve all networking challenges on its own.

Service Discovery Across Clusters

One of the trickiest parts of multi-cluster networking is service discovery. How does a service in Cluster A know how to find a service in Cluster B?

There are several strategies to tackle this:

DNS-Based Service Discovery

Many organizations expand Kubernetes DNS so it works seamlessly across multiple clusters. CoreDNS (the default DNS provider in Kubernetes) can be configured to forward queries to other clusters.

YAML
# Example CoreDNS configuration for cross-cluster service discovery
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
 fallthrough in-addr.arpa ip6.arpa
 }
 forward . /etc/resolv.conf
 cache 30
 loop
 reload
 loadbalance
 }
 cluster-b.svc.local:53 {
 errors
 cache 30
 forward . 10.96.0.10 {
 force_tcp
 }
    }



Custom Service Registry

Another approach is to implement a custom service registry (like Consul or etcd) that spans all your clusters and provides a unified view of services.

YAML
# Example Consul deployment for cross-cluster service discovery
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: consul
 namespace: consul
spec:
 serviceName: consul
 replicas: 3
 selector:
 matchLabels:
 app: consul
 template:
 metadata:
 labels:
 app: consul
 spec:
 containers:
 - name: consul
 image: consul:1.10.3
 args:
 - "agent"
 - "-server"
 - "-bootstrap-expect=3"
 - "-ui"
 - "-client=0.0.0.0"
 - "-retry-join=consul-0.consul.consul.svc.cluster.local"
 - "-retry-join=consul-1.consul.consul.svc.cluster.local"
 - "-retry-join=consul-2.consul.consul.svc.cluster.local"
 ports:
 - containerPort: 8500
 name: http
 - containerPort: 8301
 name: serf-lan
 - containerPort: 8302
 name: serf-wan
 - containerPort: 8300
 name: server
 - containerPort: 8600
          name: dns



Load Balancing Between Clusters

Once your services can discover each other across clusters, you need to think about load balancing. How do you distribute traffic efficiently?

Global Load Balancers

For production environments, global load balancers like AWS Global Accelerator, Google Cloud Load Balancing, or solutions like F5 or NGINX can direct traffic to the nearest or healthiest cluster.

YAML
# Example NGINX configuration as a global load balancer
apiVersion: v1
kind: ConfigMap
metadata:
 name: nginx-config
data:
 nginx.conf: |
 http {
 upstream backend_clusters {
 server cluster-a-ingress.example.com weight=1;
 server cluster-b-ingress.example.com weight=1;
 server cluster-c-ingress.example.com backup;
 }
 server {
 listen 80;
 location / {
 proxy_pass http://backend_clusters;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 }
 }
    }



BGP-Based Solutions

For advanced users, BGP (Border Gateway Protocol) can be used to announce services across networks. This is especially useful in on-premises environments or when working with bare metal Kubernetes clusters.

YAML
# Example MetalLB configuration using BGP
apiVersion: v1
kind: ConfigMap
metadata:
 namespace: metallb-system
 name: config
data:
 config: |
 peers:
 - peer-address: 10.0.0.1
 peer-asn: 64501
 my-asn: 64500
 address-pools:
 - name: default
 protocol: bgp
 addresses:
      - 192.168.10.0/24



Security Considerations

Connecting multiple clusters expands your attack surface, so security needs extra attention.

Network Policies

Set up network policies in each cluster to control which pods are allowed to communicate with services in other clusters.

YAML
# Example NetworkPolicy for cross-cluster communication
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
 name: allow-cross-cluster
 namespace: app-namespace
spec:
 podSelector:
 matchLabels:
 app: frontend
 policyTypes:
 - Egress
 egress:
 - to:
 - ipBlock:
 cidr: 10.20.0.0/16 # CIDR range of the target cluster
 ports:
 - protocol: TCP
      port: 8080



Service Accounts and RBAC

When using service meshes or federation, carefully configure service accounts and RBAC to ensure only authorized services can communicate across cluster boundaries.

YAML
# Example RBAC configuration for cross-cluster access
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
 name: cross-cluster-reader
rules:
- apiGroups: [""]
 resources: ["services", "endpoints", "pods"]
 verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
 name: cross-cluster-reader-binding
subjects:
- kind: ServiceAccount
 name: cross-cluster-sa
 namespace: default
roleRef:
 kind: ClusterRole
 name: cross-cluster-reader
  apiGroup: rbac.authorization.k8s.io



Mutual TLS

Implement mutual TLS (mTLS) between services across clusters to encrypt traffic and verify service identities.

YAML
# Example Istio mTLS policy
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
 name: default
 namespace: istio-system
spec:
 mtls:
    mode: STRICT



Practical Implementation

Let's pull everything together with a practical example. Imagine we have three Kubernetes clusters:

  • Production cluster in AWS us-east-1
  • Disaster recovery cluster in AWS us-west-2
  • Development cluster in Google Cloud

We want services to communicate securely across these environments while maintaining isolation. 

Here’s one way to set that up using Istio. First, we install Istio in each cluster with multi-cluster settings:

Shell
# Install Istio on primary cluster
istioctl install --set profile=default \
 --set values.global.meshID=mesh1 \
 --set values.global.multiCluster.clusterName=cluster-east \
 --set values.global.network=network-east

# Install Istio on DR cluster
istioctl install --set profile=default \
 --set values.global.meshID=mesh1 \
 --set values.global.multiCluster.clusterName=cluster-west \
 --set values.global.network=network-west

# Install Istio on dev cluster
istioctl install --set profile=default \
 --set values.global.meshID=mesh1 \
 --set values.global.multiCluster.clusterName=cluster-dev \
  --set values.global.network=network-dev



Next, we create a remote secret for each cluster to enable cross-cluster communication:

Shell
# From primary cluster, create remote secrets for other clusters
istioctl x create-remote-secret --name=cluster-west | kubectl apply -f -
istioctl x create-remote-secret --name=cluster-dev | kubectl apply -f -

# From DR cluster
istioctl x create-remote-secret --name=cluster-east | kubectl apply -f -
istioctl x create-remote-secret --name=cluster-dev | kubectl apply -f -

# From dev cluster
istioctl x create-remote-secret --name=cluster-east | kubectl apply -f -
istioctl x create-remote-secret --name=cluster-west | kubectl apply -f -



We can then deploy our services with proper labels to enable cross-cluster traffic:

YAML
# Example deployment with network metadata
apiVersion: apps/v1
kind: Deployment
metadata:
 name: my-service
 namespace: default
spec:
 selector:
 matchLabels:
 app: my-service
 template:
 metadata:
 labels:
 app: my-service
 network: network-east # Matches the network name defined in Istio
 spec:
 containers:
 - name: my-service
 image: my-service:1.0
 ports:
 - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
 name: my-service
 namespace: default
 labels:
 app: my-service
 network: network-east
spec:
 ports:
 - port: 8080
 name: http
 selector:
    app: my-service



With this setup, services can discover and call each other across clusters using the Kubernetes service name with the proper namespace:

Python
# Example Python code calling a service across clusters
import requests

# This will work from any cluster in the mesh
response = requests.get("http://my-service.default.svc.cluster.local:8080/api/data")
print(response.json())



Common Challenges and Solutions

Let's be real—multi-cluster networking isn't all sunshine and rainbows. Here are some common challenges you might run into and how you can handle them:

Network Latency

When clusters span geographic regions, latency can become a significant issue. Consider implementing:

  • Regional routing policies to prefer local services
  • Caching layers for frequently accessed data
  • Asynchronous communication patterns where possible

Debugging Complexity

Debugging network issues across clusters can be incredibly challenging. Invest in:

  • Distributed tracing with tools like Jaeger or Zipkin
  • Centralized logging with the ELK stack or similar
  • Service mesh observability features

Configuration Drift

Keeping networking configurations consistent across clusters can be difficult. Consider:

  • GitOps workflows with the use of ArgoCD or Flux
  • Automated compliance checking
  • Regular audits of network policies and routes

Conclusion: Connecting Your Containerized Environment

Multi-cluster networking might seem daunting at first, but it's a challenge worth tackling. The ability to connect services across environments gives you tremendous flexibility and resilience.

Kubernetes Docker (software) Data cluster

Opinions expressed by DZone contributors are their own.

Related

  • Smart Deployment Strategies for Modern Applications
  • How We Diagnosed a Hidden Scheduler Failure in a Docker Swarm Cluster Serving 2 Million Users
  • Java Backend Development in the Era of Kubernetes and Docker
  • Docker Hardened Images for Container Security

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

Let's be friends: