Aller au contenu
Conteneurs & Orchestration medium

Podman kube : tester et générer des YAML Kubernetes localement

23 min de lecture

logo podman

Podman kube permet de tester des manifests Kubernetes localement, sans cluster. Vous pouvez aussi générer des YAML depuis vos conteneurs existants. C'est idéal pour valider vos manifests avant de les déployer sur un vrai cluster.

À la fin de ce guide, vous saurez :

  • Lancer des pods depuis des YAML Kubernetes
  • Générer des manifests depuis des conteneurs existants
  • Itérer rapidement : generate → edit → play
  • Nettoyer proprement avec kube down

Les quatre raccourcis ci-dessous couvrent le cycle de vie complet : kube play crée les ressources depuis un manifest, kube generate fait le chemin inverse à partir de conteneurs déjà lancés, et kube down défait le tout. Si votre besoin se limite à vérifier qu'un YAML démarre, le premier lien suffit.

Deux besoins reviennent quand on écrit des manifests : vérifier qu'un YAML démarre réellement, et récupérer un manifest depuis des conteneurs déjà en place. Podman traite les deux sans control plane, en traduisant les objets Kubernetes en pods et conteneurs locaux.

Vous avez un manifest Kubernetes et vous voulez le tester :

  • Sans installer Minikube ou Kind
  • Sans déployer sur un cluster de dev
  • Rapidement, pour valider la syntaxe et le comportement

podman kube play lit un fichier YAML Kubernetes et crée les ressources localement avec Podman.

Flux kube play : manifest YAML vers Pod Podman local

Podman ne simule pas un cluster Kubernetes complet, il se concentre sur les ressources que vous pouvez tester localement sans orchestrateur. Les ressources de type "réseau" (Services, Ingress) ne sont pas supportées car elles dépendent d'un control plane Kubernetes.

RessourceSupportéNotes
PodComplet
DeploymentCrée les pods
DaemonSetCrée un pod
ConfigMapMonté comme fichiers ou envvars
SecretMonté comme fichiers ou envvars
PersistentVolumeClaimCrée un volume Podman
ServiceUtilisez les ports du pod
IngressNon supporté
NetworkPolicyNon supporté

Podman lit le document de haut en bas et crée les objets dans l'ordre où il les rencontre. Un Pod donne un pod Podman, un Deployment donne autant de pods que de replicas, et les ConfigMap ou Secret doivent apparaître avant l'objet qui les référence.

La commande prend le chemin d'un fichier YAML. Podman crée les ressources puis démarre les conteneurs dans la foulée : sauf option contraire, un kube play qui rend la main a déjà lancé le pod.

Fenêtre de terminal
podman kube play <fichier.yaml>

Ce manifest décrit un Pod nginx avec un hostPort, seul moyen d'atteindre le conteneur depuis l'hôte puisque les Services ne sont pas gérés. À l'étape de vérification, le compteur de conteneurs affiche 2 : votre conteneur nginx, plus le conteneur d'infrastructure que Podman ajoute à chaque pod pour tenir les espaces de noms.

  1. Créer un manifest simple

    nginx-pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
    name: nginx-web
    labels:
    app: nginx
    spec:
    containers:
    - name: nginx
    image: docker.io/library/nginx:1.31.3-alpine
    ports:
    - containerPort: 80
    hostPort: 8080
  2. Lancer avec kube play

    Fenêtre de terminal
    podman kube play nginx-pod.yaml
    Résultat
    Pod:
    nginx-web
    Container:
    nginx-web-nginx
  3. Vérifier

    Fenêtre de terminal
    podman pod ps
    Résultat
    POD ID NAME STATUS CREATED # OF CONTAINERS
    abc123def456 nginx-web Running 10 seconds ago 2
  4. Tester

    Fenêtre de terminal
    curl http://localhost:8080

Podman lit le champ replicas et instancie autant de pods indépendants à partir du template. Aucun controller ne les surveille ensuite : si l'un s'arrête, rien ne le recrée, contrairement au comportement d'un Deployment sur un vrai cluster.

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 2 # Crée 2 pods
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: docker.io/library/nginx:1.31.3-alpine
ports:
- containerPort: 80
Fenêtre de terminal
podman kube play deployment.yaml

Les options les plus utiles permettent de contrôler le réseau, de charger des ConfigMaps externes, et surtout d'itérer rapidement avec --replace.

OptionDescriptionExemple
--networkRéseau à utiliser--network my-net
--configmapCharger un ConfigMap externe--configmap cm.yaml
--startDémarrer les pods (défaut: true)--start=false
--replaceRemplacer les pods existants--replace
--downAlias pour kube down(voir section dédiée)

