Додаток Python нічого не друкує під час запуску від'єднаних у докері


160

У мене є додаток Python (2.7), який запускається в моєму докерфілі:

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

main.py друкує деякі рядки, коли він запускається і після цього переходить у цикл:

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

Поки я запускаю контейнер із прапором -it, все працює так, як очікувалося:

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

І я можу побачити той самий вихід через журнали пізніше:

$ docker logs myapp
> App started

Якщо я спробую запустити один і той же контейнер із прапорцем -d, контейнер, здається, запуститься нормально, але я не бачу жодного виводу:

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

Але контейнер як і раніше працює;

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

Додаток також не відображає нічого:

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

Будь-які ідеї, що йде не так? Чи поводиться "друк" по-різному, коли працює у фоновому режимі?

Версія докера:

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

Відповіді:


265

Нарешті я знайшов рішення побачити вихід Python при запуску демонізованого в Docker, завдяки @ahmetalpbalkan в GitHub . Відповідаючи на це тут, для подальшої довідки:

Використання розблокованого виводу за допомогою

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

замість

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

вирішує проблему; Ви можете бачити вихід (і Stderr, і Stdout) через

docker logs myapp

зараз!


2
-у, здається, працює для мене, але чи є десь документація з описом того, що вона насправді робить?
Маленький вундер

7
Як пропонують інші відповіді, ви можете спробувати встановити змінну середовища, ENV PYTHONUNBUFFERED=0якщо -uпрапор не працює.
Фаршид Т

1
Це була і моя проблема. Більш детальне пояснення читайте на сайті stackoverflow.com/a/24183941/562883
Джонатан Страй


1
Працює як сон на python3, тоді як встановлення PYTHONUNBUFFERED = 0 не допомагає.
Лех Мігдал

71

У моєму випадку запуск Python з -uнічого не змінив. Однак, у чому полягає хитрість: встановити PYTHONUNBUFFERED=0змінну середовища:

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

6
У моєму випадку додавання -e PYTHONUNBUFFERED=0допомагає.
Девід Нг

1
Дякую! Я годинами стукав головою об стіну, і навіть не міг змусити журналів працювати -u. Ваше рішення зафіксувало мене на Docker для Mac з Django
Someguy123,

2
Я думаю, що це краще рішення, що нам не доведеться перебудовувати зображення
докера,

2
Це велике спасибі. Варто згадати, що для роботи відповідно до документів PYTHONUNBUFFERED
Зірка

Працював для докер-композитного інтерфейсу. Ніколи б не здогадувався
заглиблення

24

Для мене це особливість, а не помилка. Без псевдо-TTY неможливо нічого зробити. Тому простим рішенням є виділення псевдо-TTY для вашого запущеного контейнера за допомогою:

$ docker run -t ...

Це не дає відповіді на запитання. Щоб критикувати або вимагати роз'яснення у автора, залиште коментар під їх публікацією.
Президент Джеймс К. Полк

@JamesKPolk, це краще зараз?
Пітер Сенна

docker не вимагає виділення псевдо tty для stdout та stderr
Метт

3
tty: trueу складі землі
заглиблення

15

Дивіться цю статтю, яка детально пояснює причину поведінки:

Зазвичай буферизуються три режими:

  • Якщо дескриптор файлу розблокований, то буферизація взагалі не відбувається, а виклики функцій, які читають або записують дані, виникають негайно (і блокуються).
  • Якщо дескриптор файлу повністю буферизований, тоді використовується буфер фіксованого розміру, а для читання або запису дзвінків просто читайте або записуйте з буфера. Буфер не змивається, поки він не заповниться.
  • Якщо дескриптор файлу буферизований рядками, то буферизація чекає, поки він не побачить символ нового рядка. Таким чином, дані будуть буферуватися і буферуватися, поки \ n не буде видно \ n, і тоді всі дані, що завантажуються, будуть очищені в цей момент часу. Насправді буфер зазвичай має максимальний розмір (так само, як у випадку повністю буферизованого випадку), тому правило насправді більше схоже на "буфер, поки не буде показаний символ нового рядка або не знайдеться 4096 байт даних, що відбувається вперше".

І GNU libc (glibc) використовує такі правила буферизації:

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

Отже, якщо використовувати -t, з документа документа , він виділить псевдо-tty, то stdoutстане line-buffered, таким чиномdocker run --name=myapp -it myappimage зможе побачити вихід з одного рядка.

І, якщо просто використовувати -d, не було виділено жодного tty, значить, stdoutє fully-bufferedодин рядокApp started , звичайно , НЕ в змозі очистити буфер.

Потім використовуйте -dtдля make stdout line bufferedабо додати -uв пітона , щоб flush the bufferце спосіб виправити це.



6

Ви можете бачити журнали на відокремленому зображенні, якщо ви переходите printнаlogging .

main.py:

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

Докерфайл:

FROM python:2.7-stretch
ADD . /app
WORKDIR /app
CMD ["python","main.py"]

1
приємно. порада: використовуйте Python 3.
adhg

питання в Python 2 (друкований вислів без дужок), тому я тут використовую 2. Хоча це саме таку поведінку на Python3.6, тому дякую за підказку;)
The Hog

6

Оскільки я ще не бачив такої відповіді:

Ви також можете видалити stdout після друку на ньому:

import time

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

1
для мене це прекрасно працювало, дурне, що це потрібно там бути, але зараз чудово працює.
jamescampbell


3

Для швидкого виправлення спробуйте:

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

Це працює для мене, коли я стикаюся з тими ж проблемами. Але, чесно кажучи, я не знаю, чому трапляється ця помилка.


Дякую за пораду! Спробувавши замінити всі відбитки вашою версією, на жаль, це не спрацювало для мене, досі не можу отримати жодного виводу через докерські журнали (зміна між sys.stderr / sys.stdout не має видимого результату). Це помилка докер?
jpdus

Дивіться мою відповідь , причина полягає в тому, що stderr був нерозподіленим, тому ви можете виправити це за допомогою свого рішення.
atline

1

Мені довелося використовувати PYTHONUNBUFFERED=1у своєму файлі docker-compose.yml, щоб побачити вихід з django runserver.


0

Зазвичай ми переспрямовуємо його до певного файлу (встановивши об'єм з хосту і записуючи його в цей файл).

Додавання tty за допомогою -t також чудово. Його потрібно забрати в докерські журнали.

Використовуючи великі виходи з журналу, у мене не виникло жодних проблем із зберіганням буфера, не вкладаючи його в журнал докерів.


-1

Якщо ви не використовуєте docker-composeі просто нормально, dockerзамість цього, ви можете додати це до свого, Dockerfileщо розміщує додаток для фляжок

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

CMD [ "flask", "run" ]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.