KUBERNETES – On Premise 🏠 – REX sur un contexte client

Début 2020, un projet DevOps a été initié chez un de nos clients pour répondre à plusieurs besoins. Nous sommes dans un contexte où l’équipe d’exploitation (Ops) gère un grand nombre d’applications métier très hétérogènes avec un scope de responsabilités assez vaste, allant des facilities (gestion des salles serveurs), du provisionnement de serveurs virtuels (VMs,) jusqu’au déploiement des applications en production, en intégrant évidemment le monitoring applicatif, le stockage SAN, le backup disques/LTO7.

Les processus en place n’étaient plus suffisamment efficients et les demandes d’hébergement, de déploiement en production grandissant, il fallait repenser certaines méthodes de travail. Cette perte d’efficacité était en quelque sorte liée au turn-over régulier dans l’équipe d’Ops, au large scope d’intervention mais aussi et surtout, au manque de dialogue avec les (nombreuses) équipes de développeurs.

La première phase de ce projet a été de faire en sorte que l’equipe des Ops adopte la méthodologie DevOps (mais surtout GitOps) pour la faire gagner en agilité, afin de pouvoir ensuite migrer sur une architecture résiliente et automatisée.

La phase suivante a été de reprendre les processus d’exploitation de l’infrastructure legacy et de trouver la meilleure solution qui s’offrait pour répondre à tous les besoins du client.

A partir de là, le choix s’est rapidement porté sur la solution ☸ Kubernetes â˜¸

Dans cet article, nous allons expliquer ce choix et présenter les problématiques rencontrées sur sa mise en place dans l’infrastructrure on premise du client.

Choix de la solution Kubernetes ☸

A quels besoins Kubernetes permet-il de répondre et à quelles contraintes s’expose-t-on dans un environnement on premise ?

Kubernetes ☸ est avant tout une solution open source initiée par Google pour orchestrer énormément de conteneurs en interne, puis le projet a été ouvert et est vite devenu une référence du catalogue de la CNCF (Cloud Native Computing Fundation). Ce qui fait la force de cet outil, c’est la possibilité de créer un cluster sur des hôtes qui soient on premise (datacenters internes à l’entreprise) ou dans tous les types de cloud-computing.
Kubernetes ☸ est maintenant bien connu et cette notoriété se distingue facilement lorqu’on se penche sur les différents services managés, proposés par les principaux fournisseurs de Cloud du marché actuel (EKS chez AmazonAKS chez AzureGKE chez Google). De plus en plus d’entreprises migrent vers ce type d’infrastructure et les profils sur le marché de l’IT sont de plus en plus recherchés pour assurer le Build & Run autour de cette solution.

La communauté GitOps autour de Kubernetes ☸ est très active, tout comme la fréquence des sorties de versions :

TIP
– une majeure tous les 3 mois
– une mineur tous les mois
WARNING
– une majeure tous les 3 mois
– une mineur tous les mois

En introduction, nous soulignions le fait que les processus en place dans l’équipe d’Ops nétaient plus assez efficients en comparaison du nombre de demandes et qu’il manquait une certaine Â«Â agilité ».

Les mécanismes internes de Kubernetes, s’ils sont bien utilisés et en respectant la démarche DevOps, doivent normalement permettre de palier à plusieurs de ces difficultés…à condition d’avoir une équipe d’Ops qui soit suffisament bien dimensionnée mais surtout bien qualifiée !

C’est pourquoi dans notre contexte client, pour accélérer la montée en compétence de l’équipe, un mentor DevOps avec une expertise avancée de Kubernetes | Helm | IaC (Infrastructure As Code) a été détaché directement dans l’équipe d’Ops.

Le client avait plusieurs besoins initiaux :

  • Conserver les données en interne
  • Maîtrise des coûts liés à l’infrastructure
  • Améliorer la fréquence et la qualité des déploiements des applications métier via pipelines CI/CD
  • Résilience accrue des applications (qu’on peut obtenir grâce à Kubernetes avec le self-healing, liveness/readyness…)
  • Rendre les équipes de développeurs plus autonomes
  • Conteneuriser ce qui peut l’être et repenser les applications en micro-service
  • Faciliter la gestion de l’infrastructure et des OS liés au contexte client
  • Décrire l’infrastructure As Code
  • Mutualiser les bonnes pratiques entre les Dev et les Ops

