Claudie : Un manager de clusters kubernetes multi-cloud/hybrides

thumbernail kubernetes

Claudie

Mots clés :

  • Kubernetes
  • AWS (Amazon Web Services)
  • DevOps
  • Azure
  • GCP (Google Cloud Platform)
  • Kubernetes-cluster
  • PaaS (Platform as a Service)
  • Cloudflare
  • OCI (Oracle Cloud Infrastructure)
  • cloud-native
  • Hetzner
  • hybrid-cloud
  • multi-cloud
  • managed-kubernetes
  • inter-cloud

1: Analyse de la solution

Cas d'utilisation et fonctionnalités de Claudie:

Cas d'utilisations typiques:

  • Réaliser des économies de coûts.
  • Respect de la localisation des données et de la conformité (ex. RGPD)
  • Offrir du Kubernetes managé pour les fournisseurs qui n'en proposent pas
  • Faire du cloud bursting (débordement vers le cloud, ex. extension d'un cluster vers un fournisseur moins cher pendant certaines phases d'un projet)
  • Interconnecter des services

Fonctionnalités:

  • Gérer des clusters Kubernetes multi-cloud et hybrides
  • Gestion via infrastructure as code (IaC)
  • Montée en charge/descente en charge rapide de l'infrastructure
  • Load balancing intégré
  • Volumes de stockage persistants

Comment fonctionne claudie

Claudie est une plateforme conçue pour gérer des infrastructures Cloud à travers plusieurs fournisseurs de Cloud. Elle utilise une architecture basée sur des microservices, chacun ayant un rôle spécifique dans le processus de déploiement et de gestion de l'infrastructure.

Les composants principaux de Claudie sont :

  • Context-box : C'est l'unité de contrôle qui s'occupe de gérer les configurations en attente. Il détecte les changements de configuration et interagit avec l'InputManifest CRD.

  • Scheduler : Ce microservice travaille pour amener l'infrastructure à l'état désiré en fonction du manifeste reçu.

  • Builder : Il aligne l'état actuel de l'infrastructure avec l'état désiré en utilisant divers outils comme Terraformer et Ansibler.

  • Terraformer, Ansibler, Kube-eleven, Kuber : Ce sont des outils qui gèrent différentes parties de l'infrastructure, comme la création des clusters Kubernetes ou la configuration des nœuds.

  • Claudie-operator : C'est un contrôleur pour la ressource personnalisée InputManifest dans Kubernetes. Il surveille et maintient l'état du manifeste soumis par l'utilisateur.

Les données sont stockées dans des bases de données : MongoDB, Minio et DynamoDB, et les outils Terraform, Ansible, KubeOne, Longhorn, Nginx et Calico sont utilisés pour gérer divers aspects de l'infrastructure.

Le flux général de Claudie consiste à recevoir une configuration de l'utilisateur sous forme de InputManifest CRD, que Claudie-operator détecte et traite.

Lorsque l'utilisateur supprime ce manifeste, Claudie-operator initie un processus de suppression de l'infrastructure correspondante.

Les services comme Scheduler et Builder n'offrent pas d'API car ils fonctionnent en tant que clients gRPC.

Ils communiquent avec Context-box pour réaliser leurs tâches.

Kuber, par exemple, utilise kubectl pour manipuler les ressources du cluster et gérer des aspects spécifiques comme la configuration de l'autoscaling ou la suppression de nœuds.

claudie implémente sa propre solution de stockage.

Claudie permet de gérer les charges de travail avec état dans des environnements multi-cloud en créant un "cluster de stockage" à travers plusieurs fournisseurs de cloud. Chaque fournisseur a sa propre "zone" dans le cluster de stockage, et chaque zone stocke ses données de volumes persistants séparément.

La solution utilise Longhorn, un système de stockage distribué pour Kubernetes, qui est préinstallé dans les clusters créés par Claudie. Longhorn utilise les nœuds de travail pour stocker des données et fournit une classe de stockage par défaut nommée longhorn. Cette classe de stockage crée des volumes répliqués sur des nœuds aléatoires du cluster.

En plus de la classe de stockage par défaut, Claudie peut créer des classes de stockage personnalisées qui contraignent les volumes persistants à être créés sur des nœuds spécifiques en fonction de l'instance de fournisseur utilisée.

Cela signifie que vous pouvez avoir des nœuds pour le stockage provisionnés par un fournisseur et d'autres nœuds pour les tâches de calcul provisionnés par un autre fournisseur.

