L'application Python n'imprime rien lors de l'exécution de détaché dans le docker

160

J'ai une application Python (2.7) qui démarre dans mon dockerfile:

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

main.py imprime des chaînes au démarrage et entre dans une boucle par la suite:

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

Tant que je démarre le conteneur avec l'indicateur -it, tout fonctionne comme prévu:

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

Et je peux voir la même sortie via les journaux plus tard:

$ docker logs myapp
> App started

Si j'essaie d'exécuter le même conteneur avec l'indicateur -d, le conteneur semble démarrer normalement, mais je ne vois aucune sortie:

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

Mais le conteneur semble toujours fonctionner;

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

Attach n'affiche rien non plus:

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

Des idées qui ne vont pas? «Print» se comporte-t-il différemment lorsqu'il est exécuté en arrière-plan?

Version 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
la source

Réponses:

266

Enfin, j'ai trouvé une solution pour voir la sortie Python lors de l'exécution démonisée dans Docker, grâce à @ahmetalpbalkan sur GitHub . Répondant ici moi-même pour référence ultérieure:

Utilisation de la sortie sans tampon avec

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

au lieu de

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

résout le problème; vous pouvez voir la sortie (à la fois, stderr et stdout) via

docker logs myapp

maintenant!

jpdus
la source
2
-u semble fonctionner pour moi, mais y a-t-il une documentation quelque part avec une description de ce qu'il fait réellement?
Little geek
7
Comme suggéré par d'autres réponses, vous pouvez essayer de définir une variable d'environnement ENV PYTHONUNBUFFERED=0au cas où l' -uindicateur ne fonctionnerait pas.
Farshid T
1
C'était aussi mon problème. Pour une explication plus détaillée, voir stackoverflow.com/a/24183941/562883
Jonathan Stray
1
Fonctionne comme un rêve sur python3, alors que définir PYTHONUNBUFFERED = 0 n'aidait pas.
Lech Migdal
71

Dans mon cas, exécuter Python avec -un'a rien changé. L'astuce, cependant, était de définir PYTHONUNBUFFERED=0comme variable d'environnement:

docker run --name=myapp -e PYTHONUNBUFFERED=0 -d myappimage
Victor
la source
6
Dans mon cas, l'ajout -e PYTHONUNBUFFERED=0aide.
David Ng
1
Je vous remercie! Je me cognais la tête contre un mur pendant des heures et je ne pouvais même pas faire fonctionner les journaux -u. Votre solution l'a corrigé pour moi sur Docker pour Mac avec Django
Someguy123
2
je pense que c'est une meilleure solution, que nous n'avons pas à reconstruire l'image du docker pour voir les sorties
FF0605
2
C'est un grand merci. Il convient de mentionner que cela doit juste être un personnage non vide pour fonctionner selon la documentation PYTHONUNBUFFERED
A Star
A travaillé pour l'interface docker-compose. Je
n'aurais
24

Pour moi, c'est une fonctionnalité, pas un bug. Sans pseudo-TTY, il n'y a rien sur lequel sortir. Une solution simple consiste donc à allouer un pseudo-TTY à votre conteneur en cours d'exécution avec:

$ docker run -t ...
Peter Senna
la source
Cela ne répond pas à la question. Pour critiquer ou demander des éclaircissements à un auteur, laissez un commentaire sous sa publication.
Président James K. Polk
@JamesKPolk, est-ce mieux maintenant?
Peter Senna
docker ne nécessite pas d'allocation de pseudo tty pour stdout et stderr
Matt
3
tty: truedans compose land
deepelement
15

Consultez cet article qui explique en détail la raison du comportement:

