May 1, 2023

Kubernetes multi cluster et multi région en GitOps avec ArgoCD

Je présenterai dans cet article comment gérer les applications et ressources déployées sur plusieurs clusters Kubernetes, déployés dans plusieurs datacenters, avec ArgoCD.

ArgoCD

ArgoCD est un outil de déploiement continu pour Kubernetes, permettant de configurer en GitOps vos clusters. Cet outil est capable de gérer tout type de ressources Kubernetes (sans aucune limitations, CRD incluses). Il supporte également très bien l’écosystème Kubernetes standard pour gérer les manifests de déploiements (Helm, Kustomize…​ mais étendre l’outil avec ses propres outils est également possible), et dispose égalemement d’une très bonne interface utilisateur.

ArgoCD est l’outil que je recommande le plus aujourd’hui pour les utilisateurs de Kubernetes.

Nous commencerons dans cet article par expliquer rapidement le fonctionnement d’ArgoCD, puis nous déploierons 3 clusters Kubernetes sur Exoscale. SKS, l’offre Kubernetes as a service d’Exoscale, est actuellement la meilleure offre "managed Kubernetes" européenne, meilleure en tout point de vue par rapport à celles de Scaleway et OVH. C’est aussi celle la plus facile et rapide à déployer et à administrer.

Mais il serait tout à fait possible de réaliser l’architecture de ce tutoriel sur un autre cloud provider, on premise, ou même en mode "multi cloud" pour une meilleure tolérance aux pannes ! C’est une des forces de Kubernetes: c’est disponible partout.

Un cluster sera utilisé pour déployer ArgoCD. Les deux autres seront eux gérés par l’instance ArgoCD installée sur le premier cluster. Chaque cluster sera déployé dans un datacenter séparé, dans des pays différents. Je trouve cette approche intéressante pour plusieurs raisons:

  • ArgoCD pilote l’ensemble des clusters et permet donc d’avoir une gestion de configuration de ces clusters centralisée, notamment grâce aux ApplicationSet que je présenterai plus loin. Toutes les ressources déployées dans tous les clusters seront par exemple visibles dans l’interface d’ArgoCD.

  • Le cluster où ArgoCD tourne peut être très simple (avec seulement ArgoCD et pour un cas réel de production quelques outils de monitoring). Cela donne un cluster facile à administrer, facile à reconstruire, et peu coûteux. Cela peut avoir l’air d’un Single Point of Failure mais perdre ArgoCD n’est pas très grave: toutes les applications continuent de tourner, seulement les mises à jour seront impossibles.
    Si le cluster est complètement reconstructible en quelques minutes (ce qui est largement faisable sur le cloud), ArgoCD n’est plus vraiment un SPOF. Vous pouvez même si vous le souhaitez avoir un cluster Kubernetes passif avec ArgoCD préinstallé mais désactivé (0 replica), pour pouvoir à tout moment le redémarrer en cas de perte du cluster principal. Votre source de vérité sera de toute façon Git, le cluster ArgoCD sera complètement stateless.

Architecture de la solution, avec une instance ArgoCD pilotant deux clusters Kubernetes et pouvant aussi gérer son propre cluster

On voit sur cette image que seul le cluster Kubernetes à Genève aura ArgoCD d’installé. Il pilotera les deux autres clusters installés dans deux autres datacenters (Zurich et Vienne), et pourra également être utilisé pour se configure soit même.

Avec cette architecture, des load balancers devant vos clusters Kubernetes et du DNS, vous pouvez facilement avoir des applications fortement tolérantes aux pannes, en actif/passif entre régions et clouds ou même en actif/actif si votre architecture le permet.

Je vais maintenant présenter très brièvement les différentes CRD d’ArgoCD. Pour plus d’informations, lisez la documentation de l’outil.

Project

La première ressource importante d’ArgoCD est AppProject. Un AppProject est une abstraction permettant de regrouper des Applications (présentées juste après) ensemble.

Les AppProject permettent d’exprimer des choses comme "dans le namespace monitoring de Kubernetes, je n’ai le droit que de déployer que les applications venant du dépot gît dont l’URL est github.com/mon-org/monitoring.git, et j’autorise seulement ces types de ressources Kubernetes (deployment, service, ingress…​) à être déployés".

Un exemple:

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: example-project
  namespace: argocd
spec:
  sourceRepos:
  - '*'
  destinations:
  - namespace: '*'
    server: '*'
  clusterResourceWhitelist:
  - group: '*'
    kind: '*'

