angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Python регистрация приложений с Docker

Docker лучшие практики предлагают регистрироваться в stdout , но есть некоторые сложности.

5 декабря 2022
post main image
https://unsplash.com/@agk42

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

Несколько лет назад я начал использовать Docker для разработки и производства. При переходе на Docker я не сделал много изменений. Файлы журнала приложения по-прежнему находятся в каталоге журнала на томе Docker . Это означает, что файлы журнала являются частью приложения framework.

Лучшие практики в интернете предлагают использовать логирование Docker . Это означает, что наше приложение Docker должно печатать записи журнала в stdout (и/или sterr), вместо того, чтобы записывать их в файлы журнала нашего приложения. Когда мы отправляем наши записи журнала в stdout в Docker, мы можем указать драйвер журнала для экспорта их во внешний инструмент, такой как Syslog, используя поставляемые Docker драйверы журнала или сторонние драйверы журнала. Справедливо, я понимаю, что это может иметь преимущества или даже быть необходимым.

Опять же, я не хочу менять слишком много и придерживаюсь драйвера регистрации по умолчанию в Docker: json-file. Наши записи журнала будут присутствовать в файлах:

/var/lib/docker/containers/<container id>/<container id>-json.log

Один из моих текущих проектов Python - это приложение, состоящее из множества (микро) сервисов. Сервис представлен контейнером с лог-файлами еще в контексте / framework сервиса. В этом посте я преобразовываю существующий метод логирования приложения в логирование Docker .

Изменение модуля протоколирования

Для своих приложений я разработал пользовательский модуль логирования, внутри этого модуля находятся стандартные методы логирования Python , то есть в коде моего приложения есть такие строки как:

logger.debug(...)
logger.info(...)
logger.error(...)

Я использую этот модуль логирования везде. Это означает, что мне нужно было только добавить новый режим логирования, используя переменную use_docker_logging=True, которая указывает на запись данных журнала в stdout вместо файла.

Мы можем сделать это очень просто, используя logging.StreamHandler(stdout). При использовании модуля регистрации Python данные сбрасываются после каждой записи. Это означает, что нет необходимости запускать Python как 'python -u' или использовать переменную окружения PYTHONUNBUFFERED.

Docker Exec, проблемы, где мои записи журнала?

К сожалению, мы не закончили, есть проблема. Не знаю как вы, но я сегодня всю свою разработку веду с использованием контейнеров Docker . Во многих случаях у меня есть команда в файле docker-compose.yml, например:

command: tail -f /dev/null

Это означает, запустить контейнер и поддерживать его в рабочем состоянии. Затем я 'Docker Exec' в контейнер (shell), и запускаю скрипты Python . Когда я это сделал, записи журнала из сессии 'Docker Exec' не появились в журналах Docker .

После поиска в интернете я нашел эту (Docker issues) страницу 'Proposal: additional logging options for docker exec', см. ссылки ниже.

Похоже, что когда вы 'Docker Exec' в контейнер, stdout этой сессии не является stdout начальной сессии. На это будут свои причины, мы не можем это изменить, и нам придется с этим смириться.

Решение заключается в перенаправлении записей журнала:

/proc/1/fd/1

Затем я нашел еще одну (Docker issues) страницу в интернете 'Echoing to /dev/stdout does not appear in docker logs', см. ссылки ниже. Одно из предложений - вести журнал в файл с сим-связкой. Мы создаем symlink в Dockerfile:

RUN ln -sf /proc/1/fd/1 /var/log/test.log

Возвращаясь к решению, которое у нас было до сих пор, мы заменяем logging.StreamHandler на logging.FileHandler:

logging.FileHandler('/var/log/test.log')

Теперь записи журнала от скриптов Python , запущенных в сессии 'Docker Exec', появляются в журналах Docker .

Docker Exec сессия, нет консольного логгирования

К сожалению, мы не закончили, есть еще одна проблема. Поскольку мы использовали logging.FileHandler, мы ведем журнал только в файл stdout, являющийся журналом Docker . Чтобы увидеть записи журнала на экране в сессии 'Docker Exec', мы должны снова добавить logging.StreamHandler .

Но подождите, мы должны сделать это только для сессии 'Docker Exec', иначе мы увидим дублирование записей в журналах Docker .

Я решил эту проблему немного хакерским способом, найдя имя верхнего родительского процесса. Если это имя процесса 'sh' или 'bash' , то я предполагаю, что мы использовали Docker Exec для входа в контейнер.

