17 min de lecture

Objectifs d'apprentissage

À la fin de ce module, vous serez en mesure de:

  • Mettre à disposition un Managed Kubernetes Cluster et Node Pools avec Terraform et récupérer le kubeconfig directement à partir de l'état
  • Déployer des déploiements, des services, des ConfigMaps et des secrets sur MKS, en tirant des images de Private Container Registry avec `imagePullSecrets`
  • Exposer correctement le trafic d'application étant donné que le type de service `LoadBalancer` sur MKS n'est pas un véritable Load Balancer externe, et mettre en avant le Cluster avec un Application Load Balancer mis à disposition séparément
  • Gérer Node Pools de manière programmatique : mettre à l'échelle les comptes Node, exécuter des mises à niveau de version et isoler les charges de travail sur plusieurs pools
  • Déboguer les charges de travail en cours d'exécution avec `kubectl logs`, `exec` et les événements, en sachant quels signaux la plateforme IONOS affiche ou non

Unité 3.2 : Déploiement et exploitation de Kubernetes

Introduction

Vous avez conteneurisé TaskBoard's API, frontend et worker dans l'Unité 3.1 et les avez poussés vers Private Container Registry avec des étiquettes git-SHA. Maintenant, vous avez besoin d'un endroit pour les exécuter. Dans cette unité, vous allez provisionner un Managed Kubernetes (MKS) Cluster en tant que code, connecter les informations d'identification du registre dans le Cluster afin qu'il puisse extraire vos images, et déployer les trois services en tant que ressources Kubernetes standard.

Le plan de contrôle MKS est géré pour vous, mais deux réalités spécifiques à IONOS influencent chaque déploiement que vous écrivez. Premièrement, un Service de type LoadBalancer sur MKS ne provisionne pas un véritable Load Balancer externe, donc l'entrée de production est gérée par un Application Load Balancer (ALB) provisionné séparément. Deuxièmement, les événements de plan de contrôle que vous pourriez attendre dans un flux de journal centralisé ne sont pas là, ce qui change la façon dont vous déboguez. Vous allez écrire du code pour ces deux réalités, et non les contourner après qu'elles vous ont posé des problèmes en production.

1. Provisionnement du Cluster avec Terraform

Un Managed Kubernetes Cluster sur IONOS est constitué de deux ressources distinctes : le Cluster (le plan de contrôle géré) et une ou plusieurs Node Pools (le calcul des workers que vous payez). Le plan de contrôle lui-même est gratuit ; vous ne payez que pour le calcul sous-jacent du Node Pool et les volumes Block Storage que votre Pods met en place. Créez d'abord le Cluster, puis attachez les Node Pools qui le référencent.

1.1 Ressources du Cluster et du Node Pool

La ressource ionoscloud_k8s_cluster définit le plan de contrôle et sa version Kubernetes. La ressource ionoscloud_k8s_node_pool définit les Worker Nodes à l'intérieur d'un centre de données spécifique.

resource "ionoscloud_k8s_cluster" "taskboard" {
  name        = "taskboard-prod"
  k8s_version = "1.34"

  maintenance_window {
    day_of_the_week = "Sunday"
    time            = "03:00:00Z"
  }
}

resource "ionoscloud_k8s_node_pool" "app" {
  name              = "taskboard-app-pool"
  k8s_cluster_id    = ionoscloud_k8s_cluster.taskboard.id
  datacenter_id     = ionoscloud_datacenter.taskboard.id
  k8s_version       = ionoscloud_k8s_cluster.taskboard.k8s_version
  cpu_family        = "INTEL_SKYLAKE"
  server_type       = "DedicatedCore"
  node_count        = 3
  cores_count       = 4
  ram_size          = 8192
  availability_zone = "AUTO"
  storage_type      = "SSD"
  storage_size      = 100
}

Les versions Kubernetes prises en charge sont 1.34, 1.33, 1.32, et 1.31. Épinglez une version k8s_version explicite plutôt que de suivre la dernière version, car chaque mise à niveau du Node Pool est exécutée pendant la fenêtre de maintenance et peut provoquer des déconnexions. Les Node Pools acceptent à la fois les types de serveurs DedicatedCore et vCPU, vous pouvez donc dimensionner le pool d'applications de TaskBoard sur Dedicated Core pour des performances prévisibles.

