Déploiement d'une application Flask minimale dans Docker - Problèmes de connexion au serveur

108

J'ai une application dont la seule dépendance est flask, qui fonctionne bien en dehors de docker et se lie au port par défaut 5000. Voici la source complète:

from flask import Flask

app = Flask(__name__)
app.debug = True

@app.route('/')
def main():
    return 'hi'

if __name__ == '__main__':
    app.run()

Le problème est que lorsque je déploie ceci dans docker, le serveur est en cours d'exécution mais est inaccessible de l'extérieur du conteneur.

Voici mon Dockerfile. L'image est ubuntu avec flacon installé. Le tar contient juste ce qui index.pyprécède;

# Dockerfile
FROM dreen/flask
MAINTAINER dreen
WORKDIR /srv

# Get source
RUN mkdir -p /srv
COPY perfektimprezy.tar.gz /srv/perfektimprezy.tar.gz
RUN tar x -f perfektimprezy.tar.gz
RUN rm perfektimprezy.tar.gz

# Run server
EXPOSE 5000
CMD ["python", "index.py"]

Voici les étapes que je fais pour déployer

$> sudo docker build -t perfektimprezy .

Pour autant que je sache, ce qui précède fonctionne bien, l'image contient le contenu du tar /srv. Maintenant, démarrons le serveur dans un conteneur:

$> sudo docker run -i -p 5000:5000 -d perfektimprezy
1c50b67d45b1a4feade72276394811c8399b1b95692e0914ee72b103ff54c769

Est-ce qu'il fonctionne réellement?

$> sudo docker ps
CONTAINER ID        IMAGE                   COMMAND             CREATED             STATUS              PORTS                    NAMES
1c50b67d45b1        perfektimprezy:latest   "python index.py"   5 seconds ago       Up 5 seconds        0.0.0.0:5000->5000/tcp   loving_wozniak

$> sudo docker logs 1c50b67d45b1
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat

