Ports et accès au service
- Leçon 7
- 40 min
- Module 3 : Réseau, configuration et persistance
Leçon 7 sur 15 · L'essentiel de Docker et du nuage pour les développeurs en IAPorts et accès au service
1. Comprendre les conteneurs
2. Construire l'image de l'application
3. Réseau, configuration et persistance
4. Les services de base du nuage
5. Déployer et nettoyer
Ce que vous saurez faire
Accédez à l’API de billets de soutien dans son conteneur depuis votre ordinateur, et uniquement depuis celui-ci. Apprenez pourquoi un conteneur peut fonctionner sans répondre. Permettez à deux conteneurs de communiquer par leur nom.
Avant de commencerCe qu’il faut savoir et avoir sous la main
Ce que vous devez savoir
- Les ports et l’adresse
127.0.0.1, présentés dans Du navigateur au serveur : les bases : ports : quel programme. - Pourquoi un serveur écoute sur
0.0.0.0, expliqué dans Premiers pas avec l’hébergement : mode développement et mode production. docker run,docker ps,docker logsetdocker exec, présentés dans Images et conteneurs en cours d’exécution : un premier conteneur et Exemple détaillé : un fichier dans un conteneur.
Ce qu’il vous faut sur votre ordinateur
- Docker en cours d’exécution, comme dans Installer et vérifier : vérifier l’installation.
- Le projet
~/projects/ticket-apiavec sonDockerfile, et l’imageticket-api:1.0.0, présentés dans Sécurité à l’exécution : Exercice du module. Vérifiez avecdocker image ls ticket-api. - L’image
python:3.14.8-slim-trixie, présentée dans Images et conteneurs en cours d’exécution : obtenir une image. Si elle manque, Docker la télécharge lors de sa première utilisation. - Pour une étape : un téléphone ou un deuxième ordinateur sur le même réseau Wi-Fi. Si vous n’en avez pas, lisez la sortie dans la leçon.
- Si votre projet manque ou ne fonctionne plus, copiez l’instantané
end-of-m02, comme l’indique Utiliser un instantané. Construisez ensuite l’image dans le dossier du projet :docker build -t ticket-api:1.0.0 .
Testé avec Docker Desktop 4.81.0 (Docker Engine 29.6.1) sur macOS, avec une puce Apple. Les étapes Windows et Linux n’ont pas été testées.
L’application fonctionne, mais vous ne pouvez pas y accéder
Section intitulée « L’application fonctionne, mais vous ne pouvez pas y accéder »Commencez par un petit test. Amira lance l’image sans options supplémentaires, sauf la clé d’API :
docker run -d --name ticket-api -e API_KEY=local-dev-key ticket-api:1.0.0docker psLa sortie commence par l’identifiant du nouveau conteneur :
7afe3d39fe5b1f08c2e9474dafa7aef9acfa3902a2abf0e137a52da4dc7b4903CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES7afe3d39fe5b ticket-api:1.0.0 "fastapi run --host …" 3 seconds ago Up 3 seconds 8000/tcp ticket-apiLe conteneur fonctionne, et l’API écoute sur le port 8000. Elle l’appelle depuis son ordinateur :
curl -sS http://127.0.0.1:8000/healthLa sortie :
curl: (7) Failed to connect to 127.0.0.1 port 8000 after 0 ms: Couldn't connect to serverElle fait ensuite la même requête depuis l’intérieur du conteneur. L’image allégée ne contient pas curl, alors elle utilise urllib de Python :
docker exec ticket-api python -c "import urllib.request; print(urllib.request.urlopen('http://127.0.0.1:8000/health').read().decode())"La sortie :
{"status":"ok"}L’application fonctionne. Il manque seulement un accès depuis l’extérieur. Un conteneur possède son propre réseau : ses adresses réseau et ses ports sont distincts de ceux de votre ordinateur. Le port 8000 du conteneur n’est pas le port 8000 de votre ordinateur. PORTS 8000/tcp (ports : 8000/tcp) dans docker ps provient de la ligne EXPOSE 8000 du Dockerfile. EXPOSE documente seulement le port. Cette instruction n’ouvre rien.
docker rm -f ticket-apiPublier un port
Section intitulée « Publier un port »Pour permettre l’accès depuis votre ordinateur, publiez un port au démarrage du conteneur avec -p. Docker redirige les connexions reçues sur ce port de votre ordinateur vers un port du conteneur :
-p 127.0.0.1:8000:8000 └ host address └ host port └ container port- Le port du conteneur est celui où l’application écoute : 8000, selon la commande de démarrage du Dockerfile.
- Le port de l’hôte est le port que vous utilisez sur votre ordinateur. Il peut être différent, par exemple
8001. - L’adresse de l’hôte indique quelle adresse réseau de votre ordinateur accepte la connexion.
Amira relance l’API, cette fois avec le port publié :
docker run -d --name ticket-api -p 127.0.0.1:8000:8000 -e API_KEY=local-dev-key ticket-api:1.0.0docker psdocker port ticket-apicurl -s http://127.0.0.1:8000/healthLa sortie :
721de8ac187e07bc74f72ee4b8a94b213c2dd56e73b349a88cf04778d23fd51bCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES721de8ac187e ticket-api:1.0.0 "fastapi run --host …" 3 seconds ago Up 3 seconds 127.0.0.1:8000->8000/tcp ticket-api8000/tcp -> 127.0.0.1:8000{"status":"ok"}127.0.0.1:8000->8000/tcp signifie : « Le port 8000 de l’adresse 127.0.0.1 de cet ordinateur mène au port 8000 du conteneur. » http://localhost:8000/health donne la même réponse, car localhost est le nom de 127.0.0.1.
Le journal de l’application montre l’origine de la requête :
docker logs --tail 2 ticket-apiLa sortie :
INFO ticket_api: GET /health 200 5ms id=338f1241cca9INFO: 172.17.0.1:58980 - "GET /health HTTP/1.1" 200 OKLe client est 172.17.0.1, et non 127.0.0.1. Pour l’application, la requête vient du réseau de Docker, et non de l’adresse de votre ordinateur. Sur un autre ordinateur, les nombres peuvent différer : vous voyez une adresse comme 172.17.0.1.
Choisir qui peut se connecter
Section intitulée « Choisir qui peut se connecter »L’adresse de l’hôte détermine qui peut accéder au port publié. Amira teste l’accès depuis un autre appareil. Elle trouve d’abord l’adresse de son ordinateur sur le réseau local :
ipconfig getifaddr en0en0 correspond généralement au Wi-Fi. Si la commande n’affiche rien, essayez en1.
ipconfigUtilisez l’IPv4 Address (adresse IPv4) de votre carte Wi-Fi.
hostname -IUtilisez la première adresse.
L’adresse de son ordinateur était 10.0.0.109. Depuis un deuxième appareil sur le même réseau, elle ouvre http://10.0.0.109:8000/health. Voici le résultat avec curl depuis cette adresse :
curl: (7) Failed to connect to 10.0.0.109 port 8000 after 8 ms: Couldn't connect to serverElle démarre ensuite un deuxième conteneur, sans adresse d’hôte dans -p, sur le port 8001 de l’hôte :
docker run -d --name ticket-api-open -p 8001:8000 -e API_KEY=local-dev-key ticket-api:1.0.0docker ps --filter name=ticket-api-opencurl -s http://10.0.0.109:8001/healthLa sortie :
bd5e48b1b1def0fc2b29ed84076bd91fa2304121002a480e8c16d09c28d290a9CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESbd5e48b1b1de ticket-api:1.0.0 "fastapi run --host …" 3 seconds ago Up 3 seconds 0.0.0.0:8001->8000/tcp, [::]:8001->8000/tcp ticket-api-open{"status":"ok"}0.0.0.0 (et [::], l’équivalent pour IPv6) signifie toutes les adresses réseau de cet ordinateur. Tout appareil qui peut accéder à votre ordinateur peut maintenant appeler l’API. Sur le Wi-Fi d’un café, cela inclut tout le monde dans le café.
Option -p |
Affichage de docker ps |
Qui peut se connecter |
|---|---|---|
-p 127.0.0.1:8000:8000 |
127.0.0.1:8000->8000/tcp |
Seulement les programmes sur cet ordinateur |
-p 8001:8000 |
0.0.0.0:8001->8000/tcp, [::]:8001->8000/tcp |
Tout appareil qui peut accéder à cet ordinateur |
Sans -p |
8000/tcp |
Personne à l’extérieur du réseau du conteneur |
Ce cours utilise toujours -p 127.0.0.1:.... Omettez l’adresse seulement si d’autres appareils doivent se connecter, et seulement sur un réseau auquel vous faites confiance.
Amira supprime le conteneur ouvert aux autres appareils :
docker rm -f ticket-api-openL’adresse à l’intérieur du conteneur
Section intitulée « L’adresse à l’intérieur du conteneur »L’adresse de l’hôte représente la moitié du trajet. L’autre moitié est l’adresse où l’application écoute dans le conteneur. Le Dockerfile démarre l’application avec fastapi run --host 0.0.0.0 --port 8000. Et s’il utilisait --host 127.0.0.1, comme le font fastapi dev et uvicorn par défaut?
Faites une prédiction : le conteneur fonctionne, et le port est publié. Qu’affiche curl?
Amira supprime le conteneur fonctionnel avec docker rm -f ticket-api. Elle démarre ensuite l’image avec une commande différente à la fin de docker run. Cette commande remplace le CMD de l’image :
docker run -d --name ticket-api -p 127.0.0.1:8000:8000 -e API_KEY=local-dev-key ticket-api:1.0.0 fastapi run --host 127.0.0.1 --port 8000docker pscurl -sS http://localhost:8000/healthLa sortie :
af205d8ff12517ec100dfb4adebfed5b3bc3aaff4d44aca93c88de17933fdac7CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESaf205d8ff125 ticket-api:1.0.0 "fastapi run --host …" 4 seconds ago Up 3 seconds 127.0.0.1:8000->8000/tcp ticket-apicurl: (52) Empty reply from serverTout semble correct dans docker ps, mais la réponse est vide. Le journal montre la cause :
docker logs ticket-apiLa sortie (la dernière ligne) :
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)Dans le conteneur, 127.0.0.1 désigne le conteneur lui-même, et non votre ordinateur. L’application accepte seulement les connexions qui commencent dans le conteneur. La connexion redirigée par Docker arrive à l’adresse réseau du conteneur, par exemple 172.17.0.2, où aucun programme n’écoute. Un test depuis l’intérieur du conteneur fonctionne toujours. Donc, docker exec seul ne détecte pas ce problème.
Description textuelle du diagramme
Sur votre ordinateur, curl appelle 127.0.0.1 au port 8000. C’est le port publié. L’adresse Wi-Fi 10.0.0.109 n’a pas de port 8000, car -p a choisi 127.0.0.1. Docker redirige la connexion vers l’adresse réseau du conteneur, 172.17.0.2, au port 8000. Le conteneur possède aussi sa propre adresse 127.0.0.1, que seuls les programmes du conteneur peuvent utiliser. fastapi run --host 0.0.0.0 écoute sur les deux adresses du conteneur, donc la connexion redirigée arrive à l’application. Avec --host 127.0.0.1, l’application écoute seulement sur l’adresse 127.0.0.1 du conteneur. La connexion redirigée ne trouve aucun programme à cette adresse.
Description textuelle du diagramme
Sur votre ordinateur, curl envoie GET /health au port publié. Docker la redirige vers l’adresse du conteneur, 172.17.0.2, au port 8000. Si l’application écoute sur 0.0.0.0, elle répond 200 {"status":"ok"}, et Docker transmet la réponse. Si l’application écoute sur 127.0.0.1, aucun programme n’écoute sur 172.17.0.2. Docker ferme la connexion sans réponse, et curl affiche curl: (52) Empty reply from server (réponse vide du serveur).
Amira supprime le conteneur qui ne fonctionne pas :
docker rm -f ticket-apilocalhost désigne ce conteneur
Section intitulée « localhost désigne ce conteneur »La même règle s’applique dans l’autre sens. Un programme dans un conteneur qui appelle localhost appelle son propre conteneur. Amira redémarre l’API avec la commande normale, puis l’appelle depuis un deuxième conteneur :
docker run -d --name ticket-api -p 127.0.0.1:8000:8000 -e API_KEY=local-dev-key ticket-api:1.0.0docker run --rm python:3.14.8-slim-trixie python -c "import urllib.request; print(urllib.request.urlopen('http://localhost:8000/health').read().decode())"La sortie (la dernière ligne) :
urllib.error.URLError: <urlopen error [Errno 111] Connection refused>Aucune application n’écoute sur le port 8000 du deuxième conteneur, donc la connexion est refusée.
Pour appeler un programme de votre ordinateur depuis un conteneur, Docker Desktop fournit le nom spécial host.docker.internal. Avec http://host.docker.internal:8000/health, la même commande a affiché {"status":"ok"} ici. Sur Linux, Docker Engine exige une option supplémentaire pour ce nom (--add-host=host.docker.internal:host-gateway); ce cours ne l’a pas testée.
D’un conteneur à l’autre : un réseau défini par l’utilisateur
Section intitulée « D’un conteneur à l’autre : un réseau défini par l’utilisateur »Deux conteneurs qui travaillent ensemble, comme l’API et une base de données, ont besoin d’un accès direct, sans passer par votre ordinateur. Placez-les sur le même réseau défini par l’utilisateur. C’est un réseau que vous créez et nommez. Docker y donne à chaque conteneur un nom que les autres peuvent utiliser : son nom de conteneur.
Commençons par le réseau par défaut. Chaque conteneur démarré sans --network le rejoint. Amira appelle l’API par son nom :
docker run --rm python:3.14.8-slim-trixie python -c "import urllib.request; print(urllib.request.urlopen('http://ticket-api:8000/health').read().decode())"La sortie (la dernière ligne) :
urllib.error.URLError: <urlopen error [Errno -2] Name or service not known>Sur le réseau par défaut, les noms ne fonctionnent pas. Amira supprime le conteneur avec docker rm -f ticket-api. Elle crée ensuite un réseau et y démarre l’API :
docker network create ticket-netdocker run -d --name ticket-api --network ticket-net -e API_KEY=local-dev-key ticket-api:1.0.0docker run --rm --network ticket-net python:3.14.8-slim-trixie python -c "import urllib.request; print(urllib.request.urlopen('http://ticket-api:8000/health').read().decode())"docker logs --tail 1 ticket-apiLa sortie :
6b3a99b0f573fcdcc2eada873a3281c43a8e7f3f499e0fd889a3f98e590fbed7859b97b8df79a958829450eea324a06347cda25073a2759a6d9b230d6d79c030{"status":"ok"}INFO: 172.21.0.3:54638 - "GET /health HTTP/1.1" 200 OKLes deux premières lignes sont les identifiants du nouveau réseau et du nouveau conteneur. Le nom ticket-api a fonctionné. La requête provenait de l’adresse du deuxième conteneur sur ticket-net, soit 172.21.0.3. L’API n’avait pas besoin de -p : les conteneurs du même réseau accèdent directement aux ports des conteneurs. Un port publié sert seulement à votre ordinateur.
Dans les prochaines leçons, Docker Compose crée ce réseau pour vous. L’API trouve alors sa base de données avec le nom db.
Amira fait le nettoyage :
docker rm -f ticket-apidocker network rm ticket-netExercice guidé : accéder à l’API
Section intitulée « Exercice guidé : accéder à l’API »-
Ouvrez un terminal dans
~/projects/ticket-api. -
Démarrez l’API avec le port publié sur
127.0.0.1:Fenêtre de terminal docker run -d --name ticket-api -p 127.0.0.1:8000:8000 -e API_KEY=local-dev-key ticket-api:1.0.0Si Docker affiche
Bind for 127.0.0.1:8000 failed: port is already allocated(liaison impossible : port déjà attribué), un autre programme utilise le port 8000. Lancezdocker pspour trouver un conteneur qui l’utilise, puis supprimez-le. Supprimez ensuite le conteneur dont le démarrage a échoué avecdocker rm ticket-api, puis réessayez. -
Lancez
docker ps. Vérifiez quePORTS(ports) affiche127.0.0.1:8000->8000/tcp. -
Appelez l’API :
curl -s http://127.0.0.1:8000/health. Vous devriez obtenir{"status":"ok"}. -
Trouvez l’adresse réseau de votre ordinateur, comme dans Choisir qui peut se connecter. Appelez
http://<that address>:8000/healthdepuis un téléphone ou un autre ordinateur sur le même Wi-Fi. La connexion devrait échouer. -
Lancez
docker logs --tail 2 ticket-api. Trouvez l’adresse du client pour votre requête de l’étape 4. -
Supprimez le conteneur :
docker rm -f ticket-api.
Afficher les résultats attendus
Étape 3 : 127.0.0.1:8000->8000/tcp dans la colonne PORTS. Étape 4 : {"status":"ok"}. Étape 5 : le navigateur du téléphone indique qu’il ne peut pas se connecter, car le port écoute seulement sur 127.0.0.1. Étape 6 : une ligne comme INFO: 172.17.0.1:58980 - "GET /health HTTP/1.1" 200 OK. L’adresse est celle de Docker, et non 127.0.0.1. Les nombres peuvent différer sur votre ordinateur. Étape 7 : Docker affiche ticket-api.
À vous de jouer : un autre port hôte
Section intitulée « À vous de jouer : un autre port hôte »Changez une condition : publiez l’API sur le port 9000 de l’hôte et placez-la sur un réseau personnalisé nommé ticket-net. Avant d’exécuter quoi que ce soit, notez deux URL : celle qui fonctionne depuis votre ordinateur et celle qui fonctionne depuis un deuxième conteneur sur ticket-net. Testez ensuite les deux, puis nettoyez.
Afficher la réponse
docker network create ticket-netdocker run -d --name ticket-api --network ticket-net -p 127.0.0.1:9000:8000 -e API_KEY=local-dev-key ticket-api:1.0.0curl -s http://127.0.0.1:9000/healthdocker run --rm --network ticket-net python:3.14.8-slim-trixie python -c "import urllib.request; print(urllib.request.urlopen('http://ticket-api:8000/health').read().decode())"docker rm -f ticket-apidocker network rm ticket-netDepuis votre ordinateur : http://127.0.0.1:9000/health, le port de l’hôte. Depuis le deuxième conteneur : http://ticket-api:8000/health, le port du conteneur. Le port de l’hôte existe seulement sur votre ordinateur. Un appel du deuxième conteneur à http://ticket-api:9000/health donne Connection refused (connexion refusée). Rien dans le conteneur de l’API n’écoute sur le port 9000.
Erreur courante
Le conteneur fonctionne, mais curl indique : Empty reply from server (réponse vide du serveur)
Symptôme : docker ps affiche le conteneur comme Up (en cours d’exécution), avec le port publié. Un appel avec curl au port publié affiche curl: (52) Empty reply from server (réponse vide du serveur).
Cause : Docker a accepté la connexion sur votre ordinateur, mais rien n’a répondu à l’autre bout, dans le conteneur. Trois causes produisent ce même symptôme :
- L’application écoute sur
127.0.0.1dans le conteneur, et non sur0.0.0.0. - Le port du conteneur dans
-pest incorrect. Par exemple,-p 127.0.0.1:8000:80redirige vers le port 80, mais l’application écoute sur le port 8000. Cela a aussi donné(52)ici. - L’application est encore en train de démarrer. Une requête dans la première seconde après le démarrage peut recevoir
(52). Attendez, puis réessayez.
Solution :
- Lisez la ligne de démarrage dans
docker logs <container>:Uvicorn running on http://0.0.0.0:8000(Uvicorn fonctionne sur http://0.0.0.0:8000) est correct.http://127.0.0.1:8000indique un problème d’adresse d’écoute. Modifiez la commande de démarrage pour utiliser--host 0.0.0.0. - Comparez le port de cette ligne avec le deuxième nombre dans
docker port <container>. Ils doivent être identiques. - Si les deux sont corrects, attendez quelques secondes et faites un nouvel appel.
Pour un vrai service d’IA : les serveurs de modèles et les cadres d’API écoutent souvent sur 127.0.0.1 par défaut, par sécurité sur un portable. Dans un conteneur sur une plateforme infonuagique, ce même réglage produit un service qui fonctionne, mais ne répond jamais. La vérification de l’état du service échoue, et la plateforme redémarre le conteneur sans arrêt (module 5).
Vérifiez votre compréhension
Quelle est la différence entre -p 127.0.0.1:8000:8000 et -p 8000:8000?
Afficher la réponse
Les deux redirigent le port 8000 de votre ordinateur vers le port 8000 du conteneur. Avec 127.0.0.1, seuls les programmes sur votre ordinateur peuvent se connecter. Sans adresse, Docker écoute sur toutes les adresses réseau de votre ordinateur (0.0.0.0 et [::]). Tout appareil pouvant joindre votre ordinateur peut alors appeler l’API, par exemple sur le même réseau Wi-Fi. Sous Linux, un tel port peut aussi contourner le pare-feu ufw.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Un conteneur démarre l'application avec --host 127.0.0.1 et son port est publié avec -p 127.0.0.1:8000:8000. docker ps l'affiche comme Up (en cours d'exécution). Prédisez ce que curl http://localhost:8000/health affiche et expliquez pourquoi.
Afficher la réponse
curl: (52) Empty reply from server. Dans le conteneur, 127.0.0.1 désigne le conteneur lui-même. L’application accepte donc uniquement les connexions provenant de l’intérieur du conteneur. Docker redirige la requête vers l’adresse réseau du conteneur (par exemple 172.17.0.2), où rien n’écoute. La solution est d’utiliser --host 0.0.0.0 dans la commande de démarrage du conteneur.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Le conteneur B doit appeler l'API du conteneur A. Pourquoi http://localhost:8000 ne fonctionne-t-il pas dans le conteneur B, et quelle solution fonctionne?
Afficher la réponse
Dans le conteneur B, localhost désigne le conteneur B lui-même, qui n’a aucune API sur le port 8000. Placez les deux conteneurs sur le même réseau personnalisé (docker network create, puis --network pour les deux). Appelez le conteneur A par son nom et son port de conteneur, par exemple http://ticket-api:8000. Aucun port publié n’est nécessaire pour cela.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Récapitulatif
- Un conteneur a son propre réseau.
EXPOSEsert seulement à documenter un port.-p host-address:host-port:container-portle publie. -p 127.0.0.1:...autorise uniquement les connexions de votre ordinateur. Sans l’adresse, tout appareil pouvant joindre votre ordinateur peut se connecter.- L’application dans le conteneur doit écouter sur
0.0.0.0. Avec127.0.0.1, le conteneur fonctionne, mais curl reçoit(52) Empty reply from server(réponse vide du serveur). - Dans un conteneur,
localhostdésigne ce conteneur. Les conteneurs sur un réseau personnalisé se trouvent par leur nom, sur le port du conteneur.
Ensuite, vous fournissez au conteneur ses paramètres et ses secrets au démarrage. L’API s’arrête avec un message clair quand un paramètre obligatoire manque.
Leçon suivante : Configuration à l'exécution