1.2 Récupération du kubeconfig à partir de l'état

Vous ne téléchargez pas le kubeconfig à partir d'une interface utilisateur. La ressource Cluster expose la configuration directement, de sorte que Terraform puisse l'écrire sur le disque pour que kubectl et CI puissent la consommer.

output "kubeconfig" {
  value     = ionoscloud_k8s_cluster.taskboard.kube_config
  sensitive = true
}

resource "local_file" "kubeconfig" {
  content         = ionoscloud_k8s_cluster.taskboard.kube_config
  filename        = "${path.module}/kubeconfig.yaml"
  file_permission = "0600"
}
export KUBECONFIG=$(pwd)/kubeconfig.yaml
kubectl get nodes

Le kubeconfig est également récupérable via le API à GET /k8s/{k8sClusterId}/kubeconfig et via ionosctl k8s kubeconfig get --cluster-id <id>. Traitez-le comme un secret : il accorde un accès complet au Cluster. Marquez la sortie Terraform sensitive et ne commitez jamais le fichier généré.

2. Déploiement de charges de travail sur MKS

Une fois que kubectl atteint le Cluster, MKS se comporte comme un Kubernetes standard en amont. Il n'y a pas de dialecte de manifeste spécifique à IONOS. Vous appliquez des déploiements, des services, des ConfigMaps et des Secrets exactement comme vous le feriez sur n'importe quel Cluster conforme. Le CNI est Calico et est fixe, donc la création de stratégies de réseau suit les sémantiques de Calico sans option pour échanger le plugin.

2.1 Déploiement, ConfigMap et Secret

La TaskBoard API nécessite une configuration non secrète et des informations d'identification secrètes. Séparez-les : une ConfigMap pour l'hôte de base de données et les indicateurs de fonctionnalités, un Secret pour le mot de passe de connexion.

apiVersion: v1
kind: ConfigMap
metadata:
  name: taskboard-config
  namespace: taskboard
data:
  DB_HOST: "pg-cluster.taskboard.internal"
  CACHE_TTL: "300"
---
apiVersion: v1
kind: Secret
metadata:
  name: taskboard-db
  namespace: taskboard
type: Opaque
stringData:
  DB_PASSWORD: "REPLACED_FROM_TERRAFORM_OUTPUT"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taskboard-api
  namespace: taskboard
spec:
  replicas: 3
  selector:
    matchLabels:
      app: taskboard-api
  template:
    metadata:
      labels:
        app: taskboard-api
    spec:
      imagePullSecrets:
        - name: registry-cred
      containers:
        - name: api
          image: <registry-name>.cr.de-fra.ionos.com/taskboard-api:<git-sha>
          ports:
            - containerPort: 8080
          envFrom:
            - configMapRef:
                name: taskboard-config
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: taskboard-db
                  key: DB_PASSWORD
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 15

Les données secrètes sont chiffrées à REST dans MKS, mais les Secrets Kubernetes ne sont que codés en base64 dans le manifeste, donc gardez toujours les valeurs sources en dehors de Git et injectez-les à partir de la sortie Terraform au moment du déploiement. La sonde de prêt est importante car les mises à jour progressives attendent qu'elle soit terminée avant de modifier le trafic.

2.2 Téléchargement d'images avec imagePullSecrets

Private Container Registry nécessite une authentification pour chaque téléchargement, et il s'agit d'une authentification basée sur le jeton Docker, sans RBAC et sans répertoires par équipe. Kubernetes nécessite un Secret dockerconfigjson construit à partir d'un jeton d'enregistrement, référencé comme imagePullSecrets dans la spécification du pod ci-dessus.

kubectl create namespace taskboard

kubectl create secret docker-registry registry-cred \
  --namespace taskboard \
  --docker-server=<registry-name>.cr.de-fra.ionos.com \
  --docker-username=<token-name> \
  --docker-password=<registry-token>

