
Одна из самых частых и понятных задач в разработке и эксплуатации — доставка логов. И дальше в статье мы с вами используем Fluent Bit для доставки логов из виртуальной машины в сервис Yandex Cloud Logging.
Шаг 1. Пишем логи в systemd
Возможно, у вас уже есть сервис, поставку чьих логов вы хотели бы настроить. Тогда переходите к шагу 2. Если же нет, то можно воспользоваться следующим кодом на Python, чтобы генерировать тестовые логи, которые впоследствии мы будем отправлять.
Итак, нам понадобится Python 3.6 и выше.
1. Заведём директорию, куда сложим все необходимые файлы:
sudo mkdir /usr/local/bin/logtest cd /usr/local/bin/logtest
2. Добавим следующий код в файл logtest.py:
import logging import random import sys import time from systemdlogging.toolbox import check_for_systemd from systemdlogging.toolbox import SystemdFormatter from systemdlogging.toolbox import SystemdHandler # Следующие несколько строк могут быть заменены на вызов `systemdlogging.toolbox.init_systemd_logging` # Но я приведу этот код полностью в целях демонстрации. # Проверяем доступен ли systemd systemd_ok = check_for_systemd() if systemd_ok: handler = SystemdHandler() # syslog_id: значение которое будет использовано в поле SYSLOG_IDENTIFIER. handler.syslog_id = '' handler.setFormatter(SystemdFormatter()) # получаем инстанс объекта логгера logger = logging.getLogger() logger.addHandler(handler) else: # Ветка будет использована для локальной отладки, если у вас нет systemd (MacOS, Windows) logger = logging.getLogger(__name__) # Выводить логи будем в STDOUT handler = logging.StreamHandler(stream=sys.stdout) handler.setFormatter(logging.Formatter( '[%(levelname)s] %(code)d %(message)s' )) logger.addHandler(handler) # Опционально можно настроить уровень логирования по умолчанию logger.setLevel(logging.DEBUG) # Мы могли бы обойтись и простым логированием случайных чисел, но я решил генерировать URL-подобные значения. PATHS = [ '/', '/admin', '/hello', '/docs', ] PARAMS = [ 'foo', 'bar', 'query', 'search', None ] def fake_url(): path = random.choice(PATHS) param = random.choice(PARAMS) if param: val = random.randint(0, 100) param += '=%s' % val code = random.choices([200, 400, 404, 500], weights=[10, 2, 2, 1])[0] return '?'.join(filter(None, [path, param])), code if __name__ == '__main__': while True: # создаем пару код и значение URL path, code = fake_url() # Если код 200, то пишем в лог с уровнем Info # Cloud Logging ориентируется на текстовое написание уровня логирования, поэтому в `context` передадим # дополнительное значение `SEVERITY`. Оно попадет в journald и сможет быть прочитано в плагине yc-logging. # В продуктовом коде это стоит вынести в `SystemdHandler`, чтобы не потерять, # но в примере я оставлю так для наглядности. if code == 200: logger.info( 'Path: %s', path, extra={"code": code, "context": {"SEVERITY": "info"}}, ) # Иначе с уровнем Error else: logger.error( 'Error: %s', path, extra={"code": code, "context": {"SEVERITY": "error"}}, ) # Ждем 1 секунду, чтобы излишне не засорять журнал time.sleep(1)
3. Заведём virtualenv и установим в него все нужные зависимости:
sudo apt install python3-pip python3.8-venv python -m venv venv source venv/bin/activate pip3 install systemd-logging
Теперь нам понадобится скрипт logtest.sh, который будет вызываться systemd для запуска нашего сервиса:
#!/bin/bash SCRIPT_PATH=$(dirname "$(realpath "$0")") . "$SCRIPT_PATH/venv/bin/activate" python "$SCRIPT_PATH/logtest.py"
4. Создадим файл logtest.service с описанием нашего сервиса:
[Unit] Description=Sample to show logging from a Python application to systemd After=network.target [Service] Type=simple ExecStart=/usr/local/bin/logtest/logtest.sh Restart=on-abort [Install] WantedBy=multi-user.target
5. Этот файл может лежать в любом каталоге, который вам нравится. Я бы предложил положить его с исходным кодом на Python для этого приложения. Но чтобы systemd нашёл файл, нам нужно придерживаться соглашения о том, куда складывать unit-файлы. Не буду вдаваться в подробности о systemd (тут на целую книгу хватит, это выходит за рамки поста в блоге) — убедимся, что наш файл модуля имеет symlink в /etc/systemd/system. Для этого выполним команду:
sudo ln -s "$(pwd)/logtest.service" \ /etc/systemd/system/logtest.service
6. Теперь можно перезагрузить systemd:
sudo systemctl daemon-reload
7. Проверим, что всё ОК:
systemctl status logtest.service
Там должно быть написано, что сервис загружен, но не активен.
8. Запустим systemd:
systemctl start logtest.service
Теперь, если снова посмотреть статус, то там должно быть active (running).
Шаг 2. Ставим Fluent Bit
Но сначала проясним пару моментов.

