- Preparing and customizing Cilium Helm values for Cluster Mesh
- Installing Cilium on both clusters
- Enabling the clustermesh-apiserver and connecting clusters
- Deploying a test application and validating global service behavior (shared and affinity modes)
- Cleaning up
Make sure each cluster has a unique cluster ID and non-overlapping pod CIDR ranges before enabling Cluster Mesh.
Related references:
1) Verify clusters and node readiness
Confirm your kubectl contexts and verify node status on both clusters. Nodes will show NotReady until a CNI (Cilium) is installed.2) Prepare Cilium Helm values for Cluster Mesh
Fetch the default Cilium Helm values and edit them to set a unique cluster name/id and non-overlapping pod CIDR pools.values.yaml (only relevant snippets shown). Each cluster must have a unique cluster.name and cluster.id, and operator.clusterPoolIPv4PodCIDRList (and IPv6 counterpart if used) must not overlap between clusters.
- Example for cluster1:
- Example for cluster2:
3) Install Cilium on each cluster
Add the Cilium Helm repo and install (or upgrade) Cilium on each cluster using the modifiedvalues.yaml.
values.yaml for kind-cluster2 with the cluster2 name/id/CIDR ranges.
After installation, nodes should transition to Ready because Cilium provides the CNI:
4) Enable Cluster Mesh API server on each cluster
Use the Cilium CLI to enable the clustermesh-apiserver on each cluster. On some environments (like kind) the CLI cannot auto-detect Service type; specify--service-type=LoadBalancer if you run into auto-detection errors.
clustermesh-apiserver Service in kube-system and note its EXTERNAL-IP (LoadBalancer IP), which will be used for cluster-to-cluster communication:
5) Connect the clusters into the Cluster Mesh
You only need to run thecilium clustermesh connect command once from any machine that has access to both kubectl contexts. This configures both sides.
6) Deploy the test application into both clusters
Create a simple HTTP echo deployment and service on both clusters using hashicorp/http-echo. Use the same service name and namespace on both clusters and set the echo text to indicate the cluster identity. Template file:deploy-clusterX.yaml â change the -text value per cluster before applying.
-text value accordingly):
curl myapp-service).
7) Default behavior (no global service annotation)
By default (no global annotation), services are local-only. A pod querying the service sees only local endpoints. From a test pod in cluster1:8) Enable a Global Service (shared across clusters)
To advertise a Service globally across the mesh so backends in all clusters are available, annotate the Service withservice.cilium.io/global: "true". Apply the updated Service manifest to both clusters using the same name and namespace.
Service snippet (add the annotation under metadata.annotations):
myapp-service from any cluster will be load-balanced across pods in both clusters.
9) Disable sharing on a specific cluster (service.cilium.io/shared)
If you want a cluster to keep its local backends private (not advertised to the mesh), annotate its Service withservice.cilium.io/shared: "false". Apply this annotation only on the cluster you want to stop sharing from.
Example:
- The cluster that sets
shared: "false"will not advertise its local endpoints to other clusters. - Pods in that cluster still use the global service (they can consume remote backends if available) â
sharedcontrols advertising, not consumption.
10) Service affinity: local vs remote
Cilium supports per-cluster affinity to prefer local or remote backends for a global service. Use the annotationservice.cilium.io/affinity with values:
localâ prefer local backends; fallback to remote only if no local backends existremoteâ prefer remote backends; fallback to local only if remote backends are unavailablenoneâ default global load balancing (no affinity)
service.cilium.io/global: "true":
affinity: "local":
- With local backends present, pods in the cluster will hit local pods.
- If local backends are scaled to zero, requests automatically fail over to remote cluster backends.
affinity: "remote" has the inverse preference (prefer remote, fallback to local).
11) Clean up
Delete test deployments and troubleshooting pods when finished. If you need to disconnect the Cluster Mesh, use the cilium clustermesh disable command.- Ensure unique cluster IDs (1..255) and unique pod CIDR pools per cluster before enabling Cluster Mesh.
- When using LoadBalancer service type on kind clusters, a layer that provides an external IP (e.g., a MetalLB deployment) is required to obtain EXTERNAL-IP addresses.
- Cilium Cluster Mesh â Getting Started
- Cilium Documentation
- Kubernetes Documentation
- MetalLB Load Balancer for bare-metal Kubernetes
- hashicorp/http-echo image on Docker Hub
- nicolaka/netshoot image on Docker Hub