
Ce guide vous apprend à isoler et connecter vos conteneurs en construisant une architecture 3 tiers complète. À la fin, vous saurez créer des réseaux privés, activer le DNS entre conteneurs, et exposer uniquement ce qui doit l’être.
Ce que vous allez construire
Section intitulée « Ce que vous allez construire »Une mini-application 3 tiers avec isolation réseau :
| Conteneur | Réseau(x) | Rôle |
|---|---|---|
db | backend (isolé) | Base de données, invisible de l’extérieur |
api | backend + frontend | Pont entre les deux mondes |
web | frontend | Seul conteneur exposé sur un port |
Compétences acquises :
- Créer un réseau isolé (
--internal) - Activer le DNS pour résoudre les conteneurs par nom
- Connecter un conteneur à plusieurs réseaux
- Exposer un port sur localhost uniquement (sécurité)
- Diagnostiquer quand ça ne marche pas
Modèle mental : comment Podman gère le réseau
Section intitulée « Modèle mental : comment Podman gère le réseau »Avant de manipuler, comprenez ce qui se passe sous le capot.
Un réseau Podman = bridge + NAT + (optionnel) DNS
Section intitulée « Un réseau Podman = bridge + NAT + (optionnel) DNS »Quand vous créez un réseau Podman, il crée :
- Un bridge : interface réseau virtuelle sur l’hôte
- Du NAT : les conteneurs accèdent à Internet via l’IP de l’hôte
- Un serveur DNS (optionnel) : résout les noms de conteneurs en IP
Le réseau podman par défaut : attention au DNS
Section intitulée « Le réseau podman par défaut : attention au DNS »Podman crée automatiquement un réseau podman. Vérifions s’il a le DNS activé :
podman network inspect --format '{{.DNSEnabled}}' podmanfalseLe DNS est désactivé par défaut sur le réseau podman. Les conteneurs ne peuvent pas se joindre par leur nom.
Rootless : pasta ou slirp4netns
Section intitulée « Rootless : pasta ou slirp4netns »En mode rootless (sans sudo), Podman ne peut pas créer un vrai bridge. Il utilise :
- pasta (défaut depuis Podman 5) : meilleure performance
- slirp4netns (legacy) : plus lent mais compatible
Cela impacte les performances réseau et certaines fonctionnalités. Pour vérifier quel backend est utilisé :
podman info --format '{{.Host.NetworkBackendInfo.Backend}}'Lab — Étape 1 : le réseau backend isolé
Section intitulée « Lab — Étape 1 : le réseau backend isolé »On commence par le plus restrictif : un réseau sans accès Internet pour la base de données.
Créer un réseau interne
Section intitulée « Créer un réseau interne »L’option --internal crée un réseau isolé : les conteneurs ne peuvent pas atteindre Internet.
podman network create --internal --subnet 172.30.0.0/24 backendVérifiez les propriétés :
podman network inspect --format 'DNS: {{.DNSEnabled}}, Internal: {{.Internal}}' backendDNS: true, Internal: trueLe DNS est activé automatiquement sur les réseaux personnalisés. L’option --internal bloque l’accès extérieur.
Lancer la base de données
Section intitulée « Lancer la base de données »podman run -d --name db \ --network backend \ -e POSTGRES_PASSWORD=secret \ -e POSTGRES_DB=app \ docker.io/library/postgres:16-alpineVérifiez que le conteneur est bien sur le réseau backend :
podman inspect db --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}: {{$v.IPAddress}}{{"\n"}}{{end}}'backend: 172.30.0.2Tester l’isolation
Section intitulée « Tester l’isolation »La base de données ne doit pas pouvoir atteindre Internet :
podman exec db ping -c 1 -W 2 8.8.8.8ping: sendto: Network is unreachableIsolation confirmée. La DB est invisible depuis l’extérieur.
Lab — Étape 2 : le réseau frontend avec DNS
Section intitulée « Lab — Étape 2 : le réseau frontend avec DNS »Maintenant, créons le réseau où sera exposé le frontend.
Créer le réseau frontend
Section intitulée « Créer le réseau frontend »Ce réseau a accès à Internet (pas --internal) :
podman network create --subnet 172.31.0.0/24 frontendVérifiez :
podman network inspect --format 'DNS: {{.DNSEnabled}}, Internal: {{.Internal}}' frontendDNS: true, Internal: falseLancer l’API (sur backend pour l’instant)
Section intitulée « Lancer l’API (sur backend pour l’instant) »L’API doit communiquer avec la DB. On la place d’abord sur le réseau backend :
podman run -d --name api \ --network backend \ docker.io/library/alpine:3.20 \ sleep 3600Testons que l’API peut résoudre et atteindre la DB par son nom :
podman exec api ping -c 2 dbPING db (172.30.0.2): 56 data bytes64 bytes from 172.30.0.2: seq=0 ttl=42 time=0.089 ms64 bytes from 172.30.0.2: seq=1 ttl=42 time=0.112 msLe DNS fonctionne : db est résolu en 172.30.0.2.
Lancer le frontend
Section intitulée « Lancer le frontend »podman run -d --name web \ --network frontend \ docker.io/library/nginx:alpineÀ ce stade, web ne peut pas joindre api ni db (ils sont sur des réseaux différents).
Lab — Étape 3 : multi-network (le point clé)
Section intitulée « Lab — Étape 3 : multi-network (le point clé) »L’API doit être le seul pont entre frontend et backend. On la connecte aux deux réseaux.
Connecter l’API au réseau frontend
Section intitulée « Connecter l’API au réseau frontend »podman network connect frontend apiVérifiez que l’API a maintenant deux interfaces :
podman inspect api --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}: {{$v.IPAddress}}{{"\n"}}{{end}}'backend: 172.30.0.3frontend: 172.31.0.3Tester la connectivité
Section intitulée « Tester la connectivité »Depuis web vers api (doit fonctionner) :
podman exec web ping -c 1 apiPING api (172.31.0.3): 56 data bytes64 bytes from 172.31.0.3: seq=0 ttl=42 time=0.098 msDepuis web vers db (doit échouer) :
podman exec web ping -c 1 -W 2 db 2>&1 || echo "Isolation OK : web ne peut pas atteindre db"ping: bad address 'db'Isolation OK : web ne peut pas atteindre dbDepuis api vers db (doit fonctionner) :
podman exec api ping -c 1 dbPING db (172.30.0.2): 56 data bytes64 bytes from 172.30.0.2: seq=0 ttl=42 time=0.045 msArchitecture obtenue
Section intitulée « Architecture obtenue »L’API est le seul conteneur capable de communiquer avec les deux mondes. La DB reste isolée.
Exposition de ports : règles de sécurité
Section intitulée « Exposition de ports : règles de sécurité »Maintenant qu’on a une architecture isolée, exposons uniquement le frontend.
Règle 1 : par défaut, n’exposez rien
Section intitulée « Règle 1 : par défaut, n’exposez rien »Les réseaux internes suffisent pour la communication entre conteneurs. L’exposition de ports n’est nécessaire que pour les services accessibles depuis l’hôte ou l’extérieur.
Règle 2 : en dev, exposez sur localhost uniquement
Section intitulée « Règle 2 : en dev, exposez sur localhost uniquement »# Arrêter et recréer web avec un port exposépodman rm -f webpodman run -d --name web \ --network frontend \ -p 127.0.0.1:8080:80 \ docker.io/library/nginx:alpineLe port 80 de nginx est uniquement accessible depuis localhost:8080 :
curl -s http://127.0.0.1:8080 | head -3<!DOCTYPE html><html><head>Depuis une autre machine du réseau, ce port n’est pas accessible.
Règle 3 : en prod, soyez explicite
Section intitulée « Règle 3 : en prod, soyez explicite »# Exposer sur une interface spécifiquepodman run -d -p 192.168.1.100:8080:80 nginx:alpine
# Ou sur toutes les interfaces (à sécuriser par firewall)podman run -d -p 8080:80 nginx:alpineVérifier les ports exposés
Section intitulée « Vérifier les ports exposés »Après avoir démarré un conteneur avec -p, confirmez que le mapping est correct. Cette commande est essentielle pour le debug :
podman port web80/tcp -> 127.0.0.1:8080Le format indique : port interne → interface:port externe. Ici, le port 80 du conteneur est accessible uniquement sur 127.0.0.1:8080.
Troubleshooting : 6 réflexes quand ça ne marche pas
Section intitulée « Troubleshooting : 6 réflexes quand ça ne marche pas »Quand un conteneur ne peut pas joindre un autre, procédez dans cet ordre pour isoler le problème.
1. Vérifier les réseaux du conteneur
Section intitulée « 1. Vérifier les réseaux du conteneur »Le conteneur est-il sur le bon réseau ? Cette commande liste tous les réseaux auxquels il est connecté :
podman inspect <conteneur> --format '{{json .NetworkSettings.Networks}}' | jq2. Vérifier la configuration du réseau
Section intitulée « 2. Vérifier la configuration du réseau »Le réseau a-t-il le DNS activé ? Est-il interne (sans Internet) ?
podman network inspect <réseau> --format 'DNS: {{.DNSEnabled}}, Internal: {{.Internal}}, Subnet: {{range .Subnets}}{{.Subnet}}{{end}}'3. Vérifier le DNS dans le conteneur
Section intitulée « 3. Vérifier le DNS dans le conteneur »Le conteneur sait-il où envoyer ses requêtes DNS ? Vérifiez que nameserver pointe vers une IP du réseau Podman :
podman exec <conteneur> cat /etc/resolv.conf4. Tester la résolution DNS
Section intitulée « 4. Tester la résolution DNS »Le nom est-il résolu en IP ? Si cette étape échoue, le problème est DNS (pas réseau) :
podman exec <conteneur> nslookup <cible># oupodman exec <conteneur> getent hosts <cible>5. Tester la connectivité
Section intitulée « 5. Tester la connectivité »Si le DNS fonctionne mais la connexion échoue, testez la connectivité IP directe :
# Ping (si ICMP disponible)podman exec <conteneur> ping -c 1 <cible>
# Ou avec netcat (plus fiable pour tester un port spécifique)podman exec <conteneur> nc -zv <cible> <port>6. Vérifier les ports côté hôte
Section intitulée « 6. Vérifier les ports côté hôte »Si le conteneur écoute mais l’hôte n’y accède pas, vérifiez le binding :
# Ports mappés par Podmanpodman port <conteneur>
# Ports en écoute sur l'hôtess -tlnp | grep <port>Problèmes fréquents
Section intitulée « Problèmes fréquents »| Symptôme | Cause probable | Solution |
|---|---|---|
bad address 'nom' | DNS désactivé ou réseaux différents | Vérifier DNSEnabled, utiliser même réseau |
Connection refused | Service non démarré ou mauvais port | Vérifier podman logs, tester port |
Network unreachable | Réseau --internal sans route | Normal si isolation voulue |
| Port non accessible | Binding sur 127.0.0.1 | Vérifier podman port, ajuster binding |
Nettoyage du lab
Section intitulée « Nettoyage du lab »Après avoir validé l’architecture, supprimez les ressources créées pour libérer les ports et les adresses IP :
podman rm -f db api webpodman network rm backend frontendLes volumes anonymes créés par PostgreSQL sont automatiquement supprimés avec -f. Pour un nettoyage complet, utilisez podman system prune.
Alias DNS
Section intitulée « Alias DNS »Les alias DNS permettent à un conteneur de répondre à plusieurs noms. C’est utile pour :
- Compatibilité : votre code attend
redismais le conteneur s’appelleredis-cache - Migration : pointer temporairement deux noms vers le même service
- Généricité : utiliser
cacheau lieu d’un nom spécifique
Ajoutez des noms DNS alternatifs avec --network-alias :
podman run -d --name redis-cache \ --network frontend \ --network-alias cache \ --network-alias redis \ docker.io/library/redis:alpineLe conteneur répond maintenant à trois noms : redis-cache (nom du conteneur), cache et redis (alias). Tous résolvent vers la même IP.
À retenir
Section intitulée « À retenir »- Créez toujours un réseau dédié : le réseau
podmann’a pas de DNS activé --internalpour isoler : la DB n’a pas besoin d’Internet- Multi-network pour les ponts : l’API connecte frontend et backend
- Exposez sur localhost en dev :
-p 127.0.0.1:port:port - DNS explicite : utilisez
--dns-enableden prod pour éviter les surprises - 6 commandes de debug : inspect conteneur, inspect réseau, resolv.conf, ping/nc, podman port, ss
Modes réseau spéciaux
Section intitulée « Modes réseau spéciaux »Ces modes sont rarement nécessaires. Utilisez-les uniquement si vous avez un besoin spécifique.
Mode host
Section intitulée « Mode host »Le conteneur partage directement le réseau de l’hôte :
podman run --rm --network host alpine ip addr showQuand l’utiliser : performances réseau critiques (serveur DNS, proxy haute charge).
Risque : aucune isolation réseau, le conteneur peut écouter sur n’importe quel port.
Mode none
Section intitulée « Mode none »Aucun réseau, uniquement loopback :
podman run --rm --network none alpine ip addr showQuand l’utiliser : jobs de calcul sans besoin réseau, sécurité maximale.
IP statique
Section intitulée « IP statique »Assignez une IP fixe dans un réseau avec subnet :
podman run -d --name db-fixed \ --network backend \ --ip 172.30.0.100 \ alpine sleep 300Quand l’utiliser : intégration avec des outils externes qui attendent une IP fixe.
Préférez le DNS : plus flexible et maintenable.
Formats de port publishing
Section intitulée « Formats de port publishing »| Format | Description |
|---|---|
-p 8080:80 | 80 → 8080 sur toutes les interfaces |
-p 127.0.0.1:8080:80 | 80 → 8080 sur localhost uniquement |
-p 80 | 80 → port aléatoire |
-p 8080-8090:80-90 | Plage de ports |
Tableau récapitulatif des commandes
Section intitulée « Tableau récapitulatif des commandes »| Commande | Description |
|---|---|
podman network create <nom> | Créer un réseau |
podman network create --internal <nom> | Créer un réseau isolé |
podman network ls | Lister les réseaux |
podman network inspect <nom> | Détails d’un réseau |
podman network rm <nom> | Supprimer un réseau |
podman network prune | Supprimer les inutilisés |
podman network connect <net> <ctr> | Connecter un conteneur |
podman network disconnect <net> <ctr> | Déconnecter |
podman port <conteneur> | Voir les ports mappés |