2020-10 upd: we reached the first fundraising goal and rented a server in Hetzner for development! Thank you for donating !
Экспорт и отображение jail и bhyve метрик с CBSD, Grafana и Prometheus
Часто возникает потребность знать, насколько сильно и по каким компонентам ваши контейнера и виртуальные среды (в рамках этой статьи речь идет о FreeBSD jail(8) и bhyve(8) виртуальных машинах) потребляют ресурсы в различные моменты времени и иметь визуализацию этого в виде графиков. Основные метрики которые интересны в первую очередь это, как правило, базовые системные:
- CPU usage;
- Memory usage;
- Storage I/O bandwitch;
- Storage IOPS;
- Traffic bandwitch;
Источники информации по этим метрикам в FreeBSD ОС избыточны в том плане, что вы можете получать их из разных мест - потребление CPU можно считывать из bhyve, из фреймворка rctl(4), из утилиты top(1) и ps(1) (kvm_getprocs(3) в частности); информацию по I/O - через GEOM или 'zfs iostat', vmstat(8)/iostat(8); трафик - с утилиты netstat(1) или счетчиков ipfw(8) и pf(4) и т.д.
Скрипты CBSD могут собирать и накапливать данную статистику и предоставлять к ним интерфейс для дальнейшей обработки. В качестве примера, вы можете посмотреть информацию о получении трафика в CBSD для определенного контейнера: trafstat. В этой статье рассказывается, каким образом цифры можно экспортировать в графическое представление для более наглядной формы. Начиная с версии 11.1.0, CBSD позволяет экспортировать метрики с jrctl и trafstat утилит в унифицированном формате как для jail так и для bhyve в формате prometheus.
Настройка Prometheus
Итак, наши компоненты:
- FreeBSD как ОС общего пользования, на которой все и происходит;
- bhyve и jail как гипервизор и контейнера, в которых запускаются некие сервисы и метрику с которых мы будем снимать;
- CBSD - фреймворк управления bhyve и jail, позволяющий также выгрузить статистику по тому или иному гипервизору или контейнеру в формате prometheus;
- Prometheus - система сбора метрик в формате key-value pairs с возможностью делать выборку запросами, с эффективным движком хранения данных (leveldb), имеющую встроенную интеграцию с Grafana, а также, имеющая клиентские библиотеки для популярных языков программирования;
- Grafana - популярная и гибкая система Dashboard-ов с аналитикой и графиками по любым метрикам;
Получить информацию о загрузке виртуальной машины или контейнера по их имени в CBSD вы можете по команде 'cbsd jrctl'. Этот скрипт использует метрики, предоставляемые RACCT фреймворком, поэтому убедитесь, что ваш /boot/loader.conf имеет соответствующую настройку:
kern.racct.enable=1
По-умолчанию, 'cbsd jrctl' выводит информацию в формате ключ-значение:
% cbsd jrctl mode=show jname=f11 datasize=2080K stacksize=264K coredumpsize=0 memoryuse=63M memorylocked=0 maxproc=1 openfiles=40 vmemoryuse=1099M nthr=29 nsemop=0 wallclock=6206 pcpu=0 readbps=0 writebps=0 readiops=0 writeiops=0
Если аргумент jname= опустить, вы увидете информацию по всем имеющимся окружениям. Если данная команда вызвана с ключем prometheus=1, вывод будет осуществляться в том формате, который ожидают prometheus targets.
Экспортировать метрики в prometheus можно по-разному. Один из способов - отправлять метрику напрямую в prometheus используя любой комфортный для вас язык программирования, где имеется библиотека клиента prometheus.
Мы реализуем второй вариант, когда сервер prometheus будет сам обходить точки и собирать необходимую статистику. Для этого можно написать простенький демон, проксирующий вывод команды 'cbsd jrctl mode=show prometheus=1' на определенном tcp порту. Либо, чтобы избежать потенциальных проблем с безопасностью (вам нужно обезопасить этот демон от действий возможных злоумышленников, поскольку работа с командой 'cbsd' требует привелигированного пользователя) и перенаправить вывод 'cbsd jrctl' в промежуточный файл, который будет доступен на отдачу через любой WEB сервер, например NGINX.
Схематично, это может выглядеть следующим образом:

В качестве сборщика и генератора index.html файла может выступать написанный самостоятельно демон, либо скрипт вызываемый из cron, либо демонизированный sh-скрипт, который и описывается здесь.
Каждая нода будет экспортировать свои метрики по http://<FQDN>/rctl/ , задача prometheus - собрать статистику и показать grafana-е.
Prometheus и Grafana мы установим также в jail, для чего запустим диалог создания контейнера:
В качестве jname, введем произвольное имя, например: grafana
% cbsd jconstruct-tui