Si vous omittez la référence imagePullSecrets, ou si vous limitez le Secret à un espace de noms incorrect, Pods sera bloqué dans ImagePullBackOff. Le Secret est limité à un espace de noms, donc créez-le dans chaque espace de noms qui exécute des images d'enregistrement. Dans CI, vous générez le jeton d'enregistrement une seule fois et le stockez comme secret de pipeline, puis créez le Secret Kubernetes comme étape de déploiement.

3. Exposition du trafic : la réalité du LoadBalancer

Ceci est le fait le plus important spécifique à IONOS dans cette unité. Un Service de type LoadBalancer sur MKS ne provisionne pas un véritable Load Balancer externe. IONOS Cloud réserve une adresse publique statique IP et l'attribue en tant qu'adresse secondaire IP à un worker Node, qui devient le Node d'entrée, et kube-proxy NAT ensuite le trafic vers le pod cible.

3.1 Ce que cela signifie pour vos manifests

Deux conséquences découlent directement. La source client IP est perdue à moins que vous ne définissiez externalTrafficPolicy: Local, et le débit est limité à 2 Gbit/s, le plafond public de ce seul Node d'entrée, car tout le trafic passe par un seul Node. Il n'y a pas d'haute disponibilité automatique entre les nœuds pour cette IP.

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: ingress-nginx
  ports:
    - port: 443
      targetPort: 8443

Pour mettre à l'échelle le trafic au-delà d'un seul Node, vous réservez plusieurs adresses LB IP et les distribuez sur plusieurs nœuds d'entrée en utilisant DNS pour l'équilibrage de charge. Puisque seuls les Node Pools publics prennent en charge le type de service LoadBalancer (les Node Pools privés ne le font pas), gardez votre contrôleur d'entrée orienté internet sur un pool public. N'exposez que le contrôleur d'entrée en tant que LoadBalancer, et non un Service par application.

3.2 Frontage du Cluster avec un ALB provisionné séparément

Pour la production TaskBoard, provisionnez un Application Load Balancer séparément via Terraform. L'ALB n'est pas créé automatiquement à partir d'un manifeste Kubernetes, il n'y a donc aucune annotation de contrôleur d'entrée qui le crée. Vous le provisionnez en tant qu'infrastructure et pointez ses règles de transfert vers les adresses IP Node qui servent le contrôleur d'entrée.

resource "ionoscloud_application_loadbalancer" "taskboard" {
  name          = "taskboard-alb"
  datacenter_id = ionoscloud_datacenter.taskboard.id
  listener_lan  = ionoscloud_lan.public.id
  ips           = [ionoscloud_ipblock.alb.ips[0]]
  target_lan    = ionoscloud_lan.app.id
}

resource "ionoscloud_application_loadbalancer_forwardingrule" "https" {
  datacenter_id               = ionoscloud_datacenter.taskboard.id
  application_loadbalancer_id = ionoscloud_application_loadbalancer.taskboard.id
  name                        = "https-rule"
  protocol                    = "HTTP"
  listener_ip                 = ionoscloud_ipblock.alb.ips[0]
  listener_port               = 443
}

Notez que les règles NSG ne s'appliquent pas à l'ALB. Les Network Security Groups sont liés au niveau du serveur-NIC, et non à l'ALB géré ou à MKS, vous contrôlez donc le trafic d'entrée via les règles de transfert ALB et les règles de sécurité sur les NICs de worker Node, et non en attachant un NSG au Load Balancer.

4. Node Pool Operations

Les Node Pools sont la surface opérationnelle que vous gérez pendant la durée de vie du Cluster : mise à l'échelle pour la charge, mise à jour des versions de Kubernetes, et isolation des charges de travail. Les nœuds eux-mêmes sont immuables, donc les modifications de configuration qui touchent un Node le remplacent plutôt que de le modifier en place.

4.1 Mise à l'échelle et isolation des charges de travail

La mise à l'échelle d'un pool est une modification node_count appliquée via Terraform ou le API. Un pool de Node peut contenir jusqu'à 100 nœuds (20 recommandés), un Cluster peut contenir jusqu'à 500 Node Pools (50 recommandés) et jusqu'à 5000 nœuds au total, et chaque Node peut exécuter jusqu'à 110 Pods avec jusqu'à 20 volumes attachés. Utilisez des pools séparés pour isoler les charges de travail, par exemple un pool de Dedicated Core pour les API sensibles à la latence et un pool de vCPU pour les travailleurs en arrière-plan.