claudie implémente sa propre solution de loadbalancing

claudie permet de créer des load balancers pour les clusters Kubernetes afin d'assurer une haute disponibilité.

Voici les points clés :

  • Rôles : Dans Claudie, les rôles définissent la configuration des load balancers pour différents usages. Un cluster de load balancers peut avoir plusieurs rôles assignés.

  • Cluster Kubernetes ciblé : Chaque répartiteur de charge est assigné à un cluster Kubernetes spécifique, identifié par le champ targetedK8s. Un seul répartiteur de charge peut avoir le rôle du serveur API (port 6443) pour un cluster donné.

  • DNS : Claudie gère automatiquement les enregistrements DNS A pour les load balancers, en utilisant l'adresse IP publique des machines. En cas de changement de configuration, Claudie met à jour ces enregistrements DNS.

  • Nodepools : Les load balancers sont construits à partir de groupes de nœuds définis par l'utilisateur, permettant une échelle et des modifications faciles.

  • Ingress Controller Cluster : Il est nécessaire de déployer son propre ingress controller pour utiliser le load balancer, qui doit être configuré pour utiliser le node port définis dans la configuration des rôles.

Support de l'autoscaling

Claudie est une plateforme qui prend en charge l'autoscaling (mise à l'échelle automatique) des clusters qu'elle crée en installant le Cluster Autoscaler avec une implémentation personnalisée appelée autoscaler-adapter.

Pour que l'autoscaling soit actif, il suffit qu'un cluster ait au moins un groupe de nœuds (node pool) configuré avec le champ autoscaler.

Vous pouvez modifier la spécification du groupe de nœuds pour activer ou désactiver l'autoscaling à tout moment, et Claudie s'occupera de la configuration nécessaire automatiquement.

L'augmentation de la taille d'un groupe de nœuds (scale up) est déclenchée lorsque :

  • Il y a des pods dans le cluster qui ne peuvent pas être planifiés (unschedulable) et qui pourraient l'être si un groupe de nœuds avec l'autoscaling activé augmentait sa taille.
  • Ces groupes de nœuds ne sont pas encore à leur taille maximale.

Notez que si les demandes de ressources des pods sont plus importantes que ce que les nouveaux nœuds peuvent offrir, l'augmentation ne sera pas déclenchée.

Le cluster est vérifié toutes les 10 secondes pour s'assurer d'une réponse rapide aux besoins.

