Devops
Difficulte: Advanced
21 min de lecture

Kubernetes Avancé : Operators, Helm Security et GitOps avec ArgoCD

Maîtrisez Kubernetes avancé : Operators et CRD, sécurité Helm, GitOps avec ArgoCD, hardening cluster, Prometheus Operator. Guide complet DevSecOps 2026.

Retour aux tutoriels
Prérequis
Ce tutoriel suppose une connaissance solide de Kubernetes (Pods, Deployments, Services, RBAC, namespaces). Si vous débutez, consultez d'abord le guide d'introduction à Kubernetes et le guide Docker avancé. Un cluster fonctionnel (minikube, kind ou production) est nécessaire pour les exemples.

Pourquoi Kubernetes avancé ? Au-delà des Deployments basiques

Kubernetes réduit les Deployments, Services et ConfigMaps à leur minimum. La plupart des équipes s'y arrêtent, et s'arrêter là crée des angles morts : des opérations répétitives faites manuellement, des secrets exposés dans Git, des déploiements non reproductibles, des clusters sans politique de sécurité cohérente, et un monitoring qui disparaît dès qu'un service est redéployé.

Les patterns avancés de Kubernetes adressent chacun de ces problèmes de manière systématique :

  • Operators : automatiser les opérations stateful complexes (bases de données, messaging) via le pattern Controller
  • Helm Security : gérer les secrets sans les exposer en Git, signer les charts, appliquer le RBAC
  • GitOps avec ArgoCD : Git comme source unique de vérité, déploiements déclaratifs et auto-réconciliés
  • Hardening cluster : PodSecurityAdmission, NetworkPolicies, OPA/Gatekeeper pour zéro-trust
  • Prometheus Operator : monitoring auto-découvrant, alerting déclaratif par namespace

Ce guide est conçu pour être appliqué séquentiellement sur un cluster réel. Chaque section est indépendante et peut être adoptée progressivement.

1. Kubernetes Operators : automatiser l'opérationnel

Le pattern Controller et les CRD

Kubernetes repose sur la boucle de réconciliation : un contrôleur observe l'état courant du cluster, le compare à l'état désiré défini dans les objets API, et agit pour les faire converger. Les Custom Resource Definitions (CRD) étendent l'API Kubernetes avec de nouveaux types d'objets. Un Operator combine ces deux concepts : il définit un CRD pour son domaine applicatif et un contrôleur qui sait réconcilier ces ressources personnalisées.

# CRD : définir un nouveau type de ressource "PostgreSQLCluster"
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: postgresqlclusters.db.example.com
spec:
  group: db.example.com
  versions:
    - name: v1alpha1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              required: [replicas, version]
              properties:
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 7
                version:
                  type: string
                  pattern: '^\d+\.\d+$'
                storage:
                  type: string
                  default: "10Gi"
                backupSchedule:
                  type: string
                  description: "Cron expression pour les sauvegardes automatiques"
            status:
              type: object
              properties:
                phase:
                  type: string
                  enum: [Pending, Running, Degraded, Failed]
                readyReplicas:
                  type: integer
                primaryEndpoint:
                  type: string
  scope: Namespaced
  names:
    plural: postgresqlclusters
    singular: postgresqlcluster
    kind: PostgreSQLCluster
    shortNames: [pgc]
# Utilisation de la ressource personnalisée
apiVersion: db.example.com/v1alpha1
kind: PostgreSQLCluster
metadata:
  name: mon-cluster-postgres
  namespace: production
spec:
  replicas: 3
  version: "16.1"
  storage: "50Gi"
  backupSchedule: "0 2 * * *"   # Sauvegarde à 2h du matin

Contrôleur Operator en Go : structure de base

Le framework Kubebuilder est le standard pour créer des Operators Go. Il génère le boilerplate (scaffolding) et intègre controller-runtime, la bibliothèque de reconciliation de Kubernetes.

