Kubernetes pour développeurs : ce qu'il faut vraiment maîtriser

Kubernetes pour développeurs : ce qu'il faut vraiment maîtriser

Kubernetes est partout. Mais en tant que développeur, avez-vous vraiment besoin de comprendre les CRD, les Operators, et les Network Policies ? Non. Voici ce qui compte vraiment pour votre quotidien.

Pourquoi Kubernetes concerne les développeurs

Kubernetes n'est plus réservé aux ops. Aujourd'hui, les développeurs doivent comprendre où et comment leur code s'exécute. Pas pour devenir DevOps, mais pour :

  • Debugger efficacement : comprendre pourquoi votre application ne démarre pas
  • Optimiser les ressources : éviter les OOMKilled et les throttling CPU
  • Collaborer avec les ops : parler le même langage
  • Écrire du code cloud-native : health checks, graceful shutdown, configuration externe

Les 5 concepts essentiels

1. Pod : l'unité de base

Un Pod est le plus petit objet déployable dans Kubernetes. Il contient un ou plusieurs containers qui partagent le même réseau et stockage.

# pod-simple.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mon-api
  labels:
    app: mon-api
spec:
  containers:
    - name: api
      image: mon-registry/mon-api:1.0.0
      ports:
        - containerPort: 8080
      resources:
        requests:
          memory: "256Mi"
          cpu: "250m"
        limits:
          memory: "512Mi"
          cpu: "500m"

Ce que vous devez retenir :

  • Un Pod = une instance de votre application
  • Les Pods sont éphémères (ils peuvent être supprimés à tout moment)
  • Toujours définir resources pour éviter les problèmes de scheduling

2. Deployment : gérer le cycle de vie

Un Deployment gère la création et la mise à jour des Pods. C'est ce que vous utiliserez 90% du temps.

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mon-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mon-api
  template:
    metadata:
      labels:
        app: mon-api
    spec:
      containers:
        - name: api
          image: mon-registry/mon-api:1.0.0
          ports:
            - containerPort: 8080
          # Health checks obligatoires
          livenessProbe:
            httpGet:
              path: /health/live
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /health/ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"

Pourquoi les health checks sont critiques :

ProbeQuestion poséeConséquence si échec
livenessProbe"L'app est-elle vivante ?"Kubernetes redémarre le Pod
readinessProbe"L'app peut-elle recevoir du trafic ?"Kubernetes retire le Pod du load balancer

Implémentation côté code (Spring Boot) :

@RestController
public class HealthController {

    @Autowired
    private DataSource dataSource;

    @GetMapping("/health/live")
    public ResponseEntity<String> liveness() {
        // Vérification basique : l'app répond
        return ResponseEntity.ok("OK");
    }

    @GetMapping("/health/ready")
    public ResponseEntity<String> readiness() {
        // Vérifier les dépendances critiques
        try {
            dataSource.getConnection().isValid(1);
            return ResponseEntity.ok("OK");
        } catch (SQLException e) {
            return ResponseEntity.status(503).body("Database unavailable");
        }
    }
}

3. Service : exposer votre application

Un Service donne une adresse stable à un groupe de Pods. Sans Service, impossible d'accéder à vos Pods de manière fiable.

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mon-api
spec:
  selector:
    app: mon-api  # Cible les Pods avec ce label
  ports:
    - port: 80          # Port exposé par le Service
      targetPort: 8080  # Port du container
  type: ClusterIP       # Accessible uniquement dans le cluster

Les types de Service :

TypeUsageExemple
ClusterIPCommunication interneAPI appelée par d'autres services
NodePortExposer sur un port du nodeTests, debugging
LoadBalancerExposer via cloud providerAPI publique

DNS automatique : Kubernetes crée automatiquement une entrée DNS pour chaque Service.

# Depuis n'importe quel Pod du même namespace
curl http://mon-api/endpoint

# Depuis un autre namespace
curl http://mon-api.mon-namespace.svc.cluster.local/endpoint

4. ConfigMap et Secret : configuration externe

Ne jamais hardcoder la configuration. Kubernetes fournit ConfigMap (données non sensibles) et Secret (données sensibles).

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mon-api-config
data:
  DATABASE_HOST: "postgres.database.svc"
  LOG_LEVEL: "INFO"
  FEATURE_FLAG_NEW_UI: "true"
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mon-api-secrets
type: Opaque
data:
  DATABASE_PASSWORD: cGFzc3dvcmQxMjM=  # Base64 encoded
  API_KEY: c2VjcmV0LWtleQ==

Injection dans le Deployment :

spec:
  containers:
    - name: api
      image: mon-registry/mon-api:1.0.0
      envFrom:
        - configMapRef:
            name: mon-api-config
        - secretRef:
            name: mon-api-secrets
      # Ou injection sélective
      env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mon-api-secrets
              key: DATABASE_PASSWORD

5. Namespace : isolation logique

Les Namespaces séparent les ressources. En développement, vous travaillerez probablement dans un namespace dédié.

# Voir les namespaces
kubectl get namespaces

# Travailler dans un namespace spécifique
kubectl -n mon-equipe get pods

# Définir un namespace par défaut
kubectl config set-context --current --namespace=mon-equipe

Bonnes pratiques :

  • Un namespace par environnement (dev, staging, prod)
  • Ou un namespace par équipe/projet
  • Ne jamais déployer dans default

Les commandes kubectl indispensables