resource "ionoscloud_k8s_node_pool" "worker" {
  name           = "taskboard-worker-pool"
  k8s_cluster_id = ionoscloud_k8s_cluster.taskboard.id
  datacenter_id  = ionoscloud_datacenter.taskboard.id
  k8s_version    = ionoscloud_k8s_cluster.taskboard.k8s_version
  server_type    = "vCPU"
  node_count     = 2
  cores_count    = 2
  ram_size       = 4096
  storage_type   = "SSD"
  storage_size   = 50
}

Planifiez le déploiement du travailleur sur ce pool avec un nodeSelector correspondant au pool, en le gardant hors des nœuds API.

4.2 Mises à jour de version

Augmentez la version k8s_version sur le pool de Node pour effectuer une mise à jour. L'opération aligne les ressources dans le centre de données cible et le pool revient à Active lorsqu'il est terminé, mais il s'exécute pendant la maintenance et peut causer des déconnexions, donc faites-le délibérément. Gardez la version mineure du plan de contrôle et les versions du pool de Node dans les écarts de version pris en charge, et mettez à jour le plan de contrôle avant les Node Pools.

ionosctl k8s nodepool update \
  --cluster-id <cluster-id> \
  --nodepool-id <nodepool-id> \
  --k8s-version 1.34

Les volumes persistants sont provisionnés par le pilote IONOS CSI (provisionneur cloud.ionos.com) pris en charge par Block Storage, donc les Pods avec des PVC peuvent être replanifiés sur des nœuds de remplacement pendant une mise à jour sans perdre de données.

5. Débogage des charges de travail sur MKS

Lorsqu'un déploiement se comporte de manière anormale, travaillez à partir du pod vers l'extérieur. La chaîne standard de triage kubectl s'applique, mais une lacune de plateforme change votre stratégie : les événements de contrôle de Kubernetes ne transitent pas par le Logging Service d'IONOS, donc ne vous attendez pas à trouver des journaux de planificateur ou de API-serveur ici.

5.1 La chaîne de triage

# pod status and restart counts
kubectl get pods -n taskboard -o wide

# why a pod is stuck (events at the bottom)
kubectl describe pod taskboard-api-7d9f -n taskboard

# application stdout/stderr, including the previous crashed container
kubectl logs taskboard-api-7d9f -n taskboard --previous

# cluster-scoped events, newest last
kubectl get events -n taskboard --sort-by=.lastTimestamp

# shell into a running container to test connectivity
kubectl exec -it taskboard-api-7d9f -n taskboard -- sh

kubectl describe est où ImagePullBackOff, les échecs de planification et les échecs de sonde apparaissent, donc lisez-le avant de chercher des journaux. Pour l'observabilité au niveau de l'application, transmettez explicitement vos propres journaux de conteneur au Logging Service, car la plateforme ne capturera pas les signaux de contrôle pour vous.

5.2 Connaître la limite de la plateforme

Si vous supposez que les événements de contrôle sont enregistrés de manière centralisée, vous perdrez des heures à rechercher un flux qui ne les contient jamais. Configurez la transmission de journaux au niveau Cluster (par exemple, un DaemonSet Fluent Bit qui envoie des journaux de pod au Logging Service) pour les journaux d'application que vous contrôlez, et comptez sur kubectl get events pour la visibilité de la couche de contrôle. Associez ceci aux sondes de prêt et de vivacité de la Section 2, de sorte que le Cluster redémarre et réorganise automatiquement les Pods non sains pendant que vous investigatez.

API Référence Quick Card

Points de terminaison clés API pour Managed Kubernetes :

Méthode Point de terminaison Description
GET /k8s Liste tous les clusters Kubernetes
POST /k8s Crée un nouveau Cluster
GET /k8s/{k8sClusterId}/kubeconfig Récupère le kubeconfig Cluster
POST /k8s/{k8sClusterId}/nodepools Crée un pool Node
PUT /k8s/{k8sClusterId}/nodepools/{nodepoolId} Met à l'échelle ou met à niveau un pool Node

