Kubernetes де-факто стал стандартом при разработке контейнерных приложений, поскольку он предоставляет огромный набор функциональных возможностей "из коробки", которые помогают разработчикам создавать масштабируемые и отказоустойчивые системы. Все выглядит прекрасно, если вы разрабатываете что-то с нуля, но всем известно, что для большинства компаний это не так! Со временем многие унаследованные системы превратились в гигантских монолитных монстров, которые работают не на контейнерах, а на виртуальных машинах (ВМ). Рефакторить такие системы очень сложно по разным причинам: Технические причины (например, зависит от устаревших операционных систем или ядер) Бизнес-причины (например, время до выхода на рынок, стоимость преобразования) Трудности с вендорами (например, вендор не предоставляет решение в формате контейнера). Или, трудно поверить, но новые приложения действительно могут быть разработаны для работы на ВМ вместо контейнеров. Это не должно помешать вам использовать Kubernetes (K8s), а KubeVirt - подходящий инструмент для того, чтобы перенести эти ВМ в мир K8s. KubeVirt - это надстройка по управлению ВМ для K8s. Его цель - обеспечить общую основу для решений виртуализации поверх K8s. С помощью KubeVirt можно управлять ВМ как ресурсом K8s, подобно подам. Вы можете объявлять, запускать, останавливать, удалять, масштабировать и... контролировать их! Таким же образом, как и в K8s. Сейчас я расскажу не о самом KubeVirt, а о том, как контролировать ВМ аналогично тому, как вы контролируете контейнеры в K8s. У KubeVirt имеется замечательное руководство пользователя, если вы захотите узнать о нем больше. Если вы работаете с Kubernetes достаточно долго, то заметите, что он сам по себе может собирать некоторые метрики о ресурсах, потребляемых подами, такие как использование процессора и памяти. Часто этого бывает недостаточно, и приложения обычно разрабатываются так, чтобы предоставлять дополнительные метрики, которые помогают следить за тем, чтобы они работали так, как ожидалось. Если мы переносим ВМ на K8s, перед нами встает та же проблема! Процессора и памяти недостаточно, чтобы определить, правильно ли она работает или нет. Обычно нам нужно проверить множество других показателей, например, использование диска и свопа, а так как ВМ не являются эфемерными, как контейнеры, мы должны следить за тем, работает она или нет. Prometheus - это известное решение для мониторинга и оповещения, включающее в себя множество удобных интеграций с K8s, ставшее самым популярным вариантом при обсуждении выбора мониторинга контейнерных приложений. В основном он используется с Prometheus-Operator, который предоставляет некоторые дополнительные компоненты, значительно облегчающие настройку, и именно его мы будем применять в нашем руководстве. Сообщество Prometheus также разработало node-exporter, который в основном используется для мониторинга узлов K8s, а также будет использоваться для мониторинга наших ВМ KubeVirt. Итак, приступим! В данном руководстве будет использоваться следующий набор инструментов: Helm v3 - для развертывания Prometheus-Operator. minikube - предоставит нам кластер K8s, однако вы можете выбрать любого другого поставщика K8s. kubectl - для развертывания различных ресурсов K8s. virtctl - для взаимодействия с ВМ KubeVirt, может быть загружен из репозитория KubeVirt. После того, как у вас есть кластер K8s, с minikube или любым другим провайдером, первым шагом будет деплой Prometheus Operator. Причина в том, что KubeVirt CR, будучи установленным на кластере, определит, существует ли уже ServiceMonitor CR. Если да, то он создаст ServiceMonitors, настроенные на мониторинг всех компонентов KubeVirt (virt-controller, virt-api и virt-handler) "из коробки". Хотя мониторинг самого KubeVirt не рассматривается в этом руководстве, хорошей практикой является развертывание Prometheus Operator перед установкой KubeVirt. Чтобы развернуть Prometheus Operator, вам нужно сначала создать его пространство имен, например, monitoring: Затем разверните оператор в новом пространстве имен: После того как все развернуто, можно удалить все, что было загружено с помощью helm: Следует помнить об имени релиза, которое мы добавили здесь: kubevirt-prometheus. Имя релиза будет использоваться при объявлении нашего ServiceMonitor позже. Итак, следующим шагом будет деплой самого KubeVirt. Начнем с его оператора. Возьмем последнюю версию, затем воспользуемся kubectl create для деплоя манифеста непосредственно с Github: Перед развертыванием KubeVirt CR убедитесь, что все реплики kubevirt-operator готовы; это можно сделать с помощью: После этого мы можем развернуть KubeVirt и аналогичным образом дождаться готовности всех его компонентов: Если мы хотим мониторить ВМ, которые могут перезапускаться, необходимо, чтобы наши node-exporter были персистентными, и, следовательно, нужно установить для них персистентное хранилище. CDI будет компонентом, отвечающим за это, поэтому мы также развернем его оператор и пользовательский ресурс. Как всегда, подождем, пока нужные компоненты будут готовы, прежде чем приступить к работе: Теперь у нас есть все необходимое. Давайте настроим ВМ. Начнем с PersistentVolumes, которые необходимы для ресурсов DataVolume в CDI. Поскольку я использую minikube без провайдера динамического хранилища, то создам 2 PV со ссылкой на PVC, которые будут на них претендовать. Обратите внимание на claimRef в каждом из PV. Вы можете создать манифест YAML с приведенным выше содержимым и создать PV с помощью kubectl apply -f your-pv-manifest.yaml . С установленным персистентным хранилищем можно создать нашу ВМ со следующим манифестом: Это довольно большой манифест с массой информации, поэтому давайте разобьем его на части. Используем API, добавленный KubeVirt, и создаем новый ресурс VirtualMachine с названием monitorable-vm. В спецификации ВМ мы указываем KubeVirt выполнить автозапуск виртуальной машины после ее создания: Параметр template используется для создания VirtualMachineInstance (или сокращенно VMI), который представляет собой работающую ВМ. Способ объявления VirtualMachineInstance очень похож на то, как объявляются поды, мы добавляем некоторые ресурсы, диски и тома. Но, обратите внимание на параметры, поскольку некоторые из них немного отличаются от спецификации подов. Здесь важно заметить, что мы обозначили наш VMI с помощью prometheus.kubevirt.io: "node-exporter", эта метка будет использоваться нашим будущим Service для идентификации того, что мы хотим отслеживать именно эту ВМ. DataVolume будет получен из dataVolumeTemplate, который мы создали ниже. DataVolume — это абстракция PersistentVolumeClaim, которая импортирует образ операционной системы в это персистентное хранилище. Обратите внимание на имя dataVolume, которое должно совпадать с тем, которое мы использовали в шаблоне VMI. Также заметьте, что мы извлекаем образ непосредственно из интернета, но для этого существуют и другие способы. И последнее замечание, этот dataVolume создаст 2 PVC с идентичными характеристиками, оба PVC будут носить имя dataVolume, но у одного из них добавится суффикс -scratch. Помните, что имя должно совпадать с параметром claimRef, добавленным к ранее созданным PV. Ну, а теперь создайте свой YAML манифест и запустите его с помощью: PVC будут созданы, затем CDI создаст под с названием importer-cirros-dv для импорта нашего образа в PVC. После его завершения будет создан ресурс нашей VirtualMachine. Поскольку мы указали KubeVirt запустить нашу ВМ сразу после ее создания, вы также обнаружите ресурс VirtualMachineInstance. И, наконец, VirtualMachineInstances KubeVirt создают новый компонент под названием virt-launcher . Это не что иное, как под, который запускает процесс виртуализации, так что... мы все еще запускаем контейнеры... Виртуальные машины запускаются внутри контейнера virt-launcher Если хотите проверить, что все на месте, должно быть что-то похожее на это: После запуска VirtualMachineInstance мы можем подключиться к его консоли с помощью команды virtctl console monitorable-vm. Если требуется ввести пользователя и пароль, укажите свои учетные данные соответствующим образом. Если вы используете образ диска из этого руководства, то пользователь и пароль - cirros и gocubsgo соответственно. Нижеследующий скрипт установит node-exporter и настроит ВМ на постоянный запуск экспортера при загрузке. P.S.: Если вы используете другой исходный образ, пожалуйста, настройте node-exporter на запуск во время загрузки соответствующим образом. Настроить Prometheus на скрейпинг node-exporter (или других приложений) очень просто. Все, что нам нужно, это создать новые Service и ServiceMonitor: Давайте разберем все по порядку, чтобы убедиться, что мы все настроили правильно. Начнем с Service: Согласно спецификации, мы создаем новый порт с названием metrics, который будет перенаправлен на каждый под с меткой prometheus.kubevirt.io: "node-exporter", на порту 9100, который является номером порта по умолчанию для node-exporter. Мы также маркируем сам Service с помощью prometheus.kubevirt.io: "node-exporter", который будет использоваться объектом ServiceMonitor. Теперь давайте посмотрим на нашу спецификацию ServiceMonitor: Поскольку ServiceMonitor будет развернут в пространстве имен monitoring, а наш сервис находится в пространстве имен default, необходимо, чтобы namespaceSelector.any=true. Мы также указываем нашему ServiceMonitor, что Prometheus должен соскрейпить конечные точки из сервисов, маркированных prometheus.kubevirt.io:node-exporter" и порты которых называются metrics. К счастью, именно это мы и сделали с нашим Service. И последнее, на что следует обратить внимание. Конфигурация Prometheus может быть настроена на наблюдение за несколькими ServiceMonitors. Посмотреть, за какими ServiceMonitors следит наш Prometheus, можно с помощью следующей команды: Убедитесь, что наш ServiceMonitor имеет все метки, необходимые для Prometheus's Service Monitor Selector. Как правило, селектором является имя релиза, которое мы задали при развертывании нашего Prometheus с помощью helm! Эта часть может быть действительно сложной. Если у вас возникли проблемы, пожалуйста, посмотрите на Prometheus-operator troubleshooting. Вы можете выполнить быстрый тест, перенаправив (пробросив) порты через веб-интерфейс Prometheus и выполнив некоторые PromQL: Для проверки того, что все работает, зайдите на localhost:9090/graph и выполните PromQL up{pod=~"virt-launcher.*"}. Prometheus должен вернуть данные, которые собираются из node-exporter monitorable-vm. Чтобы посмотреть, как ведут себя метрики, можно поиграть с virtctl, остановить и запустить ВМ. Заметьте, что при остановке ВМ с помощью virtctl stop monitorable-vm, VirtualMachineInstance уничтожается и, таким образом, уничтожается и под. В результате наш Service не сможет найти конечную точку пода, и тогда она будет удалена из целей Prometheus. С таким поведением оповещения, подобные приведенному ниже, не будут работать, поскольку наша цель буквально исчезла, а не отключилась. НО если у ВМ постоянно происходит крэш без остановки, то под не убивается, а цель по-прежнему будет отслеживаться. Node-exporter никогда не запустится или будет постоянно падать вместе с ВМ, поэтому такое оповещение может сработать: Для вашего удобства почти полностью автоматизированный сценарий можно найти по адресу https://github.com/ArthurSens/kubevirt-VM-monitoring. Скрипт deploy-everything.sh установит Prometheus, KubeVirt, CDI в ваш кластер k8s, а также настроит PersistentVolume, VirtualMachine, Service и ServiceMonitor. Затем он подключится к консоли VirtualMachine, где вам нужно будет установить node-exporter с помощью shell-скрипта, который можно найти по адресу install-node-exporter.sh. Вот и все! Теперь вы знаете, как перенести ваши устаревшие виртуальные машины в кластер Kubernetes, настроить некоторые основные ресурсы и следить за ними, как за контейнерными приложениями! Обратите внимание, что node-exporter - это только пример, вы можете следовать той же логике для windows ВМ с помощью windows-exporter или ваших собственных приложений, которые работают внутри ваших ВМ. Все, что вам нужно, это чтобы они передавали метрики через HTTP в формате Prometheus, затем настройте Service и ServiceMonitor на правильный номер порта. Материал подготовлен для будущих студентов курса «Observability: мониторинг, логирование, трейсинг». Всех желающих приглашаем на открытый урок «Grafana: формирование дашбордов». На занятии: Проанализируем возможности по формированию дашбордов.  Разберем, как использовать дашборды и тиражирование, а также переменные. Посмотрим формирование дашбордов для различных окружений.