Implementación de una aplicación de matraz mínimo en Docker: problemas de conexión del servidor

108

Tengo una aplicación cuya única dependencia es el matraz, que funciona bien fuera de la ventana acoplable y se une al puerto predeterminado 5000. Aquí está la fuente completa:

from flask import Flask

app = Flask(__name__)
app.debug = True

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

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

El problema es que cuando implemento esto en la ventana acoplable, el servidor se está ejecutando pero no se puede acceder desde fuera del contenedor.

A continuación se muestra mi Dockerfile. La imagen es ubuntu con el matraz instalado. El alquitrán solo contiene lo index.pyenumerado anteriormente;

# 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"]

Estos son los pasos que estoy siguiendo para implementar

$> sudo docker build -t perfektimprezy .

Por lo que sé, lo anterior funciona bien, la imagen tiene el contenido del tar en formato /srv. Ahora, iniciemos el servidor en un contenedor:

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

¿Está realmente funcionando?

$> 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

Sí, parece que el servidor de matraces se está ejecutando. Aquí es donde se vuelve extraño. Hagamos una solicitud al servidor:

 $> 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

Respuesta vacía ... ¿Pero se está ejecutando el proceso?

$> 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

Ahora ingresemos al servidor y verifiquemos ...

$> 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

Está bien ... pero no desde afuera :( ¿Qué estoy haciendo mal?

Dreen
fuente
lo relevante es "Causado por <class 'httplib.BadStatusLine'>", consulte stackoverflow.com/questions/16592568/…
user2915097
Sin embargo, solo estoy tratando de conectarme una vez y estoy bastante seguro de que esto no es un error en httpie (cambié el ejemplo a curl ahora), ni en el servidor, ya que funciona bien fuera de la ventana acoplable. Tengo la sensación de que se trata de un error de configuración / implementación de
Docker
Verifique el contenedor con docker exec -it 1c50b67d45b1 bashy luego el netstat -ancomando habitual o cualquier comando que haría cuando depura un Flask (tail, cat ...)
user2915097
@ user2915097: he agregado algunos resultados desde el servidor
Dreen
'No se puede conectar ...' @Dreen, puede conectarse , solo obtiene una respuesta vacía ( Connected to 127.0.0.1)
ForceBru

Respuestas:

179

El problema es que solo se vincula a la interfaz localhost, debe vincularse 0.0.0.0si desea que el contenedor sea accesible desde el exterior. Si cambia:

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

a

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

Deberia de funcionar.

Adrian Mouat
fuente
Esta solución funciona. Uno puede ver el resultado usando un script completo de Dockerfile y Python aquí como describe esta solución.
¿cual es la diferencia?
Jwan622
1
@ Jwan622 La interfaz localhost solo está disponible dentro del contenedor. 0.0.0.0 se une a todas las interfaces. Las interfaces se conectan a varias redes (por lo que podría tener una para wifi, lans, etc.)
Adrian Mouat
Asegúrese de recordar vincular el puerto 5000 a su contenedor con la -p 5000:5000bandera con su docker runcomando.
T Loch.
39

Al usar el flaskcomando en lugar de app.run, puede pasar la --hostopción para cambiar el host. La línea en Docker sería:

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

o

CMD flask run --host 0.0.0.0
Marku
fuente
2
Muchas gracias por esta solución, tengo el mismo problema, ¿sabes cuál es la razón por la app.run(host="0.0.0.0") que no funciona? También hice una publicación para esta pregunta: stackoverflow.com/q/53133350/3279996
xirururu
1
En esta nota, asegúrese de no usar python run.py --host=0.0.0.0 . Eso me atrapa de vez en cuando debido a mis convenciones de nomenclatura. Ese código parecerá funcionar, pero el servidor se ejecutará en el host local.
Braden Holt
¿No ejecuta esto el servidor de matraces incorporado, que no está diseñado para entornos de producción?
code_dredd
Esto es genial, por la razón que sea, casi toda la información de Flask-with-Docker no usa la CLI de Flask, que afaik es la forma Flask 1.0 de iniciar una aplicación.
Zach Valenta
3
Aquí en 2020 con Flask 1.1.1 app.run(host="0.0.0.0")fallando y CMD ["flask", "run", "--host", "0.0.0.0" ]trabajando como un campeón.
Joe
1

Su contenedor Docker tiene más de una interfaz de red. Por ejemplo, mi contenedor tiene lo siguiente:

$ 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 tu corres docker network inspect bridge , puede ver que su contenedor está conectado a ese puente con la segunda interfaz en el resultado anterior. Este puente predeterminado también está conectado al proceso de Docker en su host.

Por lo tanto, tendría que ejecutar el comando:

CMD flask run --host 172.17.0.2

Para acceder a su aplicación Flask que se ejecuta en un contenedor Docker desde su máquina host. Reemplace 172.17.0.2con cualquier dirección IP particular de su contenedor.

Bert
fuente
1

Para aprovechar otras respuestas:

Imagina que tienes dos computadoras. Cada computadora tiene una interfaz de red (WiFi, por ejemplo), que es su IP pública. Cada computadora tiene una interfaz loopback / localhost, en 127.0.0.1. Esto significa "solo esta computadora".

Si enumeró 127.0.0.1 en la computadora A, no esperaría poder conectarse a través de 127.0.0.1 cuando se ejecuta en la computadora B. Después de todo, pidió escuchar en el local de la computadora A la dirección privada

Docker tiene una configuración similar; técnicamente es la misma computadora, pero el kernel de Linux permite que cada contenedor se ejecute con su propia pila de red aislada. Por lo tanto, 127.0.0.1 en un contenedor es lo mismo que 127.0.0.1 en una computadora diferente a su host; no puede conectarse a él.

Versión más larga, con diagramas: https://pythonspeed.com/articles/docker-connection-refused/

Itamar Turner-Trauring
fuente
0

En primer lugar, en su secuencia de comandos de Python, debe cambiar el código de

app.run()

a

app.run(host="0.0.0.0")

En segundo lugar, en su archivo docker, la última línea debería ser como

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

Y en la máquina host si0.0.0.0:5000 no funciona, debería intentarlo conlocalhost:5000

Nota: el comando CMD debe ser el adecuado. Porque el comando CMD proporciona valores predeterminados para ejecutar el contenedor.

Nivedita Shah
fuente