Серверная инфраструктура проекта CyberDAS, включающая систему мониторинга, логирования и маршрутизации запросов.
Это набор конфигов и docker-compose
файлов, настраивающих окружение на сервере проекта CyberDAS. Всё окружение состоит из набора обособленных контейнеров, выполняющих свою задачу. Если вы никогда не сталкивались с docker
и вам не знакомо понятие контейнера, рекомендую пройти по этой ссылке: https://docs.docker.com/get-started/.
В нашей инфраструктуре есть только один контейнер,'смотрящий' в публичную сеть и направляющий запросы к контейнерам - traefik.
Такое решение может показаться странным, ведь если упадет этот контейнер - то сразу теряется доступ ко всей остальной системе. Ну да. Но вариант поставить один глобальный Nginx
был бы ни чем не лучше.
Но у traefik
есть один огромный плюс: добавление новых контейнеров производится без дописывания глобального конфига. Если разработчик хочет опубликовать новый сервис, он просто присоединяет свой контейнер к определенной Docker network
(в нашем случае она называется "proxy") и объявляет в одной строчке о том, что ему нужен доступ в веб. Вуаля! Docker
делает всю остальную работу.
На примере (фрагмент docker-compose
файла абстрактного сервиса):
services:
backend:
build: backend
expose:
- 5000
networks:
- proxy
labels:
- traefik.enable=true
networks:
proxy:
external: true
Что при этом произойдет?
- Приложение, которое работает в контейнере
backend
на 5000-ом порту окажется доступным по адресу https://backend.cyberdas.net - Все HTTP запросы к контейнеру автоматически перенаправляются на протокол HTTPS
- На этот поддомен автоматически оформится SSL-сертификат и будет обновляться, до тех пор пока этот контейнер существует
- В логах доступа автоматически появится новая категория, позволяющая анализировать запросы только к этому контейнеру (ура, можно не вести своих access логов!)
Пара важных моментов:
- При отсутствии ключа
TRAEFIK_REAL_CERTS
в окружении,traefik
будет обращаться к стейджинг АПИ LetsEncrypt (отлично подходит для тестов!). Если этот ключ есть в окружении, тоtraefik
будет пытаться получить настоящие сертификаты. - Раздача имен сервисам (
backend.cyberdas.net
из примера выше) происходит нехитрым образом: из полного имени контейнера, состоящего из имени самого контейнера и имени проекта, отрываемся имя проекта. То, что осталось - создается как сабдомен. Следите за коллизиями и именуйте семантически! Кстати, не все сервисы должны стоять на сабдоменах, и это поведение полностью кастомизируется. По всем вопросам обращайтесь к документации: https://doc.traefik.io/ - При локальной разработке получить доступ к сервисам можно обращаясь на локалхост; например,
traefik.localhost
.
Логирование происходит в несколько этапов:
- Логи формируются в приложении, будь то traefik или БД
- Они отправляются в stdout/stderr, где их может читать Docker
- Docker, используя драйвер
fluentd
отправляет их по указанному адресу (в нашем случае localhost:24224) - Fluentd производит первичную обработку и маршрутизацию логов, отправляя их Loki
- Loki хранит логи до востребования
Такая сложная последовательность действий - не моя прихоть.
Например, у Loki есть свой драйвер для Docker'а, но у него не хватает возможностей для первичной обработки. Без неё неотформатированные логи (например, от сторонних приложений, таких как traefik) очень сложно и долго обрабатываются в конечных системах.
А прослойка в виде Docker'а для отправки в Fluentd позволяет в любой момент поменять агента не внося правок в код приложений.
Главное не забыть что все компоненты этой системы должны видеть друг друга и правильно настроить сети.
И так, приняв такую тяжелую архитектуру, нужно понять как с ней работать:
- Fluentd крутится на локалхосте (127.0.0.1:24244), потому что он должен быть доступен не только из одного
docker-compose
. Благодяра этому, в любых контейнерах в пределах одной машины мы можем сделать так:
services:
backend:
build: backend
logging:
driver: fluentd
options:
fluentd-address: localhost:24224
tag: app.backend
- Тэги - это важно. Вся маршрутизация и обработка логов в Fluentd основывается на тэгах. Сам конфиг лежит в
fluentd/fluent.conf
. В этом проекте такое соглашение: все самописные приложения имеют первоуровневым тэгомapp
и обязаны предоставлять структурированные JSON-логи, обязательно указываяlevel
и полеapp_name
, которое в идеале совпадает с именем контейнера (не забываем про коллизии!). Сторонние приложения - тяжелый случай, так как формат их логов зачастую неконтроллируемый, поэтому для них можно, и даже нужно, делать кастомные тэги и писать свои обработчики. - Не нужно писать свои access логи, так как traefik предоставляет очень богатый набор данных, которого хватит с головой. Только в будущем нужно трэйсинг добавить.
В случае возникновения вопросов обращайтесь к документации: https://docs.fluentd.org/
Погодите, а разве на логах мы не закончили? Что это еще за мониторинг такой?
На самом деле нет, не закончили. Логи обычно говорят нам о результатах исполнения какой либо функции\метода. Это важно - если пользователь не может аутентифицироваться, надо бить тревогу. Но это не всё.
Если на сервере половина приложений улетит в своп из-за утечки памяти, то пользователи может и будут успешно аутентифицироваться, но вряд ли им понравится ждать ответов от сервера по 10 секунд. Поэтому важно также наблюдать и за пассивным "здоровьем" наших сервисов - за нагрузкой на сервер, за использованием памяти и процессорного времени, за наличием свободного дискового пространства и так далее.
А самое важное то, что с помощью метрик можно создавать красивые дэшборды.
Закончив с вопросом "зачем" и поняв важность мониторинга, перейдем к вопросу "как".
- Так как наша базовая единица - это контейнер, то полезно будет следить за базовыми характеристиками всех контенейров на системе. Сколько памяти и процессорного времен они потребляют, сколько посылают и принимают по сети. Для этого есть специальный (угадайте, что) контейнер - cAdvisor. Работает без особых конфигов и сразу на все контейнеры.
- Помимо этого интерес представляют характеристики самого хоста, на котором работают контейнеры. Учитывая то, что контейнеры очень любят быть большими, и то, что мы собираем логи, - место на жестком диске может кончится довольно неожиданно. Также, хочется следить за свопом и температурой железа. И да, на это тоже есть контейнер - node-exporter. Тоже хватает одного на всю систему.
- Теперь у нас есть данные. Но их еще нужно куда-то записывать. В этом поможет Prometheus. Но на этом его функционал не заканчивается. С его же помощью записываются метрики с приложений типа
traefik
.
Но мы не будем останавливаться только лишь на сборе и хранении данных. В проекте присутствует система уведомлений о каких-то критичных событиях, критерии для которых вычисляются на основе метрик, - Alertmanager.
Уведомления - это хорошо. Но иногда хочется посмотреть на красивые дэшборды и подумать о том, какие процессы скрываются за диаграмами и графиками. В этом поможет еще один сервис, - Grafana. Кстати, его можно было увидеть на картинке в начале документа.
Если вам тоже кажется, что как-то сервисов стало через чур много, то вам не кажется.
Небольшое лирическое отступление: до того как остановиться на этом стэке, я рассматривал ELK (ElasticSearch, Logstash, Kibana), который предназначается для той же задачи. Когда я всё это поставил, то под 70% памяти на хосте забрала система для... мониторинга? Короче говоря, лучше иметь 6 сервисов, которые вместе потребляют 300 МБ оперативной памяти, чем 3 сервиса, которые вместе потребляют 4 ГБ оперативной памяти.
Для управления всем этим добром есть конфиги. Коротко о них:
- В конфиге Prometheus указаны все сервисы, с которых снимаются метрики. В случае добавления нового сервиса, его нужно будет указать там.
- Рядом с конфигом Prometheus лежит список правил, вызывающих алерты.
- Конфиг Loki очень загадочный и непонятный. Рекомендую не трогать
- Alertmanager позволяет в своем конфиге указать получателей и способы группировки эвентов. Но он плохо задокументирован, поэтому тоже лучше не трогать.
- В папке с Grafana можно настроить источники данных 'по умолчанию', а также добавить парочку дэшбордов, которые будут доступны 'из коробки'.
Одной кнопочкой.
docker-compose -f docker-compose.yml -f monitoring-compose.yml up -d
И, вуаля, ваш компьютер превращается в сервер. Ну почти. Только контейнеры с сервисами поставьте. И статичный IP оформите.
На самом деле еще нужно поменять настройки в файле .env
. Они довольно очевидные