Fluent Bit vs Fluentd
Чтобы упростить работу с логами, когда-то появился Fluentd. Он был написан на Ruby и со временем вырос в целую экосистему, которая включает и Fluent Bit. Быстрое сравнение приведено в таблице:

Как видно, для установки Fluent Bit не нужны никакие зависимости типа Ruby. И хотя к нему есть меньше плагинов, чем для Fluentd, зато он потребляет значительно меньше памяти.
Теперь разберёмся, как его поставить
1. Добавим GPG-ключ, которым подписаны пакеты в репозитории Fluent Bit.
$ wget -qO - https://packages.fluentbit.io/fluentbit.key | sudo apt-key add -
2. В Ubuntu добавим в файл /etc/apt/sources.list строчку:
deb https://packages.fluentbit.io/ubuntu/focal focal main
3. Затем обновим индексы:
sudo apt-get update
4. Теперь можно установить последнюю версию td-agent-bit:
sudo apt-get install td-agent-bit
5. После этого его остаётся только перезапустить:
sudo service td-agent-bit start
6. Что всё ОК, можно проверить командой:
sudo service td-agent-bit status
Там должно быть active (running).
Шаг 3. Подключаем плагин
Fluent Bit поддерживает плагины, написанные на Go. Это экспериментальный, но вполне рабочий API.
1. Итак, клонируем репозиторий с кодом плагина:
git clone https://github.com/yandex-cloud/fluent-bit-plugin-yandex.git
2. Для начала нам понадобится Go. Инструкция по установке тут.
3. Теперь скомпилируем библиотеку:
export fluent_bit_version=1.8.6 export plugin_version=dev CGO_ENABLED=1 go build -buildmode=c-shared \ -o ./yc-logging.so \ -ldflags "-X main.PluginVersion=${plugin_version}" \ -ldflags "-X main.FluentBitVersion=${fluent_bit_version}"
4. Копируем полученную библиотеку:
sudo cp yc-logging.so /usr/lib/td-agent-bit/yc-logging.so
5. И регистрируем её в файле с конфигурацией плагинов /etc/td-agent-bit/plugins.conf:
[PLUGINS] Path /usr/lib/td-agent-bit/yc-logging.so
6. Вносим изменения в конфиг самого сервиса. folder_id укажите свой. Если к ВМ привязан service account с правами писать в Yandex Cloud Logging, то authorization укажите instance-service-account. Другие варианты авторизации и значение остальных параметров можно посмотреть тут.
[INPUT] Name systemd Tag host.* Systemd_Filter _SYSTEMD_UNIT=logtest.service [OUTPUT] Name yc-logging Match * resource_type logtest folder_id b1g*** message_key MESSAGE level_key SEVERITY default_level WARN authorization instance-service-account
7. Перезагружаем сервис td-agent-bit:
sudo systemctl restart td-agent-bit
8. Отлично. Теперь можно убедиться, что логи поступают:

P. S. Yandex Cloud Logging — это serverless-сервис в Yandex.Cloud. Если вам интересна экосистема Serverless-сервисов и все, что с этим связано, заходите в сообщество в Telegram, где можно обсудить serverless в целом.
Настройка работы Fluent Bit в контейнерах ещё проще. Но о ней я расскажу в следующей публикации.
