Créer une usine à clusters kubernetes - partie 1

thumbernail kubernetes

1. But

On souhaite pouvoir créer des clusters Kubernetes à la demande, en les mutualisant sur une infrastructure unique afin de réduire les coûts. On veut pouvoir gérer et fournir des environnements multiples dans un cadre sécurisé bien isolé et contrôlé.

2. Composants utilisés

  • Cluster Kubernetes Hôte, il faudra avoir un cluster suffisamment puissant pour offrir un service efficace. Utilisez des environnements fournissant de l'autoscaling au niveau des nœuds de compute. Dans mon billet de blog je vais utiliser un kuberneets en service managé sur Azure (AKS).

  • Un ingress controller déployé sur votre cluster kubernetes

  • Un certificate manager déployé et configuré si vous souhaitez exposer les vcluster et leur api en https

  • Pour la partie "virtualisation" de cluster sur un cluster existant, nous allons utiliser vcluster. Vous pouvez consulter le billet de blog sur la solution vcluster

  • Pour la partie création des clusters en mode cloud-native et declaratif, nous allons utiliser la cluster-api car elle propose un support de vcluster. Vous pouvez trouver toutes les informations sur cette solution ici (https://cluster-api.sigs.k8s.io/) et plus précisément ici (https://cluster-api.sigs.k8s.io/user/quick-start.html)

3. Installation des composants

3.1 Ingress Controller

Toute la documentation est ici : (https://kubernetes.github.io/ingress-nginx/deploy/)

# Spécifiquement pour aure nous avons besoin d'une annotation

helm upgrade --install ingress-nginx ingress-nginx \
      --repo https://kubernetes.github.io/ingress-nginx \
      --version 4.6.1 \
      --create-namespace \
      --namespace ingress-nginx \
      --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz \
      --set controller.extraArgs.enable-ssl-passthrough=

Note : j'ai pris la version 4.6.1 car il y actuellement (4.8.3) un problème avec le ssl-passthrough qui bug

3.2 Cert-Manager

3.2.1 Déploiement

helm repo add jetstack https://charts.jetstack.io

helm repo update

# Version est mise mais vous pouvez utiliser la latest, la premiere fois utilisez le flag installCRDs=true
helm upgradec --install cert-manager jetstack/cert-manager \
     --namespace cert-manager \
     --create-namespace \
     --version v1.12.0  \
     --set installCRDs=true

3.2.2 Configuration

Je vais utiliser letsencrypt et ce en mode ClusterIssuer

Par exemple

Le domaine de référence sera "*.caas.fr"

cat <<EOF | kubectl apply --filename -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: herve.leclerc@alterway.fr
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
       ingress:
         class: nginx
EOF k get clusterissuer
NAME                READY   AGE
letsencrypt         True    8h

3.3. Installation de la CLI pour la cluster-api : clusterctl

Vous avez dans la page du quick-start toutes les informations pour installer cette CLI sur différents OS manuellement ou avec des gestionnaire de package (homebrew)

Une fois la CLI installée le programme clusterctl est disponible

exemple

 clusterctl version -o short

v1.5.3

Il faudra initialiser le contrôleur de la capi pour vcluster

 clusterctl init --infrastructure vcluster

Fetching providers
Skipping installing cert-manager as it is already installed
Installing Provider="cluster-api" Version="v1.5.3" TargetNamespace="capi-system"
Installing Provider="bootstrap-kubeadm" Version="v1.5.3" TargetNamespace="capi-kubeadm-bootstrap-system"
Installing Provider="control-plane-kubeadm" Version="v1.5.3" TargetNamespace="capi-kubeadm-control-plane-system"
Installing Provider="infrastructure-vcluster" Version="v0.1.3" TargetNamespace="cluster-api-provider-vcluster-system"

Your management cluster has been initialized successfully!

You can now create your first workload cluster by running the following:

  clusterctl generate cluster [name] --kubernetes-version [version] | kubectl apply -f -

3.4 Création de l'environnement de management dans notre cluster

3.4.2 Préparation - Test unitaire

Avant de créer des template utilisables pour automatiser et industrialiser notre usine à cluster nous allons faire un déploiement de manière unitaire pour vérifier que tout est ok

  • Créer si besoin un fichier values pour la variable d'environnement HELM_VALUES

ex: "

syncer:
  extraArgs:
    - --tls-san=${CLUSTER_NAME}.${CLUSTER_DOMAIN}
    - --out-kube-config-server=https://${CLUSTER_NAME}.${CLUSTER_DOMAIN}
ingress:
  enabled: true
  pathType: ImplementationSpecific
  ingressClassName: nginx
  host: ${CLUSTER_NAME}.${CLUSTER_DOMAIN}
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: HTTPS
    nginx.ingress.kubernetes.io/ssl-passthrough: 'true'
    nginx.ingress.kubernetes.io/ssl-redirect: 'true'
    cert-manager.io/cluster-issuer: letsencrypt
  tls:
    - secretName: ${CLUSTER_NAME}.${CLUSTER_DOMAIN}-tls
      hosts:
        - ${CLUSTER_NAME}.${CLUSTER_DOMAIN}

Vous pouvez éditer le fichier pour enlever les \n et les remplacer par \n (dans vi :%s/\n/\\n/g) Copier coller la ligne pour la mettre dans la variable d'environnement

  • Créer un fichier d'environnement que vous pourrez utiliser en utilisant la commande source
# file env.sh

# On active cette Feature
export CLUSTER_TOPOLOGY=true
# Pour configurer la cluster api pour générer des Vcluster (https://github.com/loft-sh/cluster-api-provider-vcluster/blob/main/docs/quick-start.md)

export CLUSTER_NAME=dev0
export CLUSTER_NAMESPACE=dev0-ns
export KUBERNETES_VERSION="1.27.6"
export CLUSTER_DOMAIN=caas.fr



export HELM_VALUES=$(cat n2n.yaml| envsubst | sed -z 's/\n/\\n/g')

# ou ...
#export HELM_VALUES="syncer:\n  extraArgs:\n    - --tls-san=${CLUSTER_NAME}.${CLUSTER_DOMAIN}\n    - --out-kube-config-server=https://${CLUSTER_NAME}.${CLUSTER_DOMAIN}\ningress:\n  enabled: true\n  pathType: ImplementationSpecific\n  ingressClassName: nginx\n  host: ${CLUSTER_NAME}.${CLUSTER_DOMAIN}\n  annotations:\n    nginx.ingress.kubernetes.io/backend-protocol: HTTPS\n    nginx.ingress.kubernetes.io/ssl-passthrough: 'true'\n    nginx.ingress.kubernetes.io/ssl-redirect: 'true'\n    cert-manager.io/cluster-issuer: letsencrypt\n  tls:\n    - secretName: ${CLUSTER_NAME}.${CLUSTER_DOMAIN}-tls\n      hosts:\n        - ${CLUSTER_NAME}.${CLUSTER_DOMAIN}"

Vérifiez que la structure de la variable HELM_VALUES est ok echo ${HELM_VALUES}

3.4.2 génération du template de création du cluster

On ne va pas générer directement le cluster mais préparer un template pouvant être "kustomisé" par la suite

source env.sh

clusterctl generate cluster ${CLUSTER_NAME} \
    --infrastructure vcluster \
    --kubernetes-version ${KUBERNETES_VERSION} \
    --target-namespace ${CLUSTER_NAMESPACE} > ${CLUSTER_NAME}.yaml

Affichage des Custom resources créées

apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: dev0
  namespace: dev0-ns
spec:
  controlPlaneRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
    kind: VCluster
    name: dev0
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
    kind: VCluster
    name: dev0
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: VCluster
metadata:
  name: dev0
  namespace: dev0-ns
spec:
  controlPlaneEndpoint:
    host: ""
    port: 0
  helmRelease:
    chart:
      name: null
      repo: null
      version: null
    values: |
      syncer:
        extraArgs:
          - --tls-san=dev0.vclusters.caas.fr
          - --out-kube-config-server=https://dev0.vclusters.caas.fr
      ingress:
        enabled: true
        pathType: ImplementationSpecific
        ingressClassName: nginx
        host: dev0.vclusters.caas.fr
        annotations:
          nginx.ingress.kubernetes.io/backend-protocol: HTTPS
          nginx.ingress.kubernetes.io/ssl-passthrough: 'true'
          nginx.ingress.kubernetes.io/ssl-redirect: 'true'
          cert-manager.io/cluster-issuer: letsencrypt
        tls:
          - secretName: dev0.vclusters.caas.fr-tls
            hosts:
              - dev0.vclusters.caas.fr
  kubernetesVersion: 1.27.4

Si vous souhaitez déployer une autre distribution que k3s par défaut il faudra modifier le bloc

helmRelease:
    chart:
      name: null
      repo: null
      version: null

avec les bonnes valeurs

Par exemple pour déployer un k8s vanilla

helmRelease:
    chart:
      name: vcluster-k8s
      repo: https://charts.loft.sh
      version: null

3.4.3 Test de création du cluster

Simplement appliquez le manifeste généré

 kubectl apply -f dev0.yaml
cluster.cluster.x-k8s.io/dev0 created
vcluster.infrastructure.cluster.x-k8s.io/dev0 created


# vérification kubectl get cluster -n ${CLUSTER_NAMESPACE}

NAME   PHASE          AGE   VERSION
dev0   Provisioning   63s

❯ kubectl get cluster -n ${CLUSTER_NAMESPACE}
NAME   PHASE         AGE   VERSION
dev0   Provisioned   1m  

Si vous avez installé la CLI de vcluster vous pouvez aussi le cluster

 vcluster list

    NAME |    CLUSTER    | NAMESPACE | STATUS  | VERSION | CONNECTED |            CREATED            |  AGE  | DISTRO
  -------+---------------+-----------+---------+---------+-----------+-------------------------------+-------+---------
    dev0 | k8s-ims-admin | dev0-ns   | Running | 0.11.1  |           | 2023-11-29 14:54:39 +0100 CET | 14m2s | OSS

3.4.4 Récupération du kubeconfig

Le kubeconfig du cluster se trouve dans un secrets ayant comme règle de nommage vc-[nom du cluster]

 kubectl get secret vc-$CLUSTER_NAME -n $CLUSTER_NAMESPACE --template={{.data.config}} | base64 -d > kubeconfig.yaml

3.4.5 Tests de requêtes sur l'api

 KUBECONFIG=./kubeconfig.yaml k get nodes
NAME                          STATUS   ROLES    AGE   VERSION
aks-sys-17083323-vmss00001c   Ready    <none>   18m   v1.24.1+k3s1

❯ KUBECONFIG=./kubeconfig.yaml k get ns
NAME              STATUS   AGE
kube-system       Active   19m
default           Active   19m
kube-public       Active   19m
kube-node-lease   Active   19m

❯ KUBECONFIG=./kubeconfig.yaml k create deploy test --image=nginx -n default
deployment.apps/test created


❯ KUBECONFIG=./kubeconfig.yaml k get po
NAME                    READY   STATUS    RESTARTS   AGE
test-764c85dd84-pgmz7   1/1     Running   0          24s


❯ KUBECONFIG=./kubeconfig.yaml k expose deploy test --port 80 -n default
service/test exposed

❯ KUBECONFIG=./kubeconfig.yaml k port-forward -n default svc/test 9000:80
Forwarding from 127.0.0.1:9000 -> 80
Forwarding from [::1]:9000 -> 80 curl localhost:9000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
....

Tout fonctionne parfaitement, nous avons le socle pour créer notre usine à cluster !

Suite à la partie 2

Découvrez les technologies d'alter way