O aplicativo Python não imprime nada ao executar desanexado na janela de encaixe

160

Eu tenho um aplicativo Python (2.7) iniciado no meu dockerfile:

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

main.py imprime algumas strings quando é iniciado e entra em um loop depois:

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

Desde que inicie o contêiner com o sinalizador -it, tudo funcionará conforme o esperado:

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

E eu posso ver a mesma saída via logs depois:

$ docker logs myapp
> App started

Se eu tentar executar o mesmo contêiner com o sinalizador -d, o contêiner parece iniciar normalmente, mas não consigo ver nenhuma saída:

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

Mas o contêiner ainda parece estar funcionando;

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

Anexar também não exibe nada:

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

Alguma idéia que está dando errado? "Imprimir" se comporta de maneira diferente quando executado em segundo plano?

Versão do 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
fonte

Respostas:

266

Finalmente, encontrei uma solução para ver a saída do Python ao executar daemon no Docker, graças a @ahmetalpbalkan no GitHub . Respondendo aqui pessoalmente para mais referências:

Usando saída sem buffer com

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

ao invés de

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

resolve o problema; você pode ver a saída (stderr e stdout) via

docker logs myapp

agora!

jpdus
fonte
2
-u parece funcionar para mim, mas há alguma documentação em algum lugar com uma descrição do que realmente faz?
Little geek
7
Conforme sugerido por outras respostas, você pode tentar definir uma variável de ambiente, ENV PYTHONUNBUFFERED=0caso o -usinalizador não funcione.
Farshid T
1
Este também foi o meu problema. Para uma explicação mais detalhada, consulte stackoverflow.com/a/24183941/562883
Jonathan Stray
Um pouco mais sobre -uaqui: stackoverflow.com/questions/107705/disable-output-buffering
cardamomo
1
Funciona como um sonho em python3, enquanto definir PYTHONUNBUFFERED = 0 não estava ajudando.
Lech Migdal
71

No meu caso, rodar Python com -unão mudou nada. O que fez o truque, no entanto, foi definir PYTHONUNBUFFERED=0como variável de ambiente:

docker run --name=myapp -e PYTHONUNBUFFERED=0 -d myappimage
Vencedor
fonte
6
No meu caso, adicionar -e PYTHONUNBUFFERED=0ajuda.
David Ng
1
Obrigado! Eu estava batendo minha cabeça contra uma parede por horas, e não conseguia fazer os logs trabalharem -u. A sua solução fixa-lo para mim em Docker para Mac com Django
Someguy123
2
Penso que esta é uma solução melhor, que não tem que reconstruir a imagem janela de encaixe para ver as saídas
FF0605
2
Isso é ótimo, obrigado. Vale ressaltar que isso só precisa ser um caractere não vazio para funcionar de acordo com os documentos PYTHONUNBUFFERED
A Star
Trabalhou para a interface docker-compose. Nunca teria adivinhado
deepelement
24

Para mim, é um recurso, não um bug. Sem um pseudo-TTY, não há nada a se desviar. Portanto, uma solução simples é alocar um pseudo-TTY para seu contêiner em execução com:

$ docker run -t ...
Peter Senna
fonte
Isso não fornece uma resposta para a pergunta. Para criticar ou solicitar esclarecimentos a um autor, deixe um comentário abaixo da postagem.
Presidente James K. Polk,
@JamesKPolk, está melhor agora?
Peter Senna
docker não requer um tty pseudo a ser alocada para stdout e stderr
Matt
3
tty: trueem
compost
15

Consulte este artigo que explica os motivos detalhados do comportamento:

Normalmente, existem três modos de buffer:

  • Se um descritor de arquivo não for armazenado em buffer, não ocorre armazenamento em buffer, e as chamadas de função que lêem ou gravam dados ocorrem imediatamente (e serão bloqueadas).
  • Se um descritor de arquivo for totalmente armazenado em buffer, é usado um buffer de tamanho fixo e as chamadas de leitura ou gravação simplesmente leem ou gravam no buffer. O buffer não é liberado até ficar cheio.
  • Se um descritor de arquivo estiver em buffer de linha, o buffer aguardará até que ele veja um caractere de nova linha. Portanto, os dados serão armazenados em buffer e armazenados em buffer até que a \ n seja visto e, em seguida, todos os dados armazenados em buffer serão liberados nesse momento. Na realidade, normalmente há um tamanho máximo no buffer (assim como no caso com buffer total), então a regra é mais como "buffer até que um caractere de nova linha seja visto ou 4096 bytes de dados sejam encontrados, o que ocorrer primeiro".

E o GNU libc (glibc) usa as seguintes regras para armazenar em buffer:

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

Portanto, se usado -t, a partir do documento docker , ele alocará uma pseudo-tty e stdoutse tornará line-buffered, assim,docker run --name=myapp -it myappimage poderia ver a saída de uma linha.

E, se o uso apenas -d, não tty foi alocado, então, stdouté fully-buffered, uma linha App startedcertamente não é capaz de liberar o buffer.

Em seguida, use -dta make stdout line bufferedou add -uem python para flush the bufferé a maneira de corrigi-lo.

na linha
fonte
6

Você pode ver os logs na imagem desanexada se mudar printpara 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"]
The Hog
fonte
1
legais. dica: use Python 3.
adhg 6/09/19
questão está em Python 2 (declaração de impressão sem parênteses), portanto, estou usando 2 aqui. Embora seja exatamente o mesmo comportamento em Python3.6 por isso obrigado por uma dica;)
O Hog
6

Como ainda não vi essa resposta:

Você também pode liberar o stdout após imprimir nele:

import time

if __name__ == '__main__':
    while True:
        print('cleaner is up', flush=True)
        time.sleep(5)
tycl
fonte
1
isso funcionou perfeitamente para mim, estúpido que isso precise estar lá, mas funciona muito bem agora.
jamescampbell
5

Tente adicionar essas duas variáveis ​​de ambiente à sua solução PYTHONUNBUFFERED=1ePYTHONIOENCODING=UTF-8

Lukasz Dynowski
fonte
3

Como uma solução rápida, tente o seguinte:

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

Isso funciona para mim quando encontro os mesmos problemas. Mas, para ser sincero, não sei por que esse erro ocorre.

Vitaly Isaev
fonte
Obrigado pela dica! Tentei substituir todas as impressões pela sua versão, infelizmente não funcionou para mim, ainda não consigo obter nenhuma saída via logs do docker (a alteração entre sys.stderr / sys.stdout não tem resultado visível). Isso é um bug do docker?
Jpdus 16/04
Veja minha resposta , o motivo é: stderr não foi armazenado em buffer, para que você possa corrigi-lo com sua solução.
atline 5/09/19
1

Eu tive que usar PYTHONUNBUFFERED=1no meu arquivo docker-compose.yml para ver a saída do django runserver.

Adam Radestock
fonte
0

Geralmente, nós o redirecionamos para um arquivo específico (montando um volume do host e gravando-o nesse arquivo).

Adicionar um tty usando -t também é bom. Você precisa buscá-lo nos logs da janela de encaixe.

Usando grandes saídas de log, não tive nenhum problema com o armazenamento de buffer sem colocá-lo no log de janelas de encaixe.

Edward Aung
fonte
-1

Se você não estiver usando docker-composee apenas normal docker, adicione-o ao seu Dockerfileque está hospedando um aplicativo de balão

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

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