Voir ce qui se passe

# Lister les pods (avec état)
kubectl get pods -o wide

# Voir les logs
kubectl logs mon-api-7d9f8b6c5-x2k4n

# Suivre les logs en temps réel
kubectl logs -f mon-api-7d9f8b6c5-x2k4n

# Logs de tous les pods d'un deployment
kubectl logs -l app=mon-api --all-containers

# Décrire un pod (événements, état détaillé)
kubectl describe pod mon-api-7d9f8b6c5-x2k4n

Debugger un problème

# Pourquoi mon pod ne démarre pas ?
kubectl describe pod mon-api-7d9f8b6c5-x2k4n | grep -A 20 "Events:"

# Exécuter une commande dans le container
kubectl exec -it mon-api-7d9f8b6c5-x2k4n -- /bin/sh

# Tester la connectivité réseau
kubectl exec -it mon-api-7d9f8b6c5-x2k4n -- curl http://autre-service/health

# Port-forward pour tester localement
kubectl port-forward pod/mon-api-7d9f8b6c5-x2k4n 8080:8080

Déployer et mettre à jour

# Appliquer une configuration
kubectl apply -f deployment.yaml

# Mettre à jour l'image
kubectl set image deployment/mon-api api=mon-registry/mon-api:1.1.0

# Voir l'historique des déploiements
kubectl rollout history deployment/mon-api

# Rollback si problème
kubectl rollout undo deployment/mon-api

# Scaler manuellement
kubectl scale deployment/mon-api --replicas=5

Ce que vous pouvez ignorer (au début)

En tant que développeur, ces concepts peuvent attendre :

ConceptPourquoi attendreQuand l'apprendre
OperatorsGestion avancée, souvent par les opsQuand vous gérez des bases de données
CRDExtension de l'API KubernetesQuand vous créez des outils internes
Network PoliciesSécurité réseau avancéeQuand vous travaillez sur la sécurité
RBACGestion des permissionsQuand vous configurez les accès
HelmPackaging d'applicationsQuand vous avez plusieurs environnements
Service MeshIstio, LinkerdQuand vous avez des centaines de services

Patterns essentiels pour développeurs

Graceful shutdown

Kubernetes envoie un signal SIGTERM avant de tuer un Pod. Votre application doit terminer proprement.

// Spring Boot : configuration automatique
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
# application.yaml
server:
  shutdown: graceful

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s
# Dans le Deployment
spec:
  containers:
    - name: api
      lifecycle:
        preStop:
          exec:
            command: ["/bin/sh", "-c", "sleep 5"]  # Attendre que le LB se mette à jour
  terminationGracePeriodSeconds: 45

Configuration par environnement

Utilisez des ConfigMaps différentes par environnement, même image Docker.

# Structure recommandée
k8s/
├── base/
│   ├── deployment.yaml
│   └── service.yaml
├── dev/
│   └── configmap.yaml
├── staging/
│   └── configmap.yaml
└── prod/
    └── configmap.yaml

Logs structurés

Kubernetes collecte stdout/stderr. Utilisez des logs JSON pour faciliter l'analyse.

// logback-spring.xml
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>
{"@timestamp":"2026-01-12T10:30:00.000Z","level":"INFO","message":"Request processed","requestId":"abc123","duration":45}

Checklist avant déploiement

Avant de déployer votre application sur Kubernetes :

  • ☐ Health checks implémentés (/health/live, /health/ready)
  • ☐ Graceful shutdown configuré
  • ☐ Configuration externalisée (pas de secrets dans le code)
  • ☐ Ressources définies (requests et limits)
  • ☐ Logs en JSON sur stdout
  • ☐ Image Docker optimisée (multi-stage build)
  • ☐ Variables d'environnement documentées

Debugging : les erreurs courantes

CrashLoopBackOff

L'application crash au démarrage.

# Voir les logs du crash
kubectl logs mon-api-7d9f8b6c5-x2k4n --previous

# Causes fréquentes :
# - Variable d'environnement manquante
# - Base de données inaccessible
# - Port déjà utilisé

ImagePullBackOff

Kubernetes ne peut pas télécharger l'image.

# Vérifier le nom de l'image
kubectl describe pod mon-api-7d9f8b6c5-x2k4n | grep "Image:"

# Causes fréquentes :
# - Nom d'image incorrect
# - Registry privé sans credentials
# - Tag inexistant

OOMKilled

Le container a dépassé sa limite mémoire.

# Voir la raison du restart
kubectl describe pod mon-api-7d9f8b6c5-x2k4n | grep -A 5 "Last State:"

# Solution : augmenter les limits ou optimiser l'application
resources:
  limits:
    memory: "1Gi"  # Augmenter si nécessaire

Conclusion

Kubernetes peut sembler complexe, mais pour un développeur, l'essentiel tient en 5 concepts : Pod, Deployment, Service, ConfigMap/Secret, Namespace. Maîtrisez ces fondamentaux et les commandes kubectl de base, et vous serez capable de déployer, debugger et collaborer efficacement avec les équipes ops.

Le reste (Operators, Service Mesh, CRD) viendra naturellement quand vous en aurez besoin. Pour l'instant, concentrez-vous sur écrire du code cloud-native : health checks, graceful shutdown, configuration externe, logs structurés.


Pour aller plus loin : Kubernetes 1.35 : les nouveautés cloud-native et Docker Desktop 4.50 : Kubernetes local simplifié