L'app Python non stampa nulla quando viene eseguita staccata nella finestra mobile


160

Ho un'app Python (2.7) avviata nel mio dockerfile:

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

main.py stampa alcune stringhe quando viene avviato e va in un ciclo successivo:

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

Finché avvio il contenitore con il flag -it, tutto funziona come previsto:

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

E in seguito vedrò lo stesso output tramite i log:

$ docker logs myapp
> App started

Se provo a eseguire lo stesso contenitore con il flag -d, il contenitore sembra avviarsi normalmente, ma non riesco a vedere alcun output:

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

Ma il contenitore sembra ancora funzionare;

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

Allega non mostra nulla neanche:

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

Qualche idea di cosa non va? La "stampa" si comporta diversamente quando viene eseguita in background?

Versione 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

Risposte:


266

Alla fine ho trovato una soluzione per vedere l'output di Python durante l'esecuzione daemonizzata in Docker, grazie a @ahmetalpbalkan su GitHub . Rispondendo qui da solo per ulteriori riferimenti:

Utilizzo dell'output senza buffer con

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

invece di

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

risolve il problema; puoi vedere l'output (sia, stderr che stdout) via

docker logs myapp

adesso!


2
-su sembra funzionare per me, ma c'è qualche documentazione da qualche parte con una descrizione di ciò che effettivamente fa?
Piccolo geek il

7
Come suggerito da altre risposte, puoi provare a impostare la variabile di ambiente ENV PYTHONUNBUFFERED=0nel caso in cui il -uflag non funzioni.
Farshid T,

1
Anche questo era il mio problema. Per una spiegazione più dettagliata, consultare stackoverflow.com/a/24183941/562883
Jonathan Stray,


1
Funziona come un sogno su python3, mentre l'impostazione PYTHONUNBUFFERED = 0 non aiuta.
Lech Migdal,

71

Nel mio caso, l'esecuzione di Python con -unon ha cambiato nulla. Quello che ha fatto il trucco, tuttavia, era impostare PYTHONUNBUFFERED=0come variabile d'ambiente:

docker run --name=myapp -e PYTHONUNBUFFERED=0 -d myappimage

6
Nel mio caso, l'aggiunta di -e PYTHONUNBUFFERED=0aiuti.
David Ng,

1
Grazie! Stavo sbattendo la testa da un muro per ore e non riuscivo nemmeno a far funzionare i tronchi -u. La tua soluzione l'ha risolto per me su Docker per Mac con Django
Someguy123

2
penso che questa sia una soluzione migliore, che non dobbiamo ricostruire l'immagine
docker

2
Grazie mille. Vale la pena ricordare che questo deve solo essere un personaggio non vuoto per funzionare secondo i documenti PYTHONUNBUFFERED
A Star

Ha funzionato per l'interfaccia comporre docker. Non avrei mai immaginato
deepelement il

24

Per me è una funzionalità, non un bug. Senza uno pseudo-TTY non c'è nulla a cui stdout. Quindi una soluzione semplice è allocare uno pseudo-TTY per il container in esecuzione con:

$ docker run -t ...

Questo non fornisce una risposta alla domanda. Per criticare o richiedere chiarimenti a un autore, lascia un commento sotto il suo post.
Presidente James K. Polk,

@JamesKPolk, è meglio adesso?
Peter Senna,

la finestra mobile non richiede l'assegnazione di una pseudo tty per stdout e stderr
Matt

3
tty: truein terra composta
deepelement

15

Vedi questo articolo che spiega i motivi dettagliati del comportamento:

Esistono in genere tre modalità per il buffering:

  • Se un descrittore di file è senza buffer, allora non si verifica alcun buffering e le chiamate di funzione che leggono o scrivono dati si verificano immediatamente (e si bloccheranno).
  • Se un descrittore di file è completamente bufferizzato, viene utilizzato un buffer di dimensioni fisse e le chiamate di lettura o scrittura vengono semplicemente lette o scritte dal buffer. Il buffer non viene scaricato fino a quando non si riempie.
  • Se un descrittore di file è bufferizzato, il buffering attende fino a quando non vede un carattere di nuova riga. Quindi i dati verranno bufferizzati e bufferizzati fino a quando non viene visualizzato un \ n, quindi tutti i dati bufferizzati verranno scaricati a quel punto nel tempo. In realtà, in genere esiste una dimensione massima nel buffer (proprio come nel caso del buffer completo), quindi la regola è in realtà più simile a "buffer fino a quando non viene visualizzato un carattere di nuova riga o vengono rilevati 4096 byte di dati, a seconda di quale evento si verifica per primo".

E GNU libc (glibc) usa le seguenti regole per il buffering:

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

Quindi, se utilizzato -t, dal documento docker , allocherà una pseudo-tty, quindi stdoutdiventa line-buffered, quindi docker run --name=myapp -it myappimagepotrebbe vedere l'output di una riga.

E, se proprio uso -d, non tty stato allocato, quindi, stdoutè fully-buffered, una linea App startedsicuramente non in grado di scaricare il buffer.

Quindi, l'uso -dtdi make stdout line bufferedo aggiungere -uin Python per flush the bufferè il modo per risolvere il problema.



6

È possibile visualizzare i registri sull'immagine separata se si cambia printin 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"]

1
simpatico. consiglio: usa Python 3.
adhg

la domanda è in Python 2 (istruzione di stampa senza parentesi) quindi sto usando 2 qui. Anche se è esattamente lo stesso comportamento su Python3.6, quindi grazie per un suggerimento;)
The Hog

6

Dal momento che non ho ancora visto questa risposta:

Puoi anche scaricare lo stdout dopo averlo stampato:

import time

if __name__ == '__main__':
    while True:
        print('cleaner is up', flush=True)
        time.sleep(5)

1
questo ha funzionato perfettamente per me, stupido che questo deve essere lì, ma funziona benissimo ora.
Jamescampbell,

5

Prova ad aggiungere queste due variabili d'ambiente alla tua soluzione PYTHONUNBUFFERED=1ePYTHONIOENCODING=UTF-8


3

Come soluzione rapida, prova questo:

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

Questo funziona per me quando incontro gli stessi problemi. Ma, a dire il vero, non so perché si verifichi questo errore.


Grazie per il consiglio! Ho provato a sostituire tutte le stampe con la tua versione, sfortunatamente non ha funzionato per me, ancora non riesco a ottenere alcun output tramite i log docker (il passaggio tra sys.stderr / sys.stdout non ha risultati visibili). È un bug docker?
jpdus,

Vedi la mia risposta , il motivo è: stderr era senza buffer, quindi puoi risolverlo con la tua soluzione.
atline

1

Ho dovuto usare il PYTHONUNBUFFERED=1mio file docker-compose.yml per vedere l'output del runner di django.


0

Di solito, lo reindirizziamo a un file specifico (montando un volume dall'host e scrivendolo su quel file).

Anche aggiungere un tty usando -t va bene. Devi raccoglierlo nei registri della finestra mobile.

Utilizzando output di log di grandi dimensioni, non ho riscontrato alcun problema con la memorizzazione del buffer senza metterlo nel registro delle finestre mobili.


-1

Se non lo stai utilizzando docker-composee dockerinvece è normale , puoi aggiungerlo al tuo Dockerfileche ospita un'app di pallone

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

CMD [ "flask", "run" ]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.