K8S in K8S (KinK) grâce à vcluster
K8S in K8S (KinK) grâce à vcluster

K8S in K8S (KinK) grâce à vcluster

K8S Virtual Clusters: Hébergez des clusters Kubernetes virtuels « lightweight » qui s’exécutent dans un namespace de vos clusters Kubernetes « hôtes ».

Pourquoi faire ?

Dans le cas où vous souhaiteriez mettre en place une gestion multi-tenant de vos clusters K8S, l’outil vcluster découvert à l’occasion d’un talk à la KubeConEU 2023 à Amsterdam, propose une approche intéressante.

Les vclusters sont basés sur K3S (une version lightweight de K8S développée par Rancher), ils disposent de leur propre API server ce qui les rend bien plus puissants et bien plus isolés comparativement à une segmentation des tenants « classiques », séparés par namespace par exemple.

Comme des machines virtuelles, un cluster K8S virtuel permet de partitionner un même cluster K8S physique en plusieurs clusters virtuels.

Cela présente plusieurs avantages par rapport à une isolation par namespace.

En effet, même si les namespaces Kubernetes permettent de segmenter plusieurs environnements à l’intérieur d’un même cluster, ils sont limités en terme de ressources globales au cluster (CRDs, cluster roles, persistent volumes, etc..) et aussi par le fait que le control plane est partagé entre tous les namespaces.

Concernant les ressources globales au cluster, dans une segmentation par namespace, il n’est par exemple pas possible d’installer plusieurs versions d’un même opérateur Kubernetes dans un même cluster.

De la même manière, le fait que le control plane soit partagé entre tous les namespaces présente des limitations / inconvénients notamment en termes de résilience.

A titre d’exemple, le rate-limiting du storage ou bien des requêtes par namespace peut s’avérer complexe à mettre en œuvre et toute erreur de configuration à ce niveau présente un risque de défaillance du cluster tout entier. Dans le cas d’un cluster mutualisé entre plusieurs équipes, cela représente un défaut majeur en termes d’isolation et de résilience.

Enfin, les vclusters se révèlent moins chers à opérer que la création de clusters « réels » segmentés par application, par client, en bref par tenant puisqu’ils vous permettent d’économiser de multiples control-planes K8S « réels » bien souvent plus coûteux et fréquemment sous-exploités.

A titre d’exemple un cluster EKS sur AWS vous sera facturé $73 USD / mois (juste pour le control plane sans worker nodes) ce qui annuellement représente tout de même un budget significatif en ces temps de crise 🙂

En résumé, ci-dessous un comparatif rapide entre vcluster et d’autres techniques de segmentation K8S :

image

Architecture de vcluster

Comme évoqué en introduction, les clusters virtuels sont des clusters Kubernetes 100% opérationnels qui s’exécutent par dessus d’autres clusters Kubernetes tout simplement dans un namespace qui leur est dédié.

image

Par défaut, vcluster s’exécute dans un pod unique orchestré en tant que statefulset sur le cluster kubernetes « hôte ». Le pod vcluster se compose de deux conteneurs distincts :

  • Control Plane
    • Ce conteneur contient l’API server, le controller manager ainsi que la connexion au datastore du vcluster. Par défaut vcluster utilise SQLite en principal datastore mais d’autres sont supportés comme etcd, mysql ou encore postgesql
    • k3s qui est une distribution certifiée de Kubernetes, est utilisée pour l’API server et le controller manager du vcluster. C’est aussi un projet initialement conçu par Rancher et actuellement en phase sandbox de la CNCF.
  • Syncer
    • Ce qui rend un vcluster virtuel est qu’il n’existe pas de « worker nodes » à proprement parler dans le cluster.
    • A la place, vcluster utilise le composant syncer pour copier les pods déclarés au niveau du vcluster pour les orchestrer sur le cluster « hôte »

Scheduling des Pods

Par défaut, vcluster réutilise le scheduler kubernetes du cluster « hôte ». Cela économise des ressources mais présente quelques limitations:

  1. Le labeling des nodes à l’intérieur du vcluster n’a aucun effet sur le scheduling des pods.
  2. Drainer ou Tainter les nodes à l’intérieur du vcluster n’a aucun effet.
  3. Il n’est pas possible d’utiliser un custom scheduler à l’intérieur du vcluster
image

Cela pose une limite certaine en terme de scheduling des pods notamment lorsque vous voulez modifier le scheduling de vos pods de sorte qu’ils s’exécutent sur des nodes avec un label particulier ou bien que vous souhaitez répartir vos pods sur vos nodes à des fins de haute-disponibilité ou d’une meilleure utilisation des ressources du cluster.

Pour palier à ce scénario, vcluster permet d’exécuter un scheduler custom à l’intérieur du vcluster vous permettant ainsi de contrôler le scheduling de vos pods par affinity ou via un algorithme de topology spreading.

Pour activer le virtual scheduler, modifiez le fichier values.yml de votre vcluster :