// controllers/postgresqlcluster_controller.go
package controllers

import (
    "context"
    "fmt"
    "time"

    appsv1 "k8s.io/api/apps/v1"
    corev1 "k8s.io/api/core/v1"
    "k8s.io/apimachinery/pkg/api/errors"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    ctrl "sigs.k8s.io/controller-runtime"
    "sigs.k8s.io/controller-runtime/pkg/client"
    "sigs.k8s.io/controller-runtime/pkg/log"

    dbv1alpha1 "github.com/example/pg-operator/api/v1alpha1"
)

// PostgreSQLClusterReconciler implémente la boucle de réconciliation
type PostgreSQLClusterReconciler struct {
    client.Client
    Scheme *runtime.Scheme
}

// Reconcile est appelé à chaque changement sur les PostgreSQLCluster
// ou sur les ressources qu'il gère (StatefulSet, Service, etc.)
func (r *PostgreSQLClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    logger := log.FromContext(ctx)

    // 1. Récupérer la ressource PostgreSQLCluster
    pgc := &dbv1alpha1.PostgreSQLCluster{}
    if err := r.Get(ctx, req.NamespacedName, pgc); err != nil {
        if errors.IsNotFound(err) {
            // La ressource a été supprimée, rien à faire
            return ctrl.Result{}, nil
        }
        return ctrl.Result{}, err
    }

    // 2. Vérifier si le StatefulSet existe déjà
    existingSts := &appsv1.StatefulSet{}
    err := r.Get(ctx, req.NamespacedName, existingSts)

    if errors.IsNotFound(err) {
        // 3. Créer le StatefulSet s'il n'existe pas
        sts := r.buildStatefulSet(pgc)
        if err := r.Create(ctx, sts); err != nil {
            logger.Error(err, "Impossible de créer le StatefulSet")
            return ctrl.Result{}, err
        }
        logger.Info("StatefulSet créé", "name", pgc.Name)
        return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
    } else if err != nil {
        return ctrl.Result{}, err
    }

    // 4. Réconcilier : mettre à jour si les specs ont changé
    if *existingSts.Spec.Replicas != int32(pgc.Spec.Replicas) {
        replicas := int32(pgc.Spec.Replicas)
        existingSts.Spec.Replicas = &replicas
        if err := r.Update(ctx, existingSts); err != nil {
            return ctrl.Result{}, err
        }
        logger.Info("Replicas mis à jour", "replicas", replicas)
    }

    // 5. Mettre à jour le statut de la ressource
    pgc.Status.Phase = "Running"
    pgc.Status.ReadyReplicas = int(existingSts.Status.ReadyReplicas)
    if err := r.Status().Update(ctx, pgc); err != nil {
        return ctrl.Result{}, err
    }

    return ctrl.Result{RequeueAfter: 30 * time.Second}, nil
}

// buildStatefulSet construit le StatefulSet pour PostgreSQL
func (r *PostgreSQLClusterReconciler) buildStatefulSet(pgc *dbv1alpha1.PostgreSQLCluster) *appsv1.StatefulSet {
    replicas := int32(pgc.Spec.Replicas)
    labels := map[string]string{
        "app":        "postgresql",
        "controller": pgc.Name,
    }

    return &appsv1.StatefulSet{
        ObjectMeta: metav1.ObjectMeta{
            Name:      pgc.Name,
            Namespace: pgc.Namespace,
            // OwnerReference : si le PostgreSQLCluster est supprimé,
            // le StatefulSet l'est aussi automatiquement (garbage collection)
            OwnerReferences: []metav1.OwnerReference{
                *metav1.NewControllerRef(pgc, dbv1alpha1.GroupVersion.WithKind("PostgreSQLCluster")),
            },
        },
        Spec: appsv1.StatefulSetSpec{
            Replicas:    &replicas,
            ServiceName: pgc.Name + "-headless",
            Selector: &metav1.LabelSelector{
                MatchLabels: labels,
            },
            Template: corev1.PodTemplateSpec{
                ObjectMeta: metav1.ObjectMeta{Labels: labels},
                Spec: corev1.PodSpec{
                    Containers: []corev1.Container{
                        {
                            Name:  "postgresql",
                            Image: fmt.Sprintf("postgres:%s-alpine", pgc.Spec.Version),
                            Ports: []corev1.ContainerPort{{ContainerPort: 5432}},
                            Env: []corev1.EnvVar{
                                {Name: "POSTGRES_PASSWORD", ValueFrom: &corev1.EnvVarSource{
                                    SecretKeyRef: &corev1.SecretKeySelector{
                                        LocalObjectReference: corev1.LocalObjectReference{Name: pgc.Name + "-credentials"},
                                        Key: "password",
                                    },
                                }},
                            },
                        },
                    },
                },
            },
        },
    }
}