Avoir la gestion complète d’un cluster Kubernetes ☸ sur un environnement on-premise est bien différent d’un cluster Elastic Kubernetes Service (EKS) chez Amazon AWS par exemple. Avec AWS, le déploiement d’un cluster est simplifié au maximum et vous n’avez pas à vous préoccuper du control-plane contenant les masters-nodes. Tout est géré par les équipes IT d’Amazon AWS.

Sur une infrastructure on-premise, il faut gérer l’installation et le déploiement complet du cluster, d’où l’importance d’avoir des équipes d’Ops qualifiées, avec une bonne connaissance des mécanismes et des composants internes de Kubernetes ☸.

Voici les principaux composants d’un cluster Kubernetes ☸ :

ComposantRôle
EtcdBase clé/valeur qui stocke les données d’état du cluster, elle est généralement déployée en mode cluster sur les nœuds du control-plane.
Kube-schedulerComposant dont le rôle est d’assigner des pods à des nœuds du data plane en se basant sur des données contenues dans l’Etcd.
Kube-controller-managerDaemon qui observe en continu l’état du cluster, c’est un composant au cœur du fameux mécanisme de boucle de régulation.
Kube-api-serverServeur exposant une API REST, valide et applique les changements sur des ressources du cluster.

Pour être capable de déployer le plus facilement et le plus rapidement possible, un cluster Kubernetes ☸ dans l’infrastructure du client, il n’y avait pas d’autre choix que d’automatiser au maximum toute la chaîne.

C’est là qu’entre en jeu un terme bien à la mode, celui d’infrastructure As Code (Iac).

Infrastructure As Code ✍️

Cette phase du projet a été cruciale car il fallait absolument être capable de déployer (ou de redéployer) un cluster Kubernetes ☸ en quelques minutes,
chose inenvisageable à la main car il y a trop de sources d’erreur, manque de reproductibilité…

L’utilisation de plus en plus massive de Kubernetes ☸ sur des infrastructures critiques de grandes entreprises, a permis l’émergence d’outils cloud-native pour justement répondre à ces problématiques de plus en plus fréquentes chez les équipes d’Ops.

La société HashiCorp en a fait son businness en proposant notamment deux outils libres et open source, capables de déployer de l’infrastructure à partir de fichiers descriptifs :

Un troisième outil libre et open source a été utilisé pour faire de la gestion de configuration soit pour provisionner des fichiers sur les machines, soit pour modifier la configuration des OS, démarrer des services avec systemD ou encore installer des paquets YUM :

A quoi servent-ils respectivement ?

PackerTerraformAnsible
Il est utilisé pour la création d’images immuables multi-plateformes (templates de VMs).Il permet aux Ops de prédire de manière certaine, la création, le changement et l’évolution de l’infrastructure via du code versionné.Les playbooks et les rôles Ansible servent à provisionner les fichiers de configuration ou les fichiers système.

Ce qui fait la force de ces outils, c’est l’utilisation des API REST pour dialoguer avec les infrastructures et sont donc compatibles avec pratiquement tous les providers du marché (VMware, AWS, OpenShift…).

Le gros avantage de ces outils orientés GitOps, c’est justement l’utilisation de Git et le fait de bénéficier d’un système de branching et de versionning. Tout le code qui décrit notre infrastructure Kubernetes est alors stocké dans le GitLab hébergé en interne.

Dans notre contexte client, l’infrastructure en place repose sur un cluster constitué de 10 hôtes ESXi VMware vSphere 6.7, la rendant compatible avec ces trois outils.

Architecture du cluster client 🧱

Le cluster du client a été déployé avec kubeadm et en respectant les bonnes pratiques de la documentation Kubernetes :

cluster_k8s

Tout le travail d’IaC effectué en amont avec la stack | Packer | Terrraform | Ansible | nous a permis d’avoir un cluster qui se déploie et s’initialise en quelques minutes seulement.
Bien entendu, pour que tout cela fonctionne, nous avons été obligés d’intégrer plusieurs éléments importants dans nos templates de VMs Packer :

  • l’installation via YUM des binaires dockerkubelet et kubeadm dans /usr/local/bin
  • les certificats SSL ainsi que les autorités de certification du client (Root CA) dans /etc/pki/tls/certs/
  • les scripts Bash et les clés SSH
  • les différents fichiers de configuration (kube-config, calico…)

