Python регистрация приложений с Docker
Docker лучшие практики предлагают регистрироваться в stdout , но есть некоторые сложности.
Когда вы разрабатываете программное приложение, вероятно, первое, что вы настроите, это ведение журнала. Сначала только в консоль, но вскоре вы добавите файлы журналов.
Несколько лет назад я начал использовать 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
Подробнее
Docker Docker-compose Log file Logging
Недавний
- Скрытие первичных ключей базы данных UUID вашего веб-приложения
- Don't Repeat Yourself (DRY) с Jinja2
- SQLAlchemy, PostgreSQL, максимальное количество строк для user
- Показать значения в динамических фильтрах SQLAlchemy
- Безопасная передача данных с помощью шифрования Public Key и pyNaCl
- rqlite: альтернатива dist с высокой степенью готовности и SQLite
Большинство просмотренных
- Используя Python pyOpenSSL для проверки SSL-сертификатов, загруженных с хоста
- Использование UUID вместо Integer Autoincrement Primary Keys с SQLAlchemy и MariaDb
- Подключение к службе на хосте Docker из контейнера Docker
- Использование PyInstaller и Cython для создания исполняемого файла Python
- SQLAlchemy: Использование Cascade Deletes для удаления связанных объектов
- Flask Удовлетворительный запрос API проверка параметров запроса с помощью схем Маршмэллоу