import psutil

    ...
    # get 'top' parent process of this docker exec session
    parent_process_pid = os.getpid()
    parent_process_name = None
    while True:
        #print('parent_process_pid = {}'.format(parent_process_pid))
        parent_process = psutil.Process(parent_process_pid)
        pid = parent_process.ppid()
        parent_process_name = parent_process.name()
        if pid == 0:
            break
        parent_process_pid = pid

    if parent_name in ['sh', 'bash']:
        # add console logging
        ...
    else:
        # no console logging
        ...
    ...

Строки журнала с дополнительными полями

Все записи журнала контейнера теперь находятся в одном файле журнала. Если в контейнере запущено несколько служб (процессов), вы, вероятно, захотите добавить в строки журнала дополнительные поля, идентифицирующие службу (процесс). Это дополнение к полям logging.DEBUG, logging.ERROR и т.д., которые мы вставляем в строки журнала.

В журнале Python мы можем добавить дополнительное поле в строку журнала с помощью метода logging.setLogRecordFactory() . Пример приведен на странице 'Using LogRecordFactory in python to add custom fields for logging', см. ссылки ниже.

Другие изменения

Обратите внимание, что с драйвером логирования по умолчанию Docker мы можем записать в stdout только строку, запись JSON-объекта (словаря) невозможна. Серьезное ограничение, но с ним придется смириться.

Если мы хотим добавить метки ко всем (!) записям журнала, мы можем использовать метки в Docker-Compose:

Пример:

  some-service:
    image: ...
    ports:
      - "8082:8000"
    labels:
      log_for_application: "myapp"

При записи журнала в Docker временная метка добавляется автоматически Docker. Это означает, что мы должны удалить ее из нашей строки журнала Python . Мой окончательный форматтер строки журнала выглядит следующим образом:

'%(proc_id)-15.15s %(levelname)-8.8s [%(filename)-30s%(funcName)20s():%(lineno)03s] %(message)s'

Просмотр журналов Docker всех контейнеров с Dozzle

Существует множество решений для просмотра журналов Docker всех контейнеров. Я попробовал несколько, и самым простым в настройке и использовании оказался Dozzle. Dozzle - это программа просмотра логов в реальном времени для контейнеров docker, см. ссылки ниже. Чтобы запустить его, просто потяните контейнер, и вы готовы к работе. Dozzle похож на Logs Explorer, расширение для Docker Desktop, см. ссылки ниже.

Будьте внимательны, по умолчанию Dozzle соединяет вас с Google Analytics, убедитесь, что вы запустили Dozzle с флагом '--no-analytics':

docker run --name dozzle -d --volume=/var/run/docker.sock:/var/run/docker.sock -p 8888:8080 amir20/dozzle:latest --no-analytics

После запуска направьте браузер на:

http://127.0.0.1:8888

С помощью Dozzle очень легко просматривать журналы Docker .

Поиск использует regex, то есть, если вы, например, хотите отфильтровать два термина, 'Term1' и 'Term2',
вы можете ввести в поле поиска:

Term1.*?Term2

Еще одна приятная вещь с Dozzle заключается в том, что мы можем запустить его также для одного контейнера или нескольких контейнеров. Чего действительно не хватает, так это способа хранения и повторного выбора поисковых запросов. Но я не жалуюсь. Отличный инструмент!

Резюме

Это заняло гораздо больше времени, чем я ожидал. Это было гораздо больше, чем просто печать на stdout. Стоит ли оно того? Для текущего приложения, состоящего из множества сервисов, представленных контейнерами, я считаю, что стоит.

Теперь, когда логирование осуществляется через Docker, я могу рассмотреть другие способы просмотра логов, генерации предупреждений и т.д.

Ссылки / кредиты

Docker - Configure logging drivers
https://docs.docker.com/config/containers/logging/configure

Dozzle, a real-time log viewer for docker containers
https://dozzle.dev

Echoing to /dev/stdout does not appear in 'docker logs' #19616
https://github.com/moby/moby/issues/19616

Proposal: additional logging options for docker exec #8662
https://github.com/moby/moby/issues/8662

Using LogRecordFactory in python to add custom fields for logging
https://stackoverflow.com/questions/59585861/using-logrecordfactory-in-python-to-add-custom-fields-for-logging

Оставить комментарий

Комментируйте анонимно или войдите в систему, чтобы прокомментировать.

Комментарии

Оставьте ответ

Ответьте анонимно или войдите в систему, чтобы ответить.