Ce projet appelé example-project autorise tout. SourceRepos, la liste des dépôts Git autorisés à être déployés pour ce projet, contient *. On pourrait la remplacer comme dit précédemment par les addresses des dépôts autorisés à être déployés. destinations permet de configurer dans quelles namespaces et servers (clusters kubernetes) les applications peuvent être déployées. Enfin, clusterResourceWhitelist permet de spécifier quelles types de ressources Kubernetes sont autorisées.

Application

Une Application représente un groupe de ressources Kubernetes gérées par ArgoCD:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: guestbook
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/argoproj/argocd-example-apps.git
    targetRevision: HEAD
    path: guestbook
  destination:
    server: https://kubernetes.default.svc
    namespace: guestbook

Cette application s’appelle guestbook. Le paramètre source dans la spec permet de référencer un dépôt Git contenant les fichiers Kubernetes à déployer. Le paramètre destination indique sur quel cluster les déployer, et dans quel namespace. Ici c’est le cluster où ArgoCD est déployé qui est ciblé.

Ici, ArgoCD récupérera les fichiers présents dans https://github.com/argoproj/argocd-example-apps, dans le dossier guestbook (le paramètre path de la source), et les déploiera sur le cluster Kubernetes local (où ArgoCD tourne, accessible sur https://kubernetes.default.svc) et dans le namespace guestbook

Dans cet exemples de simples fichiers sont déployés. La ressource Application supporte aussi des outils comme Helm, ou Kustomize.

ApplicationSet

On voit que la partie destination permet déjà de déployer sur plusieurs clusters Kubernetes depuis une même instance d’ArgoCD. Cela est pratique mais a un désavantage: si vous souhaitez déployer la même ressource sur 3 clusters, vous allez devoir définir 3 applications, chacunes avec une destination différente. C’est là que les ApplicationSet interviennent.

Voici un exemple d’ApplicationSet:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: guestbook
  namespace: argocd
spec:
  generators:
  - clusters: {}
  template:
    metadata:
      name: '{{name}}-guestbook'
    spec:
      project: "default"
      source:
        repoURL: https://github.com/argoproj/argocd-example-apps/
        targetRevision: HEAD
        path: guestbook
      destination:
        server: '{{server}}'
        namespace: guestbook

On voit qu’une ApplicationSet est assez similaire à une Application: la partie template contient d’ailleurs la définition d’une Application. L’ApplicationSet va en fait être utilisée pour générer des ressources de type Application en fonction de générateurs (generators dans la configuration).

Dans cet exemple, on utilise le generateur clusters qui génère par défaut une Application ArgoCD pour chaque cluster configuré dans ArgoCD (la partie suivante montrera comment configurer des clusters). La ressource ApplicationSet permet également de templatiser en fonction du générateur utilisé la configuration des Applications générées. Par exemple ici:

  • La variable {{name}} contiendra le nom du cluster cible de l’application générée.

  • La variable {{server}} contiendra l’URL du cluster cible.

Si j’ai par exemple deux clusters configurés dans ArgoCD, l’un appelé cluster1 ayant pour URL cluster1.com, et l’autre appelé cluster2 ayant pour URL cluster2.com, cette ApplicationSet me génèrera 2 applications dont les noms seront cluster1-guestbook et cluster2-guestbook et dont les destinations seront les URL correspondantes.

Il existe de très nombreux générateurs, permettant d’automatiser la génération d’applications ArgoCD en se basant sur de nombreux critères (clusters, Pull Requests ouvertes sur un projet Git, en fonction de fichiers, en fonction d’une simple liste de variables…​). Les ApplicationSet sont un très bon outil pour éviter de passer son temps à réécrire plusieurs fois des Applications quasiment identiques.

Les ApplicationSet autorisent aussi depuis peu des rollout progressifs, pour mettre à jour des clusters un par un dans un ordre pré-déterminé.

Cas pratique

Création des clusters

Comme dit précédemment je vais utiliser Exoscale pour ce tutoriel. Vous pouvez reproduire l’exercice sur un autre cloud également avec quelques variations mais je vous recommande vraiment d’essayer l’offre d’Exoscale qui est de bonne qualité, et qui permet d’avoir dans son mode starter un contrôle plane Kubernetes gratuit.

La documentation d’Exoscale vous explique comment créer un cluster grâce à sa CLI. Vous aurez besoin aussi de kubectl pour interagir avec Kubernetes.

Le script suivant vous permettra de créer les 3 clusters comme montré dans la première image (dans 3 régions différentes). A noter qu’en production il vaudrait mieux créer un security group par cluster pour plus de sécurité, mais dans ce contexte de POC ce n’est pas trop grave de partager le même:

