La aplicación Python no imprime nada cuando se ejecuta separada en la ventana acoplable

160

Tengo una aplicación Python (2.7) que se inicia en mi dockerfile:

CMD ["python","main.py"]

main.py imprime algunas cadenas cuando se inicia y luego entra en un bucle:

print "App started"
while True:
    time.sleep(1)

Mientras inicio el contenedor con el indicador -it, todo funciona como se esperaba:

$ docker run --name=myapp -it myappimage
> App started

Y puedo ver la misma salida a través de registros más tarde:

$ docker logs myapp
> App started

Si intento ejecutar el mismo contenedor con el indicador -d, el contenedor parece comenzar normalmente, pero no puedo ver ningún resultado:

$ docker run --name=myapp -d myappimage
> b82db1120fee5f92c80000f30f6bdc84e068bafa32738ab7adb47e641b19b4d1
$ docker logs myapp
$ (empty)

Pero el contenedor todavía parece funcionar;

$ docker ps
Container Status ...
myapp     up 4 minutes ... 

Adjuntar tampoco muestra nada:

$ docker attach --sig-proxy=false myapp
(working, no output)

¿Alguna idea de qué va mal? ¿"Print" se comporta de manera diferente cuando se ejecuta en segundo plano?

Versión de Docker:

Client version: 1.5.0
Client API version: 1.17
Go version (client): go1.4.2
Git commit (client): a8a31ef
OS/Arch (client): linux/arm
Server version: 1.5.0
Server API version: 1.17
Go version (server): go1.4.2
Git commit (server): a8a31ef
jpdus
fuente

Respuestas:

266

Finalmente encontré una solución para ver la salida de Python cuando se ejecuta daemonized en Docker, gracias a @ahmetalpbalkan en GitHub . Respondiendo aquí yo mismo para mayor referencia:

Usar salida sin búfer con

CMD ["python","-u","main.py"]

en vez de

CMD ["python","main.py"]

resuelve el problema; Puede ver la salida (stderr y stdout) a través de

docker logs myapp

¡ahora!

jpdus
fuente
2
-u parece funcionar para mí, pero ¿hay alguna documentación en alguna parte con una descripción de lo que realmente hace?
Pequeño geek
77
Como lo sugieren otras respuestas, puede intentar establecer una variable de entorno ENV PYTHONUNBUFFERED=0en caso de que el -uindicador no funcione.
Farshid T
1
Este fue mi problema también. Para obtener una explicación más detallada, consulte stackoverflow.com/a/24183941/562883
Jonathan Stray el
Un poco más sobre -uaquí: stackoverflow.com/questions/107705/disable-output-buffering
cardamomo
1
Funciona como un sueño en python3, mientras que configurar PYTHONUNBUFFERED = 0 no fue de ayuda.
Lech Migdal
71

En mi caso, ejecutar Python con -uno cambió nada. Sin embargo, el truco fue establecer PYTHONUNBUFFERED=0como variable de entorno:

docker run --name=myapp -e PYTHONUNBUFFERED=0 -d myappimage
Víctor
fuente
66
En mi caso, agregar -e PYTHONUNBUFFERED=0ayuda.
David Ng
1
¡Gracias! Estuve golpeando mi cabeza contra la pared durante horas, y no pude conseguir troncos para trabajar incluso con ellos -u. Su solución me lo arregló en Docker para Mac con Django
Someguy123
2
Creo que esta es una mejor solución, que no tenemos que reconstruir la imagen del
acoplador
2
Esto es genial, gracias. Vale la pena mencionar que esto solo debe ser un personaje no vacío para funcionar de acuerdo con los documentos PYTHONUNBUFFERED
A Star
Trabajó para la interfaz docker-compose. Nunca lo habría adivinado
profundización
24

Para mí es una característica, no un error. Sin un pseudo-TTY no hay nada a lo que stdout. Entonces, una solución simple es asignar un pseudo-TTY para su contenedor en ejecución con:

$ docker run -t ...
Peter Senna
fuente
Esto no proporciona una respuesta a la pregunta. Para criticar o solicitar una aclaración de un autor, deje un comentario debajo de su publicación.
Presidente James K. Polk
@JamesKPolk, ¿está mejor ahora?
Peter Senna
docker no requiere que se asigne un pseudo tty para stdout y stderr
Matt
3
tty: trueen tierra compuesta
profundización
15

Vea este artículo que explica la razón detallada del comportamiento:

