Приложение Python ничего не печатает при запуске в автономном режиме


160

У меня есть приложение Python (2.7), которое запускается в моем dockerfile:

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 ... 

Attach также ничего не отображает:

$ 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

Ответы:


266

Наконец, я нашел решение увидеть вывод Python при запуске daemonized в Docker, благодаря @ahmetalpbalkan на GitHub . Отвечая на это здесь сам для дальнейшей ссылки:

Использование небуферизованного вывода с

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

вместо того

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

решает проблему; вы можете увидеть вывод (оба, stderr и stdout) через

docker logs myapp

сейчас!


2
-у, кажется, работает для меня, но есть ли где-нибудь документация с описанием того, что он на самом деле делает?
Маленький гик

7
Как подсказывают другие ответы, вы можете попробовать установить переменную окружения, ENV PYTHONUNBUFFERED=0если -uфлаг не работает.
Farshid T

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
я думаю, что это лучшее решение, нам не нужно перестраивать образ докера, чтобы увидеть результаты
FF0605

2
Это большое спасибо. Стоит отметить, что это просто должен быть непустой персонаж, чтобы работать в соответствии с документами PYTHONUNBUFFERED
A Star

Работал для интерфейса docker-compose. Никогда бы не догадался
углубление

24

Для меня это особенность, а не ошибка. Без псевдо-TTY нет ничего, что могло бы вылиться. Итак, простое решение - выделить псевдо-TTY для вашего работающего контейнера с помощью:

$ docker run -t ...

Это не дает ответа на вопрос. Чтобы критиковать или запросить разъяснения у автора, оставьте комментарий под своим постом.
Президент Джеймс К. Полк

@JamesKPolk, теперь лучше?
Питер Сенна

Docker не требует выделения псевдотерминала для stdout и stderr
Мэтт

3
tty: trueв составе земли
углубление

15

Смотрите эту статью, которая подробно объясняет причину поведения:

Обычно существует три режима буферизации:

  • Если дескриптор файла не буферизован, тогда буферизация не происходит, и вызовы функций, которые читают или записывают данные, происходят немедленно (и будут блокироваться).
  • Если файловый дескриптор полностью буферизован, то используется буфер фиксированного размера, а вызовы чтения или записи просто читают или пишут из буфера. Буфер не очищается, пока не заполнится.
  • Если файловый дескриптор буферизирован строкой, то буферизация ждет, пока не увидит символ новой строки. Таким образом, данные будут буферизироваться и буферизироваться до тех пор, пока не будет замечено \ 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, не терминал не был выделен, то 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)

Dockerfile:

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

Так как я еще не видел этот ответ:

Вы также можете сбросить стандартный вывод после печати на него:

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 был небуферизован, так что вы можете исправить это с помощью своего решения.
позднее

1

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


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.