Oui, il semble que le serveur flask fonctionne. Voici où ça devient bizarre. Permet de faire une demande au serveur:

 $> curl 127.0.0.1:5000 -v
 * Rebuilt URL to: 127.0.0.1:5000/
 * Hostname was NOT found in DNS cache
 *   Trying 127.0.0.1...
 * Connected to 127.0.0.1 (127.0.0.1) port 5000 (#0)
 > GET / HTTP/1.1
 > User-Agent: curl/7.35.0
 > Host: 127.0.0.1:5000
 > Accept: */*
 >
 * Empty reply from server
 * Connection #0 to host 127.0.0.1 left intact
 curl: (52) Empty reply from server

Réponse vide ... Mais le processus est-il en cours d'exécution?

$> sudo docker top 1c50b67d45b1
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                2084                812                 0                   10:26               ?                   00:00:00            python index.py
root                2117                2084                0                   10:26               ?                   00:00:00            /usr/bin/python index.py

Maintenant, ssh dans le serveur et vérifions ...

$> sudo docker exec -it 1c50b67d45b1 bash
root@1c50b67d45b1:/srv# netstat -an
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 127.0.0.1:5000          0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:47677         127.0.0.1:5000          TIME_WAIT
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags       Type       State         I-Node   Path
root@1c50b67d45b1:/srv# curl -I 127.0.0.1:5000
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 5447
Server: Werkzeug/0.10.4 Python/2.7.6
Date: Tue, 19 May 2015 12:18:14 GMT

C'est bien ... mais pas de l'extérieur: (Qu'est-ce que je fais de mal?

Dreen
la source
la chose pertinente est "Causé par <class 'httplib.BadStatusLine'>", voir stackoverflow.com/questions/16592568
...
J'essaie seulement de me connecter une fois et je suis assez certain que ce n'est pas un bogue dans httpie (j'ai changé l'exemple en curl maintenant), ni dans le serveur car cela fonctionne bien en dehors de docker. J'ai le sentiment fort qu'il s'agit d'un problème de faux pas de configuration / déploiement de docker
Dreen
Archivez le conteneur avec docker exec -it 1c50b67d45b1 bash, puis la netstat -ancommande habituelle ou n'importe quelle commande que vous feriez lorsque vous déboguez un Flask (queue, chat ...)
user2915097
@ user2915097: ive a ajouté une sortie depuis le serveur
Dreen
'Can't connect ...' @Dreen, vous pouvez vous connecter , vous obtenez juste une réponse vide ( Connected to 127.0.0.1)
ForceBru

Réponses:

179

Le problème est que vous ne liez qu'à l'interface localhost, vous devriez être lié 0.0.0.0si vous voulez que le conteneur soit accessible de l'extérieur. Si vous changez:

if __name__ == '__main__':
    app.run()

à

if __name__ == '__main__':
    app.run(host='0.0.0.0')

Cela devrait marcher.

Adrian Mouat
la source
Cette solution fonctionne. On peut voir le résultat en utilisant un script Dockerfile et python complet ici, comme le décrit cette solution.
quelle est la différence?
Jwan622
1
@ Jwan622 L'interface localhost n'est disponible qu'à l'intérieur du conteneur. 0.0.0.0 se lie à toutes les interfaces. Les interfaces se connectent à divers réseaux (vous pouvez donc en avoir un pour le wifi, les réseaux locaux, etc.)
Adrian Mouat
N'oubliez pas de lier le port 5000 à votre conteneur avec l' -p 5000:5000indicateur avec votre docker runcommande.
T Loch.
40

Lorsque vous utilisez la flaskcommande au lieu de app.run, vous pouvez transmettre l' --hostoption de changement d'hôte. La ligne dans Docker serait:

CMD ["flask", "run", "--host", "0.0.0.0"]

ou

CMD flask run --host 0.0.0.0
Marku
la source
2
Merci beaucoup pour cette solution, j'ai le même problème, savez-vous quelle est la raison pour laquelle app.run(host="0.0.0.0") ne fonctionne pas? J'ai également fait un post pour cette question: stackoverflow.com/q/53133350/3279996
xirururu
1
Sur cette note, assurez-vous de ne pas utiliser python run.py --host=0.0.0.0 . Cela me rend de temps en temps en raison de mes conventions de dénomination. Ce code semblera fonctionner mais le serveur fonctionnera sur l'hôte local.
Braden Holt le
1
Cela n'exécute-t-il pas le serveur flask intégré, qui n'est pas destiné aux environnements de production?
code_dredd
C'est génial, pour une raison quelconque, presque toutes les informations Flask-with-Docker ne parviennent pas à utiliser la CLI Flask, qui est le moyen Flask 1.0 de démarrer une application.
Zach Valenta
3
Ici en 2020 avec Flask 1.1.1 avec app.run(host="0.0.0.0")échouer et CMD ["flask", "run", "--host", "0.0.0.0" ]travailler comme un champion.
Joe
2

Votre conteneur Docker possède plusieurs interfaces réseau. Par exemple, mon conteneur a les éléments suivants:

$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
32: eth0@if33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

si vous exécutez docker network inspect bridge, vous pouvez voir que votre conteneur est connecté à ce pont avec la deuxième interface dans la sortie ci-dessus. Ce pont par défaut est également connecté au processus Docker sur votre hôte.

Par conséquent, vous devrez exécuter la commande:

CMD flask run --host 172.17.0.2

Pour accéder à votre application Flask exécutée dans un conteneur Docker à partir de votre machine hôte. Remplacez 172.17.0.2par quelle que soit l'adresse IP particulière de votre conteneur.

Bert
la source
2

Pour construire sur d'autres réponses:

Imaginez que vous ayez deux ordinateurs. Chaque ordinateur dispose d'une interface réseau (WiFi, par exemple), qui est son adresse IP publique. Chaque ordinateur a une interface de bouclage / localhost, à 127.0.0.1. Cela signifie «juste cet ordinateur».

Si vous avez répertorié 127.0.0.1 sur l'ordinateur A, vous ne vous attendriez pas à pouvoir vous y connecter via 127.0.0.1 lors de l'exécution sur l'ordinateur B. Après tout, vous avez demandé à écouter sur l' adresse privée locale de l' ordinateur A.

Docker est une configuration similaire; techniquement, c'est le même ordinateur, mais le noyau Linux permet à chaque conteneur de fonctionner avec sa propre pile réseau isolée. Ainsi, 127.0.0.1 dans un conteneur est identique à 127.0.0.1 sur un autre ordinateur que votre hôte - vous ne pouvez pas vous y connecter.

Version plus longue, avec schémas: https://pythonspeed.com/articles/docker-connection-refused/

Itamar Turner-Trauring
la source
0

Tout d'abord, dans votre script python, vous devez changer le code de

app.run()

à

app.run(host="0.0.0.0")

Deuxièmement, dans votre fichier docker, la dernière ligne devrait ressembler à

CMD ["flask", "run", "-h", "0.0.0.0", "-p", "5000"]

Et sur la machine hôte, si 0.0.0.0:5000cela ne fonctionne pas, vous devriez essayer aveclocalhost:5000

Remarque - La commande CMD doit être correcte. Parce que la commande CMD fournit des valeurs par défaut pour l'exécution du conteneur.

Nivedita Shah
la source