Gestion des registry Docker 🐳 / Helm ⚙️

Le contexte client impliquait l’abscence d’accès direct à Internet sur l’ensemble de l’infrastrcuture. Il a donc fallu récupérer toutes les images Docker nécessaires à la création du cluster Kubernetes et aux Charts Helm dans un espace GitLab prévu à cet effet.
Cette fonctionnalité intégrée à GitLab est connue sous le nom de shared-registry. Elle s’active au sein d’un projet GitLab et est ensuite accessible sur le port 5050.

Utilisation de la shared-registry pour nos images Docker 🐳 :

registry_dockers

Docker login :

class="wp-block-syntaxhighlighter-code">docker login gitlab.client.local:5050

Docker build / push :

class="wp-block-syntaxhighlighter-code">docker build -t gitlab.client.local:5050/pole_ie/docker/shared-registry .
docker push gitlab.client.local:5050/pole_ie/docker/shared-registry

Le même procédé a été utilisé pour stocker nos Charts Helm ⚙️ :

registry_dockers

Helm registry login :

class="wp-block-syntaxhighlighter-code">helm registry login gitlab.client.local:5050

Helm save / push :

class="wp-block-syntaxhighlighter-code">cd charts/<chart name>
helm chart save . gitlab.client.local:5050/pole_ie/helm-charts/<chart name>:<chart version>
helm chart push gitlab.client.local:5050/pole_ie/helm-charts/<chart name>:<chart version>

Connexion aux clusters

Une fois que le cluster s’initialise, on récupère les tokens d’admin sur un des noeuds master du control-plane, puis on le place sur notre poste de travail dans notre home_directory .kube/.

Warning
Attention, cette manière de faire n’est pas une bonne pratique.
Il faut mettre en place une solution d’authentification pour les utilisateurs.

L’idéal est d’avoir une solution d’authentification sur l’annuaire de l’entreprise (OIDC, SAML…etc).

Dans notre contexte, chaque fichier correspond à un cluster Kubernetes (test, dev, prod) .

Cette configuration permet de Â«Â switcher » rapidement d’un cluster à un autre par l’intermédiaire du binaire kubectl :

class="wp-block-syntaxhighlighter-code">❯ grep -ri "KUBECONFIG" ~/.zshrc
export KUBECONFIG=$KUBECONFIG:$HOME/.kube/production:$HOME/.kube/development:$HOME/.kube/sandbox

Avec l’aide d’alias, on peut alors lister ou utiliser les différents contextes disponibles (donc changer de cluster)

class="wp-block-syntaxhighlighter-code">❯ kcgc
CURRENT   NAME                          CLUSTER       AUTHINFO           NAMESPACE
*         dev                           development   kubernetes-admin   argocd
          kubernetes-admin@kubernetes   kubernetes    kubernetes-admin   monitoring
          prod                          production    production-admin   argocd
❯ kcuc prod
Switched to contexte "prod".
❯ kcgc
CURRENT   NAME                          CLUSTER       AUTHINFO           NAMESPACE
          dev                           development   kubernetes-admin   argocd
          kubernetes-admin@kubernetes   kubernetes    kubernetes-admin   monitoring
*         prod                          production    production-admin   argocd

Déployer avec Argo CD ! 🐙

Au début du projet, les applications étaient déployées dans Kubernetes via des Charts Helm depuis nos postes de travail respectifs, à l’aide du binaire Helm dans sa version 3.

La tâche était assez laborieuse car il fallait d’abord configurer nos postes de travail, se souvenir du nom de la Chart, dans quel repo Git elle se trouve, se synchroniser sur la bonne branche…
De plus, nous n’avions pas directement la possibilité de connaître le dernier état dans lequel s’était déployée la Chart Helm.

Exemple de déploiement d’une Chart Helm à la main :

Configurer la variable HELM_EXPERIMENTAL_OCI

class="wp-block-syntaxhighlighter-code">export HELM_EXPERIMENTAL_OCI=1

Se connecter en CLI à la registry GitLab qui contient nos Charts Helm :

class="wp-block-syntaxhighlighter-code">helm registry login gitlab.client.local:5050

Ajout du repo Helm :

class="wp-block-syntaxhighlighter-code">helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

Premier déploiement :