Normalmente hay tres modos de almacenamiento en búfer:

  • Si un descriptor de archivo no tiene búfer, no se produce ningún búfer, y las llamadas a funciones que leen o escriben datos ocurren inmediatamente (y se bloquearán).
  • Si un descriptor de archivo está completamente protegido, entonces se usa un buffer de tamaño fijo, y las llamadas de lectura o escritura simplemente leen o escriben desde el buffer. El búfer no se vacía hasta que se llena.
  • Si un descriptor de archivo tiene un buffer de línea, entonces el buffer espera hasta que vea un carácter de nueva línea. Por lo tanto, los datos se almacenarán en el búfer y en el búfer hasta que se vea un \ n, y luego todos los datos que se almacenan en el búfer se vacían en ese momento. En realidad, generalmente hay un tamaño máximo en el búfer (al igual que en el caso de búfer completo), por lo que la regla es en realidad más como "búfer hasta que se ve un carácter de nueva línea o se encuentran 4096 bytes de datos, lo que ocurra primero".

Y GNU libc (glibc) usa las siguientes reglas para el almacenamiento en búfer:

Stream               Type          Behavior
stdin                input         line-buffered
stdout (TTY)         output        line-buffered
stdout (not a TTY)   output        fully-buffered
stderr               output        unbuffered

Entonces, si se usa -t, desde el documento de docker , asignará un pseudo-tty, luego se stdoutconvierte line-buffered, por lo que docker run --name=myapp -it myappimagepodría ver la salida de una línea.

Y, si solo se usa -d, no se asignó tty, entonces, stdoutes decir fully-buffered, una línea App startedseguramente no puede vaciar el búfer.

Entonces, el uso -dtde make stdout line bufferedo complemento -uen python a flush the bufferes la forma de solucionarlo.

en línea
fuente
6

Puede ver registros en la imagen separada si cambia printa logging.

main.py:

import time
import logging
print "App started"
logging.warning("Log app started")
while True:
    time.sleep(1)

Dockerfile:

FROM python:2.7-stretch
ADD . /app
WORKDIR /app
CMD ["python","main.py"]
El cerdo
fuente
1
bonito. consejo: use Python 3.
adhg el
la pregunta está en Python 2 (declaración de impresión sin paréntesis), por lo tanto, estoy usando 2 aquí. Aunque es exactamente el mismo comportamiento en Python3.6, así que gracias por un consejo;)
The Hog
6

Como todavía no he visto esta respuesta:

También puede eliminar stdout después de imprimir en él:

import time

if __name__ == '__main__':
    while True:
        print('cleaner is up', flush=True)
        time.sleep(5)
tycl
fuente
1
Esto funcionó perfectamente para mí, estúpido que esto necesita estar allí, pero funciona muy bien ahora.
jamescampbell
5

Intente agregar estas dos variables de entorno a su solución PYTHONUNBUFFERED=1yPYTHONIOENCODING=UTF-8

Lukasz Dynowski
fuente
3

Como solución rápida, intente esto:

from __future__ import print_function
# some code
print("App started", file=sys.stderr)

Esto funciona para mí cuando me encuentro con los mismos problemas. Pero, para ser honesto, no sé por qué ocurre este error.

Vitaly Isaev
fuente
¡Gracias por el consejo! Intenté reemplazar todas las impresiones con su versión, desafortunadamente no funcionó para mí, todavía no puedo obtener ningún resultado a través de los registros de la ventana acoplable (cambiar entre sys.stderr / sys.stdout no tiene ningún resultado visible). ¿Es esto un error de docker?
jpdus
Vea mi respuesta , la razón es: stderr no tenía búfer, por lo que puede solucionarlo con su solución.
el
1

Tuve que usar PYTHONUNBUFFERED=1en mi archivo docker-compose.yml para ver la salida de django runserver.

Adam Radestock
fuente
0

Por lo general, lo redirigimos a un archivo específico (montando un volumen desde el host y escribiéndolo en ese archivo).

Agregar un tty usando -t también está bien. Debe recogerlo en los registros de la ventana acoplable.

Utilizando salidas de registro grandes, no tuve ningún problema con el almacenamiento del búfer sin ponerlo en el registro de los acopladores.

Edward Aung
fuente
-1

Si no está usando docker-composey simplemente es normal docker, puede agregar esto a su Dockerfilehost de una aplicación de matraz

ARG FLASK_ENV="production"
ENV FLASK_ENV="${FLASK_ENV}" \
    PYTHONUNBUFFERED="true"

CMD [ "flask", "run" ]
G Wayne
fuente