Il existe généralement trois modes de mise en mémoire tampon:

  • Si un descripteur de fichier n'est pas mis en tampon, aucune mise en mémoire tampon ne se produit et les appels de fonction qui lisent ou écrivent des données se produisent immédiatement (et se bloquent).
  • Si un descripteur de fichier est entièrement tamponné, un tampon de taille fixe est utilisé et les appels de lecture ou d'écriture lisent ou écrivent simplement à partir du tampon. Le tampon n'est pas vidé tant qu'il ne se remplit pas.
  • Si un descripteur de fichier est mis en tampon en ligne, le tampon attend jusqu'à ce qu'il voit un caractère de nouvelle ligne. Ainsi, les données seront mises en tampon et en mémoire tampon jusqu'à ce qu'un \ n soit vu, puis toutes les données qui ont été mises en mémoire tampon sont vidées à ce moment-là. En réalité, il y a généralement une taille maximale sur le tampon (tout comme dans le cas de la mémoire tampon complète), donc la règle est en fait plus comme «tampon jusqu'à ce qu'un caractère de nouvelle ligne soit vu ou 4096 octets de données sont rencontrés, selon la première éventualité».

Et GNU libc (glibc) utilise les règles suivantes pour la mise en mémoire tampon:

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

Donc, si vous l'utilisez -t, à partir du document docker , il allouera un pseudo-tty, puis stdoutdevient line-buffered, ainsi docker run --name=myapp -it myappimagepourrait voir la sortie sur une ligne.

Et, si l' utilisation juste -d, pas TTY a été attribué, alors, stdoutest fully-buffered, une ligne App startedsûrement pas en mesure de vider le tampon.

Ensuite, l' utilisation -dtde make stdout line bufferedou ajouter -uen python flush the bufferest le moyen de le réparer.

à la ligne
la source
6

Vous pouvez voir les journaux sur l'image détachée si vous passez printà 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"]
Le porc
la source
1
agréable. astuce: utilisez Python 3.
adhg
La question est en Python 2 (instruction d'impression sans parenthèses) donc j'utilise 2 ici. Bien que ce soit exactement le même comportement sur Python3.6, merci pour un conseil;)
The Hog
6

Puisque je n'ai pas encore vu cette réponse:

Vous pouvez également vider stdout après avoir imprimé dessus:

import time

if __name__ == '__main__':
    while True:
        print('cleaner is up', flush=True)
        time.sleep(5)
tycl
la source
1
cela a parfaitement fonctionné pour moi, stupide que cela doit être là, mais fonctionne très bien maintenant.
jamescampbell
5

Essayez d'ajouter ces deux variables d'environnement à votre solution PYTHONUNBUFFERED=1etPYTHONIOENCODING=UTF-8

Lukasz Dynowski
la source
3

En guise de solution rapide, essayez ceci:

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

Cela fonctionne pour moi lorsque je rencontre les mêmes problèmes. Mais, pour être honnête, je ne sais pas pourquoi cette erreur se produit.

Vitaly Isaev
la source
Merci pour le conseil! J'ai essayé de remplacer toutes les impressions par votre version, malheureusement cela n'a pas fonctionné pour moi, je ne peux toujours pas obtenir de sortie via les journaux docker (le changement entre sys.stderr / sys.stdout n'a pas de résultat visible). Est-ce un bug de docker?
jpdus
Voir ma réponse , la raison est: stderr n'a pas été tamponné, vous pouvez donc le réparer avec votre solution.
atline le
1

J'ai dû utiliser PYTHONUNBUFFERED=1dans mon fichier docker-compose.yml pour voir la sortie de django runserver.

Adam Radestock
la source
0

Habituellement, nous le redirigeons vers un fichier spécifique (en montant un volume depuis l'hôte et en l'écrivant dans ce fichier).

Ajouter un tty en utilisant -t est également très bien. Vous devez le récupérer dans les journaux du docker.

En utilisant de grandes sorties de journal, je n'ai eu aucun problème avec le stockage du tampon sans le mettre dans le journal des dockers.

Edward Aung
la source
-1

Si vous n'utilisez pas docker-composeet simplement normal à la dockerplace, vous pouvez ajouter ceci à votre Dockerfilequi héberge une application flask

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

CMD [ "flask", "run" ]
G Wayne
la source