- Вопрос или проблема
- stdout-logs – модификация Docker для любого контейнера
- Ответ или решение
- Как заставить Docker-приложение записывать логи в stdout
- Способ 1: Символические ссылки на stdout и stderr
- Способ 2: Использование tail
- Способ 3: Перенаправление через /proc
- Способ 4: Специальные настройки для сервисов, таких как Apache
- Способ 5: Использование Docker-мода для универсальной работы с логами
- Заключение
Вопрос или проблема
Я разворачиваю стороннее приложение в соответствии с 12 факторным руководством, и один из пунктов говорит, что логи приложения должны выводиться на stdout/stderr: тогда программное обеспечение для кластеризации сможет их собирать.
Однако приложение может только записывать в файлы или syslog. Как мне вместо этого вывести эти логи?
# перенаправляем запросы и ошибки логов в сборщик логов docker
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
Просто, приложение может продолжать записывать в него как в файл, но в итоге строки будут отправлены на stdout
& stderr
!
Для фонового процесса в контейнере docker, например, подключаясь с помощью exec к /bin/bash, я смог использовать.
echo "тестовый лог1" >> /proc/1/fd/1
Это отправляет вывод на stdout pid 1, который docker захватывает и записывает.
В другом вопросе, Убить дочерний процесс, когда родитель выходит, я получил ответ, который помог разобраться с этим.
Таким образом, мы настраиваем приложение так, чтобы оно записывало в файл, и непрерывно tail -f
его.
К счастью, tail
может принимать --pid PID
: он выйдет, когда указанный процесс завершится. Мы ставим $$
там: PID текущей оболочки.
В конечном итоге запущенное приложение будет exec
‘ed, что означает, что текущая оболочка полностью заменяется этим приложением.
Скрипт запуска, 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
Для 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;'
В моем случае создание символической ссылки на stdout не сработало, поэтому вместо этого я запустил следующую команду
ln -sf /proc/self/fd/1 /var/log/main.log
Я только что решил эту проблему с apache2 и долго мучился с использованием CustomLog, чтобы попробовать перенаправить на /proc/1/fd/1
, но не смог сделать это работающим. В моей реализации apache не работал как pid 1
, поэтому ответ колыpto не сработал как есть. Подход Питера казался убедительным, поэтому я объединил оба, и результат работает великолепно:
# Перенаправить выходной лог apache в сборщик логов docker
RUN ln -sf /proc/1/fd/1 /var/log/apache2/access.log \
&& ln -sf /proc/1/fd/2 /var/log/apache2/error.log
Технически это сохраняет лог access.log
и error.log
apache, направляя их на stdout
и stderr
с точки зрения сборщика логов docker, но было бы здорово, если бы был способ разделить их вне контейнера, как переключатель для docker logs
, который показывал бы только один из них…
Если использовать изображения серверов linux, вы можете использовать их модуль логов, если контейнер не записывает логи должным образом. Они говорят, что они должны это делать.
https://github.com/linuxserver/docker-mods/tree/universal-stdout-logs
stdout-logs – модификация Docker для любого контейнера
Этот мод позволяет любым указанным лог-файлам отслеживаться и включаться в
STDOUT контейнера.В любом контейнере docker аргументы, установите
переменную окружения
DOCKER_MODS=linuxserver/mods:universal-stdout-logs
Если добавляете несколько
модов, вводите их в массиве, разделяя|
, например
DOCKER_MODS=linuxserver/mods:universal-stdout-logs|linuxserver/mods:universal-mod2
Просто установите переменную окружения LOGS_TO_STDOUT с
списком лог-файлов, разделенных символом «|», например
LOGS_TO_STDOUT=/config/logs/radarr.txt|/config/logs/radarr.debug.txt
.
Ответ или решение
Как заставить Docker-приложение записывать логи в stdout
При развертывании сторонних приложений в соответствии с рекомендациями архитектурного подхода 12 Factor, одной из важных задач является правильная обработка логирования. Согласно этим принципам, все логи приложения должны отправляться в стандартные потоки stdout
и stderr
. Это облегчает сбор логов с помощью кластерного программного обеспечения или систем мониторинга. Однако, что делать, если ваше приложение не поддерживает запись в стандартные потоки, а только в файлы или syslog?
В этом руководстве мы рассмотрим несколько эффективных методов, которые помогут вам перенаправить логи приложения в stdout
.
Способ 1: Символические ссылки на stdout и stderr
Одним из наиболее простых способов является создание символических ссылок на стандартные выходные потоки. В примере с Nginx, в Dockerfile можно указать следующие команды:
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
Таким образом, когда приложение будет записывать свои логи в файлы, они фактически будут перенаправляться в стандартные потоки.
Способ 2: Использование tail
Если ваше приложение по-прежнему требует записи в файлы, вы можете воспользоваться командой tail
. Это позволит непрерывно выводить содержимое лог-файла в stdout
. Скрипт запуска вашего приложения может выглядеть так:
#! /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
, который будет следить за изменениями в файле, и, наконец, заменяет текущий процесс на ваше приложение.
Способ 3: Перенаправление через /proc
В некоторых случаях может потребоваться перенаправление логов в стандартные потоки через /proc
. Используйте следующую команду:
ln -sf /proc/self/fd/1 /var/log/main.log
Эта команда создаст символическую ссылку на stdout
, что позволит вашему приложению записывать логи в файл, но фактически они будут отправляться в stdout
.
Способ 4: Специальные настройки для сервисов, таких как Apache
Для веб-серверов, таких как Apache, можно использовать аналогичный подход. В Dockerfile можно добавить следующие команды:
RUN ln -sf /proc/1/fd/1 /var/log/apache2/access.log \
&& ln -sf /proc/1/fd/2 /var/log/apache2/error.log
Это позволит Apache отправлять свои логи в stdout
и stderr
, независимо от того, какой процесс запускается внутри контейнера.
Способ 5: Использование Docker-мода для универсальной работы с логами
Для более продвинутых пользователей можно использовать модификацию от LinuxServer, которая позволяет любым заданным лог-файлам автоматически отправляться в stdout
. Просто установите переменную окружения DOCKER_MODS=linuxserver/mods:universal-stdout-logs
и укажите файлы логов через LOGS_TO_STDOUT
:
LOGS_TO_STDOUT=/config/logs/log1.txt|/config/logs/log2.txt
Заключение
Все предложенные методы позволяют эффективно перенаправлять логи ваших Docker-приложений в stdout
и stderr
, соблюдая рекомендации 12-Factor App и обеспечивая удобство дальнейшего сбора и анализа логов. Выбор подходящего метода зависит от ваших требований и конфигураций приложения, но он однозначно позволит улучшить управление логированием и совместимость с кластерными системами.
Для получения более подробной информации о рекомендованных подходах и примерах, вы можете обратиться к документации Docker и руководствам по работе с конкретными приложениями.