Хочу вам рассказать историю, когда обычный банальный скрипт выявил два бага. Один баг официально подтверждён, а второй ещё предстоит понять и оформить с вашей помощью. Кому интересны загадки, которые подбрасывает жизнь админам, прошу к прочтению.
Мы используем систему виртуализации проекта Proxmox VE (Debian GNU/Linux, KVM, Qemu, ...), которая обладает прекрасными для нас свойствами: бесплатна, развитый API, мощный CLI, интуитивный Web GUI. Данная система виртуализации у нас является платформой (фундаментом), поверх которой работают виртуальные машины, обслуживающие запросы пользователей. Всё больше и больше ВМ создаются для различных отделов нашего предприятия и управляются людьми (изнутри ВМ), из числа назначенные быть ответственными. Всегда есть конкретный человек, который "заходит внутрь" с помощью RDP в случае MS Windows Server или с помощью SSH в случае Linux систем, и выполняет там требуемые действия. Чтобы подстраховаться на случай разрушительных действий сотрудника, который не относится к ИТ отделу, кроме создания по ночам резервных копий всех ВМ, решено было создавать в автоматическом режиме снимки (snapshot), чтобы можно было быстро откатиться к рабочему состоянию. Так как пла́той за использование снимков является возросшее занятое место и увеличенное количество операций записи, то решили с ума не сходить и делать не более 3 снимков: 1 день назад (вчера), 2 дня назад (позавчера) и 3 дня назад (позапозавчера).
В основном мои скрипты на bash. Да, они несколько топорны, но просты в понимании, что не маловажно. Скрипт должен создавать каждый день новый снимок и удалять самый старый снимок, чтобы всегда было доступно столько снимков сколько решил администратор для данной ВМ. Нужно предусмотреть ситуацию, что снимки может создавать админ через web интерфейс и нельзя их удалять. Скрипт удаляет только те снимки, что создавал сам.
Полный текст скрипта вы найдёте в конце статьи, а для объяснения сути двух багов мне нужно представить первоначальные версии.
Создал для скрипта вспомогательный файл vmid_snapshot.txt, в котором строки типа
125 3
124 2
116 3
Цифра VMID виртуальной машины и через пробел желаемое админом количество снимков у данной ВМ.
Циклом while читаем файл
while read vmid days
do
# тело цикла
done < vmid_snapshot.txt
В теле цикла самые важные вещи:
1) через CLI pvesh узнать на какой ноде в данный момент работает конкретная ВМ
2) через CLI pvesh сделать снимок
# узнаем на какой ноде нужная VM
NODE=`/usr/bin/pvesh get /cluster/resources --type vm | jq --raw-output ".[] | select(.vmid == ${vmid}) | .node"`
# создаём снимок
/usr/bin/pvesh create /nodes/${NODE}/qemu/${vmid}/snapshot -snapname "ss_${curdate}" -vmstate false -description "automatically created"
И баг нашёлся там где его совсем не ждали. Снимки будут иметь имя в виде ss_датаВвидеГОДмесяцДЕНЬчасМИНУТА и описание automatically created, чтобы в дальнейшем скрипт мог идентифицировать снимки, созданные им, а не человеком. Скрипт будет трудиться на одной из нод кластера виртуализации и отдавать приказ о создании снимка конкретной ВМ, где бы она не находилась. Так вот скрипт, запущенный на сервер1, не мог создать снимок у ВМ, которая находится не на сервер1!
Вываливалась ошибка типа
400 too many arguments create nodes/p6/qemu/116/snapshot -snapname[OPTIONS] proxy handler failed
Почти интуитивно фраза too many arguments намекнула мне что пробел в описании что-то поломал в недрах кода pvesh. Сделал описание -description "automatically" и всё заработало отлично! Решил запостить ошибку на баг-трекере проекта, но не нашёл как указать сбойный компонент pvesh - его там тупо не было! Поэтому отписался на форуме где опытный товарищ подтвердил и всё-таки попросил создать отчёт. Поискал в каком разделе раньше люди уведомляли об ошибках в pvesh и понял что это компонент pve-common. Создал отчёт под номером 1539
Решил добавить в vmid_snapshot.txt ещё строки, чтобы более всесторонне обкатать работу скрипта. Было 3 строки - стало 5! И тут на ровном месте отхватил баг, который просто не ожидал. Цикл while отрабатывал только 3 строки, словно ещё 2 строки не были добавлены. Подумал сначала, что может грешным делом вместо пробела между двумя числами в строке поставил табуляцию. Перепроверил. Поставлен пробел! Подумал, может гипотетически слишком быстро требую от кластера выполнения задания и превышаю некие пороги? Добавил sleep 60. Не помогло.
Трансформировал чтение файла к виду - cat vmid_snapshot.txt | while read vmid days. Бестолку. Отсекая строку за строкой вышел на команду, которая запрашивает количество снимков у данной ВМ перед процедурой удаления старого снимка.
# сколько снимков "automatically" у данной VM ?
countss=`/usr/bin/pvesh get /nodes/${NODE}/qemu/${vmid}/snapshot | jq --raw-output "sort_by(.snaptime)[] | select(.description == \"automatically\") | .name" | wc -l`
Если строку заремарить, то цикл while отработает 5 строк из vmid_snapshot.txt, если строка есть, то только 3. В данном месте я понял, что ничего не понял.
Сделал минимальный тестовый скрипт, чтобы понять что не брежу. Комментирую строку kolvo= и получаю отработанных 5 строк! Убираю комментарий у kolvo= - 3 строки!
cat vmid_snapshot.txt | while IFS=: read -r -e vmid days
do
# узнаём на какой ноде нужная VM
NODE=`/usr/bin/pvesh get /cluster/resources --type vm | jq --raw-output ".[] | select(.vmid == ${vmid}) | .node"`
# дай JSON ответ - сколько у ВМ снапшотов?
kolvo=`/usr/bin/pvesh get /nodes/${NODE}/qemu/${vmid}/snapshot`
echo ${kolvo}
done
И вот тут вынужден признаться, что не понимаю механизма влияния pvesh на цикл while. Как после отработки 3 строк сломаться? Нашёл на просторах Интернета такого же бедалагу, которому никто не помог - pvesh stops "Bash loop".
Не раскрыв суть бага, ибо моих знаний не хватает, и уповая на вашу помощь, тупо заменил while на for и получил рабочую версию скрипта photographer.sh
Оцените мой тонкий юмор Фотограф делает снимки.
#!/bin/bash curdate=`/bin/date "+%Y%m%d%H%M"` for x in $(cat vmid_snapshot.txt) do vmid=`echo $x | cut -d \: -f 1` days=`echo $x | cut -d \: -f 2` # узнаём на какой ноде нужная VM NODE=`/usr/bin/pvesh get /cluster/resources --output-format json --type vm | jq --raw-output ".[] | select(.vmid == ${vmid}) | .node"` echo "-------- ${NODE} -- ${vmid} ------- ${days} --------" result=`/usr/bin/pvesh create /nodes/${NODE}/qemu/${vmid}/snapshot -snapname "ss${curdate}" -vmstate false -description "automatically"` echo ${result} # сколько снимков "automatically" у данной VM ? countss=`/usr/bin/pvesh get /nodes/${NODE}/qemu/${vmid}/snapshot --output-format json | jq --raw-output "sort_by(.snaptime)[] | select(.description == \"automatically\") | .name" | wc -l` # пока снимков больше чем нужно - удаляем COUNTER=${days} while [ ${COUNTER} -lt ${countss} ]; do /bin/sleep 15 namess=`/usr/bin/pvesh get /nodes/${NODE}/qemu/${vmid}/snapshot --output-format json | jq --raw-output "sort_by(.snaptime)[] | select(.description == \"automatically\") | .name" | head -1` echo "Count snapshot="${countss}" daysaves="${days}" VMID="${vmid}" name snapshot="${namess} result=`/usr/bin/pvesh delete /nodes/${NODE}/qemu/${vmid}/snapshot/${namess}` echo ${result} # сколько снимков "automatically" у данной VM ? countss=`/usr/bin/pvesh get /nodes/${NODE}/qemu/${vmid}/snapshot --output-format json | jq --raw-output "sort_by(.snaptime)[] | select(.description == \"automatically\") | .name" | wc -l` done echo "--------------------------------" done exit 0
Не решил проблему, трусливо сбежав на другой цикл. Прошу помощи опытных админов, которые разжуют механизм ошибки, чтобы впредь быть во всеоружии.
Дата последней правки: 2023-12-26 17:37:46