Заставьте приложение Docker написать в stdout

49

Я развертываю стороннее приложение в соответствии с рекомендациями по 12 факторам , и один из моментов говорит о том, что журналы приложений должны быть напечатаны в stdout / stderr: тогда программное обеспечение для кластеризации может их собрать.

Тем не менее, приложение может записывать только файлы или системный журнал. Как мне распечатать эти журналы вместо этого?

kolypto
источник
Они уже собираются в системный журнал. Вы можете просто забрать их оттуда!
Майкл Хэмптон
@MichaelHampton, кажется, все в порядке, но Docker запускает один процесс, который может записывать в стандартный вывод, и это звучит как объединение двух из них?
Колыпто
1
У вас может быть демон-процесс, использующий syslog, и фронт-процесс, который печатает?
qkrijger
@qkrijger, хорошая мысль! Теперь, если у кого-то есть опыт с этим ...?
Колыпто

Ответы:

107

Удивительный рецепт дается в Dockerfile nginx :

# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

Просто приложение может продолжать запись в него в виде файла, но в результате строки будут идти в stdout& stderr!

kolypto
источник
2
Этот. Является. Только что. Brilliant. И универсальный.
космонавт
11
Единственная проблема, которая связана с этим, заключается в том, что запускаемая вами вещь настаивает на переходе в фоновый режим как процесс без полномочий root. У меня есть это с squid3, а затем возникают проблемы с разрешениями на /dev/stdout.
Микепурвис
4
Это не всегда работает - например, если приложение пытается найти файл, этот метод обычно не будет работать.
mixja
Ссылка не работает ...
Бабкен Варданян
4
Все это файл для победы.
RubberDuck
16

В другом вопросе « Убить дочерний процесс при выходе из родительского процесса» я получил ответ, который помог разобраться в этом.

Таким образом, мы настраиваем приложение таким образом, чтобы оно регистрировалось в файл и непрерывно tail -fего. К счастью, tailможет принять --pid PID: он выйдет при выходе из указанного процесса. Мы помещаем $$туда: PID текущей оболочки.

В качестве последнего шага запущенное приложение execредактируется, что означает, что текущая оболочка полностью заменена этим приложением.

Скрипт бегуна run.sh, будет выглядеть так:

#! /usr/bin/env bash
set -eu

rm -rf /var/log/my-application.log
tail --pid $$ -F /var/log/my-application.log &

exec /path/to/my-application --logfile /var/log/my-application.log

ПРИМЕЧАНИЕ: используя tail -Fсписок имен файлов, он будет читать их, даже если они появятся позже!

Наконец, минималистичный Dockerfile:

FROM ubuntu
ADD run.sh /root/run.sh
CMD ['/root/run.sh']

Примечание: для работы с очень странным tail -fповедением (которое говорит «был заменен удаленным файлом. Отказывается от этого имени»), я попробовал другой подход: все известные файлы журналов создаются и усекаются при запуске: таким образом, я гарантирую, что они существуют и только потом - хвост им:

#! /usr/bin/env bash
set -eu

LOGS=/var/log/myapp/

( umask 0 && truncate -s0 $LOGS/http.{access,error}.log )
tail --pid $$ -n0 -F $LOGS/* &

exec /usr/sbin/apache2 -DFOREGROUND
kolypto
источник
Специально для сервера Apache я использую журналы Piped ( httpd.apache.org/docs/2.4/logs.html#piped ) и, похоже, работает.
php-кодер
Решает аналогичные проблемы с alpine и, похоже, отлично работает на BusyBox tailбез опции --pid.
Райан
Обходной путь работал для меня. чрезвычайно полезно, спасибо.
Тамас Кальман
8

Для фонового процесса в Docker-контейнере, например, соединение с exec в / bin / bash, которое я смог использовать.

echo "test log1" >> /proc/1/fd/1

Это отправляет вывод на стандартный вывод pid 1, который является одним из подключаемых докеров.

Pieter
источник
1
Это правильный ответ. Журналы должны быть отправлены на стандартный вывод для PID1, и если ваше приложение работает под другим PID, оно не будет просматриваться docker logsили kubectl logs. У меня есть контейнер, который планирует задачу через crontab, который не работает как PID1.
leeman24
6

для Nginx вы можете nginx.confуказывать /dev/stderrи /dev/stdoutнравится это

user  nginx;
worker_processes  4;
error_log  /dev/stderr;
http {
    access_log  /dev/stdout  main;
...

и ваша Dockerfileзапись должна быть

/usr/sbin/nginx -g 'daemon off;'
Муайяд Алсади
источник
Не работает, когда основной процесс не выполняется какroot
peedee