#!/bin/bash

set -e

## Création des règles réseaux

exo compute security-group create sks-argo-tuto

exo compute security-group rule add sks-argo-tuto \
    --description "NodePort services" \
    --protocol tcp \
    --network 0.0.0.0/0 \
    --port 30000-32767

exo compute security-group rule add sks-argo-tuto \
    --description "SKS kubelet" \
    --protocol tcp \
    --port 10250 \
    --security-group sks-argo-tuto

exo compute security-group rule add sks-argo-tuto \
    --description "Calico traffic" \
    --protocol udp \
    --port 4789 \
    --security-group sks-argo-tuto

## Création des 3 clusters

### Premier cluster dans la région ch-gva-2 qui hébergera ArgoCD

exo compute sks create argocd \
    --zone ch-gva-2 \
    --service-level starter \
    --nodepool-name argocd \
    --nodepool-instance-prefix argocd \
    --nodepool-size 3 \
    --nodepool-security-group sks-argo-tuto

### Second cluster dans la région ch-zrh-1 (anciennement appelée ch-dk-2)

exo compute sks create zrh1 \
    --zone ch-dk-2 \
    --service-level starter \
    --nodepool-name zrh1 \
    --nodepool-instance-prefix zrh1 \
    --nodepool-size 3 \
    --nodepool-security-group sks-argo-tuto

### Troisième cluster dans la région at-vie-1

exo compute sks create vie1 \
    --zone at-vie-1 \
    --service-level starter \
    --nodepool-name vie1 \
    --nodepool-instance-prefix vie1 \
    --nodepool-size 3 \
    --nodepool-security-group sks-argo-tuto

Récupération des kubeconfig

Nous allons maintenant récupérer des fichiers Kubeconfig root pour chaque cluster dans le but de pouvoir intéragir avec eux via kubectl:

exo compute sks kubeconfig argocd kube-admin --zone ch-gva-2 --group system:masters > argocd.kubeconfig
exo compute sks kubeconfig zrh1 kube-admin --zone ch-dk-2 --group system:masters > zrh1.kubeconfig
exo compute sks kubeconfig vie1 kube-admin --zone at-vie-1 --group system:masters > vie1.kubeconfig

Vous devriez maintenant pouvoir par exemple lister les noeuds de vos clusters, avec kubectl --kubeconfig <kubeconfig-file> get nodes, par exemple kubectl --kubeconfig argocd.kubeconfig get nodes.

Installation d’ArgoCD

Nous allons maintenant installer ArgoCD sur le cluster Kubernetes de la zone ch-gva-2:

kubectl --kubeconfig argocd.kubeconfig create namespace argocd
kubectl --kubeconfig argocd.kubeconfig apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

Pour ce tutoriel nous désactivons pour plus de facilité l’authentification d’ArgoCD. ArgoCD supporte une gestion fine des utilisateurs et permissions (et supporte également des protocoles comme OIDC), donc n’hésitez pas à jeter un oeil à la documentation officielle.

Exécutez tout d’abord kubectl --kubeconfig argocd.kubeconfig -n argocd edit configmap argocd-cm pour désactiver l’authentification. Rajoutez le block suivant dans le YAML affiché, sauvegardez et fermez votre éditeur:

data:
  users.anonymous.enabled: "true"

Faisons la même chose pour une seconde configmap via kubectl --kubeconfig argocd.kubeconfig -n argocd edit configmap argocd-rbac-cm, pour rendre admin l’utilisateur par défaut.

data:
  policy.default: role:admin

Rappel: ne faites pas ça sur un "vrai" cluster, ou même un cluster de test où ArgoCD est exposé sur internet !

Je ne vais pas configurer d’ingress controller dans ce tutoriel l’accès à ArgoCD se fera via kubectl port-forward.

Lancez kubectl --kubeconfig argocd.kubeconfig port-forward svc/argocd-server -n argocd 8080:443 et ouvrez votre navigateur sur localhost:8080: une fois le certificat auto signé accepté vous devriez pouvoir accéder à l’interface d’ArgoCD.

Configuration des clusters

ArgoCD permet par défaut de déployer des applications sur le cluster où il est installé mais pas ailleurs. Nous allons donc reconfigurer les clusters dans ArgoCD.

Cela se fait en créant un secret Kubernetes par cluster. Nous utiliserons ici directement les certificats contenus dans les kubeconfig générés précédemment pour s’authentifier aux clusters distants (pour le cluster local, l’authentification se fera par défaut via le service account d’ArgoCD).