La réduction de la taille d'un groupe de nœuds (scale down) est déclenchée lorsque :

  • La somme des demandes de CPU et de mémoire de tous les pods sur un nœud est inférieure à 50 % (à l'exception des pods DaemonSet et des Mirror pods).

Tous les pods sur le nœud (sauf ceux qui s'exécutent par défaut sur tous les nœuds, comme les pods exécutés via DaemonSets) peuvent être planifiés sur d'autres nœuds.

Le nœud n'a pas une annotation qui désactive la réduction de taille.

Claudie déploie le Cluster Autoscaler et l'Autoscaler Adapter dans chaque cluster configuré pour l'autoscaling.

Ces composants sont déployés dans le même cluster que Claudie.

Il est important de suivre les bonnes pratiques de Cluster Autoscaler lors de l'utilisation de l'autoscaling avec Claudie.

De plus, comme le nombre de nœuds dans les groupes de nœuds autoscalés peut varier, il faut planifier attentivement l'utilisation du stockage sur ces groupes.

Le support de Longhorn pour l'autoscaling est encore en phase expérimentale.

2: On teste pour vous la solution

Hé bien, sur le papier, ca à l'air top qu'en est il en réalité ?

Cas d'usage

  • Multiples nodepools sur Azure dans des souscriptions et régions différentes
  • Gestion DNS sur cloudflare
  • Débordement sur AWS

Préparation

Création d'un cluster de management

Je vais prendre un cluster sur azure qui me sert à toutes mes démos. Le pré-requis (cert-manager) pour claudie est déjà déployé.

Déploiement du control-plane de claudie

On applique le manifeste suivant : https://github.com/berops/claudie/releases/latest/download/claudie.yaml

J'ai modifié la variable d'environnement GOLANG_LOG en passant sa valeur a debug dans le configmap : dynamodb-cm

kubectl apply -f https://github.com/berops/claudie/releases/latest/download/claudie.yaml


❯ kubectl get deployment,sts,svc
NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ansibler           1/1     1            1           4m15s
deployment.apps/builder            1/1     1            1           4m15s
deployment.apps/claudie-operator   1/1     1            1           4m15s
deployment.apps/context-box        1/1     1            1           4m14s
deployment.apps/dynamodb           1/1     1            1           4m14s
deployment.apps/kube-eleven        1/1     1            1           4m14s
deployment.apps/kuber              1/1     1            1           4m14s
deployment.apps/mongodb            1/1     1            1           4m14s
deployment.apps/scheduler          1/1     1            1           4m14s
deployment.apps/terraformer        1/1     1            1           4m13s

NAME                     READY   AGE
statefulset.apps/minio   4/4     4m14s

NAME                       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
service/ansibler           ClusterIP   10.0.122.72    <none>        50053/TCP           4m18s
service/claudie-operator   ClusterIP   10.0.126.166   <none>        443/TCP,50058/TCP   4m18s
service/context-box        ClusterIP   10.0.207.125   <none>        50055/TCP           4m17s
service/dynamo             ClusterIP   10.0.222.117   <none>        8000/TCP            4m17s
service/kube-eleven        ClusterIP   10.0.157.138   <none>        50054/TCP           4m17s
service/kuber              ClusterIP   10.0.72.241    <none>        50057/TCP           4m17s
service/minio              ClusterIP   None           <none>        9000/TCP,9001/TCP   4m17s
service/mongodb            ClusterIP   10.0.168.125   <none>        27017/TCP           4m17s
service/terraformer        ClusterIP   10.0.129.116   <none>        50052/TCP           4m16s


❯ k get po
NAME                                READY   STATUS      RESTARTS   AGE
ansibler-ff79c4964-5tjpg            1/1     Running     0          4m26s
builder-67bcf777bd-tcmk8            1/1     Running     0          62s
claudie-operator-6884fcc477-kjt88   1/1     Running     0          4m26s
context-box-5c6bd5bf99-fr8xt        1/1     Running     0          4m25s
create-table-job-v66hv              0/1     Completed   3          4m24s
dynamodb-6d65df988-c9prh            1/1     Running     0          4m25s
kube-eleven-6fb6db6b57-jg2gw        1/1     Running     0          4m25s
kuber-5dcff7c6f5-dntwz              1/1     Running     0          4m25s
make-bucket-job-djzkg               0/1     Completed   1          4m24s
minio-0                             1/1     Running     0          4m24s
minio-1                             1/1     Running     0          4m24s
minio-2                             1/1     Running     0          4m24s
minio-3                             1/1     Running     0          4m24s
mongodb-5574c9b7-dg5nv              1/1     Running     0          4m25s
scheduler-69485fbb6c-rrkzz          1/1     Running     0          4m24s
terraformer-7f5fb999d9-x2zc2        1/1     Running     0          4m24s

Création des différents secrets pour permettre a claudie de déployer des clusters kubernetes

  • Azure
  • Création d'un rôle custom pour la gestion des resources groups

J'ai un tenant avec 2 souscriptions

  • 86xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  • 2dxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
#### Role Policy

Je vais créer des "role policy" pour claudie sur les 2 souscriptions de mon tenant

cat > policy.json <<EOF
{
   "Name":"Claudie ControlPlane",
   "Id":"aacd72a7-1185-99ef-bn56-f777fba81xe0",
   "IsCustom":true,
   "Description":"Claudie ControlPlane: resource groups, network contributor, ",
   "Actions":[
      "Microsoft.Resources/subscriptions/resourceGroups/read",
      "Microsoft.Resources/subscriptions/resourceGroups/write",
      "Microsoft.Resources/subscriptions/resourceGroups/delete",
      "Microsoft.Authorization/*/read",
      "Microsoft.Insights/alertRules/*",
      "Microsoft.Network/*",
      "Microsoft.ResourceHealth/availabilityStatuses/read",
      "Microsoft.Resources/deployments/*",
      "Microsoft.Resources/subscriptions/resourceGroups/read",
      "Microsoft.Support/*",
      "Microsoft.Authorization/*/read",
      "Microsoft.Compute/availabilitySets/*",
      "Microsoft.Compute/locations/*",
      "Microsoft.Compute/virtualMachines/*",
      "Microsoft.Compute/virtualMachineScaleSets/*",
      "Microsoft.Compute/cloudServices/*",
      "Microsoft.Compute/disks/write",
      "Microsoft.Compute/disks/read",
      "Microsoft.Compute/disks/delete",
      "Microsoft.DevTestLab/schedules/*",
      "Microsoft.Insights/alertRules/*",
      "Microsoft.Network/applicationGateways/backendAddressPools/join/action",
      "Microsoft.Network/loadBalancers/backendAddressPools/join/action",
      "Microsoft.Network/loadBalancers/inboundNatPools/join/action",
      "Microsoft.Network/loadBalancers/inboundNatRules/join/action",
      "Microsoft.Network/loadBalancers/probes/join/action",
      "Microsoft.Network/loadBalancers/read",
      "Microsoft.Network/locations/*",
      "Microsoft.Network/networkInterfaces/*",
      "Microsoft.Network/networkSecurityGroups/join/action",
      "Microsoft.Network/networkSecurityGroups/read",
      "Microsoft.Network/publicIPAddresses/join/action",
      "Microsoft.Network/publicIPAddresses/read",
      "Microsoft.Network/virtualNetworks/read",
      "Microsoft.Network/virtualNetworks/subnets/join/action",
      "Microsoft.RecoveryServices/locations/*",
      "Microsoft.RecoveryServices/Vaults/backupFabrics/backupProtectionIntent/write",
      "Microsoft.RecoveryServices/Vaults/backupFabrics/protectionContainers/protectedItems/*/read",
      "Microsoft.RecoveryServices/Vaults/backupFabrics/protectionContainers/protectedItems/read",
      "Microsoft.RecoveryServices/Vaults/backupFabrics/protectionContainers/protectedItems/write",
      "Microsoft.RecoveryServices/Vaults/backupPolicies/read",
      "Microsoft.RecoveryServices/Vaults/backupPolicies/write",
      "Microsoft.RecoveryServices/Vaults/read",
      "Microsoft.RecoveryServices/Vaults/usages/read",
      "Microsoft.RecoveryServices/Vaults/write",
      "Microsoft.ResourceHealth/availabilityStatuses/read",
      "Microsoft.Resources/deployments/*",
      "Microsoft.Resources/subscriptions/resourceGroups/read",
      "Microsoft.SerialConsole/serialPorts/connect/action",
      "Microsoft.SqlVirtualMachine/*",
      "Microsoft.Storage/storageAccounts/listKeys/action",
      "Microsoft.Storage/storageAccounts/read"

   ],
   "assignableScopes": [
            "/subscriptions/86...",
            "/subscriptions/2d..."
   ]
}
EOF

Service Account

Je vais créer un compte de service et lui appliquer le custom role

# Création du service account
az ad sp create-for-rbac --name claudie-sp -o json

{
  "appId": "appid-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "displayName": "claudie-sp",
  "password": "appPassword",
  "tenant": "tenantid-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}


# Affection du rôle au sp subscription 1

az role assignment create \
--assignee [appId retourné par la commande précédente ] \
--role "Claudie ControlPlane" \
--scope /subscriptions/86... \
-o json

# Affection du role au sp subscription 2
az role assignment create \
--assignee [appId retourné par la commande précédente ] \
--role "Claudie ControlPlane" \
--scope /subscriptions/2d... \
-o json


APP_ID="appid-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
APP_SECRET="appPassword"
TENANT_ID="tenantid-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
SUBSCRIPTION_ID="86..."

cat > azure-secret-1.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: azure-secret-1
stringData:
  clientid: $APP_ID
  clientsecret: $APP_SECRET
  subscriptionid: $SUBSCRIPTION_ID
  tenantid: $TENANT_ID
type: Opaque

EOF



SUBSCRIPTION_ID="2d..."

cat > azure-secret-2.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: azure-secret-2
strinData:
  clientid: $APP_ID
  clientsecret: $APP_SECRET
  subscriptionid: $SUBSCRIPTION_ID
  tenantid: $TENANT_ID
type: Opaque

EOF
  • Création du secret azure dans le namespace de claudie

  • Cloudflare

  • J'ai un domaine géré pat cloudflare : leclerc.biz

  • Je génère un token d'API (https://dash.cloudflare.com/profile/api-tokens avec les droits suivants :

    • Zone: Read
    • DNS: Read
    • DNS: Edit
      Ce qui me permettra de gérer dynamiquement la zone
  • Création du secret dans le namespace de claudie

API_TOKEN="cloudflareapitokengivenbycf"


cat > cloudflare-secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-secret
stringData:
  apitoken: $API_TOKEN
type: Opaque
EOF

Par commodité, je vais créer un namespace ou je vais déployer toutes les ressources nécessaires pour que claudie déploie les nœuds de mon cluster multi zone.

kubectl create ns aw-claudie-cluster 

Création des secrets

kubectl create -n aw-claudie-cluster -f azure-secret-1.yaml
kubectl create -n aw-claudie-cluster -f azure-secret-2.yaml
kubectl create -n aw-claudie-cluster -f cloudflare-secret.yaml

Création de la custom resource nous permettant de créer le cluster.

apiVersion: claudie.io/v1beta1
kind: InputManifest
metadata:
  name: aw-claudie-cluster
  labels:
    app.kubernetes.io/part-of: claudie
spec:
  providers:
    - name: cloudflare-1
      providerType: cloudflare
      secretRef:
        name: cloudflare-secret
        namespace: aw-claudie-cluster
    - name: azure-1
      providerType: azure
      secretRef:
        name: azure-secret-1
        namespace: aw-claudie-cluster
    - name: azure-2
      providerType: azure
      secretRef:
        name: azure-secret-2
        namespace: aw-claudie-cluster

  nodePools:
    dynamic:
      - name: control-azure-1
        providerSpec:
          # Name of the provider instance.
          name: azure-1
          # Location of the nodepool.
          region: westeurope
          # Zone of the nodepool.
          zone: "1"
        count: 1
        # VM size name.
        serverType: Standard_B2s
        # URN of the image.
        image: Canonical:0001-com-ubuntu-minimal-jammy:minimal-22_04-lts:22.04.202212120

      #- name: control-azure-2
      #  providerSpec:
      #    # Name of the provider instance.
      #    name: azure-2
      #    # Location of the nodepool.
      #    region: northeurope
      #    # Zone of the nodepool.
      #    zone: "2"
      #  count: 1
      #  # VM size name.
      #  serverType: Standard_B2s
      #  # URN of the image.
      #  image: Canonical:0001-com-ubuntu-minimal-jammy:minimal-22_04-lts:22.04.202212120

      - name: compute-azure-1
        providerSpec:
          # Name of the provider instance.
          name: azure-1
          # Location of the nodepool.
          region: westeurope
          # Zone of the nodepool.
          zone: "2"
        count: 1
        # VM size name.
        serverType: Standard_B2s
        # URN of the image.
        image: Canonical:0001-com-ubuntu-minimal-jammy:minimal-22_04-lts:22.04.202212120
        storageDiskSize: 50

      #- name: compute-azure-2
      #  providerSpec:
      #    # Name of the provider instance.
      #    name: azure-2
      #   # Location of the nodepool.
      #    region: northeurope
      #    # Zone of the nodepool.
      #    zone: "3"
      #  count: 1
      #  # VM size name.
      #  serverType: Standard_B2s
      #  # URN of the image.
      #  image: Canonical:0001-com-ubuntu-minimal-jammy:minimal-22_04-lts:22.04.202212120
      #  storageDiskSize: 50

      - name: loadbalancer-1
        providerSpec:
          # Name of the provider instance.
          name: azure-1
          # Location of the nodepool.
          region: northeurope
          # Zone of the nodepool.
          zone: "1"
        count: 1
        # VM size name.
        serverType: Standard_B2s
        # URN of the image.
        image: Canonical:0001-com-ubuntu-minimal-jammy:minimal-22_04-lts:22.04.202212120
        storageDiskSize: 50

  kubernetes:
    clusters:
      # targetedK8s
      - name: aw-claudie-cluster
        version: "v1.26.0"
        network: 192.168.2.0/24
        pools:
          control:
            - control-azure-1
            #- control-azure-2
          compute:
            - compute-azure-1
          #  - compute-azure-2
  loadBalancers:
    roles:
      - name: apiserver
        protocol: tcp
        port: 6443
        targetPort: 6443
        target: k8sControlPlane
    clusters:
      - name: apiserver-lb-claudie
        roles:
          - apiserver
        dns:
          dnsZone: leclerc.biz
          provider: cloudflare-1
        targetedK8s: aw-claudie-cluster
        pools:
          - loadbalancer-1

Création du cluster via la custom resource

kubectl -n  aw-claudie-cluster -f aw-claudie-cluster.yaml


inputmanifest.claudie.io/aw-claudie-cluster created

Vérification de l'avancement de la création du cluster.

kubectl get inputmanifests.claudie.io -n aw-claudie-cluster aw-claudie-cluster  -o jsonpath={.status} | jq .


❯ kubectl get inputmanifests.claudie.io -n aw-claudie-cluster aw-claudie-cluster  -o jsonpath={.status} | jq .
{
  "state": "IN_PROGRESS"
} kubectl get inputmanifests.claudie.io -n aw-claudie-cluster aw-claudie-cluster  -o jsonpath={.status} | jq .
{
  "clusters": {
    "aw-claudie-cluster": {
      "message": "building infrastructure",
      "phase": "TERRAFORMER",
      "state": "IN_PROGRESS"
    }
  },
  "state": "IN_PROGRESS"
} kubectl get inputmanifests.claudie.io -n aw-claudie-cluster aw-claudie-cluster  -o jsonpath={.status} | jq .
{
  "clusters": {
    "aw-claudie-cluster": {
      "message": "installing VPN",
      "phase": "ANSIBLER",
      "state": "IN_PROGRESS"
    }
  },
  "state": "IN_PROGRESS"
}
...
❯ kubectl get inputmanifests.claudie.io -n aw-claudie-cluster aw-claudie-cluster  -o jsonpath={.status} | jq .
{
  "clusters": {
    "aw-claudie-cluster": {
      "message": "setting up Loadbalancers",
      "phase": "ANSIBLER",
      "state": "IN_PROGRESS"
    }
  },
  "state": "IN_PROGRESS"
} kubectl get inputmanifests.claudie.io -n aw-claudie-cluster aw-claudie-cluster  -o jsonpath={.status} | jq .
{
  "clusters": {
    "aw-claudie-cluster": {
      "message": "building kubernetes cluster",
      "phase": "KUBE_ELEVEN",
      "state": "IN_PROGRESS"
    }
  },
  "state": "IN_PROGRESS"
} kubectl get inputmanifests.claudie.io -n aw-claudie-cluster aw-claudie-cluster  -o jsonpath={.status} | jq .
{
  "clusters": {
    "aw-claudie-cluster": {
      "message": "setting up storage",
      "phase": "KUBER",
      "state": "IN_PROGRESS"
    }
  },
  "state": "IN_PROGRESS"
} kubectl get inputmanifests.claudie.io -n aw-claudie-cluster aw-claudie-cluster  -o jsonpath={.status} | jq .
{
  "clusters": {
    "aw-claudie-cluster": {
      "message": "patching k8s nodes",
      "phase": "KUBER",
      "state": "IN_PROGRESS"
    }
  },
  "state": "IN_PROGRESS"
} kubectl get inputmanifests.claudie.io -n aw-claudie-cluster aw-claudie-cluster  -o jsonpath={.status} | jq .
{
  "clusters": {
    "aw-claudie-cluster": {
      "phase": "NONE",
      "state": "DONE"
    }
  },
  "state": "DONE"
}

On peut suivre toutes les étapes de création du cluster, et l'utilisation de tous les microservices.

Coup d'œil au niveau de log des microservices de claudie

Vous pouvez utiliser stern qui un outils très sympa pour afficher les logs des pods.

Je surveille toutes les log dans le namespace claudie de cette façon.

stern -n claudie -l app.kubernetes.io/part-of=claudie

Récupération du kubeconfig

 kubectl get secrets -n claudie -l claudie.io/output=kubeconfig
NAME                                    TYPE     DATA   AGE
aw-claudie-cluster-yhbfwvh-kubeconfig   Opaque   1      2m13s
kubectl get secrets -n claudie -l claudie.io/output=kubeconfig -o jsonpath='{.items[0].data.kubeconfig}' | base64 -d > kubeconfig-aw-claudie-cluster.yaml

Utilisation du kubeconfig

 kubectl --kubeconfig=kubeconfig-aw-claudie-cluster.yaml get nodes
NAME                        STATUS   ROLES           AGE     VERSION
compute-azure-1-hkkzwc0-1   Ready    <none>          4m8s    v1.26.0
control-azure-1-tbridau-1   Ready    control-plane   5m28s   v1.26.0

Si on regarde le fichier kubeconfig-aw-claudie-cluster.yaml

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: ..==
    server: https://3vhzg91r15evfjn0z.leclerc.biz:6443
  name: aw-claudie-cluster
contexts:
- context:
    cluster: aw-claudie-cluster
    user: kubernetes-admin
  name: kubernetes-admin@aw-claudie-cluster
current-context: kubernetes-admin@aw-claudie-cluster
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: LS0..
    client-key-data: LS0..

On voit bien que le nom de domaine est leclerc.biz et que le cluster s'appelle 3vhzg91r15evfjn0z

Vérification a niveau d'azure et cloudflare

Resources groupes

  • aw-claudie-cluster-yhbfwvh-westeurope
  • apiserver-lb-claudie-qeqdqf7-northeurope

  • Le cluster est positionner en West Europe et le LoadBalancer du control plane en North Europe

  • Loadbalancing est assuré par une machine virtuelle

  • Son adresse ip est :

  • Vérification au niveau Cloudflare

Le loadbalancing est fait par le déploiement d'un nginx

La configuration est dans le fichier /etc/nginx/passthrough.conf

cat /etc/nginx/passthrough.conf; 
stream{
upstream apiserver{
    server 192.168.2.1:6443 max_fails=3 fail_timeout=10s;
}

server  {
    listen 6443;
    proxy_pass apiserver;
    proxy_next_upstream on;
}

L'adresse ip 192.168.2.1 correspond à l'adresse du vpn point a point géré par wireguard

Quand on regarde la configuration du Wireguard

cat wg0.conf 
[Interface]
Address = 192.168.2.3/24
PrivateKey = KKW...=
ListenPort = 51820

[Peer]
PublicKey = 5VS...=
Endpoint = 13.81.102.110:51820
AllowedIps = 192.168.2.1/32
[Peer]
PublicKey = iyp...=
Endpoint = 13.81.102.148:51820
AllowedIps = 192.168.2.2/32

Les adresses ips correspondent aux adresses ip du controle plane et le nœud de compute

Tentative d'ajout d'un nœud de compute dans une autre souscription

On va décommenter les sections compute-azure-2 dans le fichier yaml

- name: compute-azure-2
        providerSpec:
          # Name of the provider instance.
          name: azure-2
          ...
...
kubernetes:
    clusters:
      # targetedK8s
      - name: aw-claudie-cluster
        version: "v1.26.0"
        network: 192.168.2.0/24
        pools:
          control:
            - control-azure-1
            #- control-azure-2
          compute:
            - compute-azure-1
            - compute-azure-2

On applique le nouveau manifeste pour voir si la boucle de reconciliation fonctionne.

kubectl apply -n  aw-claudie-cluster -f aw-claudie-cluster.yaml

❯ kubectl get -n aw-claudie-cluster inputmanifests.claudie.io aw-claudie-cluster  -o jsonpath={.status} | jq .
{
  "clusters": {
    "aw-claudie-cluster": {
      "message": "building infrastructure",
      "phase": "TERRAFORMER",
      "state": "IN_PROGRESS"
    }
  },
  "state": "IN_PROGRESS"
}
  • Ca ne fonctionne pas car on a des messages d'erreurs au niveau de terraform :

Error: Duplicate provider configuration

  • En regardant le code on voit qu'il n'est pas possible d'avoir plusieurs providers azure avec par exemple des souscription différentes.

  • Je vais ouvrir une issue sur ce sujet

  • Donc on va changer name: azure-2 en name: azure-1 dans les spec du worker node et relancer l'ajout

  • La je rencontre aussi un problème terraform reste en mode "failed". Je n'ai pas trouvé de moyen de lui dire de regénérer un plan et de le réappliquer

  • Donc je choisis de supprimer l' inputmanifests pour repartir sur un cluster from scratch

  • La aussi je rencontre des problèmes car terraform a encore la définition du provider en double

  • Donc je vais patcher le providers directement dans le pod terraformer et mettre un alias sur le deuxième provider azure pour débloquer la situation.

Vérifications que les resources sur Azure sont bien supprimées

Oui tout est bien supprimé

Conclusions

  • En terme de "concurrents" il y a clusterAPI et crossplane (qui utilise clusterAPI).

  • Claudie fait partie de ces outils qui permettent de facilement créer des clusters kubernetes hybrides, multi CSP, multi région sans utiliser les services managés des CSP avec une approche IAAS.

  • Claudie utilise terraform pour provisionner l'infrastructure et ansible pour les middleware

  • Dans certains cas d'utilisation cela peut être très interessant si on a des cas comme ceux vus dans l'introduction.

  • Claudie est moins extensible que crossplane

  • Claudie est plus souple que crossplane pour gérer un cluster multi-csp ou multi-région

  • Claudie ne déploie pas de kubernetes en service managés du CSP alors que crossplane va le faire.

  • Ce sont 2 approches complètement différentes.

Critère Berops/Claudie Crossplane ClusterAPI
Tendances Émergentes Intégration avec Cluster Autoscaler pour la gestion multi-cloud. S'utilise pour construire des plans de contrôle de cloud natif. Simplification du cycle de vie des clusters via API déclaratives.
Forces Gestion multi-cloud, cloud bursting, interconnexion de services, VPN Wireguard, GitOps. Projet CNCF, gestion abstraite de l'infrastructure, API Kubernetes. Modèle opérateur Kubernetes, automatisation, support multi-fournisseur.
Faiblesses Nouveauté et complexité de gestion multi-cloud. Complexité de configuration, dépendance sur l'écosystème Kubernetes. Compréhension des API déclaratives, gestion de multi-fournisseurs.
Aspect Distinctif Approche multi-cloud et hybrid-cloud au niveau des nodepools. Abstraction de haut niveau pour la gestion des ressources cloud. Gestion du cycle de vie des clusters Kubernetes.
Compatibilité GitOps Oui Oui Oui
Conclusion Choix pour gestion multi-cloud et hybrid-cloud avancée. Choix pour ceux recherchant une gestion abstraite avec Kubernetes. Choix pour une gestion simplifiée des clusters Kubernetes.

Manifest fonctionnel

apiVersion: claudie.io/v1beta1
kind: InputManifest
metadata:
  name: aw-claudie-cluster
  labels:
    app.kubernetes.io/part-of: claudie
    run: "2"
spec:
  providers:
    - name: cloudflare-1
      providerType: cloudflare
      secretRef:
        name: cloudflare-secret
        namespace: aw-claudie-cluster
    - name: azure-1
      providerType: azure
      secretRef:
        name: azure-secret-1
        namespace: aw-claudie-cluster

  nodePools:
    dynamic:
      - name: control-azure-1
        providerSpec:
          # Name of the provider instance.
          name: azure-1
          # Location of the nodepool.
          region: westeurope
          # Zone of the nodepool.
          zone: "1"
        count: 1
        # VM size name.
        serverType: Standard_B2s
        # URN of the image.
        image: Canonical:0001-com-ubuntu-minimal-jammy:minimal-22_04-lts:22.04.202212120

      - name: compute-azure-1
        providerSpec:
          # Name of the provider instance.
          name: azure-1
          # Location of the nodepool.
          region: westeurope
          # Zone of the nodepool.
          zone: "2"
        count: 1
        # VM size name.
        serverType: Standard_B2s
        # URN of the image.
        image: Canonical:0001-com-ubuntu-minimal-jammy:minimal-22_04-lts:22.04.202212120
        storageDiskSize: 50

      - name: compute-azure-2
        providerSpec:
          # Name of the provider instance.
          name: azure-1
         # Location of the nodepool.
          region: northeurope
          # Zone of the nodepool.
          zone: "3"
        count: 1
        # VM size name.
        serverType: Standard_B2s
        # URN of the image.
        image: Canonical:0001-com-ubuntu-minimal-jammy:minimal-22_04-lts:22.04.202212120
        storageDiskSize: 50

      - name: loadbalancer-1
        providerSpec:
          # Name of the provider instance.
          name: azure-1
          # Location of the nodepool.
          region: northeurope
          # Zone of the nodepool.
          zone: "1"
        count: 1
        # VM size name.
        serverType: Standard_B2s
        # URN of the image.
        image: Canonical:0001-com-ubuntu-minimal-jammy:minimal-22_04-lts:22.04.202212120
        storageDiskSize: 50

  kubernetes:
    clusters:
      # targetedK8s
      - name: aw-claudie-cluster
        version: "v1.26.0"
        network: 192.168.2.0/24
        pools:
          control:
            - control-azure-1
            #- control-azure-2
          compute:
            - compute-azure-1
            - compute-azure-2
  loadBalancers:
    roles:
      - name: apiserver
        protocol: tcp
        port: 6443
        targetPort: 6443
        target: k8sControlPlane
    clusters:
      - name: apiserver-lb-claudie
        roles:
          - apiserver
        dns:
          dnsZone: leclerc.biz
          provider: cloudflare-1
        targetedK8s: aw-claudie-cluster
        pools:
          - loadbalancer-1

Découvrez les technologies d'alter way