URL de base : https://api.ionos.com/cloudapi/v6 Authentification : Authorization: Bearer <token>

Laboratoire de code

Objectif : Mettre en place un MKS Cluster avec Terraform, déployer TaskBoard's API à partir de Private Container Registry, et y accéder via un contrôleur d'entrée.

Prérequis :

  • Compte IONOS Cloud avec jeton API (IONOS_TOKEN exporté)
  • Terraform et le fournisseur ionos-cloud/ionoscloud
  • kubectl et ionosctl installés
  • Un Private Container Registry avec l'image taskboard-api poussée (Unité 3.1)

Étape 1 : Mettre en place le pool Cluster et Node

terraform init
terraform apply -auto-approve

Sortie attendue :

ionoscloud_k8s_cluster.taskboard: Creation complete
ionoscloud_k8s_node_pool.app: Creation complete
Apply complete! Resources: 2 added.

Étape 2 : Écrire le kubeconfig et se connecter

terraform output -raw kubeconfig > kubeconfig.yaml
chmod 600 kubeconfig.yaml
export KUBECONFIG=$(pwd)/kubeconfig.yaml
kubectl get nodes

Sortie attendue :

NAME                  STATUS   ROLES    AGE   VERSION
taskboard-app-pool-1  Ready    <none>   2m    v1.34.x
taskboard-app-pool-2  Ready    <none>   2m    v1.34.x
taskboard-app-pool-3  Ready    <none>   2m    v1.34.x

Étape 3 : Créer un espace de noms et un secret de tirage de registre

kubectl create namespace taskboard
kubectl create secret docker-registry registry-cred \
  --namespace taskboard \
  --docker-server=<registry-name>.cr.de-fra.ionos.com \
  --docker-username=<token-name> \
  --docker-password=<registry-token>

Sortie attendue :

namespace/taskboard created
secret/registry-cred created

Étape 4 : Déployer le API, ConfigMap et Secret

kubectl apply -f taskboard-api.yaml
kubectl rollout status deployment/taskboard-api -n taskboard

Sortie attendue :

deployment "taskboard-api" successfully rolled out

Étape 5 : Vérifier que les Pods sont en train de tirer et d'exécuter

kubectl get pods -n taskboard

Sortie attendue :

NAME                            READY   STATUS    RESTARTS   AGE
taskboard-api-7d9f8c-abcde      1/1     Running   0          40s
taskboard-api-7d9f8c-fghij      1/1     Running   0          40s
taskboard-api-7d9f8c-klmno      1/1     Running   0          40s

Étape 6 : Exposer le contrôleur d'entrée et lire son IP

kubectl apply -f ingress.yaml
kubectl get svc ingress-nginx -n ingress

Sortie attendue :

NAME            TYPE           EXTERNAL-IP      PORT(S)
ingress-nginx   LoadBalancer   <reserved-ip>    443:3xxxx/TCP

Étape 7 : Confirmer que le point de terminaison répond

curl -sk https://<reserved-ip>/healthz

Sortie attendue :

{"status":"ok"}

Liste de validation :

  • [ ] Le pool Cluster et Node atteint Active et les nœuds sont Ready
  • [ ] Les API Pods sont Running, et non ImagePullBackOff
  • [ ] L'entrée IP renvoie une réponse saine à partir du API

Nettoyage :

kubectl delete namespace taskboard
terraform destroy -auto-approve

Erreurs courantes