Voici la configuration à appliquer, en remplaçant les valeurs server et les certificats attendus par chaque cluster par ce que vous avez dans vos kubeconfig:

---
apiVersion: v1
kind: Secret
metadata:
  name: cluster-gva2
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
type: Opaque
stringData:
  name: gva2
  server: https://kubernetes.default.svc
---
apiVersion: v1
kind: Secret
metadata:
  name: cluster-zrh1
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
type: Opaque
stringData:
  name: zrh1
  server: "<contenu de server dans zrh1.kubeconfig>"
  config: |
    {
      "tlsClientConfig": {
        "caData": "<contenu de certificate-authority-data dans dans zrh1.kubeconfig>",
        "certData": "<contenu de client-certificate-data dans dans zrh1.kubeconfig>",
        "keyData": "<contenu de client-key-data dans dans zrh1.kubeconfig>"
      }
    }
---
apiVersion: v1
kind: Secret
metadata:
  name: cluster-vie1
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
type: Opaque
stringData:
  name: vie1
  server: "<contenu de server dans vie1.kubeconfig>"
  config: |
    {
      "tlsClientConfig": {
        "caData": "<contenu de certificate-authority-data dans dans vie1.kubeconfig>",
        "certData": "<contenu de client-certificate-data dans dans vie1.kubeconfig>",
        "keyData": "<contenu de client-key-data dans dans vie1.kubeconfig>"
      }
    }
Note

Dans le cas d’Exoscale, la bonne pratique serait de créer un ClusterRole dédié pour ArgoCD pour éviter l’utilisations de system:masters pour l’authentification, et d’utiliser par exemple des certificats avec un TTL maîtrisé.

La commande exo compute sks kubeconfig permet en effet de spécifier un TTL.

Voir mon article sur le TLS et l’authentification dans Kubernetes pour plus de détails.

Une fois les valeurs remplacées, et le fichier appliqué via kubectl --kubeconfig argocd.kubeconfig apply -f <fichier>, les clusters devraient être visibles dans l’interface d’ArgoCD à l’adresse https://localhost:8080/settings/clusters:

Les clusters sont visibles dans l’interface d’ArgoCD

Déploiement d’un ApplicationSet

Déployez maintenant l’ApplicationSet suivant pour tester le setup, toujours via kubectl --kubeconfig argocd.kubeconfig apply -f <fichier>:

---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: guestbook
  namespace: argocd
spec:
  generators:
  - clusters: {}
  template:
    metadata:
      name: '{{name}}-guestbook'
    spec:
      project: "default"
      source:
        repoURL: https://github.com/argoproj/argocd-example-apps/
        targetRevision: HEAD
        path: guestbook
      destination:
        server: '{{server}}'
        namespace: guestbook
      syncPolicy:
        syncOptions:
        - CreateNamespace=true
        automated:
          prune: true
          selfHeal: true

ArgoCD devrait vous générer automatiquement 3 applications, chacunes sur un cluster différent, le tout en fonction de ce qui est stocké dans le dossier guestbook sur le répertoire github https://github.com/argoproj/argocd-example-apps/. Elles seront également visibles dans l’interface.

Les applications sont créées pour chaque cluster et visibles dans l’interface d’ArgoCD

On remarque que le nom de chaque application est bien <cluster>-guestbook, par exemple vie1-guestbook. Rappelez vous que les générateurs des ApplicationSet vous permettent de filtrer si besoin sur quelles clusters les applications doivent être déployées, via des labels par exemple.

Dans le setup actuel, ArgoCD créera automatiquement l’application pour chaque nouveau cluster ajouté, ou la supprimera si un cluster est supprimé. Chaque changement dans Git sera répercuté sur l’ensemble des clusters automatiquement également (grâce à l’option syncPolicy.automated, vous pouvez également choisir de "sync" manuellement les applications).

Vous pouvez également vérifier sur vos clusters avec kubectl que l’application a bien été déployée, par exemple avec kubectl --kubeconfig vie1.kubeconfig get po -n guestbook.

Un dernier point avant la conclusion: rien ne vous empêche d’utiliser les Application ET les ApplicationSet ArgoCD en parallèle selon les besoins.

Conclusion

ArgoCD est un outil puissant, qui peut grandement simplifier le multi region ou le multi cloud Kubernetes.
On voit encore une fois ici l’avantage de Kubernetes: l’outillage est là pour répondre à des problématiques assez complexes, et ça fonctionne très bien.

Tags: devops cloud

Add a comment








If you have a bug/issue with the commenting system, please send me an email (my email is in the "About" section).

Top of page