On souhaite pouvoir :
Toute la documentation est ici : (https://kubernetes.github.io/ingress-nginx/deploy/)
# Spécifiquement pour autre 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
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
Je vais utiliser letsencrypt et ce en mode ClusterIssuer
Le domaine de référence sera « *.caas.fr »
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
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
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 -
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
NB Créer si besoin un fichier values pour la variable d’environnement HELM_VALUES
Exemple :
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 $
On ne va pas générer directement le cluster mais préparer un template pouvant être "kustomisé" par la suite
```bash
# 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
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
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
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 !
Création industrielle de clusters
Pour les charts Helm :
Pour ArgoCD :
helm create usine-k8s
Creating usine-k8s
Cette commande va créer un chart helm qui s’appelle usine-k8s qui permet de déployer une application simple. Nous n’allons pas garder les fichiers templates et utiliser nos propres templates.
La structure du répertoire est:
tree .
.
usine-k8s
├──Chart.yaml
├──charts
├──templates
│ ├──NOTES.txt
│ ├──_helpers.tpl
│ ├──deployment.yaml
│ ├──hpa.yaml
│ ├──ingress.yaml
│ ├──service.yaml
│ ├──serviceaccount.yaml
│ └──tests
│ └──test-connection.yaml
└──values.yaml
5 directories, 10 files
Nous allons supprimer tout ce qui ne va pas nous servir.
rm -rf usine-k8s/templates/*
tree .
.
└── usine-k8s
├──Chart.yaml
├──charts
├──templates
└──values.yaml
Ce que nous allons mettre comme variables
Fichier usine-k8s/templates/cluster.yaml :
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: {{ .Values.cluster_name }}
spec:
controlPlaneRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: VCluster
name: {{ .Values.cluster_name }}
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: VCluster
name: {{ .Values.cluster_name }}
Fichier usine-k8s/templates/vcluster.yaml :
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: VCluster
metadata:
name: {{ .Values.cluster_name }}
spec:
controlPlaneEndpoint:
host: ""
port: 0
helmRelease:
chart:
name: vcluster-k8s
repo: https://charts.loft.sh
version: 0.13.0
values: |
syncer:
extraArgs:
- --tls-san={{ .Values.cluster_fqdn }}
- --out-kube-config-server=https://{{ .Values.cluster_fqdn }}
ingress:
enabled: true
pathType: ImplementationSpecific
ingressClassName: nginx
host: {{ .Values.cluster_fqdn }}
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
kubernetesVersion: {{ .Values.k8s_version }}
Editez le fichier values.yaml Supprimez toutes les lignes et ajoutez
# Default values for usine-k8s.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
cluster_name: cluster-name
cluster_fqdn: cluster-name.vclusters.caas.fr
k8s_version: 1.27.6
Nous allons utiliser la commande helm template pour générer les manifestes qui seront appliqués.
helm template usine-k8s usine-k8s
```y
Vous devriez avoir ce résultat :
```yaml
---
# Source: usine-k8s/templates/cluster.yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: cluster-name
spec:
controlPlaneRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: VCluster
name: cluster-name
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: VCluster
name: cluster-name
---
# Source: usine-k8s/templates/vcluster.yaml
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: VCluster
metadata:
name: cluster-name
spec:
controlPlaneEndpoint:
host: ""
port: 0
helmRelease:
chart:
name: vcluster-k8s
repo: https://charts.loft.sh
version: 0.13.0
values: |
syncer:
extraArgs:
- --tls-san=cluster-name.vclusters.caas.fr
- --out-kube-config-server=https://cluster-name.vclusters.caas.fr
ingress:
enabled: true
pathType: ImplementationSpecific
ingressClassName: nginx
host: cluster-name.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
kubernetesVersion: 1.27.6
Vous pouvez aussi faire ce test et vérifier que les valeurs sont bien modifiées
helm template usine-k8s usine-k8s --set cluster_name=test-k8s
Notre template est pret!
Nous allons créer 2 repos
Pour le chart (dans le répertoire usine-k8s)
git init
git add.
git commit -m "init"
git branch -M main
git remote add origin https://github.com/herveleclerc/usine-k8s.git
git push -u origin main
Vous trouverez les repo d’exemple ici:
Suivez le tuto disponible ici : getting Started Si vous n’avez pas argoCD de déployé
Déclarez les repos qu’on va utiliser dans les settings. (j’utilise dans le cadre de ce billet le projet default)
Dans le repo usine-k8s-params
Créez une arborescence de ce type
tree -d .
.
└── infrastructure
└──clusters
└──cluster-dev0
Dans le répertoire cluster-dev0
créer un fichier params.yaml
avec ce contenu :
cluster_name: cluster-dev0
cluster_fqdn: cluster-dev0.vclusters.caas.fr
k8s_version: 1.27.6
Vous devriez avoir une arborescence équivalente à :
tree .
.
└── infrastructure
└──clusters
└──cluster-dev0
└──params.yaml
Lorsque vous souhaitez ajouter un cluster il suffira de créer un nouveau répertoire avec un fichier params.yaml et de le posser sur le repo git
Dans le repo usine-k8s
Créer 3 répertoires
tree -d . -L 1
.
├── applications
├── applicationsets
└── chart
Répertoire chart
Note: Vous devrez adapter les url de votre repo
Dans le répertoire chart mettez votre chart helm précédemment créé
Répertoire applications
Mettre les fichiers suivants :
kustomization.yaml :
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- usine-k8s-root-application.yaml
et
usine-k8s-root-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: usine-k8s-root
namespace: argocd
spec:
destination:
name: ''
namespace: argocd
server: 'https://kubernetes.default.svc'
source:
path: applicationsets
repoURL: 'https://github.com/herveleclerc/usine-k8s.git'
targetRevision: HEAD
sources: []
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
On va déployer une application qui va déployer et maintenir un applicationSet qui déploiera les clusters
On utilise kustomize par commodité
Répertoire applicationsets
Mettre le fichier suivant :
usine-k8s-application-set.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: usine-k8s
spec:
goTemplate: true
generators:
- git:
repoURL: https://github.com/herveleclerc/usine-k8s-params.git
revision: HEAD
files:
- path: "infrastructure/clusters/**/params.yaml"
template:
metadata:
name: '{{.cluster_name}}'
spec:
destination:
name: ''
namespace: '{{.cluster_name}}'
server: 'https://kubernetes.default.svc'
source:
path: chart
repoURL: 'https://github.com/herveleclerc/usine-k8s.git'
targetRevision: HEAD
helm:
parameters:
- name: cluster_name
value: '{{.cluster_name}}'
- name: cluster_fqdn
value: '{{.cluster_fqdn}}'
- name: 'k8s_version'
value: '{{.k8s_version}}'
sources: []
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Cet applicationset va déployer le chart helm avec les paramètres stocké dans un autre repo.
Note : Remarquez les wildcards pour les paramètres on aura une boucle sur ces paramètres
Vous devriez avoir une arborescence identique à cela :
tree .
.
├── applications
│ ├── kustomization.yaml
│ └── usine-k8s-root-application.yaml
├── applicationsets
│ └── usine-k8s-application-set.yaml
└── chart
├──Chart.yaml
├──charts
├──templates
│ ├──cluster.yaml
│ └──vcluster.yaml
└──values.yaml
Commit et push les modifications sur le repo
Aller dans le répertoire applications lancez la commande :
kubectl -k .
L’application root va être déployée elle va elle même déployer l’applicationset
En cliquant sur l’application root :
On voit qu’elle a déployé un applicationset qui déploie des applications
En cliquant sur l’application, on voit les différentes CR (custom resource) de notre cluster
Il suffit d’appliquer des modification dans le repo usine-k8s-params et de les pousser sur le repo
exemple: créer un nouveau cluster
tree .
.
└── infrastructure
└──clusters
├──cluster-dev0
│ └──params.yaml
└──cluster-dev1
└──params.yaml
Cette modification va créer automatiquement un nouveau cluster quand les informations seront « commit »
ArgoCD synchronise les repo et déploie le nouveau cluster
Le temps que le cluster soit provisionné l’appli est en « degraded ». Et passe en déployé
Voila nous avons une usine a cluster kubernetes !
Le cluster est déployé dans un namespace qui porte le nom du cluster_name qu’on a mis dans les fichier de paramètres; Pour générer le kubeconfig rien de plus simple
Exemple
export CLUSTER_NAME="cluster-dev0"
kubectl get secret vc-${CLUSTER_NAME} -n ${CLUSTER_NAME} --template={{.data.config}} | base64 -d > kubeconfig.yaml
Vous pouvez tester ainsi :
KUBECONFIG=./kubeconfig.yaml k get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-66ffcc6b58-4lhlt 1/1 Running 0 97s
projectsveltos sveltos-agent-manager-9dcdbdbd4-pfvhd 2/2 Running 0 97s