FreeBSD virtual environment management and repository

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. Здесь работает автодополнение и вам будет проще найти искомый параметр:

Изучите на man странице rctl, в каких мерах выдается статистика - в процентах, в байтах, bit/sec или попугаях и настраивайте метрику в соответствии с этой системой счисления:

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