Создайте Docker-приложение, которое будет записывать в stdout.

Вопрос или проблема

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

Однако приложение может только записывать в файлы или syslog. Как мне вместо этого вывести эти логи?

Dockerfile для nginx:

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

Оцените материал
Добавить комментарий

Капча загружается...