Приложение Python ничего не печатает при запуске отсоединенного в докере
У меня есть приложение Python (2.7), которое запускается в моем файле docker:
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
Ответы
Ответ 1
Наконец, я нашел решение увидеть вывод Python при запуске demonized в Docker, благодаря @ahmetalpbalkan на GitHub. Отвечая на него здесь для дальнейших ссылок:
Использование небуферизованного вывода с
CMD ["python","-u","main.py"]
вместо
CMD ["python","main.py"]
решает проблему; вы можете видеть выходные данные (как stderr, так и stdout) через
docker logs myapp
сейчас
Ответ 2
В моем случае запуск Python с -u
ничего не изменил. Однако фокус в том, чтобы установить PYTHONUNBUFFERED=0
в качестве переменной среды:
docker run --name=myapp -e PYTHONUNBUFFERED=0 -d myappimage
Ответ 3
Для меня это особенность, а не ошибка. Без псевдо-TTY нет ничего общего с stdout. Поэтому простым решением является выделение псевдотематического TTY для запускаемого контейнера:
$ docker run -t ...
Ответ 4
См. эту статью, в которой подробно объясняется причина такого поведения:
Обычно существует три режима буферизации:
- Если дескриптор файла не буферизован, то буферизация вообще не происходит, и вызовы функций, которые читают или записывают данные, происходят немедленно (и будут блокироваться).
- Если файловый дескриптор полностью буферизован, то используется буфер фиксированного размера, а вызовы чтения или записи просто читают или пишут из буфера. Буфер не очищается, пока не заполнится.
- Если файловый дескриптор буферизирован строкой, то буферизация ждет, пока не увидит символ новой строки. Таким образом, данные будут буферизоваться и буферизоваться до тех пор, пока не будет замечено \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
из документа докера, он выделит псевдотетти, тогда stdout
станет line-buffered
, поэтому docker run --name=myapp -it myappimage
сможет увидеть однострочный вывод.
И, если просто использовать -d
, tty не был выделен, то stdout
- это fully-buffered
, одна строка App started
наверняка не сможет очистить буфер.
Затем используйте -dt
для make stdout line buffered
или добавьте -u
в python к flush the buffer
- это способ исправить это.
Ответ 5
В качестве быстрого исправления попробуйте следующее:
from __future__ import print_function
# some code
print("App started", file=sys.stderr)
Это работает для меня, когда я сталкиваюсь с теми же проблемами. Но, честно говоря, я не знаю, почему эта ошибка происходит.
Ответ 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"]
Ответ 7
Обычно мы перенаправляем его в определенный файл (монтируя том с хоста и записывая его в этот файл).
Добавление tty с использованием -t тоже подойдет. Вы должны поднять это в журналах докера.
Используя большие выходы журнала, у меня не было никаких проблем с хранением буфера все, не помещая это в журнал докеров.