podman kube generate lit l'état d'un conteneur ou d'un pod existant et le transcrit en manifest Kubernetes. L'image, les ports publiés, les volumes et les variables d'environnement sont repris ; les réglages propres à Podman sans équivalent Kubernetes sont laissés de côté.

Vous avez des conteneurs qui fonctionnent avec podman run. Vous voulez les "exporter" en YAML Kubernetes pour :

  • Les versionner dans Git
  • Les déployer ailleurs
  • Les transformer en vraies ressources Kubernetes

La commande écrit sur la sortie standard, d'où la redirection vers un fichier. Elle accepte le nom d'un conteneur seul ou celui d'un pod : dans le second cas, tous les conteneurs du pod se retrouvent dans le même manifest.

Fenêtre de terminal
# Depuis un conteneur
podman kube generate <container-name> > pod.yaml
# Depuis un pod
podman kube generate <pod-name> > pod.yaml

Un conteneur isolé produit malgré tout un objet Pod, avec le suffixe -pod ajouté au nom. Le mapping -p 8080:80 devient le couple containerPort / hostPort, et l'image est réécrite en nom pleinement qualifié docker.io/library/nginx:alpine.

Fenêtre de terminal
# Créer un conteneur
podman run -d --name my-nginx -p 8080:80 nginx:alpine
# Générer le YAML
podman kube generate my-nginx
Sortie
apiVersion: v1
kind: Pod
metadata:
name: my-nginx-pod
labels:
app: my-nginx-pod
spec:
containers:
- name: my-nginx
image: docker.io/library/nginx:1.31.3-alpine
ports:
- containerPort: 80
hostPort: 8080

Le pod api-stack regroupe deux conteneurs qui partagent déjà le même espace réseau. Le manifest généré les place dans la liste containers d'un seul objet Pod, et reprend la publication de port définie à la création du pod, pas celle des conteneurs.

Fenêtre de terminal
# Créer un pod avec plusieurs conteneurs
podman pod create --name api-stack -p 8080:80
podman run -d --pod api-stack --name nginx nginx:alpine
podman run -d --pod api-stack --name redis redis:alpine
# Générer le YAML
podman kube generate api-stack > api-stack.yaml

Le YAML généré contient les deux conteneurs dans le même pod.

Deux options principales : écrire directement dans un fichier (-f) ou générer aussi un Service Kubernetes (-s) pour faciliter la transition vers un vrai cluster.

OptionDescriptionExemple
-f, --filenameÉcrire dans un fichier-f pod.yaml
-s, --serviceGénérer aussi un Service-s
Fenêtre de terminal
# Générer Pod + Service
podman kube generate my-nginx -s

Le workflow typique est :

Workflow d'itération kube play : prototyper, générer, éditer, tester, itérer

Prototyper en ligne de commande coûte moins cher qu'écrire un manifest à l'aveugle : vous ajustez les options de podman run jusqu'à ce que la stack réponde, puis vous figez le résultat en YAML. Les allers-retours suivants se font sur le fichier, que kube play rejoue à chaque modification.

Les six étapes appliquent ce cycle à un pod nginx : création manuelle, génération du manifest, ajout d'un bloc resources et d'une variable d'environnement, puis relance. La dernière commande relit la configuration du conteneur avec podman inspect pour confirmer que la valeur ENV=production a bien été prise en compte.

  1. Prototyper avec le CLI

    Fenêtre de terminal
    # Créer le pod manuellement
    podman pod create --name demo -p 8080:80
    podman run -d --pod demo --name app nginx:alpine
  2. Générer le YAML

    Fenêtre de terminal
    podman kube generate demo > demo.yaml
  3. Éditer le YAML

    Ajoutez des ressources, des variables d'environnement, etc.

    demo.yaml (édité)
    apiVersion: v1
    kind: Pod
    metadata:
    name: demo
    spec:
    containers:
    - name: app
    image: docker.io/library/nginx:1.31.3-alpine
    ports:
    - containerPort: 80
    hostPort: 8080
    resources:
    limits:
    memory: "128Mi"
    cpu: "500m"
    env:
    - name: ENV
    value: "production"
  4. Supprimer l'ancien pod

    Fenêtre de terminal
    podman pod rm -f demo
  5. Relancer avec le YAML modifié

    Fenêtre de terminal
    podman kube play demo.yaml
  6. Vérifier les changements

    Fenêtre de terminal
    podman inspect demo-app --format '{{.Config.Env}}'
    # [ENV=production ...]

--replace condense les étapes 4 et 5 en une seule commande : Podman repère le pod portant le nom déclaré dans le manifest, le supprime, puis le recrée à partir du YAML courant.

Pour éviter de supprimer manuellement le pod :