Остальные параметры меняем по своему усмотрению (если это необходимо) и нажимаем 'Go PROCEED!'
Зайдем в jail и установим пакеты prometheus и grafana:
% cbsd jstart grafana % cbsd jlogin grafana % pkg install -y net-mgmt/prometheus www/grafana4
Установим сервисы в автозагрузку:
% sysrc grafana_enable="YES" % sysrc prometheus_enable="YES"
Отредактируем конфигурационный файл /usr/local/etc/prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
monitor: 'codelab-monitor'
rule_files:
scrape_configs:
- job_name: rctl
metrics_path: /rctl/
static_configs:
- targets: ['rctl.olevole.ru:80']
где:
- rctl.olevole.ru:80 - непосредственно FQDN нашего хостера и порт nginx, который мы сконфигурируем ниже.
- scrape_interval: 15s - интервал, с которым prometheus будет опрашивать метрики. Мы выбрали частоту в 15 секунд.
Запускаем сервисы:
% service grafana start % service prometheus start
и на этом с контейнером и настройкой сервисов - все.
Если вы не имеете доступ в контейнер напрямую, выполним на хостере команды для проброса необходимых портов:
% cbsd expose mode=add in=3000 jname=grafana % cbsd expose mode=add in=9090 jname=grafanaДалее напишем скрипт, который будет запускаться при старте сервера и через необходимый интервал времени будет регенирировать index.html файл с метриками на основе данных из 'cbsd jrctl'. Интервал времени логично ставить такой, с какой частотой забирает prometheus - ранее мы зафиксировали этот интервал в 15 секунд.
Сам скрипт может выглядеть следующим образом (скачать):
#!/bin/sh
while getopts "r:i:" opt; do
case "$opt" in
r) root_dir="${OPTARG}" ;;
i) interval="${OPTARG}" ;;
esac
shift $(($OPTIND - 1))
done
export TMPDIR="${root_dir}"
unset jname
if [ -z "${root_dir}" ]; then
echo "Empty root_dir, please use $0 -r path"
exit 1
fi
[ -z "${interval}" ] && interval="15"
[ ! -d "${root_dir}" ] && mkdir -p ${root_dir}
while [ true ]; do
INDEX_OLD=$( readlink ${root_dir}/index.html )
INDEX_TMP=$( mktemp )
trap "/bin/rm -f ${INDEX_TMP}" HUP INT ABRT BUS TERM EXIT
chmod 0644 ${INDEX_TMP}
truncate -s0 ${INDEX_TMP}
/usr/bin/lockf -s -t0 /tmp/cbsd-rctl.lock /usr/local/bin/cbsd jrctl mode=show prometheus=1 > ${INDEX_TMP}
ln -sf ${INDEX_TMP} ${root_dir}/index.html
rm -f ${INDEX_OLD}
sleep ${interval}
done
На входе скрипту через параметр -r мы задаем корневой каталог, в котором будем сохранять index.html, через аргумент -i - регулировать частоту обновления.
Как мы видим, скрипт работает в бесконечном цикле, вызывая команду статистики и засыпая на определенную нами паузу.
Поместим этот файл в каталог /root/bin под именем cbsdrctl: /root/bin/cbsdrctl и установим флаг исполнения:
% chmod +x /root/bin/cbsdrctl
Осталось создать rc.d-скрипт, который будет контроллировать запуск и останов /root/bin/cbsdrctl.
Напишем скрипт следующего содержания (скачать):
#!/bin/sh
#
# PROVIDE: cbsdrctl
# REQUIRE: LOGIN FILESYSTEMS sshd
# KEYWORD: shutdown
#
# cbsd_rctl_enable="YES"
#
. /etc/rc.subr
name=cbsdrctl
rcvar=cbsdrctl_enable
load_rc_config $name
start_cmd=${name}_start
stop_cmd=${name}_stop
status_cmd="${name}_status"
restart_cmd=${name}_restart
extra_commands="restart"
# Set defaults
: ${cbsdrctl_enable:="NO"}
: ${cbsdrctl_interval:="15"}
: ${cbsdrctl_root:="/tmp/metrics/rctl"}
cbsdrctl_start()
{
if [ -r ${pidfile} ]; then
echo "Already running: `cat ${pidfile}`"
exit 0
fi
/usr/sbin/daemon -f -p ${pidfile} /root/bin/cbsdrctl -r ${cbsdrctl_root} -i ${cbsdrctl_interval}
echo "STARTED: -r ${cbsdrctl_root} -i ${cbsdrctl_interval}"
}
cbsdrctl_status()
{
if [ -f "${pidfile}" ]; then
pids=$( pgrep -F ${pidfile} 2>&1 )
_err=$?
if [ ${_err} -eq 0 ]; then
echo "Running"
else
echo "Not running"
fi
else
echo "Not running"
fi
}
cbsdrctl_restart()
{
cbsdrctl_stop
cbsdrctl_start
}
cbsdrctl_stop()
{
if [ -f "${pidfile}" ]; then
pids=$( pgrep -F ${pidfile} 2>&1 )
_err=$?
if [ ${_err} -eq 0 ]; then
kill -9 ${pids} && /bin/rm -f ${pidfile}
else
echo "pgrep: ${pids}"
return ${_err}
fi
fi
}
pidfile=/var/run/$name.pid
run_rc_command "$1"
Сохраним этот скрипт как /usr/local/etc/rc.d/cbsdrctl и выдадим ему исполняемый флаг:
% chown root:wheel /usr/local/etc/rc.d/cbsdrctl % chmod 0555 /usr/local/etc/rc.d/cbsdrctl
Параметры интервала и рабочего каталога вынесены в переменные:
cbsdrctl_interval (значение по-умолчанию: 15)
cbsdrctl_root (значение по-умолчанию: /tmp/metrics/rctl)
Если эти значения вас не устраивают, вы можете переопределить через rc.conf:
% sysrc cbsdrctl_interval="15" % sysrc cbsdrctl_root="/tmp/metrics/rctl"
Установим скрипт как запускаемый:
% sysrc cbsdrctl_enable="YES"
И наконец, запустим:
% service cbsdrctl start
Настройка NGINX
Если в предыдущем шаге все получилось, в каталоге /tmp/metrics/rctl начнет обновлятся символическая ссылка index.html указывающая на файл с метрикой.
Нам необходимо настроить WEB сервер на отдачу этих файлов. Установим nginx на нашей ноде:
% pkg install -y nginx % sysrc nginx_enable=YES
Допишем в конфигурационный файл nginx /usr/local/etc/nginx/nginx.conf описание нашего виртуального хоста (в примере, FQDN сервера: rctl.olevole.ru)
server {
listen 80;
listen [::]:80;
server_name rctl.olevole.ru;
location /rctl {
root /tmp/metrics;
}
}
И запустим NGINX.
% service nginx start
На данном этапе, при обращении на http://<FQDN>/rctl вы должны видеть содержимое файла /tmp/metrics/rctl/index.html
Проверяем работу
Мы можем убедится, что prometheus получает метрики корректно. Для этого откроем броузером интерфейс предоставляемый процессом prometheus на 9090 порту.
Напротив Execute в выпадающем списке будут все метрики, которые предоставляет CBSD. Если метрика относится к контейнеру, то метрика будет начинаться с префикса jail_, если это параметр виртуальной машины, то с bhyve_.