class="wp-block-syntaxhighlighter-code"># Met à jour les dépendances
helm dep up
# Déploie la chart
helm install --namespace <namespace> <chart-name> . -f values.yaml -f values.<env>.yaml -f secrets.<env>.yaml

Mise à jour de la Chart :

class="wp-block-syntaxhighlighter-code">helm upgrade -i --namespace ingress-controller ingress-nginx . -f values.yaml

Voir les différences entre deux mises à jour de Charts :

class="wp-block-syntaxhighlighter-code">helm diff upgrade --namespace ingress-controller ingress-nginx . -f values.yaml

Heureusement, un nouvel outil est apparu dans le catalogue de la CNCF et vient largement simplifier les déploiements dans Kubernetes.

Il se fait connaitre sous le nom de Argo CD !

Il simplifie tout ce qui a été mentionné ci-dessus et permet de se concentrer uniquement sur les processus de déploiements.
Argo CD ne remplace en aucun cas des outils comme Jenkins, mais propose via une interface web intuitive, une vue en temps réel sur l’état de santé des applications déployées.
Il détecte ce qui a changé par rapport aux derniers commits et exécute un flux de synchronisation qui déploie automatiquement les changements sur le(s) cluster(s) Kubernetes.

argocd

Il a le gros avantage d’avoir été pensé « As Code« , c’est à dire qu’on peut le configurer directement via une Chart Helm de façon à ce qu’il soit Up & Ready dès qu’il est déployé dans le cluster.
De plus, on peut très bien disposer d’une instance Argo CD Master qui déploie ensuite une autre instance Argo CD !

What ??? Mais pourquoi faire ?! 🤔

Cette méthode permet de couvrir plusieurs cas d’usage :

  • un nouveau cluster est déployé sur l’infra, on peut alors Â«Â piloter » les déploiements depuis l’instance Argo CD Master
  • bénéficier d’instances auto-gérées

Sa principale force réside dans le fait de pouvoir se sourcer directement sur des repos Git pour détecter immédiatement les changements d’états des projets déployés.

argocd

Le changement d’état est visuel et la validation humaine se fait d’un simple clic sur le bouton « Sync« .
Avant de valider, il est bien sûr possible d’afficher les changements sous la forme d’un git-diff, qui revient à faire un helm diff upgrade lorsqu’on déploie à la main.

Pipelines CI/CD 🔂

La méthodologie DevOps implique que des règles communes entre Dev et Ops soient définies en avance de phase de manière à ce que tout le monde soit raccord sur la façon de travailler.

Dans ce contexte, nous avons défini des règles de déploiement qui s’articulent de la manière suivante :

Warning
Il est indispensable de créer un fichier de values pour chaque environnement à déployer, à la racine de la chart Helm
class="wp-block-syntaxhighlighter-code">values.yaml (contient les valeurs communes à tous les environnements)
values.dev.yaml
values.integration.yaml
values.recette.yaml
values.preproduction.yaml
values.production.yaml

Toute autre fichier de values qui ne respecte pas cette nomenclature fera échouer le job ou ne sera pas pris en compte.

Chaque équipe de développement est autorisée à déployer des namespaces avec la convention de nommage suivante :

En developpement :

class="wp-block-syntaxhighlighter-code">project-dev
project-integration
project-recette
project-preproduction

En production :

class="wp-block-syntaxhighlighter-code">project

Les jobs de déploiement sont déclarés dans un noeud Jenkins Ã  l’intérieur d’une multi-branch pipeline en Groovy.

Le serveur Jenkins principal fait appel à un agent Jenkins présent dans un namespace Kubernetes. Cet agent se connecte à un repo GitLab interne sur la bonne branche, puis exécute les différents steps Helm comme si on déployait une Chart manuellement :

class="wp-block-syntaxhighlighter-code">helm registry login
helm dependency update
helm diff upgrade
helm upgrade -i
class="wp-block-syntaxhighlighter-code">String environment = ENV
String namespace = "${PROJECT}-${environment == 'development' ? 'dev' : environment}"
boolean humanValidation = Boolean.parseBoolean(HUMAN_VALIDATION)