Fenêtre de terminal
# Première fois
podman kube play demo.yaml
# Modifications...
# Remplacer directement
podman kube play --replace demo.yaml

kube down prend le même manifest que kube play et défait ce qu'il avait créé. Podman ne tient aucun inventaire de déploiements : c'est le fichier YAML qui sert de référence, il doit donc rester disponible pour pouvoir nettoyer.

Le fichier passé en argument doit décrire les mêmes objets qu'au moment du play. Si le nom du pod a changé dans le YAML entre-temps, kube down ne retrouve pas le pod d'origine et le laisse tourner.

Fenêtre de terminal
podman kube down <fichier.yaml>

kube down est conservateur par défaut : il supprime les pods et conteneurs, mais préserve les volumes pour éviter toute perte de données accidentelle. Utilisez --force uniquement si vous voulez un nettoyage complet.

RessourceSupprimée ?Notes
PodsToujours
ConteneursTous les conteneurs du pod
Volumes créés par le YAML⚠️Seulement avec --force
RéseauxNon supprimés

Le cycle complet tient en trois commandes : créer, vérifier, supprimer. La sortie sépare deux temps, l'arrêt des pods puis leur suppression, ce qui permet de repérer un conteneur qui refuse de s'arrêter avant que la suppression n'échoue.

Fenêtre de terminal
# Créer
podman kube play demo.yaml
# Vérifier
podman pod ps
# Supprimer
podman kube down demo.yaml
Résultat
Pods stopped:
demo
Pods removed:
demo

Par défaut, kube down préserve les volumes (pour ne pas perdre de données).

Fenêtre de terminal
# Supprimer pods + volumes
podman kube down --force demo.yaml

Le nettoyage laisse deux traces possibles : les volumes issus d'un PersistentVolumeClaim et les réseaux créés à part. podman volume prune ne supprime que les volumes qu'aucun conteneur ne référence plus, ce qui limite le risque d'effacer des données encore utilisées.

Fenêtre de terminal
# Vérifier les pods (devrait être vide)
podman pod ps
# Vérifier les volumes (peuvent rester)
podman volume ls
# Nettoyer manuellement les volumes orphelins
podman volume prune

Podman gère ces deux objets dans le même document que le Pod, à condition qu'ils y figurent avant lui. Leur contenu se consomme de deux façons : en volume monté sous forme de fichiers, ou en variable d'environnement via configMapKeyRef et secretKeyRef.

Le manifest ci-dessous combine les deux usages sur la même ConfigMap : la clé app.conf est projetée en fichier /etc/nginx/conf.d/default.conf grâce au bloc items, tandis que LOG_LEVEL devient une variable d'environnement du conteneur.

config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
app.conf: |
server {
listen 80;
}
LOG_LEVEL: debug
---
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: nginx:1.31.3-alpine
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
volumes:
- name: config-volume
configMap:
name: app-config
items:
- key: app.conf
path: default.conf
Fenêtre de terminal
podman kube play config.yaml

Un objet Secret de type Opaque attend ses valeurs encodées en base64 dans le champ data. Cet encodage n'est pas un chiffrement : quiconque lit le fichier retrouve le mot de passe en une commande, ce manifest n'a donc rien à faire dans un dépôt Git.

secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
password: cGFzc3dvcmQxMjM= # base64 encoded
---
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: nginx:1.31.3-alpine
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password

Encoder un secret en base64 :

Fenêtre de terminal
echo -n 'password123' | base64
# cGFzc3dvcmQxMjM=

Objectif : créer une stack API avec nginx + backend, la transformer en YAML, et itérer.

  1. Créer la stack avec des commandes

    Fenêtre de terminal
    # Créer le pod
    podman pod create --name api-demo -p 8080:80
    # Backend (simule une API)
    podman run -d --pod api-demo --name backend \
    python:3.12-alpine python -m http.server 3000
    # Frontend (nginx)
    podman run -d --pod api-demo --name frontend \
    nginx:alpine
  2. Vérifier que ça fonctionne

    Fenêtre de terminal
    # État
    podman pod ps
    podman ps --pod
    # Test
    curl http://localhost:8080
  3. Générer le YAML

    Fenêtre de terminal
    podman kube generate api-demo > api-demo.yaml
    cat api-demo.yaml
  4. Éditer le YAML

    Ajoutez des variables d'environnement, des labels, etc.

  5. Supprimer et recréer

    Fenêtre de terminal
    podman pod rm -f api-demo
    podman kube play api-demo.yaml
  6. Vérifier

    Fenêtre de terminal
    podman pod ps
    curl http://localhost:8080
  7. Nettoyer

    Fenêtre de terminal
    podman kube down api-demo.yaml

