Déployer Kubernetes chez OVHcloud sur des infrastructures dédiées

23 Octobre 2023 • 9 min

L’offre Managed Kubernetes Service sur le cloud public d’OVHcloud apporte des solutions à la plupart des problèmes évoqués dans cet article. Il existe cependant des cas pertinents de déploiement de Kubernetes sur infrastructures dédiées. Aujourd’hui, on se penche sur ce scénario, on vous partage nos petites recettes pour déployer un cluster Kubernetes chez OVHcloud sur des infrastructures dédiées. On va donc l’installer nous-même avec nos petites mains (ou nos outils d’automatisation).

Quand est-ce utile ? Eh bien, par exemple, pour ces besoins spécifiques exprimés par certains de nos clients communs :

  • Souhait de ne pas déployer sur des infrastructures mutualisés et dans le cloud public
  • Avoir la main sur le control-plane (en particulier, api-servers & db etcd sous-jacente)
  • Pouvoir utiliser n’importe quel serveurs bare metal, avec ou sans GPU, en worker Kubernetes
  • Pouvoir utiliser des workers VM provenant de votre propre cluster de virtualisation (ex: offre private-cloud VMWare OVHcloud ou cluster ProxmoxVE installé par vos soins sur des serveurs bare-metal)

On retrouve dans ce type d’installation deux problématiques assez classiques pour déployer Kubernetes sur du on-premise :

  • Comment exposer mes services sur Internet ?
  • Comment stocker les données de mes workloads stateful (les PersistentVolume de Kubernetes)

C’est à ces deux problématiques que nous répondons aujourd’hui !

Exposer les Services sur Internet : Load Balancer & Ingress Controller

Il s’agit ici d’exposer ses services web sur Internet. L'utilisation d’un ingress-controller avec Kubernetes est assez évidente. Chez Enix, on utilise régulièrement l’ingress controller Nginx (celui du projet Kubernetes) car très répandu et offrant la majorité des fonctionnalités les plus utiles.

Cependant, un bon ingress controller ne suffit pas, il faut aussi pouvoir l’exposer. Sur un cluster Kubernetes managé (chez OVHcloud ou ailleurs) vous aurez la joie de pouvoir utiliser un service de type LoadBalancer qui va automatiquement configurer un load-balancer externe et attribuer une ExternalIP au service Kubernetes. Mais sur une installation de type self-managed vous n’aurez pas ce luxe : vous devrez le faire vous même.

Dans ce cas de figure, quand nous sommes sur nos propres infrastructures, nous déployons également une paire (pour la redondance) de load balancers HAProxy externes au cluster Kubernetes qui annoncent en BGP l’adresse IP du service à nos routeurs. Chez OVHcloud il n’est malheureusement pas possible d’établir une session BGP avec leurs routeurs, il faudra donc trouver une autre option.

Une solution consisterait à faire du VRRP. Les vRack propagent du layer2 et il est possible d’attribuer un block d’ip publique sur un vRack. Les load balancers pourraient donc se mettre d’accord au travers du protocole VRRP pour qu’un seul d’entre eux réponde à la VIP à un instant T. En théorie ça marcherait mais je ne suis pas hyper fan de la technologie et je préfère une installation actif/actif pour ce genre de problématique.

Utilisation du Service de Load-Balancing d’OVHcloud

La solution que nous avons éprouvée avec succès consiste à utiliser le service de load-balancer d’OVHcloud qui est, pour simplifier, une ferme de load-balancers HAProxy déployés sur plusieurs POPs (et qui utilisent sans doute en arrière boutique le protocole BGP). Il faudra prévoir d’utiliser l’offre “Pack 2” pour pouvoir attacher son load-balancer à un vRack.

Les offres load-balancer d’OVHCloud

Le frontend du load-balancer sera configuré en niveau 4 pour conserver la terminaison du SSL sur le cluster Kubernetes (et profiter de toutes les bonnes choses que peut nous apporter ce bon vieux cert-manager ).

Détails de la configuration du frontend

Le proxy-protocol sera activé sur chaque serveur de la ferme pour permettre la récupération de l’adresse IP source du client au niveau de l’ingress controller. Il ne faudra pas oublier de configurer une sonde sur chacun des serveurs pour permettre aux nœuds en panne d’être exclus de la ferme :

Détails de la configuration du serveur

💡 Il est possible de partager un seul load-balancer pour exposer les services de plusieurs clusters Kubernetes. Dans ce cas il faudra ajouter une adresse fail-over supplémentaire par cluster Kubernetes sur le load-balancer pour permettre l’utilisation du port 443 et 80 sur chacun d’eux. Il sera alors possible de configurer les frontend de chaque cluster en sélectionnant l’IP fail-over utilisée par chacun d’eux.