// SetupWithManager enregistre le contrôleur et déclare les ressources à surveiller
func (r *PostgreSQLClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
    return ctrl.NewControllerManagedBy(mgr).
        For(&dbv1alpha1.PostgreSQLCluster{}).   // Surveiller les PostgreSQLCluster
        Owns(&appsv1.StatefulSet{}).             // Et les StatefulSets qu'il crée
        Owns(&corev1.Service{}).
        Complete(r)
}

Operators existants : CloudNativePG, Strimzi, Cert-Manager

Dans la pratique, il est rarement nécessaire d'écrire un Operator from scratch. L'écosystème CNCF propose des Operators de production maintenus par des équipes spécialisées :

  • CloudNativePG : PostgreSQL en haute disponibilité avec failover automatique, streaming replication et backup vers S3
  • Strimzi : Kafka sur Kubernetes avec topic management, user management et mirror maker
  • Cert-Manager : émission et renouvellement automatique de certificats TLS (Let's Encrypt, Vault, auto-signé)
  • Prometheus Operator : déploiement et configuration déclarative de Prometheus (couvert en section 6)
# Installer CloudNativePG via kubectl
kubectl apply --server-side -f \
  https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.23/releases/cnpg-1.23.0.yaml

# Créer un cluster PostgreSQL 3 nœuds avec backup S3
cat <<EOF | kubectl apply -f -
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: postgres-prod
  namespace: database
spec:
  instances: 3
  imageName: ghcr.io/cloudnative-pg/postgresql:16.2
  storage:
    size: 50Gi
    storageClass: fast-ssd
  backup:
    barmanObjectStore:
      destinationPath: s3://mon-bucket/postgres-backups
      s3Credentials:
        accessKeyId:
          name: aws-creds
          key: ACCESS_KEY_ID
        secretAccessKey:
          name: aws-creds
          key: SECRET_ACCESS_KEY
    retentionPolicy: "30d"
  postgresql:
    parameters:
      max_connections: "200"
      shared_buffers: "256MB"
EOF

# Vérifier l'état du cluster
kubectl get cluster postgres-prod -n database
kubectl get pods -n database -l cnpg.io/cluster=postgres-prod

Contenu Premium

Ce tutoriel avancé est réservé aux membres premium.

9,90€ / mois
  • Tous les tutoriels avancés
  • Nouveaux contenus chaque semaine
  • Suivi de progression
  • Annulation à tout moment
Morgann Riu

Écrit par

Morgann Riu

Expert en cybersécurité et administration Linux. Je partage mes connaissances à travers des tutoriels gratuits et des formations pour aider les administrateurs systèmes et développeurs à sécuriser leurs infrastructures.

Partager ce tutoriel

Cet article vous a plu ?

Commentaires

Checklist Sécurité Linux

30 points essentiels pour sécuriser un serveur Linux. Recevez aussi les nouveaux tutoriels par email.

Pas de spam. Désabonnement en 1 clic.