Faire évoluer l'infrastructure avec Kubernetes et RKE2 : une analyse approfondie de la production
Guide de l'ingénieur de production sur l'architecture de cluster RKE2, la mise à l'échelle automatique horizontale et verticale, la haute disponibilité, la gouvernance des ressources et l'observabilité Prometheus/Grafana.
Exécuter un cluster Kubernetes en production est une chose. En exécuter un qui puisse absorber les pics de trafic imprévisibles, survivre aux pannes du plan de contrôle, imposer l’isolement des locataires et donner à votre équipe opérationnelle une visibilité claire sur chaque couche du système – c’est un défi totalement différent. RKE2, la distribution Kubernetes de nouvelle génération de Rancher, est spécialement conçue pour les environnements où ces exigences ne sont pas négociables.
Cet article couvre le cycle de vie complet d'un déploiement RKE2 de niveau production : architecture de cluster initiale, mise à l'échelle automatique au niveau du pod et du nœud, plans de contrôle à haute disponibilité, gouvernance des ressources et observabilité avec Prometheus et Grafana.
Pourquoi RKE2 ?
RKE2 se distingue du Kubernetes en amont et de son prédécesseur RKE1 dans trois domaines clés. Premièrement, il est livré avec une configuration renforcée par CIS Kubernetes Benchmark : les contrôleurs d'admission, la journalisation d'audit, la sécurité des pods et les paramètres TLS sont préconfigurés pour réussir une analyse CIS de niveau 1 sans intervention manuelle. Deuxièmement, il est conforme à la norme FIPS 140-2, ce qui le rend adapté aux déploiements gouvernementaux et industriels réglementés. Troisièmement, il intègre directement containersd et est livré avec son propre CNI (Canal ou Cilium selon votre choix de configuration), réduisant ainsi la surface des dépendances externes que vous devez gérer.
LeRKE2 est également compatible avec l'entrefer. Le bundle d'installation comprend toutes les images de conteneur requises, ce qui est extrêmement important dans les déploiements sur site et en périphérie où l'accès Internet à partir des nœuds de cluster est restreint, voire impossible.
Architecture de clusterUn cluster RKE2 de production est divisé en nœuds de serveur (qui exécutent le plan de contrôle et etcd) et en nœuds d'agent (qui exécutent les charges de travail). La topologie recommandée pour la haute disponibilité est composée de trois ou cinq nœuds de serveur et d'un nombre variable de nœuds d'agent organisés en pools de nœuds par classe de charge de travail.
# /etc/rancher/rke2/config.yaml (server node)
token: <shared-cluster-token>
tls-san:
- 10.0.0.10 # VIP or load balancer address
- k8s.internal.example.com
cni: cilium
cluster-cidr: 10.42.0.0/16
service-cidr: 10.43.0.0/16
etcd-expose-metrics: true
kube-apiserver-arg:
- "audit-log-path=/var/log/kubernetes/audit.log"
- "audit-log-maxage=30"
- "audit-log-maxsize=100"# /etc/rancher/rke2/config.yaml (agent node)
server: https://10.0.0.10:9345
token: <shared-cluster-token>
node-label:
- "workload-class=general"
- "topology.kubernetes.io/zone=eu-west-1a"Installez le serveur sur votre premier nœud de plan de contrôle, puis rejoignez les nœuds de serveur restants et tous les nœuds d'agent en utilisant le même jeton et la même adresse VIP. RKE2 élit automatiquement les dirigeants d'etcd et gère le quorum.
# Install and start RKE2 server
curl -sfL https://get.rke2.io | INSTALL_RKE2_TYPE=server sh -
systemctl enable --now rke2-server.service
# Retrieve the node token for joining additional nodes
cat /var/lib/rancher/rke2/server/node-token
# Install and start RKE2 agent (on worker nodes)
curl -sfL https://get.rke2.io | INSTALL_RKE2_TYPE=agent sh -
systemctl enable --now rke2-agent.servicePools de nœudset placement des charges de travail
Toutes les charges de travail n'ont pas le même profil de ressources. Les services Web sans état ont des exigences différentes de celles des tâches d'inférence GPU, des charges de travail d'analyse gourmandes en mémoire ou des bases de données sensibles à la latence. L'organisation des nœuds d'agent en pools avec des étiquettes et des teintes distinctes permet à Kubernetes de planifier chaque classe de charge de travail sur du matériel de taille appropriée.
# Label a node pool for memory-intensive workloads
kubectl label nodes worker-mem-{1..4} workload-class=memory-optimised
kubectl taint nodes worker-mem-{1..4} workload-class=memory-optimised:NoSchedule
# Label a separate pool for general compute
kubectl label nodes worker-gen-{1..8} workload-class=general# Deployment targeting the memory-optimised pool
apiVersion: apps/v1
kind: Deployment
metadata:
name: analytics-engine
spec:
template:
spec:
nodeSelector:
workload-class: memory-optimised
tolerations:
- key: workload-class
operator: Equal
value: memory-optimised
effect: NoSchedule
containers:
- name: analytics
image: registry.internal/analytics:v2.3.1
resources:
requests:
memory: "8Gi"
cpu: "2"
limits:
memory: "16Gi"
cpu: "4"Autoscaler horizontal de podsLe horizontal Pod Autoscaler (HPA) ajuste le nombre de réplicas d'un déploiement ou d'un StatefulSet en fonction des métriques observées. L'utilisation de CPU est le déclencheur classique, mais les configurations HPA modernes peuvent également évoluer sur des métriques personnalisées exposées par votre application ou sur des métriques externes provenant de sources telles que la profondeur de la file d'attente des messages.
Tout d'abord, assurez-vous que Metrics Server est en cours d'exécution : RKE2 ne le regroupe pas par défaut.
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yamlapiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 65
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # wait 5 minutes before scaling down
policies:
- type: Percent
value: 25
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 30Le blocbehaviorest essentiel à la stabilité. Sans une fenêtre de stabilisation réduite, une brève baisse du trafic supprimera les pods prématurément, vous laissant sous-provisionné au retour de la charge. La politique asymétrique (augmentation agressive et réduction prudente) constitue la solution par défaut pour la plupart des charges de travail de production.
Le Vertical Pod Autoscaler (VPA) dimensionne correctement le CPU et les demandes de mémoire sur des pods individuels en fonction de l'utilisation observée. Il résout un problème courant : les développeurs définissent les demandes de ressources initiales sur la base de conjectures, et ces valeurs ne sont jamais mises à jour, ce qui entraîne soit un surprovisionnement inutile, soit des pods OOMKilled sous charge.
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: worker-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: background-worker
updatePolicy:
updateMode: "Auto" # or "Off" to only view recommendations
resourcePolicy:
containerPolicies:
- containerName: worker
minAllowed:
cpu: 100m
memory: 256Mi
maxAllowed:
cpu: 4
memory: 8Gi
controlledResources: ["cpu", "memory"]Notez que le VPA en modeAutoexpulsera et redémarrera les pods pour appliquer de nouvelles valeurs de ressources. Pour les services pour lesquels les demandes en cours ne peuvent pas être interrompues, exécutez VPA en modeOffpour générer des recommandations que vous appliquez manuellement ou via un workflow GitOps pendant les fenêtres de maintenance.
Important :HPA et VPA ne doivent pas gérer simultanément la même ressource (CPU ou mémoire) sur le même déploiement. Utilisez HPA pour la mise à l'échelle horizontale pilotée par CPU et VPA en modeOffpour un dimensionnement correct de la mémoire, ou utilisezKEDApour une mise à l'échelle pilotée par les événements lorsqu'un contrôle précis est nécessaire.
Pod fonctionnent dans la limite de la capacité du nœud existant. Lorsque cette capacité est épuisée (les pods sont bloqués dansPendingcar aucun nœud ne dispose de ressources suffisantes), vous avez besoin du Cluster Autoscaler pour provisionner de nouveaux nœuds. À l’inverse, lorsque les nœuds sont considérablement sous-utilisés, le Cluster Autoscaler peut les drainer et les mettre hors service pour réduire les coûts d’infrastructure.
Sur les déploiements nus ou sur site, le Cluster Autoscaler s'intègre à la couche de provisionnement de votre infrastructure. Pour les déploiements cloud, des fournisseurs tels que AWS, GCP et Azure proposent des intégrations de groupes de nœuds natifs. L'exemple suivant montre la configuration principale d'un groupe AWS Auto Scaling.
apiVersion: apps/v1
kind: Deployment
metadata:
name: cluster-autoscaler
namespace: kube-system
spec:
template:
spec:
containers:
- name: cluster-autoscaler
image: registry.k8s.io/autoscaling/cluster-autoscaler:v1.29.0
command:
- ./cluster-autoscaler
- --cloud-provider=aws
- --nodes=2:10:k8s-general-worker-asg
- --nodes=1:4:k8s-memory-worker-asg
- --scale-down-delay-after-add=10m
- --scale-down-unneeded-time=10m
- --scale-down-utilization-threshold=0.5
- --skip-nodes-with-local-storage=false
- --expander=least-waste
env:
- name: AWS_REGION
value: eu-west-1L'option--expander=least-wasteindique à l'autoscaler de préférer le groupe de nœuds qui aurait la plus petite quantité de ressources inutilisées après avoir hébergé le pod en attente, ce qui minimise les coûts. Les extensions alternatives incluentrandom,most-podsetpriority.
Un plan de contrôle à trois nœuds avec etcd intégré constitue la topologie HA minimale viable. etcd nécessite un quorum — une majorité de membres doivent être sains pour que le cluster accepte les écritures. Avec trois membres, vous pouvez tolérer un échec ; avec cinq membres, vous pouvez en tolérer deux.
Les nœuds du plan de contrôle doivent se trouver derrière un équilibreur de charge. Pour les déploiements cloud, un équilibreur de charge TCP ciblant les ports 6443 (kube-apiserver) et 9345 (enregistrement RKE2) fonctionne bien. Les déploiements sur site utilisent généralement keepalived avec une adresse IP virtuelle.
# keepalived.conf on control-plane nodes
vrrp_instance VI_1 {
state MASTER # BACKUP on the other two nodes
interface eth0
virtual_router_id 51
priority 100 # 90 and 80 on the other two nodes
advert_int 1
authentication {
auth_type PASS
auth_pass securepassword
}
virtual_ipaddress {
10.0.0.10/24 # VIP used in tls-san and agent server address
}
}Vérifiez que etcd est sain après toute opération du plan de contrôle. RKE2 regroupeetcdctlet/var/lib/rancher/rke2/bin/etcdctl.
ETCDCTL_API=3 /var/lib/rancher/rke2/bin/etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt \
--cert=/var/lib/rancher/rke2/server/tls/etcd/client.crt \
--key=/var/lib/rancher/rke2/server/tls/etcd/client.key \
endpoint health --clusterQuotas de ressources et plages limites
Dans les clusters multi-tenants — où différentes équipes ou applications partagent la même infrastructure physique — ResourceQuotas et LimitRanges sont des garde-fous essentiels. ResourceQuotas fixe des limites strictes à la consommation totale des ressources au sein d'un espace de noms. LimitRanges définit les valeurs par défaut et maximales pour les conteneurs individuels, empêchant ainsi un déploiement mal configuré de demander des ressources illimitées.
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-alpha-quota
namespace: team-alpha
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.cpu: "40"
limits.memory: 80Gi
count/deployments.apps: "20"
count/services: "15"
persistentvolumeclaims: "10"
requests.storage: 500GiapiVersion: v1
kind: LimitRange
metadata:
name: team-alpha-limits
namespace: team-alpha
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 100m
memory: 128Mi
max:
cpu: "8"
memory: 16Gi
- type: PersistentVolumeClaim
max:
storage: 100GiL'application de LimitRanges garantit que les développeurs qui oublient de spécifier les demandes de ressources obtiennent toujours des valeurs par défaut raisonnables plutôt que de demander zéro CPU, ce qui obligerait le planificateur à placer le pod n'importe où et potentiellement affamer d'autres charges de travail sur le même nœud.
Surveillanceavec Prometheus et Grafana
L'observabilitédans un cluster Kubernetes repose sur trois piliers : les métriques, les journaux et les traces. Prometheus gère la collecte de métriques ; Grafana gère la visualisation. La cartekube-prometheus-stackHelm déploie l'intégralité de la pile (opérateur Prometheus, Alert Manager, Grafana, exportateurs de nœuds et un ensemble complet de tableaux de bord prédéfinis) en une seule commande.
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set prometheus.prometheusSpec.retention=30d \
--set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.storageClassName=longhorn \
--set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=100Gi \
--set grafana.adminPassword=<secure-password> \
--set alertmanager.alertmanagerSpec.storage.volumeClaimTemplate.spec.resources.requests.storage=10GiRKE2 expose les métriques etcd lorsqueetcd-expose-metrics: trueest défini dans la configuration du serveur. Ajoutez un ServiceMonitor pour que Prometheus les supprime.
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: rke2-etcd
namespace: monitoring
labels:
release: kube-prometheus-stack
spec:
namespaceSelector:
matchNames: [kube-system]
selector:
matchLabels:
app.kubernetes.io/name: rke2-etcd
endpoints:
- port: metrics
scheme: https
tlsConfig:
caFile: /etc/prometheus/secrets/etcd-client-cert/ca.crt
certFile: /etc/prometheus/secrets/etcd-client-cert/client.crt
keyFile: /etc/prometheus/secrets/etcd-client-cert/client.keyRègles d'alerte essentielles
Les tableaux de bord prédéfinisconstituent un point de départ, mais les règles d'alerte personnalisées adaptées à votre environnement permettent aux ingénieurs de garde d'agir avant que les utilisateurs ne remarquent un problème.
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: workload-alerts
namespace: monitoring
labels:
release: kube-prometheus-stack
spec:
groups:
- name: pod-health
rules:
- alert: PodCrashLooping
expr: rate(kube_pod_container_status_restarts_total[10m]) > 0.5
for: 5m
labels:
severity: critical
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is crash-looping"
- alert: NodeMemoryPressure
expr: |
(node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) < 0.1
for: 2m
labels:
severity: warning
annotations:
summary: "Node {{ $labels.node }} memory below 10%"
- alert: HPAMaxedOut
expr: |
kube_horizontalpodautoscaler_status_current_replicas
== kube_horizontalpodautoscaler_spec_max_replicas
for: 15m
labels:
severity: warning
annotations:
summary: "HPA {{ $labels.namespace }}/{{ $labels.horizontalpodautoscaler }} at maximum replicas"L'alerteHPAMaxedOutest particulièrement utile en pratique. Lorsqu'un HPA est bloqué à son maximum pendant une période prolongée, cela signifie que le trafic a dépassé votre plafond actuel. Vous devez soit augmenter le maximum, soit ajouter de la capacité au pool de nœuds – et vous voulez en savoir plus avant le prochain pic, pas pendant celui-ci.
Meilleures pratiques de production
Budgets de perturbation des podsUn PodDisruptionBudget (PDB) limite le nombre de pods d'un déploiement qui peuvent être simultanément indisponibles lors de perturbations volontaires telles que les drains de nœuds. Sans PDB, la vidange d’un nœud pour la maintenance peut mettre un déploiement entier hors ligne.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-server-pdb
namespace: production
spec:
minAvailable: 2 # or use maxUnavailable: 1
selector:
matchLabels:
app: api-serverContraintes de répartition de la topologie
Par défaut, le planificateur répartit les réplicas entre les nœuds à l'aide d'un algorithme de meilleur effort. Les contraintes de répartition de la topologie vous offrent des garanties fermes que les réplicas sont répartis sur les zones de disponibilité ou les racks.
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api-serverStratégie de mise à niveau duLeRKE2 prend en charge les mises à niveau progressives via le contrôleur de mise à niveau du système. Vous définissez un plan qui cible les nœuds de serveur ou d'agent et spécifie la version cible ; le contrôleur draine, met à niveau et déconnecte les nœuds de manière séquentielle.
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
name: rke2-server-upgrade
namespace: system-upgrade
spec:
concurrency: 1
cordon: true
nodeSelector:
matchExpressions:
- { key: node-role.kubernetes.io/control-plane, operator: In, values: ["true"] }
serviceAccountName: system-upgrade
upgrade:
image: rancher/rke2-upgrade
version: v1.29.4+rke2r1etcd Sauvegarde et restauration
LeRKE2 peut prendre automatiquement des instantanés etcd programmés. Assurez-vous qu'ils sont écrits sur un stockage durable en dehors du cluster (un compartiment S3 ou un montage NFS distant) plutôt que sur un disque local sur les nœuds du plan de contrôle.
# /etc/rancher/rke2/config.yaml additions for automated snapshots
etcd-snapshot-schedule-cron: "0 */6 * * *" # every 6 hours
etcd-snapshot-retention: 10
etcd-snapshot-dir: /mnt/nfs/etcd-snapshots
# Manual snapshot
rke2 etcd-snapshot save --name pre-upgrade-$(date +%Y%m%d)
# Restore from snapshot (run on a single server node with cluster stopped)
rke2 server --cluster-reset --cluster-reset-restore-path=/path/to/snapshot.dbConclusion
La mise à l'échelle de l'infrastructureavec RKE2 n'est pas un simple changement de configuration : il s'agit d'un système de fonctionnalités imbriquées qui doivent être conçues et exploitées ensemble. La mise à l'échelle automatique des pods horizontaux gère les rafales de trafic de courte durée au niveau de la charge de travail. La mise à l'échelle automatique des pods verticaux maintient les demandes de ressources honnêtes au fil du temps. Le Cluster Autoscaler garantit que la capacité du nœud sous-jacent suit la demande globale de vos autoscalers de pods. Les pools de nœuds et les contraintes topologiques garantissent que les charges de travail atterrissent sur le bon matériel. Les quotas de ressources et les plages limites protègent les locataires les uns des autres. Les contraintes de PodDisruptionBudget et de répartition de la topologie renforcent la disponibilité. Et Prometheus avec Grafana donne à votre équipe la visibilité nécessaire pour détecter les dégradations avant qu'elles ne se transforment en panne.
LeRKE2 mérite sa place dans la production précisément parce qu'il expédie une partie substantielle de cette pile pré-durcie et pré-intégrée. Votre responsabilité est de comprendre les boutons, de les adapter aux caractéristiques de votre charge de travail et de développer la discipline opérationnelle (runbooks, routage des alertes, cadence de mise à niveau, validation des sauvegardes) qui transforme un cluster bien configuré en une plate-forme véritablement fiable.