sync:
  nodes:
    enabled: true
    enableScheduler: true
    
    syncAllNodes: true

Puis créer ou upgrader votre vclustervia la commande

vcluster create my-vcluster -f values.yaml

Vous pouvez ensuite influer sur le scheduling de vos pods afin de par exemple les affecter à certains nodes par label en utilisant simplement des flags syncer customs dans votre values.yml

syncer:
  extraArgs:
  - --node-selector=nodeLabel=labelValue

Réseau & DNS

Par défaut les ressources de type Service et Inggress sont synchro avec le cluster « hôte » pour le bon fonctionnement du vcluster.

Traffic pod-to-pod

Par défaut les pods s’exécutent dans un même namespace du cluster « hôte » et peuvent ainsi communiquer de manière transparente entre eux via leurs internal Cluster IPs.

Traffic pod-to-service

Par défaut vcluster synchronise aussi les services avec le cluster « hôte » afin que les pods puissent communiquer par service endpoints.

Cependant, le vcluster dispose de son propre service DNS (CoreDNS par défaut) ce qui permet d’utiliser des noms DNS Kubernetes qui restent intuitifs pour l’utilisateur.

Mapper les services « hôte » avec les services du « vcluster »

La contrepartie d’avoir un CoreDNS dédié par vcluster est que, out-of-the-box il n’est néanmoins pas possible de joindre les services « hôtes » depuis le vcluster ou que les pods du cluster « hôte » puissent joindre les services du vcluster par leurs noms DNS.

Pour traiter cette problématique, vcluster offre une feature de mapping des services du cluster « hôte » vers les services « virtuels » d’un vcluster.

Simplement dans le fichier values.yamlde votre vcluster.

mapServices:
  fromHost:
  - from: my-host-namespace/my-host-service
    to: my-virtual-namespace/my-virtual-service

Il est également possible de faire le mapping inversé depuis le service « virtuel » vers un service du cluster « hôte ».

mapServices:
  fromVirtual:
  - from: my-virtual-namespace/my-virtual-service
    to: my-host-service

Stockage

Par défaut, la création de volumes persistants dans un cluster virtuel n’a pas d’effet sur le cluster « hôte ».

Néanmoins, comme pour les autres types de ressources, il est possible de synchroniser les volumes persistants d’un vcluster avec les volumes du cluster « hôte ».

Simplement dans les values.yml de votre chart helm pour vcluster :

sync:
  persistentvolumes:
    enabled: true
  # If you want to create custom storage classes
  # inside the vcluster.
  storageclasses:
    enabled: true

La manière dont vcluster fonctionne pour synchroniser les PVCs entre les vcluster et les PVCs du cluster « hôte » est relativement simple.

Lorsque la feature de synchro des PVCs est activée, vcluster va simplement rewrite les PVCs crées sur les clusters virtuels sous la forme suivante afin d’éviter les conflits. vcluster-PERSISTENT_VOLUME_NAME-x-VCLUSTER_NAMESPACE-x-VCLUSTER_NAME

Ingress Controller Traffic

vcluster a une option qui permet de synchroniser les ressources de type ingress afin de pouvoir exposer les services d’un vcluster par domaine / par url par exemple.

Néanmoins au lieu d’avoir un ingress controller séparé pour chaque vcluster, il est également possible de configurer vcluster pour qu’un unique ingress controller puisse être synchronisé sur le cluster « hôte » et mutualisé entre les vclusters.

Cela économise des ressources et facilite la communication entre les services de plusieurs vclusters si besoin.

Pour activer cette feature modifiez votre values.yml :

sync:
  ingresses:
    enabled: true

SSL Cert

Les ressources ingress supportent également les annotations cert-manager de sorte à ce qu’une l’instance de cert-manager du cluster « hôte » puisse être utilisée pour provisionner vos certificats TLS Let’s encrypt.

Haute-Disponibilité

Je vois déjà venir les « Mais est-ce que ce setup est prod-ready ? », et bien oui monsieur / oui madame 😛

k3s la distribution Kubernetes utilisée par vcluster permet de configurer les clusters virtuels en mode haute-disponibilité à 2 conditions :

  1. Utiliser un datastore externe en lieu et place du SQLite par défaut
  2. Exécuter 2 pods minimum pour servir plusieurs instances de l’API server et autres composants du control-plane k8s tels que CoreDNS par exemple

Dans votre fichier de values.yml vous pouvez utiliser la configuration suivante :

# Enable HA mode
enableHA: true

# Scale up k3s replicas
replicas: 2

# Set external datastore endpoint
vcluster:
  env:
    - name: K3S_DATASTORE_ENDPOINT
      value: mysql://username:password@tcp(hostname:3306)/database-name

# Disable persistent storage as all data (including bootstrap data) is stored in external datastore
storage:
  persistence: false

# Scale up CoreDNS replicas
coredns:
  replicas: 2
🔎
Notez la connectionString utilisée pour pointer vers MySQL en tant que datastore externe: mysql://username:password@tcp(hostname:3306)/database-name

Les bénéfices

Les bénéfices de vcluster sont multiples :

Délégation des droits d’admin

  • Possibilité de déléguer les droits d’administration sur le vcluster sans déléguer les droits sur le cluster « hôte ». Permet de déléguer la création d’operators, CRDs, namespace et de toutes autres ressources globales au cluster.
  • Capacité à taint et labelliser des nodes sans influence sur le cluster « hôte ».
  • Réutilisation et partage des services entre plusieurs vclusters facilités.

Optimisation des coûts

  • Permet de créer des cluster Kubernetes virtuels en réutilisant un même cluster « physique » ce qui évite d’avoir à créer des clusters dédiés.
  • Les vclusters sont des deployments Kubernetes standards et peuvent être auto-scalés, purgés, snapshottés et migrés facilement.

Faible Overhead

  • Les vclusters sont super « lightweight » et résident dans un simple namespace kube
  • Les vclusters s’appuient sur k3s, une distribution kubernetes à très faible empreinte en terme d’utilisation des ressources et faite pour du déploiement « at edge » comme sur des devices IoT par exemple.
  • Le control plane d’un vcluster est hébergé dans un unique pods + 1 x pod pour CoreDNS.

Pas de dégradation réseau

  • Puisque les pods d’un vcluster sont en fait synchronisés sur le cluster kubernetes « hôte », ils réutilisent la couche networking des services et des pods du cluster sous-jacent.

100% K8S Compatible

  • Les vclusters s’appuient sur k3s qui est une distribution k8s certifiée assurant ainsi une compatibilité à 100% de l’API server.
  • Les vclusters ont leurs propres API server, controller-manager et un datastore dédié ce qui leur confère une plus forte isolation comparé aux namespaces et facilite les montées de versions.

Sécurité

  • Les utilisateurs du vcluster ont besoin de moins de permissions sur le cluster « hôte ».
  • Les utilisateurs du vcluster peuvent gérer leurs propres CRDs indépendamment ainsi que RBAC dans leur vcluster.
  • Les vclusters fournissent une couche d’isolation additionnelle par rapport aux namespaces puisqu’ils disposent de leur propre API server / control plane.

Scalabilité

  • Moins de pression sur l’API server du cluster « hôte » ce qui peut faire la différence dans le cas de déploiement à (très) grande échelle.
  • Meilleures scalabilité du cluster « hôte » via la capacité de partitionner un même cluster en plusieurs vclusters
  • Pas de risque de conflit de CRDs ou de versions de CRD qui pourraient induire une défaillance du cluster « hôte ».

Les Limitations

  • Les Network Policies fonctionnent seulement si le cluster hôte les supporte.
  • La configuration d’un Service Mesh comme Istio sur le cluster virtuel ET sur le cluster « hôte » peut s’avérer complexe et poser problème.

Uses Cases Possibles

  • Scénario Kubernetes Multi-tenant
    • Lors de la KubeCon 2023 vcluster a été présenté par Codefresh.io une startup proposant justement des environnements GitOps basés sur ArgoCD qui sont justement hébergés dans des vclusters 😀.
  • Création de cluster k8s « à la volée » pour de la validation par branche ou bien pour des environnements « éphémères » de démo par exemple.
  • Déploiement Kubernetes à très grande échelle pour réduire la pression sur le control-plane du cluster « physique » sous-jacent et simplifier l’administration / les opérations.
  • Environnements de CI, de Tests, d’expérimentation, de sandbox.
  • Self-Service Kubernetes.

Techniques Alternatives

  • + KinD
    • Une ligne de commande permettant de manager des clusters KinD en tant que pods Kubernetes.
    • Un outil permettant d’exécuter un cluster Kubernetes local en utilisant des nœuds docker.
  • Clusters K8S dédiés, séparés par team / projet / client.
    • L’approche « classique » de faire, pas de multi-tenancy à l’intérieur d’un même cluster.
  • Un namespace par team / project / client à l’intérieur d’un même cluster.
    • Multi-tenancy limitée aux features et contraintes des namespaces K8S.

Ressources Utiles

vcluster github repo

vcluster documentation

k3s documentation

article de blog codefresh.io

Conclusion

En définitive vcluster semble être une approche intéressante et solide à la problématique d’exécution de cluster K8S « nested ». A la manière de la pattern Docker in Docker (DinD) à son époque, elle présente une solution viable pour virtualiser de multiples cluster Kubernetes dans une logique multi-tenant / SaaS, pour des besoins de CI/CD ou encore pour virtualiser vos sandbox de développement.

Les uses-cases sont multiples et la technologie nous semble suffisamment mature pour un PoC en environnement de développement à minima et même, si vous vous sentez suffisamment aventuriers, pour de la production 😀  Alors à vos terminaux shell ! 💻

Article issu de notre participation à la KubeCon + CloudNativeCon Europe 2023

Merci pour votre lecture. Cet article vous a plu, partagez sur vos réseaux 😉

Fabien Jallot - Septembre, 2023

image