Erreurs de développement à éviter lors du déploiement sur MKS :

  1. S'attendre à ce que type: LoadBalancer vous donne une véritable adresse Load Balancer externe

    • Problème : Vous exposez chaque Service en tant que LoadBalancer, vous attendez une haute disponibilité entre les nœuds, et les adresses IP sources affichent toutes une seule adresse Node.
    • Pourquoi cela se produit : Sur MKS, un Service LoadBalancer réserve une adresse IP statique et la relie à un worker Node comme adresse IP secondaire ; kube-proxy effectue une traduction d'adresses vers le pod et l'adresse IP source est perdue.
    • Correction : Exposez uniquement le contrôleur d'entrée en tant que LoadBalancer, définissez externalTrafficPolicy: Local pour conserver l'adresse IP source, et placez le trafic de production devant un ALB provisionné séparément :
    spec:
      type: LoadBalancer
      externalTrafficPolicy: Local
    
  2. ImagePullBackOff en raison d'un secret de tirage manquant ou mal étendu

    • Problème : Les Pods ne démarrent jamais ; kubectl describe pod affiche Failed to pull image ... no basic auth credentials.
    • Pourquoi cela se produit : Private Container Registry nécessite une authentification à chaque tirage, et le secret dockerconfigjson est étendu à l'échelle des noms d'espace. Un secret dans default ne fait rien pour les Pods dans taskboard.
    • Correction : Créez le secret d'enregistrement dans l'espace de noms de la charge de travail et référez-le sous imagePullSecrets :
    kubectl create secret docker-registry registry-cred -n taskboard \
      --docker-server=<registry-name>.cr.de-fra.ionos.com \
      --docker-username=<token-name> --docker-password=<registry-token>
    
  3. Recherche dans les Logging Service pour les événements de plan de contrôle

    • Problème : Un pod ne peut pas être planifié et vous passez une heure à rechercher des erreurs de planificateur dans les journaux centralisés qui ne sont pas là.
    • Pourquoi cela se produit : Les événements de plan de contrôle Kubernetes ne passent pas par les Logging Service d'IONOS.
    • Correction : Lisez les signaux de plan de contrôle avec kubectl, et transmettez uniquement les journaux d'application que vous contrôlez :
    kubectl get events -n taskboard --sort-by=.lastTimestamp
    kubectl describe pod <pod> -n taskboard
    

Résumé

Vous pouvez maintenant provisionner un Managed Kubernetes Cluster et Node Pools entièrement en code, récupérer le kubeconfig à partir de l'état de Terraform, et déployer des services conteneurisés qui tirent de Private Container Registry. Vous connaissez également les deux réalités IONOS qui séparent un déploiement MKS fonctionnel d'un déploiement cassé : le type de service LoadBalancer n'est pas un véritable Load Balancer externe, donc le trafic de production est géré par un ALB provisionné séparément, et les événements de plan de contrôle ne sont pas enregistrés centralement, donc la débogage se fait via kubectl plus votre propre transfert de journaux.

Avec TaskBoard's API, frontend et worker s'exécutant sur MKS derrière un ALB, vous avez une cible déployable. La prochaine unité automatisera le chemin d'un commit Git à ce Cluster en cours d'exécution.

Points clés :

  • Le plan de contrôle MKS est gratuit ; vous payez uniquement pour le calcul du Node Pool et les volumes Block Storage
  • Récupérez le kubeconfig à partir de l'attribut kube_config de la ressource ionoscloud_k8s_cluster, marqué comme sensible
  • Un service type: LoadBalancer associe une adresse IP statique à un worker Node et n'est pas un véritable Load Balancer externe ; mettez en production avec un ALB provisionné séparément
  • Tirez des images privées avec un secret docker-registry référencé comme imagePullSecrets
  • Les événements de plan de contrôle Kubernetes ne parviennent pas au Logging Service d'IONOS ; déboguez avec kubectl et transférez vous-même les journaux d'application

Terminologie importante :

  • Node Pool : Un groupe de Worker Nodes d'un type de serveur dans un centre de données, mis à l'échelle et amélioré en tant qu'unité (jusqu'à 100 nœuds, 20 recommandés)
  • kubeconfig : Le fichier d'informations d'identification et de connexion pour kubectl, exposé par la ressource Cluster et traité comme un secret
  • imagePullSecrets : Une référence de spécification de pod à un secret dockerconfigjson qui authentifie les tirages de Private Container Registry
  • externalTrafficPolicy : Local : Un paramètre de service qui préserve l'adresse IP source du client en gardant le trafic sur le nœud récepteur Node au lieu de le réacheminer
  • Provisionneur CSI (cloud.ionos.com) : Le pilote de stockage IONOS qui prend en charge les revendications de volume persistant avec Block Storage afin que les données survivent au remplacement du Node

Prochaines étapes

Continuer l'apprentissage : Unité 3.3 : CI/CD Pipelines pour IONOS

Sujets connexes :