essh@kubernetes-master:~/prometheus$ cat << EOF > ./prometheus.yml
global:
scrape_interval: 1s
evaluation_interval: 1s
scrape_configs:
– job_name: 'prometheus'
static_configs:
– targets: ['127.0.0.1:9090', '127.0.0.1:9100', '127.0.0.1:9323']
labels:
group: 'prometheus'
EOF
essh@kubernetes-master:~/prometheus$ docker rm -f prometheus
prometheus
essh@kubernetes-master:~/prometheus$ docker run \
–d \
–-net=host \
–-restart always \
–-name prometheus \
–v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml
prom/prometheus
7dd991397d43597ded6be388f73583386dab3d527f5278b7e16403e7ea633eef
essh@kubernetes-master:~/prometheus$ docker ps \
–f name=prometheus
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7dd991397d43 prom/prometheus "/bin/prometheus –c…" 53 seconds ago Up 53 seconds prometheus
Теперь доступно 1702 метрики хоста:
essh@kubernetes-master:~/prometheus$ curl http://localhost:9100/metrics | grep -v '#' | wc -l
1702
из всего разнообразия сложно искать нужные для повседневных задач, например, используемое количество памяти node_memory_Active. Для этого есть агрегаторы метрик:
http://localhost:9090/consoles/node.html
http://localhost:9090/consoles/node-cpu.html
Но лучше использовать Grafana. Установим и её, пример можно посмотреть:
essh@kubernetes-master:~/prometheus$ docker run \
–d \
–-name=grafana \
–-net=host
grafana/grafana
Unable to find image 'grafana/grafana:latest' locally
latest: Pulling from grafana/grafana
9d48c3bd43c5: Already exists
df58635243b1: Pull complete
09b2e1de003c: Pull complete
f21b6d64aaf0: Pull complete
719d3f6b4656: Pull complete
d18fca935678: Pull complete
7c7f1ccbce63: Pull complete
Digest: sha256:a10521576058f40427306fcb5be48138c77ea7c55ede24327381211e653f478a
Status: Downloaded newer image for grafana/grafana:latest
6f9ca05c7efb2f5cd8437ddcb4c708515707dbed12eaa417c2dca111d7cb17dc
essh@kubernetes-master:~/prometheus$ firefox localhost:3000
Введем логин admin и пароль admin, после чего нам предложат изменить пароль. Далее нужно выполнить последующую настройку.
В Grafana первоначальный вход по логину admin и такому паролю. Сперва на предлагается выбрать источник – выбираем Prometheus, вводим localhost:9090, выбираем подключение не как к серверу, а как к браузеру (то есть по сети) и выбираем, что аутентификация у нас базовая – все – нажимаем Save and Test и Prometheus подключен.
Понятно, что всем раздавать пароль и логин от админских прав не стоит. Для этого нужно будет завести пользователей или интегрировать их внешней базой данных пользователей, такой как Microsoft Active Directory.
Я выберу во вкладке Dashboard активирую все три перенастроенных дашборда. Из списка New Dashboard верхнего меню выберу дашборд Prometheus 2.0 Stats. Но, данных нет:
Кликну на пункт меню "+" и выберу "Dashboard", предлагается создать дашборд. Дашборд может содержать несколько виджетов, например графики, которые можно располагать и настраивать, поэтому нажимаем на кнопку добавления графика и выбираем его тип. На самом графике выбираем редактировать, выбрав размер, нажимаем редактировать, и тут самое главное – выбор демонстрируемой метрики. Выбираем Prometheus
Полная сборка доступна:
essh@kubernetes-master:~/prometheus$ wget \
https://raw.githubusercontent.com/grafana/grafana/master/devenv/docker/ha_test/docker-compose.yaml
--2019-10-30 07:29:52– https://raw.githubusercontent.com/grafana/grafana/master/devenv/docker/ha_test/docker-compose.yaml
Resolving raw.githubusercontent.com (raw.githubusercontent.com)… 151.101.112.133
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.112.133|:443… connected.
HTTP request sent, awaiting response… 200 OK
Length: 2996 (2,9K) [text/plain]
Saving to: ‘docker-compose.yaml’
docker-compose.yaml 100%[=========>] 2,93K –.-KB/s in 0s
2019-10-30 07:29:52 (23,4 MB/s) – ‘docker-compose.yaml’ saved [2996/2996]
Получение прикладных метрик приложения
До этого момента мы рассматривали случай, когда Prometheus опрашивал стандартный накопитель метрик, получая стандартные метрики. Теперь попробуем создать приложение и отдавать свои метрики. Для начала возьмём сервер NodeJS и напишем под него приложение. Для этого, создадим проект NodeJS:
vagrant@ubuntu:~$ mkdir nodejs && cd $_
vagrant@ubuntu:~/nodejs$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install < pkg> –save` afterwards to install a package and
save it as a dependency in the package.json file.
name: (nodejs)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author: ESSch
license: (ISC)
About to write to /home/vagrant/nodejs/package.json:
{
"name": "nodejs",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "ESSch",
"license": "ISC"
}
Is this ok? (yes) yes
Для начала создадим WEB-сервер. Я воспользуюсь библиотекой для его создания:
vagrant@ubuntu:~/nodejs$ npm install Express –save
npm WARN deprecated Express@3.0.1: Package unsupported. Please use the express package (all lowercase) instead.
nodejs@1.0.0 /home/vagrant/nodejs
└── Express@3.0.1
npm WARN nodejs@1.0.0 No description
npm WARN nodejs@1.0.0 No repository field.
vagrant@ubuntu:~/nodejs$ cat << EOF > index.js
const express = require('express');
const app = express();
app.get('/healt', function (req, res) {
res.send({status: "Healt"});
});
app.listen(9999, () => {
console.log({status: "start"});
});
EOF
vagrant@ubuntu:~/nodejs$ node index.js &
[1] 18963
vagrant@ubuntu:~/nodejs$ { status: 'start' }
vagrant@ubuntu:~/nodejs$ curl localhost:9999/healt
{"status":"Healt"}
Наш сервер готов к работе с Prometheus. Нам нужно настроить Prometheus на него.
Проблема масштабирования Prometheus возникает, когда данные не помещаются на один сервер, точнее, когда один сервер не успевает записывать данные и когда обработка данных одним сервером не устраивает по перформансу. Thanos решает эту проблему, не требуя настройки федерации, предоставляя пользователю интерфейс и API, которые он транслирует на инстансы Prometheus. Пользователем доступен веб-интерфейс, аналогичный Prometheus. Сам он взаимодействует с агентами, которые установлены на инстансах как side-car, как это делает Istio. Он и агенты доступны как контейнера и как Helm-чарт. Например, агент может быть поднят как контейнер, настроенный на Prometheus, а Prometheus настраивается конфигом с последующей перезагрузкой.
docker run –rm quay.io/thanos/thanos:v0.7.0 –help
docker run -d –net=host –rm \
–v $(pwd)/prometheus0_eu1.yml:/etc/prometheus/prometheus.yml \
–-name prometheus-0-sidecar-eu1 \
–u root \
quay.io/thanos/thanos:v0.7.0 \
sidecar \
–-http-address 0.0.0.0:19090 \
–-grpc-address 0.0.0.0:19190 \
–-reloader.config-file /etc/prometheus/prometheus.yml \
–-prometheus.url http://127.0.0.1:9090
Важной составляющей мониторинга являются уведомления. Уведомления состоят из триггеров срабатывания и провайдера. Триггер срабатывания пишется на PromQL как правило с условием в Prometheus. Когда сработал триггер (условие по метрике), Prometheus сигнализирует провайдеру отправить уведомление. Стандартным провайдером является Alertmanager и он способен отравлять сообщения в различные приёмники, такие как электронная почта и Slack.
, Например, метрика "up", принимающая значения 0 или 1, может быть использована, чтобы отравлять сообщение, если сервер выключен более 1 минуты. Для этого записывается правило:
groups:
– name: example
rules:
– alert: Instance Down
expr: up == 0
for: 1m
Когда метрика более 1 минуты равняется 0, то срабатывает этот триггер и Prometheus отравляет запрос на Alertmanager. В Alertmanager прописано, что делать с этим событием. Мы можем прописать, что при получении события InstanceDown нужно отравлять сообщение на почту. Для этого сконфигурируем Alertmanager это сделать:
global:
smtp_smarthost: 'localhost:25'
smtp_from: 'youraddress@example.org'
route:
receiver: example-email
receivers:
– name: example-email
email_configs:
– to: 'youraddress@example.org'
Сам Alertmanager воспользуется установленным протоколом на этом компьютере. Для того, чтобы он смог это сделать, его нужно установить. Возьмём, к примеру, протокол SMTP (Simple Mail Transfer Protocol). Для проверки, установим консольный почтовый сервер в параллель с Alert Manager – sendmail.
Быстрый и наглядный анализ логов системы
Для быстрого поиска по логам используется OpenSource движок полнотекстового поиска Lucene. На его основе были построены два низкоуровневых продукта: Sold и Elasticsearch, довольно сходных по возможностям, но отличающихся по удобству использования и лицензии. На их построены многие популярные сборки, например, просто набор поставки с ElasticSearch: ELK (Elasticsearch(Apache Lucene), Logstash, Kibana), EFK (Elasticsearch, Fluentd, Kibana), так и продукты, например, GrayLog2. Как GrayLog2, так и сборки (ELK/EFK) активно используются из-за меньшей необходимости настраивать не тестовых стендах, так, например, поставить EFK в кластере Kubernetes можно практически одной командой
helm install efk-stack stable/elastic-stack –set logstash.enabled=false –set fluentd.enabled=true –set fluentd-elastics
Альтернативой, пока не получившей большого рассмотрения являются системы, построенные на рассмотренном ранее Prometheus, например, PLG (Promtail(агент) – Loki(Prometheus) – Grafana).
Сравнение ElasticSearch и Sold (системы сопоставимы):
Elastic:
** Коммерческий с открытым кодом и возможность коммитить (через апрув);
** Поддерживает более сложные запросы, больше аналитики, поддержка из коробки распределенных запросов, более полный REST-full JSON-BASH, чейнинг, машинное обучение, SQL (платный);
*** Full-text search;
*** Real-time index;
*** Мониторинг (платный);
*** Мониторинг через Elastic FQ;
*** Машинное обучение (платно);
*** Простая индексация;
*** Больше типов данных и структур;
** Движок Lucene;
** Parent-child (JOIN);
** Scalable native;
** Документация с 2010;
Solr:
** OpenSource;
** Большая скорость при JOIN;
*** Full-text search;
*** Real-time index;
*** Мониторинг в админке;
*** Машинное обучение через модули;
*** Входные данные: Work, PDF и другие;
*** Необходима схемы для индексации;
*** Данные: вложенные объекты;
** Движок Lucene;
** JSON join;
** Scalable: Solar Cloud (настройка) && ZooKeeper (настройка);
** Документация с 2004.
В нынешнее время всё чаще применяется микро сервисная архитектура, которая позволяет за счёт слабой
связанности между своими компонентами и их простоты упростить их разработку, тестирование и отладку.
Но в целом систему из-за её распределённости становится сложнее анализировать. Для анализа состояния
в целом применяются логи, собираемые в централизованное место и преобразуемые в понятный вид. Также возникает
необходимость в анализе других данных, например, access_log NGINX, для сбора метрик о посещаемости, лога почты,
почтового сервера для выявления попытки подбора пароля и т.д. Примером такого решения возьмём ELK. Под ELK понимается
связка трёх продуктов: Logstash, Elasticsearch и Kubana, первый и последний из который сильно заточены на центральный и
обеспечивают удобство работы. Более обобщённо ELK называют Elastic Stack, так как инструмент подготовки логов Logstash
может быть заменён аналогом, например Fluentd или Rsyslog, а средство визуализации Kibana – на Grafana. Например, хоть и
Kibana предоставляет большие возможности для анализа, Grafana предоставляет отправку уведомлений при возникновениях событий, и
может использоваться совместно с другими продуктами, например, CAdVisor – анализа состояния системы и отдельных контейнеров.
Продукты EKL могут быть установлены самостоятельно, скачаны в виде самодостаточных контейнеров, для которых нужно настроить
связь или в виде одного контейнера.
Для нормальной работы Elasticsearch нужно, чтобы данные приходили в формате JSON. Если данные предавать в
текстовом формате (лог пишется одной строкой, отделяется от предыдущий переносом строки), то он сможет
предоставить только полнотекстовый поиск, так как они будут восприниматься одной строкой. Для передачи
логов в формате JSON имеется два варианта: или настроить исследуемый продукт выдавать в этом формате,
например, для NGINX имеется такая возможность. Но, зачастую это невозможно, так как имеется уже
накопленная база логов, а традиционно они пишутся в текстовом формате. Для таких случаев необходима
пост обработка логов из текстового формата в JSON, которой занимается Logstash. Важно заметить, что если
есть возможность сразу же передавать данные в структурированном виде (JSON, XML и других), то следует это
сделать, так как если сделать детальный парсинг, то любое отклонение одностороннее отклонение от формата
приведёт с неработоспособности, а при поверхностном – теряем ценную информацию. В любом случае парсинг в
этот системе является узким горлышком, хотя может ограниченно масштабироваться до сервиса или лога
файла. К счастью, всё больше и больше продуктов начинают поддерживать структурированные логи, например,
последние версии NGINX поддерживает логи в JSON формате.
Для систем, не поддерживающих данный формат можно использовать преобразование к нему с помощью таких
программ, как Logstash, File bear и Fluentd. Первый из них входит в стандартную поставку Elastic Stack от вендора
и может быть установлен одним образом ELK в Docker – контейнер. Он поддерживает получение данных от файлов, сети и
стандартного потока как на входе, так и на выходе, а главное нативно внутренний протокол Elastic Search.
Logstash мониторит log-файлы на основе даты изменения или получает по сети данные по telnet от распределённой
системы, например, контейнеров и после преобразования отравляет на выход, обычно, в Elastic Search. Он прост и
входит в стандартную поставку с Elastic Stack, благодаря чему просто и беспроблемно настраивается. Но благодаря
Java машине внутри тяжёл и не сильно функционален, хотя и поддерживает плагины, например, синхронизации с MySQL
для отправки новых данных. Чуть больше возможностей предоставляет Filebeat. Энтерпрайзным инструментом на все
случаи жизни может служить Fluentd благодаря высокой функциональности (чтение логов, системных журналов и т.д.),
масштабируемости и возможности раскатки по кластерам Kubernetes с помощью Helm чарта, и мониторинг всего
дата-центра в стандартной поставке, но об этом соответствующем разделе.
Для управления логов можно воспользоваться Curator, который сможет архивировать из ElasticSearch старые
логи или их удалять, повышая эффективность его работы.
Процесс получения логов логичен осуществляется специальными сборщиками: logstash, fluentd, filebeat или
другими.
fluentd наименее требовательный и более простой аналог Logstash. Настройка
производится в /etc/td-agent/td-agent.conf, который содержит четыре блока:
** match – содержит настройки передачи полученных данных;
** include – содержит информацию о типах файлов;
** system – содержит настройки системы.
Logstash представляет гораздо более функциональный язык конфигураций. Logstash демон агента – logstash мониторит
изменения в файлах. Если же логи находятся не локально, а на распределённой системе, то устанавливается logstash на каждый сервер и
запускается в режиме агента bin/logstash agent -f /env/conf/my.conf. Поскольку запускать
logstash только в качестве агента для пересылки логов расточительно, то можно использовать продукт от тех
же разработчиков Logstash Forwarder (ранее Lumberjack) пересылающий логи по протоколу lumberjack к
logstash серверу. Для отслеживания и получения данных с MySQL можно использовать агент Packetbeat
(https://www.8host.com/blog/sbor-metrik-infrastruktury-s-pomoshhyu-packetbeat-i-elk-v-ubuntu-14-04/).
Также logstash позволяет преобразовать данные разного типа:
** grok – задать регулярные выражения выдирания полей из строки, часто для логов из текстового формата в JSON;
** date – в случае архивных логов проставить дату создания лога не текущей датой, а взять её из самого лога;
** kv – для логов типа key=value;
** mutate – выбрать только нужные поля и изменить данные в полях, например, произвести замену символа "/" на "_";
** multiline – для многострочных логов с разделителями.
Пример, можно лог в формате "дата тип число" разложить на составляющие, например "01.01.2021 INFO 1" разложить в хэш "message":
filter {
grok {
type => "my_log"
match => ["message", "%{MYDATE:date} %{WORD:loglevel} ${ID.id.int}"]
}
}
Шаблон ${ID.id.int} берёт класс – шаблон ID, полученное значение будет подставлено в поле id и строковое значение будет преобразовано к типу int.
В блоке «Output» мы можем указать: выводить данные в консоль с помощью блока "Stdout", в файл – "File", передавать по http через JSON REST API – "Elasticsearch" или отравлять по почте – "Email". Также можно заказать условия по полям, полученным в блоке filter. Например,:
output {
if [type] == "Info" {
elasticsearch {
host => localhost
index => "log-%{+YYYY.MM.dd}"
}
}
}
Здесь индекс Elasticsearch (база данных, если проводить аналогию с SQL) меняется каждый день. Для создания нового индекса не нужно его специально создавать – так поступают БД NoSQL, так как нет жёсткого требования описывать структуру – свойство и тип. Но всё же описать рекомендуется, иначе все поля будут со строковыми значениями, если не задано число. Для отображения данных Elasticsearch используется плагин WEB-ui интерфейса на AngularJS – Kibana. Для отображения временной шкалы в её графиках нужно описать хотя бы одно поле с типом дата, а для агрегатных функция – числовое, будь то целое или с плавающей точкой. Также, если добавляются новые поля, для их индексации и отображения требуется произвести пере индексацию всего индекса, поэтому наиболее полное описание структуры поможет избежать очень трудозатратные операции – переиндексации.
Разделение индекса по дням сделано для ускорения работы Elasticsearch, а в Kibana можно выбрать несколько по шаблону, здесь log-*, также снимается ограничение в один миллион документов на индекс.
Рассмотрим более подробный плагин вывода у Logstash:
output {
if [type] == "Info" {
elasticsearch {
claster => elasticsearch
action => "create"
hosts => ["localhost:9200"]
index => "log-%{+YYYY.MM.dd}"
document_type => ....
document_id => "%{id}"
}
}
}
Взаимодействии с ElasticSearch осуществляется через JSON REST API, драйвера для которых есть для большинства современных языков. Но для того, чтобы не писать код, мы воспользуемся утилитой Logstash, которая также он умеет преобразовывать текстовые данные в JSON на основе регулярных выражений. Также есть заготовленные шаблоны, наподобие классов в регулярных выражениях, таких как %{IP:client } и других, которые можно посмотреть по https://github.com/elastic/logstash/tree/v1.1.9/patterns. Для стандартных сервисов со стандартными настройками в интернете есть множество готовых конфигов, например, для NGINX – https://github.com/zooniverse/static/blob/master/logstash— Nginx.conf. Более подобно описано в статье https://habr.com/post/165059/.
ElasticSearch – NoSQL база данных, поэтому не нужно задавать формат (набор полей и его типы). Для поиска ему он всё же нужен, поэтому он сам его определяет, и при каждом смене формата происходит пере индексации, при котором работа невозможно. Для поддержания унифицированной структуры в логгере Serilog (DOT Net) есть поле EventType в которое можно зашифровать набор полей и их типы, для остальных придётся реализовывать отдельно. Для анализа логов от приложения микро сервисной архитектуры важно задать ID пока выполнения, то есть ID запроса, который будет неизменен и передаваться от микросервиса к микросервису, чтобы можно было проследить весь путь выполнения запроса.
Установим ElasticSearch (https://habr.com/post/280488/) и проверим работу curl -X GET localhost:9200
sudo sysctl -w vm.max_map_count=262144
$ curl 'localhost:9200/_cat/indices?v'
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
green open graylog_0 h2NICPMTQlqQRZhfkvsXRw 4 0 0 0 1kb 1kb
green open .kibana_1 iMJl7vyOTuu1eG8DlWl1OQ 1 0 3 0 11.9kb 11.9kb
yellow open indexname le87KQZwT22lFll8LSRdjw 5 1 1 0 4.5kb 4.5kb
yellow open db i6I2DmplQ7O40AUzyA-a6A 5 1 0 0 1.2kb 1.2kb
Создадим запись в базе данных blog и таблице post curl -X PUT "$ES_URL/blog/post/1?pretty" -d'
Поисковой движок ElasticSearch
В предыдущем разделе мы рассмотрели стек ELK, который составляют ElasticSearch, Logstash и Kibana. В полном наборе, а часто его ещё расширяют Filebeat – более заточенный на работу с расширение Logstash, для работы с текстовыми логами. Несмотря на то, что Logstash выполняет быстро свою задачу без необходимости, не используют, а логи в формате JSON отравляют через API загрузки дампа прямо в Logstash.
Если же у нас приложение, то применяется чистый ElasticSearch, который используется как поисковой движок, а Kibana используется как средство написания и отладки запросов – блок Dev Tools. Хотя и базы реляционные данных имеют долгую историю развития, всё же остаётся принцип, что чем более данные деморализованы, тем они медленнее, ведь их приходится при каждом запросе объединять. Данная проблема решается созданием View, в которой хранится результирующая выборка. Но хоть современные базы данных обзавелись внушительным функционалом, вплоть до полнотекстового поиска, но всё же им не сравниться в эффективности и функциональности поиска с поисковыми движками. Приведу пример с работы: несколько таблиц с метриками, который объединяются в запросе в одну, и производится поиск по выбранным параметрам в админке, таким как диапазон дат, страница в пагинации и содержания в сроке столбца чата. Данный не много, на выходе получаем таблицу в пол миллиона строк, да и поиск по дате и части строки укладывается в миллисекунды. А вот пагинации тормозит, в начальных страницах её запрос выполняется около двух минут, в конечных – более четырёх. При этом объединить запрос на логичен данных и на получения пагинации в лоб не получится. А тот же зарос, при этом он не оптимизирован, в ElasticSearch выполняется за 22 миллисекунды и содержит как данные и, так и число всех данных для пагинации.
Стоит предостеречь читателя от отказа от необдуманной реляционной базы данных, хотя и ElasticSearch содержит в себе NoSQL базу данных, но она предназначена исключительно для поиска и не содержит полноценных средств для нормализации и восстановления.
ElasticSearch не имеет в стандартной поставке консольного клиента – всё взаимодействие осуществляется через http вызовы GET, PUT и DELETE. Приведём пример использования с использованием программы (команды) Curl из программной оболочки BASH ОС linux:
#Создание записей (таблица и база данных создаются автоматически)
curl -XPUT mydb/mytable/1 -d'{
....
}'
#Получен значения по id
curl -XGET mydb/mytable/1
curl -XGET mydb/mytable/1
#Простой поиск
curl -XGET mydb -d'{
"search": {
"match": {
"name": "my"
}
}
}'
#Удаление базы
curl -XDELETE mydb
Облачные системы, как источник непрерывного масштабирования: Google Cloud и Amazon AWS