Avec l'essor de la technologie cloud, les entreprises ont de plus en plus recours aux services mutualisés sur plusieurs clusters pour optimiser leurs performances et leur efficacité. C'est dans ce contexte que Cilium se démarque comme un outil innovant et puissant. Cet article a pour objectif de vous faire découvrir les avantages et l'utilisation de cette fonctionnalité, tout en détaillant son fonctionnement.
Le contexte technologique actuel
Le Cloud a révolutionné le monde de l'informatique en permettant l'accès et l'utilisation des données à partir de n'importe quel endroit. Cependant, les entreprises se retrouvent souvent confrontées à un défi de taille : comment gérer efficacement l'accès et l'utilisation des données dans différents clouds ? C'est là qu'intervient Cilium. Cilium est un logiciel open source qui permet d'automatiser l'accès et l'utilisation des données dans différents clouds (ou cluster situés dans différentes régions). En Associant à Microsoft AKS et Cilium CNI, les performances des services sont significativement améliorées, avec une réduction de 30% de la latence de routage des services par rapport à kube-proxy.
Les avantages de la mutualisation des services avec Cilium sur AKS
L'utilisation de Cilium sur AKS offre une série d'avantages. Pour commencer, il permet de mettre en œuvre des services mutualisés sur plusieurs clusters. Cela signifie que les entreprises peuvent partager leurs ressources et services entre différents clusters, réduisant ainsi les coûts et augmentant l'efficacité.
Quelques exemple d'utilisation du cluster-mesh de Cilium
-
Déploiement d'applications multi-clusters : Avec ClusterMesh, vous pouvez déployer une application répartie sur plusieurs clusters Kubernetes dans différentes régions ou zones de cloud. Cette approche permet une meilleure disponibilité, une répartition de charge efficace et une tolérance aux pannes entre les clusters.
-
Migration d'applications entre clusters : ClusterMesh facilite la migration d'applications entre clusters Kubernetes, que ce soit pour des mises à niveau, des tests ou des stratégies de déploiement complexes. Vous pouvez transférer des charges de travail d'un cluster à un autre de manière transparente et sans interruption de service.
-
Partage de services entre clusters : Avec ClusterMesh, vous pouvez exposer et partager des services entre différents clusters Kubernetes. Par exemple, un service de base de données hébergé dans un cluster peut être accessible et utilisé par des applications déployées sur d'autres clusters, facilitant ainsi la réutilisation des ressources.
-
Environnements multi-cloud et hybrides : ClusterMesh permet de connecter des clusters Kubernetes s'exécutant sur différents clouds publics (AWS, Azure, GCP) ou dans des environnements hybrides combinant des clouds publics et des infrastructures sur site. Cela offre une grande flexibilité pour les entreprises souhaitant bénéficier des avantages de différents fournisseurs de cloud ou d'une architecture multi-cloud.
-
Isolement et segmentation de réseau : Avec la fonctionnalité de mise en réseau ClusterMesh, vous pouvez isoler et segmenter le trafic réseau entre les clusters. Cela permet de renforcer la sécurité et de contrôler les communications entre les différents clusters, en appliquant des règles de réseau et des politiques de sécurité appropriées.
-
Mise à l'échelle globale : ClusterMesh facilite la mise à l'échelle globale d'applications en permettant de répartir les charges de travail sur plusieurs clusters Kubernetes dans différentes régions ou zones. Cela peut aider à réduire la latence pour les utilisateurs répartis géographiquement et à améliorer les performances globales de l'application.
En outre, Cilium assure une sécurité optimale
Voici quelques points importants concernant la sécurité offerte par Cilium :
-
Politiques de réseau : Cilium applique les politiques de réseau au niveau du nœud. Cela signifie que les règles de sécurité sont mises en œuvre après que tous les nœuds ont été re-imaged ou mis à niveau. Cette approche garantit une cohérence dans l’application des politiques sur l’ensemble du cluster.
-
"Re-imaging" des nœuds : Lorsqu’un nœud est "re-imaged" (par exemple, lors d’une mise à jour de l’image du nœud), Cilium rapplique automatiquement les règles de sécurité (network policy). Cela permet de s’assurer que les nœuds sont conformes aux politiques de sécurité définies.
-
Mise à niveau de la version de Kubernetes : De manière similaire, lorsqu’une mise à niveau de la version de Kubernetes est effectuée, Cilium s’assure que les règles de sécurité sont toujours appliquées correctement, même après la mise à niveau.
-
Visibilité et contrôle : Cilium offre une visibilité granulaire sur le trafic réseau et permet de définir des règles de sécurité basées sur des critères tels que les noms de service, les étiquettes, les ports, etc. En somme, Cilium contribue à renforcer la sécurité de votre cluster Kubernetes en appliquant des politiques de réseau de manière cohérente et en garantissant que les nœuds sont conformes aux règles de sécurité, même lors des mises à jour.
Pour établir une connexion sécurisée entre les clusters, il est nécessaire d'extraire l'Autorité de Certification (CA) utilisée par Cilium et de l'utiliser pour l'installation de Cilium sur l'autre cluster. Cela permet aux clusters de se faire mutuellement confiance en ce qui concerne le trafic crypté, ce qui est crucial pour un maillage de clusters sécurisé. Enfin, la mise en œuvre de services mutualisés sur plusieurs clusters nécessite la mise en place de l'affinité de service globale et la préparation du maillage de clusters de AKS à AKS dans notre exemple.
POC :
Pour ce Proof of Concept, une fois n'est pas coutume, je ne vais pas utiliser terraform pour le déploiement de l'infrastructure mais Pulumi Le repo est ici : https://github.com/herveleclerc/pulumi-aks-cilium Pré-requis :
- Souscription Azure
- Python > 3.11
- Pulumi
J'utilise Pulumi comme outil d'IAC. Un de ses avantages est que l'on peut pleinement profiter des assistants IA pour le code, si besoin.
Création du projet pulumi :
mkdir pulumi-cilium-aks
cd pulumi-cilium-aks
pulumi new -g --language Python \
-n pulumi-cilium-aks \
-s pulumi-cilium-aks \
-t -y
python3 -m venv venv
source venv/bin/activate
python -m pip install --upgrade pip setuptools wheel
python -m pip install -r requirements.txt
remplacez le contenu du fichier '''main.py''' par le code suivant
Fichier 'main'.py :
import pulumi
import pulumi_azure_native as azure_native
from pulumi_azure_native import containerservice, resources, authorization
import base64
"""Creates a new Azure resource group.
Args:
resource_group_name (str): The name of the resource group.
location (str): The location of the resource group.
Returns:
ResourceGroup: A new ResourceGroup object.
"""
def create_resource_group(resource_group_name, location):
return azure_native.resources.ResourceGroup(resource_group_name,
resource_group_name=resource_group_name,
location=location)
"""Creates a new Azure virtual network.
Args:
resource_group (ResourceGroup): The resource group to create the virtual network in.
vnet_name (str): The name of the virtual network.
address_prefixes (list[str]): The address prefixes (CIDR blocks) for the virtual network.
Returns:
VirtualNetwork: The new VirtualNetwork object.
"""
def create_virtual_network(resource_group, vnet_name, address_prefixes):
return azure_native.network.VirtualNetwork(vnet_name,
resource_group_name=resource_group.name,
virtual_network_name=vnet_name,
location=resource_group.location,
address_space=azure_native.network.AddressSpaceArgs(
address_prefixes=address_prefixes,
))
"""Creates a new Azure subnet.
Args:
resource_group (ResourceGroup): The resource group to create the subnet in.
virtual_network (VirtualNetwork): The virtual network to create the subnet in.
subnet_name (str): The name of the subnet.
address_prefix (str): The address prefix (CIDR block) for the subnet.
Returns:
Subnet: The new Subnet object.
"""
def create_subnet(resource_group, virtual_network, subnet_name, address_prefix):
return azure_native.network.Subnet(subnet_name,
address_prefix=address_prefix,
resource_group_name=resource_group.name,
virtual_network_name=virtual_network.name,
subnet_name=subnet_name)
"""Creates a new virtual network peering between two virtual networks.
Args:
resource_group1 (ResourceGroup): The resource group for the first virtual network.
virtual_network1 (VirtualNetwork): The first virtual network to peer.
resource_group2 (ResourceGroup): The resource group for the second virtual network.
virtual_network2 (VirtualNetwork): The second virtual network to peer.
vnet_peering_name (str): The name for the virtual network peering.
Returns:
VirtualNetworkPeering: The new VirtualNetworkPeering object.
"""
def create_vnet_peering(resource_group1, virtual_network1, resource_group2, virtual_network2, vnet_peering_name):
return azure_native.network.VirtualNetworkPeering(vnet_peering_name,
resource_group_name=resource_group1.name,
virtual_network_name=virtual_network1.name,
virtual_network_peering_name=vnet_peering_name,
remote_virtual_network=azure_native.network.SubResourceArgs(id=virtual_network2.id,),
allow_virtual_network_access=True,
allow_forwarded_traffic=True,
opts=pulumi.ResourceOptions(depends_on=[virtual_network1, virtual_network2]),
)
def create_k8s_cluster(resource_group, k8s_cluster_name, subnet_node_id, service_cidr, dns_service_ip, pod_cidr):
return azure_native.containerservice.ManagedCluster(
k8s_cluster_name,
resource_name_=k8s_cluster_name,
resource_group_name=resource_group.name,
location=resource_group.location,
node_resource_group=f"{k8s_cluster_name}-vm",
agent_pool_profiles=[{
"count": 1,
"max_pods": 250,
"mode": "System",
"name": "main",
"node_labels": {"location": resource_group.location},
"os_disk_size_gb": 30,
"os_type": "Linux",
"type": "VirtualMachineScaleSets",
"vm_size": "Standard_B2s",
"vnet_subnet_id": subnet_node_id
}],
dns_prefix='k8s-ne',
enable_rbac=True,
identity=azure_native.containerservice.ManagedClusterIdentityArgs(
type="SystemAssigned",
),
kubernetes_version='1.27.7',
network_profile=azure_native.containerservice.ContainerServiceNetworkProfileArgs(
network_plugin='none',
pod_cidr=pod_cidr,
service_cidr=service_cidr,
dns_service_ip=dns_service_ip
),
)
"""Creates Azure role assignments to grant the AKS managed identity access to the node resource group and cluster resource group.
Args:
cluster_name (str): The name of the AKS cluster.
resource_group_name (ResourceGroup): The resource group of the AKS cluster.
cluster (ManagedCluster): The AKS managed cluster.
Returns:
Tuple[RoleAssignment, RoleAssignment]: The two role assignments granting access.
"""
def create_role_assignments(cluster_name, resource_group_name, cluster):
role_assignment_1 = authorization.RoleAssignment(
f'role_assignment-aks-{cluster_name}',
scope=f'/subscriptions/{config.subscription_id}/resourceGroups/{cluster_name}-vm',
role_definition_id=f'/subscriptions/{config.subscription_id}/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7'.format(
subscriptionId=cluster.id.apply(lambda id: id.split('/')[2])),
principal_id=cluster.identity.apply(lambda i: i.principal_id),
principal_type='ServicePrincipal'
)
role_assignment_2 = authorization.RoleAssignment(
f'role_assignment-aks-{cluster_name}-2',
scope=resource_group_name.id,
role_definition_id=f'/subscriptions/{config.subscription_id}/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7'.format(
subscriptionId=cluster.id.apply(lambda id: id.split('/')[2])),
principal_id=cluster.identity.apply(lambda i: i.principal_id),
principal_type='ServicePrincipal'
)
return role_assignment_1, role_assignment_2
"""Gets Kubernetes credentials for a managed AKS cluster.
Args:
resource_group: The resource group of the AKS cluster.
k8s_cluster: The AKS managed cluster.
Returns:
The Kubernetes config file contents.
"""
def get_k8s_credentials(resource_group, k8s_cluster):
creds = containerservice.list_managed_cluster_user_credentials_output(
resource_group_name=resource_group.name,
resource_name=k8s_cluster.name)
encoded = creds.kubeconfigs[0].value
kubeconfig = encoded.apply(
lambda enc: base64.b64decode(enc).decode())
return kubeconfig
config = authorization.get_client_config()
"""
regions: A dictionary containing configuration details for Kubernetes clusters in different Azure regions.
Each region contains the following details:
- rg_name: Resource group name
- location: Azure region location
- address_prefixes: Address prefixes for the VNet
- vnet_name: Name of the VNet
- subnet_node_name: Name of the node subnet
- subnet_node_address_prefix: Address prefix for the node subnet
- subnet_pod_address_prefix: Address prefix for the pod subnet
- pod_cidr: CIDR range for pod IPs
- service_cidr: CIDR range for service IPs
- dns_service_ip: IP address for DNS service
- k8s_cluster_name: Name of the Kubernetes cluster
This provides the configuration needed to deploy Kubernetes clusters in different regions with appropriate networking.
"""
regions = {
"northeurope": {
"rg_name": "rg-aks-northeurope",
"location": "northeurope",
"address_prefixes": ["10.0.0.0/20"],
"vnet_name": "vnet-ne",
"subnet_node_name": "node-subnet-ne",
"subnet_node_address_prefix": "10.0.0.0/23",
"subnet_pod_address_prefix": "10.0.4.0/22",
"pod_cidr": "198.170.0.0/16",
"service_cidr": "192.172.0.0/16",
"dns_service_ip": "192.172.0.53",
"k8s_cluster_name": "k8s-cluster-ne",
},
"westeurope": {
"rg_name": "rg-aks-westeurope",
"location": "westeurope",
"address_prefixes": ["10.0.16.0/20"],
"vnet_name": "vnet-we",
"subnet_node_name": "node-subnet-we",
"subnet_node_address_prefix": "10.0.16.0/23",
"subnet_pod_address_prefix": "10.0.20.0/22",
"pod_cidr": "198.174.0.0/16",
"service_cidr": "192.176.0.0/16",
"dns_service_ip": "192.176.0.53",
"k8s_cluster_name": "k8s-cluster-we",
},
"francecentral": {
"rg_name": "rg-aks-francecentral",
"location": "francecentral",
"address_prefixes": ["10.0.32.0/20"],
"vnet_name": "vnet-fc",
"subnet_node_name": "node-subnet-fc",
"subnet_node_address_prefix": "10.0.32.0/23",
"subnet_pod_address_prefix": "10.0.36.0/22",
"pod_cidr": "198.178.0.0/16",
"service_cidr": "192.180.0.0/16",
"dns_service_ip": "192.180.0.53",
"k8s_cluster_name": "k8s-cluster-fc",
}
}
"""
vnet_peerings: Dictionary mapping region pairs to the peering names for connecting them.
The keys are tuples of (region1, region2) and values are tuples
(peering1_name, peering2_name). This allows creating bidirectional
peerings between regions.
"""
vnet_peerings = {
("northeurope", "westeurope"): ("vnet-peering-ne-we", "vnet-peering-we-ne"),
("northeurope", "francecentral"): ("vnet-peering-ne-fc", "vnet-peering-fc-ne"),
("westeurope", "francecentral"): ("vnet-peering-we-fc", "vnet-peering-fc-we"),
}
"""
Create Azure resource groups, virtual networks, and subnets for each region.
The resource groups, virtual networks, and node subnets are created by looping through
the regions dictionary and calling the respective Azure resource creation functions. The
names and properties for each resource are populated from the per-region dictionaries.
"""
resource_groups = {region: create_resource_group(regions[region]["rg_name"], regions[region]["location"]) for region in regions}
virtual_networks = {region: create_virtual_network(resource_groups[region], regions[region]["vnet_name"], regions[region]["address_prefixes"]) for region in regions}
subnet_nodes = {region: create_subnet(resource_groups[region], virtual_networks[region], regions[region]["subnet_node_name"], regions[region]["subnet_node_address_prefix"]) for region in regions}
"""
Create VNet peerings between regions.
Loops through the vnet_peerings dictionary to create bidirectional VNet
peerings between each region pair using the provided peering names.
Create Kubernetes clusters in each region.
Loops through the regions to create a Kubernetes cluster in each region's
resource group, subnet, and other configured networking settings.
Create role assignments to grant access to clusters.
Loops through regions to create role assignments granting access to the
cluster for the current user/service principal.
Get kubeconfig files for each cluster.
Loops through regions to download the kubeconfig files for accessing
each cluster.
"""
for (region1, region2), (peering_name1, peering_name2) in vnet_peerings.items():
vnet_peering1 = create_vnet_peering(resource_groups[region1], virtual_networks[region1], resource_groups[region2], virtual_networks[region2], peering_name1)
vnet_peering2 = create_vnet_peering(resource_groups[region2], virtual_networks[region2], resource_groups[region1], virtual_networks[region1], peering_name2)
k8s_clusters = {region: create_k8s_cluster(resource_groups[region], regions[region]["k8s_cluster_name"], subnet_nodes[region].id, regions[region]["service_cidr"], regions[region]["dns_service_ip"], regions[region]["pod_cidr"]) for region in regions}
role_assignments = {region: create_role_assignments(regions[region]["k8s_cluster_name"], resource_groups[region], k8s_clusters[region]) for region in regions}
kubeconfigs = {region: get_k8s_credentials(resource_groups[region], k8s_clusters[region]) for region in regions}
"""
Exports output values for use by other services:
- subnet_node_ids: Map of subnet ids for each region's node subnet
- virtual_network_ids: Map of virtual network ids for each region
- vnet_peering_states: Map of peering states for each vnet peering
- kubeconfigs: Map of kubeconfig files for accessing each cluster
"""
pulumi.export('subnet_node_ids', {region: subnet_nodes[region].id for region in regions})
pulumi.export('virtual_network_ids', {region: virtual_networks[region].id for region in regions})
pulumi.export('vnet_peering_states', {f"{region1}_{region2}": (vnet_peering1.peering_state, vnet_peering2.peering_state) for (region1, region2), (peering_name1, peering_name2) in vnet_peerings.items()})
pulumi.export('kubeconfigs', kubeconfigs)
- Création des clusters kubernetes
pulumi up -y
- Si tout se passe bien :
Type Name Status
+ pulumi:pulumi:Stack pulumi-cilium-aks-azure created (334s)
+ ├─ azure-native:resources:ResourceGroup rg-aks-northeurope created (1s)
+ ├─ azure-native:resources:ResourceGroup rg-aks-westeurope created (1s)
+ ├─ azure-native:resources:ResourceGroup rg-aks-francecentral created (1s)
+ ├─ azure-native:network:VirtualNetwork vnet-fc created (5s)
+ ├─ azure-native:network:VirtualNetwork vnet-we created (5s)
+ ├─ azure-native:network:VirtualNetwork vnet-ne created (4s)
+ ├─ azure-native:network:Subnet node-subnet-ne created (5s)
+ ├─ azure-native:network:Subnet node-subnet-we created (5s)
+ ├─ azure-native:network:VirtualNetworkPeering vnet-peering-ne-we created (15s)
+ ├─ azure-native:network:VirtualNetworkPeering vnet-peering-we-ne created (26s)
+ ├─ azure-native:network:Subnet node-subnet-fc created (4s)
+ ├─ azure-native:network:VirtualNetworkPeering vnet-peering-fc-we created (12s)
+ ├─ azure-native:network:VirtualNetworkPeering vnet-peering-we-fc created (13s)
+ ├─ azure-native:network:VirtualNetworkPeering vnet-peering-ne-fc created (23s)
+ ├─ azure-native:network:VirtualNetworkPeering vnet-peering-fc-ne created (12s)
+ ├─ azure-native:containerservice:ManagedCluster k8s-cluster-ne created (257s)
+ ├─ azure-native:containerservice:ManagedCluster k8s-cluster-fc created (247s)
+ ├─ azure-native:containerservice:ManagedCluster k8s-cluster-we created (311s)
+ ├─ azure-native:authorization:RoleAssignment role_assignment-aks-k8s-cluster-fc-2 created (2s)
+ ├─ azure-native:authorization:RoleAssignment role_assignment-aks-k8s-cluster-fc created (1s)
+ ├─ azure-native:authorization:RoleAssignment role_assignment-aks-k8s-cluster-ne-2 created (4s)
+ ├─ azure-native:authorization:RoleAssignment role_assignment-aks-k8s-cluster-ne created (2s)
+ ├─ azure-native:authorization:RoleAssignment role_assignment-aks-k8s-cluster-we-2 created (4s)
+ └─ azure-native:authorization:RoleAssignment role_assignment-aks-k8s-cluster-we created (3s)
- Récupération des kubeconfig : Plus simple de le faire avec az cli que pulumi (
pulumi stack output kubeconfigs --show-secrets
)
az aks get-credentials -n k8s-cluster-we -g rg-aks-westeurope -f config
az aks get-credentials -n k8s-cluster-ne -g rg-aks-northeurope -f config
az aks get-credentials -n k8s-cluster-fc -g rg-aks-francecentral -f config
- Positionnement sur le fichier de config et vérifications
export KUBECONFIG=./config
❯ kubectl get nodes --context k8s-cluster-fc
NAME STATUS ROLES AGE VERSION
aks-main-96811076-vmss000000 NotReady agent 8m23s v1.27.7
❯ kubectl get nodes --context k8s-cluster-ne
NAME STATUS ROLES AGE VERSION
aks-main-19291491-vmss000000 NotReady agent 8m31s v1.27.7
❯ kubectl get nodes --context k8s-cluster-we
NAME STATUS ROLES AGE VERSION
aks-main-24498079-vmss000000 NotReady agent 8m33s v1.27.7
On remarque bien que la partie network n'est pas prête
- Installation du CNI cilium
Je vais le faire avec la CLI de cilium on voit bien les différents paramètres a utiliser. Il est tout à fait possible de déployer Cilium avec un chart helm. Archives : https://github.com/cilium/charts Installation : https://docs.cilium.io/en/stable/installation/k8s-install-helm/
# k8s-cluster-ne
cilium install \
--set azure.resourceGroup=rg-aks-northeurope \
--set cluster.id=1 \
--set ipam.operator.clusterPoolIPv4PodCIDRList='{198.170.0.0/16}' \
--context=k8s-cluster-ne
#k8s-cluster-we
cilium install \
--set azure.resourceGroup=rg-aks-westeurope \
--set cluster.id=2 --set ipam.operator.clusterPoolIPv4PodCIDRList='{198.174.0.0/16}' \
--context=k8s-cluster-we
#k8s-cluster-fc
cilium install \
--set azure.resourceGroup=rg-aks-francecentral \
--set cluster.id=3 --set ipam.operator.clusterPoolIPv4PodCIDRList='{198.178.0.0/16}' \
--context=k8s-cluster-fc
Sortie :
Choses importantes :
- les id différents pour chacun des clusters kubernetes
- les variables clusterPoolIPv4PodCIDRList qui correspondent aux pod_cidr des clusters
Contrôle de l'installation de cilium :
for i in ne we fc
do
cilium status --context k8s-cluster-$i
kubectl get nodes --context=k8s-cluster-$i
done
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: disabled (using embedded mode)
\__/¯¯\__/ Hubble Relay: disabled
\__/ ClusterMesh: disabled
Deployment cilium-operator Desired: 1, Ready: 1/1, Available: 1/1
DaemonSet cilium Desired: 1, Ready: 1/1, Available: 1/1
Containers: cilium Running: 1
cilium-operator Running: 1
Cluster Pods: 5/5 managed by Cilium
Helm chart version: 1.15.1
Image versions cilium quay.io/cilium/cilium:v1.15.1@sha256:351d6685dc6f6ffbcd5451043167cfa8842c6decf80d8c8e426a417c73fb56d4: 1
cilium-operator quay.io/cilium/operator-generic:v1.15.1@sha256:819c7281f5a4f25ee1ce2ec4c76b6fbc69a660c68b7825e9580b1813833fa743: 1
#---
NAME STATUS ROLES AGE VERSION
aks-main-16181278-vmss000000 Ready agent 35m v1.27.7
Cilium est bien installé et les noeuds sont prêts (Statut Ready)
Déploiement du clustermesh
Super Important : Avant le déploiement nous devons partager la même CA entre les clusters
Déploiement du clustermesh
Super Important :
Avant le déploiement nous devons partager la même CA entre les clusters
neat est un plugin kubectl très utile pour faire du reverse ingineering propre.
Toutes les informations inutiles sont supprimées du yaml. (https://github.com/itaysk/kubectl-neat)
# Application aux autres clusters
kubectl apply -f cilium-ca.yaml -n kube-system --context k8s-cluster-we
kubectl apply -f cilium-ca.yaml -n kube-system --context k8s-cluster-fc
Ne vous inquiétez pas des warnings car les secrets ont été créés de manière impérative.
- Activation du clustermesh
for i in ne we fc
do
cilium clustermesh enable --context k8s-cluster-$i
done
Cette opération prend un peu de temps
Sortie :
- Vérification de l'activation
❯ for i in ne we fc
do
cilium clustermesh status --context k8s-cluster-$i
done
- Connection des clusters les uns avec les autres
cilium clustermesh connect --context k8s-cluster-we --destination-context k8s-cluster-ne
cilium clustermesh connect --context k8s-cluster-we --destination-context k8s-cluster-fc
cilium clustermesh connect --context k8s-cluster-ne --destination-context k8s-cluster-fc
Ces opérations prennent un certain temps...
Vérifications
for i in ne we fc
do
cilium clustermesh status --context k8s-cluster-$i
done
Vous devriez avoir ca :
Test avec une appli
Je vais prendre l'exemple fourni par Cilium pour mettre en évidence un classique loadbalancing entre les 2 services sur des clusters différents
kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.15.1/examples/kubernetes/clustermesh/global-service-example/cluster1.yaml \
--context k8s-cluster-ne
kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.15.1/examples/kubernetes/clustermesh/global-service-example/cluster2.yaml \
--context k8s-cluster-we
❯ for i in {1..10}
do
kubectl exec -ti deployment/x-wing --context k8s-cluster-ne -- curl rebel-base
done
{"Galaxy": "Alderaan", "Cluster": "Cluster-1"}
{"Galaxy": "Alderaan", "Cluster": "Cluster-2"}
{"Galaxy": "Alderaan", "Cluster": "Cluster-1"}
{"Galaxy": "Alderaan", "Cluster": "Cluster-2"}
{"Galaxy": "Alderaan", "Cluster": "Cluster-2"}
{"Galaxy": "Alderaan", "Cluster": "Cluster-1"}
{"Galaxy": "Alderaan", "Cluster": "Cluster-2"}
{"Galaxy": "Alderaan", "Cluster": "Cluster-2"}
{"Galaxy": "Alderaan", "Cluster": "Cluster-2"}
{"Galaxy": "Alderaan", "Cluster": "Cluster-2"}
On voit bien que les requêtes sont load balancées sur les 2 clusters
**Comment créer un service partagé **
Pour cet example on va juste supprimer le deployment de k8s-cluster-ne et ne garder que le service.
kubectl delete deploy rebel-base --context=k8s-cluster-ne
kubectl delete deploy x-wing --context=k8s-cluster-ne
kubectl get all
❯ kubectl get all
NAME TYPE CLUSTER-IP EXTERNAL-IP
service/rebel-base ClusterIP 192.172.248.186 <none> 80/TCP 108m
Maintenant lancez la commande
kubectl run -ti --rm curl --image curlimages/curl --context=k8s-cluster-ne -- sh
# au prompt
while :
do
curl rebel-base
done
Vous avez toujours accès aux pods qui sont déployés sur k8s-cluster-we via le service rebel-base :
{"Galaxy": "Alderaan", "Cluster": "Cluster-2"}
{"Galaxy": "Alderaan", "Cluster": "Cluster-2"}
{"Galaxy": "Alderaan", "Cluster": "Cluster-2"}
Si on regarde dans Hubble on a :
A cela s'ajoute la possibilité de faire du routage qui tient compte de la topologie avec deux modes :
Affinité locale de service : le trafic est d'abord routé vers les endpoints locaux avant d'aller vers d'autres clusters si nécessaire. ( annotation io.cilium/service-affinity: local)
apiVersion: v1
kind: Service
metadata:
name: mysql-db
annotations:
io.cilium/global-service: "true"
io.cilium/service-affinity: local
spec:
type: ClusterIP
ports:
- port: 3306
selector:
name: mysql-db
Affinité distante de service : le trafic est préférentiellement routé vers les endpoints distants dans d'autres clusters. (annotation io.cilium/service-affinity: remote)
apiVersion: v1
kind: Service
metadata:
name: vault
annotations:
io.cilium/global-service: "true"
io.cilium/service-affinity: remote
spec:
type: ClusterIP
ports:
- port: 8200
selector:
name: vault
Avec cilium et clustermesh on peut donc créer des architectures logicielles permettant d'avoir au niveau des applications : H - aute disponibilité et tolérance aux pannes - Découverte transparente des services - Routage IP des pods sans effort (rien à faire à part poser les bonnes annotations) - Services partagés entre les clusters - Application uniforme des politiques de réseau du niveau 3 jusqu'au niveau 7. (clustermesh + servicemesh)
Conclusions
Cilium est plus qu'un CNI basique il ajoute un ensemble de fonctionnalités réseaux très intéressantes :
- ClusterMesh
- NetworkPolicy permettant de faire des règles bien plus évoluées que les network policy classiques (ex: egress toFQDNs)
- Service Mesh : intégration du service mesh directement au niveau kernel en utilisant eBPF. Donc des performances et une simplicité à toutes épreuves.
- Observabilité : Notamment avec Hubble
Découvrez les derniers articles d'alter way
- kubevpn
- Kubernetes 1.32
- re:Invent 2024 : AWS mise tout sur l'IA pour son Cloud
- OVHcloud Summit 2024 : L’innovation au cœur d’un cloud souverain et performant
- Big Data & AI Paris 2024 : L'IA au cœur de toutes les transformations.
- Un retour vers l'open-source ? Vos outils DevOps préférés et leurs equivalents open-source.