Осталось воспользоваться плодами работы в Grafana и создать отдельный Dashboard для наших виртуальных сред.
GRAFANA
Для работы с GRAFANA, отроем в броузере сервер на порту 3000. Инсталляция GRAFANA по-умолчанию имеет пользователя 'admin' с паролем 'admin':

На первом экране нам предлагают сконфигурировать различые компоненты: завести пользователя, добавить источники данных и добавить дополнительные плагины:

Поскольку мы получаем данные от prometheus, добавим соответствующий источник. В данном примере, поскольку prometheus работает в том же самом окружении что и grafana, мы можем устанавить значение url как http://localhost:9090. Нажимаем Save&Test и переходим к созданию первого графика.

Для добавления индивидуальной метрики, вы можете начать вводить его в поле Metric lookup. Здесь работает автодополнение и вам будет проще найти искомый параметр:


Как мы видим, экспортировать доступные параметры как по jail, так и по bhyve достаточно легко. Новые сервера будут автоматически попадать в базу метрик и настроенные скрипты вам трогать уже не придется. В данном примере мы получаем метрику из FreeBSD RACCT и она доступна для снятия с каждого сервера по пути /rctl. Вы можете увеличивать и расширять метрики, например по пути /trafstat вы можете настроить метрики по трафику контейнеров, а по /billing настроить метрики с графикой тарификаций по потребляемым ресурсам. Данная работа ограничена только вашей фантазией и потребностью в информации о состоянии ваших систем и возможных трендах по утилизацию ресурсов. Информация позволяет вам держать руку на пульсе и заранее прогнозировать потенциальные проблемы и в случае с FreeBSD и CBSD - получить эту информацию можно достаточно легко.