Configuration de l’Ingress Controller

Du coté de l’ingress-controller, on le configure ainsi :

  • Controller déployé en DaemonSet
  • hostNetwork = true (pour les perfs, ainsi le serveur nginx écoute directement sur les ports 80 et 443 sans passer par une redirection nat ou ipvs)
  • Proxy protocol activé (pour matcher avec la configuration du load balancer en amont)

En pratique, voici à quoi ressemble un extrait de values pour déployer le helm chart :

---
controller:
  kind: DaemonSet

  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet

  config:
    use-proxy-protocol: "true"

  service:
    enabled: false

Stocker les Données des Workflow Stateful

Trois Catégories de Stockage pour Kubernetes

Sur nos clusters Kubernetes, selon les besoins, on peut déployer trois types de stockage (i.e. trois StorageClass) :

Un stockage local aux nœuds, qui s’appuie généralement sur des SSD en NVME très performants (en débit & latence) afin de proposer des performances maximales pour les workloads qui le nécessitent. Ce stockage étant local à chaque nœud, il induit une difficulté pour les maintenances car les workloads l’utilisant ne sont plus re-schedulable facilement sur un autre noeud (en cas de kubectl drain <node> par exemple). On réservera donc ce stockage aux workloads distribuées capables de répliquer leur stockage elles-mêmes comme la réplication d’une base de données (cela tombe bien, ce sont généralement les base de données qui ont besoin de performance sur le stockage !). Pour ce besoin, on utilise les provisioners locaux OpenEBS (zfs ou lvm) ou encore le local-path-provisioner de Rancher.

Un stockage accessible en ReadWriteMany pour certaines workloads stateful nécessitant de partager un même PersistentVolume. On utilise pour cela un serveur NFS et le nfs-client-provisioner. Idéalement, les applications “modernes” se passent de cette contrainte en utilisant plutôt un object-storage pour partager des données entre elles, mais en pratique on croise encore certaines applications qui le nécessitent. On réservera ce type de stockage aux workloads qui en ont vraiment besoin, car un serveur NFS peut réserver des surprises opérationnelles et surtout il est difficile à redonder. Notez qu’OVHcloud propose un service de NAS-HA basé sur NFS qui est utilisable avec le nfs-client-provisioner mais nous n’avons pas eu l’occasion de l’essayer.

Et enfin un stockage “general purpose”, indépendant des nœuds, pour faciliter les maintenances et la haute disponibilité. Pour ce stockage, les solutions techniques sont nombreuses. On pourrait s’intéresser aux stockages hyperconvergés mais la complexité de ceux-ci, la jeunesse des projets et les performances insuffisantes nous écartent quelque peu de cette solution. D’un autre coté, nous avons chez Enix l’habitude de gérer des clusters Ceph, une techno que nous connaissons bien. La mise en place d’un cluster de ce type chez OVHcloud est possible mais tous nos clients ne sont pas prêts à se lancer : cela nécessite beaucoup de machines et l’infogérance d’un cluster de ce type peut s’avérer coûteuse.

Utilisation du Stockage Ceph Managé d’OVHcloud

Heureusement, OVHcloud propose un stockage Ceph managé qu’on peut utiliser avec Kubernetes. Nous allons voir ensemble comment mettre cela en place.

Après avoir commandé le service “Cloud Disk Array” il faudra se rendre sur sa page de gestion et créer un pool. Nous avons l’habitude de créer 1 pool / cluster Kubernetes :

Création d’un pool Ceph

Nous aurons aussi besoin de créer un utilisateur avec accès complet en lecture / écriture au pool :

Création d’un utilisateur Ceph

Enfin, il ne faudra pas oublier d’ajouter les adresses IP publiques des nœuds Kubernetes qui accèderont au cluster Ceph via la page “Contrôle d’accès IP”. En effet, l’accès au cluster Ceph d’OVHcloud ne peut se faire qu’exclusivement par le réseau public et non par le vRack. Attention à bien évaluer et dimensionner en conséquence le débit de vos interfaces réseau publiques lors de la commande des serveur. Par ailleurs Il faudra aussi prendre ce point en compte lors de la configuration réseau des nœuds.

💡 Dans notre cas nous virtualisons les nœuds avec des hyperviseurs Proxmox VE. Pour éviter de faire passer tout le trafic réseau vers le cluster Ceph à travers le firewall de la plateforme et pour éviter d’allouer une adresse IP publique par nœud Kubernetes, nous configurons chaque hyperviseur pour faire du NAT en sortie sur un bridge dédié. Ceux-ci étant bien sûr sécurisés selon les bonnes pratiques de firewalling, y compris via du whitelisting d’IP dans la console OVHcloud.

La configuration réseau de notre hyperviseur serait par exemple :

auto vmbr1
iface vmbr1 inet static
    bridge-ports none
    bridge-stp off
    bridge-fd 0
    address 172.16.10.1/24
    post-up echo 1 > /proc/sys/net/ipv4/ip_forward
    post-up iptables -t nat -A POSTROUTING -s '172.16.10.0/24' -o bond1 -j MASQUERADE
    post-down iptables -t nat -D POSTROUTING -s '172.16.10.0/24' -o bond1 -j MASQUERADE

Il sera également nécessaire d'ajouter une route sur chaque nœud Kubernetes pour lui permettre de diriger le trafic à destination de 10.0.0.0/8 (les nœuds du cluster Ceph d’OVHcloud étant adressés dans différents subnets de ce réseau) vers l’adresse de l’hyperviseur dans le réseau du bridge :

ip route add 10.0.0.0/8 via 172.16.10.1

Sur la page “Sommaire” du service, il faudra aussi récupérer l’ID du cluster et la liste d’adresse des moniteurs :

Détails de notre service Cloud Disk Array

Ensuite, il suffira de déployer le chart helm de ceph-csi avec les commandes suivantes :

helm repo add ceph-csi https://ceph.github.io/csi-charts
helm install --namespace "ceph-csi-rbd" --create-namespace "ceph-csi-rbd" ceph-csi/ceph-csi-rbd --values ceph-csi.yaml

En utilisant le fichier values “ceph-csi.yaml” contenant ces informations :

storageClass:
  pool: k8sprod
  create: true
  clusterID: d44ada9a-f260-49be-bd84-1bdcdb7207b9  # ID du cluster trouvé sur la page "Sommaire"
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
csiConfig:
  - clusterID: "d44ada9a-f260-49be-bd84-1bdcdb7207b9"  # ID du cluster trouvé sur la page "Sommaire"
    monitors:  # Liste des moniteurs trouvé sur la page "Sommaire"
      - 10.33.79.14:3300
      - 10.97.211.58:3300
      - 10.134.39.74:3300

Les Avantages de Kubernetes en Self-managed sur OVHcloud

Nous utilisons avec succès ce type de déploiement en production sur OVHcloud depuis près de 2 ans en complément de l’offre de service Kubernetes managé en cloud public. Avec ces deux options, nous pouvons déployer et opérer des plateformes Kubernetes sur OVHcloud quels que soient les besoins clients.

Les intérêts de l’option self managed K8s sur infrastructures dédiés décrite dans cet article sont les suivants :

  • D’une part, cela nous offre un meilleur contrôle sur le cluster, son cycle de vie, le choix des composants utilisés, le tout en conservant la possibilité d’ouvrir le capot quand il est nécessaire d’investiguer un problème (sur les services K8s managés des fournisseurs cloud, le control plane est masqué et cela peut limiter certains choix de configuration nécessaires sur la plateforme client). Par ailleurs, certains clients ne souhaitent tout simplement pas déployer leurs plateformes en cloud public.
  • D’autre part, lorsque l’infrastructure du client est conséquente et ne nécessite pas d’instanciation de ressources à la demande, une infrastructure virtualisée sur les machines physiques puissantes d’OVHcloud peut permettre de réduire les coûts même en prenant en compte les frais d’infogérance d’Enix. Le client y gagne au passage l’expertise et la proximité que nous avons l’habitude de proposer.

La palette de services supplémentaires d’OVHCloud (Loadbalancer, Cloud Disk Array…) nous permet de répondre aux problématiques que nous avons l’habitude de rencontrer lorsque nous déployons ce type d’infrastructure Kubernetes self-managed : comment exposer mes services et comment stocker les données attachées à mes workloads stateful. Et cela avec des solutions standards telles que Ceph ou HAProxy sur lesquels nous avons une solide expérience.

On espère que ce billet vous permettra de monter des infrastructures Kubernetes robustes et dédiées chez OVHcloud. Pour ceux qui hésitent encore à se lancer ou qui ont des besoins sur mesure autour de Kubernetes et du Cloud Native plus globalement : n’hésitez pas à nous contacter ! ;-)


Ne ratez pas nos prochains articles DevOps et Cloud Native! Suivez Enix sur Twitter!