13 KiB
13 KiB
05 — Création du cluster k3s avec hetzner-k3s
C'est le fichier central. Suivez chaque étape dans l'ordre.
Qu'est-ce que hetzner-k3s ?
hetzner-k3s est un outil CLI qui automatise la création d'un cluster k3s sur Hetzner Cloud. En une commande, il :
- Crée les serveurs (control plane + workers)
- Configure le réseau privé
- Installe k3s sur tous les nœuds
- Installe le Hetzner Cloud Controller Manager (provisionne LB + volumes depuis K8s)
- Installe le Hetzner CSI Driver (PersistentVolumes sur Hetzner Volumes)
- Configure le Cluster Autoscaler (scale automatique des workers)
- Installe le System Upgrade Controller (upgrades k3s automatiques)
- Configure kubectl localement
Fichier de configuration du cluster
Créez le fichier cluster.yaml à la racine du projet ou dans un dossier sécurisé (jamais dans le repo Git) :
mkdir -p ~/.xpeditis
cat > ~/.xpeditis/cluster.yaml << 'EOF'
# ============================================================
# Xpeditis Production Cluster — hetzner-k3s configuration
# ============================================================
# Token API Hetzner (garder secret)
hetzner_token: "<VOTRE_HCLOUD_TOKEN>"
# Nom du cluster
cluster_name: xpeditis-prod
# Chemin du kubeconfig qui sera généré
kubeconfig_path: "~/.kube/kubeconfig-xpeditis-prod"
# Version k3s
# Vérifier la dernière stable sur https://github.com/k3s-io/k3s/releases
k3s_version: v1.30.4+k3s1
# Clés SSH
public_ssh_key_path: "~/.ssh/xpeditis_hetzner.pub"
private_ssh_key_path: "~/.ssh/xpeditis_hetzner"
use_ssh_agent: false
ssh_port: 22
# Réseaux autorisés pour SSH et API Kubernetes
# Remplacer par votre IP fixe pour plus de sécurité
ssh_allowed_networks:
- "<VOTRE_IP>/32"
api_allowed_networks:
- "<VOTRE_IP>/32"
# Réseau privé Hetzner
# Créé dans le doc 03-hetzner-setup.md
existing_network: "xpeditis-network"
private_network_subnet: 10.0.0.0/16
# CIDRs Kubernetes (ne pas changer sauf conflit)
cluster_cidr: 10.244.0.0/16
service_cidr: 10.96.0.0/16
cluster_dns: 10.96.0.10
# Image OS
image: ubuntu-24.04
snapshot_os: ubuntu
# Datacenter (même région que l'Object Storage)
location: fsn1
# k3s options
disable_flannel: false # Flannel CNI (par défaut dans k3s)
schedule_workloads_on_masters: false # Masters dédiés au control plane
# Packages additionnels installés sur chaque nœud
additional_packages:
- curl
- jq
- htop
- fail2ban # Protection brute force SSH
# Commandes post-création sur chaque nœud
post_create_commands:
- apt-get update -qq
- apt-get install -y -qq fail2ban
- systemctl enable fail2ban
- systemctl start fail2ban
- |
cat >> /etc/fail2ban/jail.local << 'FAIL2BAN'
[sshd]
enabled = true
maxretry = 3
bantime = 3600
FAIL2BAN
- systemctl restart fail2ban
# Helm charts installés automatiquement
cloud_controller_manager_manifest_url: "https://github.com/hetznercloud/hcloud-cloud-controller-manager/releases/download/v1.21.0/ccm-networks.yaml"
csi_driver_manifest_url: "https://raw.githubusercontent.com/hetznercloud/csi-driver/v2.8.0/deploy/kubernetes/hcloud-csi.yml"
# System Upgrade Controller (upgrades k3s automatiques)
system_upgrade_controller_install: true
system_upgrade_controller_manifest_url: "https://github.com/rancher/system-upgrade-controller/releases/download/v0.13.4/system-upgrade-controller.yaml"
# Cluster Autoscaler
cluster_autoscaler_install: true
cluster_autoscaler_version: "9.36.0"
cluster_autoscaler_image: "registry.k8s.io/autoscaling/cluster-autoscaler"
cluster_autoscaler_cmdline_args:
- --scan-interval=10s
- --scale-down-delay-after-add=5m
- --scale-down-unneeded-time=5m
- --max-nodes-total=12
# Metrics Server (pour HPA)
metrics_server_manifest_url: "https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml"
# kube-apiserver extra args (sécurité)
kube_api_server_args:
- "--audit-log-path=/var/log/kubernetes/audit.log"
- "--audit-log-maxage=30"
- "--audit-log-maxbackup=3"
- "--audit-log-maxsize=100"
# kubelet extra args
kubelet_args:
- "--max-pods=110"
- "--system-reserved=cpu=200m,memory=200Mi"
- "--kube-reserved=cpu=200m,memory=200Mi"
# ============================================================
# CONTROL PLANE
# ============================================================
masters:
instance_type: cx22 # 2 vCPU, 4 GB
instance_count: 1 # Passer à 3 pour HA (10 000 users)
location: fsn1
image: ~ # Utilise l'image globale
# ============================================================
# WORKER NODE POOLS
# ============================================================
worker_node_pools:
- name: app-workers
instance_type: cx32 # 4 vCPU, 8 GB (MVP)
instance_count: 2 # Min pods
location: fsn1
image: ~
additional_packages: ~
post_create_commands: ~
taints: []
labels:
- "xpeditis.io/node-role=app"
autoscaling:
enabled: true
min_instances: 2 # Minimum pour HA
max_instances: 6 # Max pour limiter les coûts
EOF
Pour le palier 1 000 users, changez
cx32→cx42etmax_instances: 8Pour le palier 10 000 users, changezcx42→cx52,instance_count: 4,max_instances: 12, etmasters.instance_count: 3
Création du cluster
# Vérifier la configuration
hetzner-k3s validate --config ~/.xpeditis/cluster.yaml
# Créer le cluster (prend 5-10 minutes)
hetzner-k3s create --config ~/.xpeditis/cluster.yaml
# Output attendu :
# Creating infrastructure...
# Creating network...
# Creating SSH key...
# Creating firewall...
# Creating placement group...
# Creating load balancer...
# Creating masters...
# Waiting for masters to be ready...
# Creating worker pools...
# Waiting for workers to be ready...
# Installing k3s on masters...
# Installing k3s on workers...
# Installing Hetzner CCM...
# Installing Hetzner CSI...
# Installing Cluster Autoscaler...
# Installing System Upgrade Controller...
# Installing Metrics Server...
# Configuring kubeconfig...
# ✅ Cluster xpeditis-prod created successfully!
Configuration de kubectl
# Définir le KUBECONFIG
export KUBECONFIG=~/.kube/kubeconfig-xpeditis-prod
# Ajouter au .zshrc ou .bashrc pour persistance
echo 'export KUBECONFIG=~/.kube/kubeconfig-xpeditis-prod' >> ~/.zshrc
# Vérifier la connexion au cluster
kubectl cluster-info
# Kubernetes control plane is running at https://<IP>:6443
# CoreDNS is running at https://<IP>:6443/api/v1/...
# Lister les nœuds
kubectl get nodes -o wide
# NAME STATUS ROLES AGE VERSION
# xpeditis-prod-cx22-master-1 Ready control-plane,master 5m v1.30.4+k3s1
# xpeditis-prod-cx32-worker-1 Ready <none> 4m v1.30.4+k3s1
# xpeditis-prod-cx32-worker-2 Ready <none> 4m v1.30.4+k3s1
# Vérifier tous les pods système
kubectl get pods --all-namespaces
# Tous les pods doivent être Running
Vérification du Hetzner Cloud Controller Manager
Le CCM permet à Kubernetes de provisionner des ressources Hetzner (LB, volumes) :
# Vérifier que le CCM tourne
kubectl get pods -n kube-system | grep hcloud
# Vérifier que les nœuds ont le label de région
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.labels.topology\.kubernetes\.io/zone}{"\n"}{end}'
# xpeditis-prod-cx22-master-1 fsn1
# xpeditis-prod-cx32-worker-1 fsn1
# xpeditis-prod-cx32-worker-2 fsn1
Vérification du Hetzner CSI Driver
# Le CSI driver permet de créer des PersistentVolumes sur Hetzner
kubectl get pods -n kube-system | grep hcloud-csi
# Vérifier les StorageClasses disponibles
kubectl get storageclass
# NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE
# hcloud-volumes (default) csi.hetzner.cloud Delete WaitForFirstConsumer
Configuration de Traefik (Ingress Controller)
k3s installe Traefik par défaut. Nous devons le configurer pour :
- Redirection HTTP → HTTPS
- Support WebSocket (Socket.IO)
- Sticky sessions pour le backend
# Créer le fichier de configuration Traefik
cat > /tmp/traefik-config.yaml << 'EOF'
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
# Logs
logs:
general:
level: INFO
access:
enabled: true
# Ports
ports:
web:
port: 8000
redirectTo:
port: websecure # Force HTTPS
websecure:
port: 8443
tls:
enabled: true
# Sticky sessions pour WebSocket
service:
spec:
externalTrafficPolicy: Local
# Annotations pour le Load Balancer Hetzner
service:
annotations:
load-balancer.hetzner.cloud/name: "xpeditis-lb"
load-balancer.hetzner.cloud/location: "fsn1"
load-balancer.hetzner.cloud/health-check-interval: "15s"
load-balancer.hetzner.cloud/health-check-timeout: "10s"
load-balancer.hetzner.cloud/health-check-retries: "3"
load-balancer.hetzner.cloud/use-private-ip: "true"
# Ressources
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
# Replicas (1 suffit pour MVP)
deployment:
replicas: 1
# Providers supplémentaires
providers:
kubernetesCRD:
enabled: true
allowCrossNamespace: true
kubernetesIngress:
enabled: true
publishedService:
enabled: true
EOF
kubectl apply -f /tmp/traefik-config.yaml
# Attendre que Traefik soit mis à jour
kubectl rollout status deployment/traefik -n kube-system --timeout=120s
Installation de cert-manager
cert-manager gère les certificats TLS automatiquement via Let's Encrypt :
# Ajouter le repo Helm cert-manager
helm repo add jetstack https://charts.jetstack.io
helm repo update
# Installer cert-manager
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.15.3 \
--set installCRDs=true \
--set resources.requests.cpu=50m \
--set resources.requests.memory=64Mi \
--set webhook.resources.requests.cpu=50m \
--set webhook.resources.requests.memory=32Mi
# Attendre que cert-manager soit prêt
kubectl wait --for=condition=Ready pod \
--selector=app.kubernetes.io/instance=cert-manager \
-n cert-manager \
--timeout=120s
# Vérification
kubectl get pods -n cert-manager
# NAME READY STATUS
# cert-manager-7f9f87595d-xxx 1/1 Running
# cert-manager-cainjector-54db9f97d8-xxx 1/1 Running
# cert-manager-webhook-8698c586b7-xxx 1/1 Running
ClusterIssuers Let's Encrypt
# Créer les issuers (staging pour test, prod pour production)
cat > /tmp/cluster-issuers.yaml << 'EOF'
---
# STAGING — Pour tester sans risquer le rate limit
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: admin@xpeditis.com # ← Remplacer
privateKeySecretRef:
name: letsencrypt-staging-key
solvers:
- http01:
ingress:
class: traefik
---
# PRODUCTION — Certificats réels (max 5 renouvellements/semaine)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@xpeditis.com # ← Remplacer
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: traefik
EOF
kubectl apply -f /tmp/cluster-issuers.yaml
# Vérifier les issuers
kubectl get clusterissuers
# NAME READY AGE
# letsencrypt-staging True 30s
# letsencrypt-prod True 30s
Récapitulatif : état du cluster après cette étape
# Vue d'ensemble complète
kubectl get nodes
kubectl get pods --all-namespaces --field-selector=status.phase!=Running
# Doit afficher :
# kube-system traefik-* Running
# kube-system hcloud-cloud-controller Running
# kube-system hcloud-csi-* Running
# kube-system coredns-* Running
# kube-system metrics-server-* Running
# cert-manager cert-manager-* Running
echo "✅ Cluster prêt pour le déploiement de l'application"
Opérations sur le cluster
Ajouter un nœud worker manuellement
# Modifier le fichier cluster.yaml
# Changer instance_count de 2 → 3 dans worker_node_pools
hetzner-k3s apply --config ~/.xpeditis/cluster.yaml
Supprimer le cluster (⚠️ irréversible)
hetzner-k3s delete --config ~/.xpeditis/cluster.yaml
Lister les composants Hetzner créés
hcloud server list
hcloud load-balancer list
hcloud network list
hcloud firewall list
hcloud placement-group list