podTemplate {
  node('jenkins-agent-pole-ie') {
        stage('Checkout repository'){
            checkout([
                $class: 'GitSCM',
                doGenerateSubmoduleConfigurations: false,
                extensions: [],
                branches: [
                    [name: BRANCH],    
                ],
                userRemoteConfigs: [[credentialsId: 'jenkins-gitlab-registry-dev', url: REPOSITORY_URL]]
            ])
        }
        container('helm') {
            stage("Helm dependency update") {
                withCredentials([
                    usernamePassword(credentialsId: 'jenkins-gitlab-registry-dev',
                    usernameVariable: 'username',
                    passwordVariable: 'password')
                ]) {
                    sh "helm registry login gitlab.client.local:5050 -u '${username}' -p '${password}'"
                }
                sh "cd ${CHART_PATH}; helm dependency update"
            }
            stage("Generate Helm Diff"){
                sh "cd ${CHART_PATH}; helm diff upgrade --allow-unreleased --namespace ${namespace} ${PROJECT} . -f values.yaml -f values.${environment}.yaml"
            }

            stage("Validate Deployment - Waiting for Human validation") {
                if (humanValidation) {
                    input(message: "Check Helm diff. Do you want to proceed to the deployment ?")
                }
            }
      
            stage("Deploy the application"){
                sh "cd ${CHART_PATH}; helm upgrade -i --namespace ${namespace} ${PROJECT} . -f values.yaml -f values.${environment}.yaml"
            }
        }
    }
}

Et comment on monitore ? 🚥

Maintenant qu’on a vu comment était déployées les applications au sein du cluster, intéressons nous à l’observabilité de celles-ci.
C’est une brique importante, surtout lorsque les premières applications arrivent en production.
Le monitoring dans Kubernetes peut être déployé rapidement, mais il faudra y consacrer beaucoup de temps pour obtenir quelque chose de robuste et fiable.

Tout le monde connait des outils de monitoring basés sur le protocole SNMP, tels que [Centreon] ou [Nagios] et il est d’ailleurs possible de monitorer un cluster Kubernetes via des plugins payants.

Il est cependant préférable d’utiliser des outils prévus à cet effet. Kubernetes expose un nombre incalculable de métriques à travers son api-server.
Ces métriques peuvent donc facilement être envoyées dans un exporter Prometheus et être affichées à travers des graphiques proposés par Grafana.

Un projet GitHub nommé kube-prometheus-stack permet d’avoir toute la stack « Monitoring » dans un cluster Kubernetes.
Elle se compose de plusieurs éléments :

Prometheus donne la possibilité de personnaliser des règles à partir des métriques qu’il récupère via l’API Kubernetes.
Par exemple, si on veut vérifier l’état des Ingress dans un namespace précis, c’est possible. Il faut connaître le nom de la métrique et jouer ensuite avec les opérateurs :

argocd

Grafana permet de gérer l’affichage des données qui lui sont transmises via une datasource (Prometheus, InfluxDB…).
Dans la Chart kube-prometheus-stack, tous les dashboards utiles à Kubernetes sont déjà présents.
Il suffit d’attendre qu’un certain nombre de données soit collecté pour que les dashboards soient alimentés.

argocd

Quant à Alertmanager, il intercepte les dépassements de seuils pour les transformer en alertes puis envoie les notifications via des receivers (mails, webhooks Slack, Teams…etc).

Alertmanager intègre aussi des règles d’alerting déjà prévues pour Kubernetes mais il faut avoir en tête qu’un alerting efficace est un alerting qui couvre les principaux types de pannes sur une application en production.

  • restart de pods
  • pods en CrashLoopBackOff
  • healthcheck sur les URLs exposées par les ingress

Conclusion

Le démarrage du projet a été compliqué car l’équipe des Ops partait de zéro et n’était pas formée aux méthodes de travail induites par tous ces nouveaux outils.

  • La courbe d’apprentissage liée à Kubernetes | Helm est assez longue et complexe.
  • Un renforcement de l’équipe et une montée en compétences a été nécessaire.
  • La stratégie de l’équipe a été repensée pour mieux répartir la charge de travail entre Build & Run et la transformation DevOps

En dépit de ces difficultés et grâce à l’implication de chacun, plusieurs applications métier ont déjà été migrées dans les clusters Kubernetes hors-production du client.

Une des étapes importante du projet à été la mise en production de la première application du client au mois de Juillet 2021.
Depuis, les déploiements en Production se sont poursuivis et l’équipe dispose d’une roadmap conséquente pour venir améliorer la robustesse des clusters.