Checklist de validation :

  • podman pod ps montre le pod
  • podman ps --pod montre les conteneurs
  • curl localhost:8080 fonctionne
  • Après kube down, podman pod ps est vide
  • podman volume ls montre les volumes restants (si PVC)

Les échecs de kube play se manifestent au moment de la création des ressources, rarement après. Le tableau ci-dessous couvre les messages les plus fréquents ; les commandes qui suivent servent à inspecter ce que Podman a réellement créé avant d'échouer.

La plupart des erreurs viennent de trois sources : un pod qui existe déjà (nom en conflit), une image mal référencée (oubli du registry), ou un port déjà utilisé.

ErreurCauseSolution
Error: pod already existsPod avec ce nom existe--replace ou supprimer le pod
image not foundImage avec tag localAjouter docker.io/library/
hostPort already in usePort déjà utiliséChanger le hostPort ou libérer le port
ConfigMap not foundConfigMap pas dans le même fichierMettre ConfigMap avant Pod dans le YAML

Ces commandes répondent dans l'ordre à trois questions : le pod existe-t-il, ses conteneurs tournent-ils, et que disent leurs logs. La dernière, avec --start=false, crée les ressources sans les démarrer : elle valide la structure du manifest sans rien exécuter.

Fenêtre de terminal
# Voir ce qui a été créé
podman pod ps
podman ps --pod
# Logs d'un conteneur
podman logs <container-name>
# Inspecter le pod
podman pod inspect <pod-name>
# Vérifier un YAML sans le lancer
podman kube play --start=false <fichier.yaml>

Un pod créé dont les conteneurs sortent aussitôt pointe vers deux causes : une image absente localement et introuvable au pull, ou un hostPort déjà occupé sur la machine. podman logs livre le message de sortie du conteneur, ss -tlnp identifie le processus qui tient le port.

Fenêtre de terminal
# Vérifier les logs
podman kube play my-pod.yaml
podman logs <container-name>
# Problème d'image ?
podman pull docker.io/library/nginx:alpine
# Problème de port ?
ss -tlnp | grep <port>

Un manifest destiné à finir sur un vrai cluster gagne à être organisé dès le départ comme s'il y était déjà. Trois points font la différence à l'usage : l'arborescence des fichiers, l'ordre des objets dans un document multi-ressources, et le nom pleinement qualifié des images.

Séparer une base commune des variantes par environnement prépare le passage à Kustomize ou Helm, qui reposent tous deux sur ce découpage. Tant que vous restez sur Podman, chaque fichier reste jouable seul avec kube play.

  • Répertoiredeploy/
    • Répertoirebase/
      • pod.yaml
      • configmap.yaml
      • secret.yaml
    • Répertoiredev/
      • pod-dev.yaml
    • Répertoireprod/
      • pod-prod.yaml

Ces quatre règles neutralisent la plupart des erreurs listées au dépannage. L'ordre ConfigMap avant Pod est le plus contraignant : Podman lit le document séquentiellement et échoue si un Pod référence un objet qu'il n'a pas encore créé.

  1. Un fichier par ressource ou tout dans un fichier avec --- séparateurs
  2. ConfigMap avant Pod : les dépendances d'abord
  3. Images complètes : docker.io/library/nginx:alpine pas nginx:alpine
  4. Labels explicites : pour le filtrage et la documentation

Un manifest généré est un artefact reproductible : mieux vaut le committer que conserver la suite de commandes podman run qui l'a produit. Le suffixe daté sert aux essais jetables, Git prend le relais dès que le fichier devient la source de vérité.

Fenêtre de terminal
# Générer avec date
podman kube generate my-pod > pod-$(date +%Y%m%d).yaml
# Ou utiliser Git
git add pod.yaml
git commit -m "feat: add resource limits"

Podman couvre les deux sens du cycle, du manifest vers le pod et du pod vers le manifest, ce qui suffit à valider la syntaxe et le comportement des conteneurs. Le passage sur un vrai cluster reste obligatoire dès que le réseau Kubernetes entre en jeu.

  1. kube play : lance un YAML Kubernetes localement
  2. kube generate : crée un YAML depuis des conteneurs existants
  3. --replace : recrée le pod sans suppression manuelle
  4. kube down : supprime le pod, préserve les volumes (sauf --force)
  5. Workflow : CLI → generate → edit → play → iterate
  6. Scope limité : pas de Services, Ingress, NetworkPolicy

Deux directions s'ouvrent ici : industrialiser l'exécution locale avec Quadlet, qui transforme ces manifests en unités systemd, ou basculer sur un cluster réel avec K3s pour retrouver les Services et l'Ingress absents de Podman.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn