Задача: подготовить к вводу в работу одиночный сервер KVM-виртуализации, воссоздав конфигурацию такового после аппаратного сбоя дисковой подсистемы, для унификации с уже имеющейся пачкой аналогичной функциональности серверов.
Обращаю внимание на то, что по сути это история восстановления сервиса в том виде, как он есть - без попыток сделать лучше. Года четыре назад "уже забыто кто" установил на десяток лезвий в корзине "HP BladeSystem" не связанные между собой инстансы KVM-виртуализации, подключил всё это посредством "Fibre Channel" к внешнему хранилищу "HP 3PAR", нарезал индивидуально для каждого лезвия "луны", навесил на всё это гроздь виртуальных машин - и не время сейчас было менять схему.
Последовательность действий по вводу в строй лезвия с KVM-виртуализацией:
1. Активируем поддержку ядром системы контроллера "Emulex FCoE".
2. Адаптируем "SCSI Multipath" для подключения к "HP 3PAR".
3. Настраиваем совместную работу "SCSI Multipath" и LVM.
4. Устанавливаем и преднастраиваем виртуальный коммутатор "Open vSwitch".
5. Добавляем VLAN-ы в виртуальный коммутатор.
6. Подключаемся к системе виртуализации через LibVirt-клиента.
2. Адаптируем "SCSI Multipath" для подключения к "HP 3PAR".
3. Настраиваем совместную работу "SCSI Multipath" и LVM.
4. Устанавливаем и преднастраиваем виртуальный коммутатор "Open vSwitch".
5. Добавляем VLAN-ы в виртуальный коммутатор.
6. Подключаемся к системе виртуализации через LibVirt-клиента.
Для начала установим несколько утилит для удобства работы в консоли:
# apt-get install aptitude sudo acl psmisc host mc htop iotop bmon
Активируем поддержку контроллера "Emulex OneConnect 10Gb FCoE".
Исходно у нас на борту два HDD, объединённых в RAID1, на который установлена операционная система, и интерфейсная карта "Fibre Channel", через два оптоволоконных канала которой подключена внешняя система хранения, на которой располагаются "диски" виртуальных машин:
# lspci
....
04:00.2 Fibre Channel: Emulex Corporation OneConnect 10Gb FCoE Initiator (be3) (rev 01)
04:00.3 Fibre Channel: Emulex Corporation OneConnect 10Gb FCoE Initiator (be3) (rev 01)
....
04:00.2 Fibre Channel: Emulex Corporation OneConnect 10Gb FCoE Initiator (be3) (rev 01)
04:00.3 Fibre Channel: Emulex Corporation OneConnect 10Gb FCoE Initiator (be3) (rev 01)
....
Чуть больше сведений об оборудовании:
# dmesg | grep -i emu
Emulex LightPulse Fibre Channel SCSI driver 11.2.0.0.
Copyright(c) 2004-2016 Emulex. All rights reserved.
be2net 0000:04:00.0: Emulex OneConnect(be3): PF FLEX10 port 1
be2net 0000:04:00.1: Emulex OneConnect(be3): PF FLEX10 port 2
scsi host1: Emulex OneConnect OCe11100, FCoE Initiator on PCI bus 04 device 02 irq 87
scsi host2: Emulex OneConnect OCe11100, FCoE Initiator on PCI bus 04 device 03 irq 92
Copyright(c) 2004-2016 Emulex. All rights reserved.
be2net 0000:04:00.0: Emulex OneConnect(be3): PF FLEX10 port 1
be2net 0000:04:00.1: Emulex OneConnect(be3): PF FLEX10 port 2
scsi host1: Emulex OneConnect OCe11100, FCoE Initiator on PCI bus 04 device 02 irq 87
scsi host2: Emulex OneConnect OCe11100, FCoE Initiator on PCI bus 04 device 03 irq 92
Из вывода "dmesg" видно, что драйвер оборудования "Emulex" в системном наборе таковых имеется, он загружен и даже заявлено, что им доставлены SCSI-устройства, но скорее всего в действительности контроллеру с внешним хранилищем согласовать подключение не удалось из-за разнобоя в ожидаемых параметрах.
Проблемой это не является - нужно просто передать драйверу "Emulex FCoE" (реализованному в виде модуля ядра "lpfc", в нашем случае) выверенные производителями оборудования и предложенные в руководстве по эксплуатации такового параметры сопряжения c "HP 3PAR".
Создаём файл конфигурации модуля ядра "lpfc" (драйвера "Emulex FCoE"):
# vi /etc/modprobe.d/emulex-hba-3par.conf
# Emulex HBA driver tuning for HP 3PAR Storage
options lpfc lpfc_devloss_tmo=14 lpfc_lun_queue_depth=16 lpfc_discovery_threads=32
options lpfc lpfc_devloss_tmo=14 lpfc_lun_queue_depth=16 lpfc_discovery_threads=32
Сразу пробуем вручную загрузить модуль, чтобы удостовериться в отсутствии критических ошибок:
# modinfo lpfc | grep -i lpfc_devloss_tmo
Уже сейчас сканирование посредством "blkid" доступных блочных устройств должно показать те, что расположены на внешней системе хранения.
Внедряем дополненную конфигурацию модуля в образ "Initrd".
В современном Linux-е драйверы устройств и файловых систем обычно загружаются на этапе начальной инициализации ядра операционной системы, собранного вместе с необходимыми модулями в компактном образе "Initrd" (от "Initial RAM Disk"), которому первым делом передаётся управление от BIOS-а несущей платформы. Так вот, каждый раз при изменении параметров ядра или его модулей необходимо всё это пересобирать в комплект "Initrd", чтобы ещё до загрузки системного и пользовательского окружения получить корректно инициализированные устройства.
Подстраховываясь, делаем резервную копию нынешнего "Initrd":
# mkdir /boot/.backup
# cp /boot/initrd.img-$(uname -r) /boot/.backup/initrd.img-$(uname -r).bak
# cp /boot/initrd.img-$(uname -r) /boot/.backup/initrd.img-$(uname -r).bak
Запускаем процедуру пересборки "Initrd", в процессе которой умная утилита просканирует директории с модулями и их настройками, после чего соберёт в новый образ всё необходимое:
# update-initramfs -v -d -k `uname -r` && update-initramfs -v -c -k `uname -r`
update-initramfs: Deleting /boot/initrd.img-4.9.0-6-amd64
update-initramfs: Generating /boot/initrd.img-4.9.0-6-amd64
....
Adding config /etc/modprobe.d/emulex-hba-3par.conf
....
Building cpio /boot/initrd.img-4.9.0-6-amd64.new initramfs
update-initramfs: Generating /boot/initrd.img-4.9.0-6-amd64
....
Adding config /etc/modprobe.d/emulex-hba-3par.conf
....
Building cpio /boot/initrd.img-4.9.0-6-amd64.new initramfs
После перезагрузки проверяем, успешно ли модулем приняты новые значения параметров, из заданных выше:
# cat /sys/module/lpfc/parameters/lpfc_devloss_tmo
Настраиваем "SCSI Multipath" на связку с "HP 3PAR".
Сейчас, когда драйвер "Emulex FCoE" наконец смог доставить с внешнего хранилища в сервер виртуализации блочные SCSI-устройства, мы увидим, что они умножены в числе по количеству каналов контроллера - в действии технология резервирования соединений "Multipath", позволяющая сохранить связь с удалённым устройством при разрыве одного из каналов - доставка данных идёт сразу по всем доступным каналам. Какое из устройств выбирать - непонятно.
Специально для работы с многоканальной поставкой SCSI-устройств в Linux-е имеется служба "multipathd". Инсталлируем её и запускаем:
# aptitude install multipath-tools
# service multipathd start
# service multipathd start
С максимальной детализацией ознакомится с состоянием сервиса "Multipath" можно следующей командой - там отображается всё необходимое, от загруженного модуля ядра, перечня полученных блочных устройств с их параметрами и привязкой к FC-каналу, до описания финального виртуального блочного устройства "multipath", которое предлагается к использованию (если оно вообще сформировано, разумеется):
# multipath -v3
Однако наверняка первое, что мы увидим при запросе статистики состояния подсистемы приведённой выше командой, в случае использования "HP 3PAR", так это уведомление об отсутствии поддержки внешним хранилищем протокола балансировки ALUA, в результате чего заготовленное виртуальное блочное устройство "multipath" не может быть предложено в эксплуатацию:
sde: alua not supported
sdb: alua not supported
....
sdh: alua not supported
DM message failed [queue_if_no_path]
reject: 360002ac0000000000000000d00005743 undef 3PARdata,VV
size=2.6T features='0' hwhandler='1 alua' wp=undef....
sdb: alua not supported
....
sdh: alua not supported
DM message failed [queue_if_no_path]
reject: 360002ac0000000000000000d00005743 undef 3PARdata,VV
size=2.6T features='0' hwhandler='1 alua' wp=undef....
Для полной уверенности уточняем, как именно полученные через FC блочные устройства анонсируют своего производителя:
# cat /sys/block/sdb/device/vendor
3PARdata
В общем, проблема совместимости "3PARdata" и "Multipath" известная, и легко решается корректировкой параметров их связки, приведением конфигурационного файла сервиса к следующему виду:
# vi /etc/multipath.conf
# Глобальные параметры сервиса
defaults {
polling_interval 10
user_friendly_names yes
find_multipaths yes
}
# Явное описание параметров сопряжения с внешними хранилищами
# (перекрывают возможно уже имеющиеся настройки "по умолчанию")
devices {
device {
vendor "3PARdata"
product "VV"
path_grouping_policy multibus
path_selector "round-robin 0"
path_checker "tur"
hardware_handler "0"
prio "const"
failback "immediate"
rr_weight "uniform"
rr_min_io 100
no_path_retry 18
}
}
blacklist {
devnode "^sda"
}
multipaths {
multipath {
# Уникальный идентификатор виртуального блочного устройства
# (генерируется автоматически при первой попытке собрать устройство)
wwid 360002ac0000000000000000d00005743
alias mpath0
}
}
defaults {
polling_interval 10
user_friendly_names yes
find_multipaths yes
}
# Явное описание параметров сопряжения с внешними хранилищами
# (перекрывают возможно уже имеющиеся настройки "по умолчанию")
devices {
device {
vendor "3PARdata"
product "VV"
path_grouping_policy multibus
path_selector "round-robin 0"
path_checker "tur"
hardware_handler "0"
prio "const"
failback "immediate"
rr_weight "uniform"
rr_min_io 100
no_path_retry 18
}
}
blacklist {
devnode "^sda"
}
multipaths {
multipath {
# Уникальный идентификатор виртуального блочного устройства
# (генерируется автоматически при первой попытке собрать устройство)
wwid 360002ac0000000000000000d00005743
alias mpath0
}
}
Перезагружаем сервис:
# service multipathd restart
Теперь, когда "Multipath" не пытается использовать для балансировки протокол ALUA, блочные устройства поставляемые через многоканальный FC должны быть приняты в работу и на их основе сформировано виртуальное блочное устройство, обращаться к которому можно по его символическому имени "mpath0":
# multipath -l
mpath0 (360002ac0000000000000000d00005743) dm-2 3PARdata,VV
size=2.6T features='2 queue_if_no_path retain_attached_hw_handler' hwhandler='0' wp=rw
`-+- policy='round-robin 0' prio=0 status=enabled
|- 1:0:3:0 sde 8:64 active undef running
|- 2:0:3:0 sdi 8:128 active undef running
....
`- 2:0:2:0 sdh 8:112 active undef running
size=2.6T features='2 queue_if_no_path retain_attached_hw_handler' hwhandler='0' wp=rw
`-+- policy='round-robin 0' prio=0 status=enabled
|- 1:0:3:0 sde 8:64 active undef running
|- 2:0:3:0 sdi 8:128 active undef running
....
`- 2:0:2:0 sdh 8:112 active undef running
Напомню, что всё внесённое в конфигурацию службы "Multipath" необходимо будет занести и в образ "Initrd", чтобы уже на первичном этапе загрузки ядра система в оптимальном режиме начала работать со множественно адресуемыми устройствами. Это мы сделаем чуть позже.
Настраиваем совместную работу "Multipath" и LVM.
Конфигурация LVM для применения такового на доставленных посредством "Multipath" блочных устройствах потребует дополнительной настройки. Дело в том, что по умолчанию сканирование на предмет LVM-разметки производится на всех доступных операционной системе блочных устройствах - а у нас некоторые из них множественно продублированы, поставляемые разным каналам "Fibre Channel".
Самый простой способ - определить фильтр, выбирающий для подсистемы LVM только те устройства, которые нам действительно нужны для работы:
# vi /etc/lvm/lvm.conf
....
# Задействуем обратный фильтр, отвергая часть неподходящих "блочных" устройств и разрешая всё остальное
filter = [ "r|/dev/block/.*|", "r|/dev/disk/.*|", "r|/dev/cciss/.*|", "a|/dev/sda.*|", "r|/dev/sd.*|", "a|.*|" ]
....
# Задействуем обратный фильтр, отвергая часть неподходящих "блочных" устройств и разрешая всё остальное
filter = [ "r|/dev/block/.*|", "r|/dev/disk/.*|", "r|/dev/cciss/.*|", "a|/dev/sda.*|", "r|/dev/sd.*|", "a|.*|" ]
....
Очень поможет исключить проблемы с определением конфигурации "блочных" устройств выключение напрочь подсистемы кеширования метаданных LVM-разметки, некстати активированную в "Debian 9" (что-то мейтейнеры в этой версии Linux сломали, отчего спарка "LVMetad" и "systemd-udevd" банально виснет и блокирует работу с LVM-разметкой):
# vi /etc/lvm/lvm.conf
....
use_lvmetad = 0
....
use_lvmetad = 0
....
Деактивируем и выключаем сервис кеширования метаданных LVM:
# systemctl disable lvm2-lvmetad
# systemctl stop lvm2-lvmetad
# systemctl stop lvm2-lvmetad
Производим сканирование доступных "блочных" устройств с LVM-разметкой и просматриваем перечень доступных:
# pvscan -v
# vgscan -v
# lvscan -v
# lsblk -f
# pvs
# vgscan -v
# lvscan -v
# lsblk -f
# pvs
PV VG Fmt Attr PSize PFree
/dev/mapper/mpath0 r2b10-storage-vg0 lvm2 a-- 2.64t 1.04t
/dev/sda1 r2b10-vg0 lvm2 a-- 838.33g 0
/dev/mapper/mpath0 r2b10-storage-vg0 lvm2 a-- 2.64t 1.04t
/dev/sda1 r2b10-vg0 lvm2 a-- 838.33g 0
После того, как мы убедимся в корректной работе фильтра "блочных" устройств для LVM, необходимо записать эту конфигурацию в "Initrd", чтобы загрузчик так же правильно работал с доступными ему "блочными" устройствами:
# mkdir /boot/.backup
# cp /boot/initrd.img-$(uname -r) /boot/.backup/initrd.img-$(uname -r).bak
# update-initramfs -v -d -k `uname -r` && update-initramfs -v -c -k `uname -r`
# cp /boot/initrd.img-$(uname -r) /boot/.backup/initrd.img-$(uname -r).bak
# update-initramfs -v -d -k `uname -r` && update-initramfs -v -c -k `uname -r`
Важно помнить, что после каждого изменения конфигурации "Multipath" и LVM нужно заново генерировать образ "Initrd", чтобы таковые применялись уже на этапе загрузки ядра системы.
Запускаем виртуальный коммутатор "Open vSwitch".
Устанавливаем пакеты программного обеспечения виртуального коммутатора, утилиты агрегирования и настройки параметров интерфейсов:
# aptitude install openvswitch-switch ifenslave ethtool
Заранее подготавливаем желаемую схему включения сервера виртуализации в сетевую инфраструктуру, агрегированным сетевым интерфейсом и "мостом" от него для виртуального коммутатора "Open VSwitch" (имеем в виду, что на период переключения в эту схему сетевые подключения пропадут):
# vi /etc/network/interfaces
....
# The primary network interface
allow-hotplug eno1
iface eno1 inet manual
address 0.0.0.0
# The secondary network interface
allow-hotplug eno2
iface eno2 inet manual
address 0.0.0.0
# The aggregated network interface
auto bond0
iface bond0 inet manual
address 0.0.0.0
bond-mode balance-tlb
bond-miimon 100
bond-downdelay 200
bond-updelay 200
bond-slaves eno1 eno2
# The bridge network interface for Open-vSwitch
#auto br0
iface br0 inet static
address 10.20.30.123
netmask 255.255.255.0
gateway 10.20.30.1
dns-nameservers 10.20.4.4 172.16.8.8
dns-search example.net
....
# The primary network interface
allow-hotplug eno1
iface eno1 inet manual
address 0.0.0.0
# The secondary network interface
allow-hotplug eno2
iface eno2 inet manual
address 0.0.0.0
# The aggregated network interface
auto bond0
iface bond0 inet manual
address 0.0.0.0
bond-mode balance-tlb
bond-miimon 100
bond-downdelay 200
bond-updelay 200
bond-slaves eno1 eno2
# The bridge network interface for Open-vSwitch
#auto br0
iface br0 inet static
address 10.20.30.123
netmask 255.255.255.0
gateway 10.20.30.1
dns-nameservers 10.20.4.4 172.16.8.8
dns-search example.net
....
Обращаю внимание на то, что в системном конфигурационном файле описания сетевых интерфейсов выше я не запускаю автоматически "мостовой" интерфейс "br0", явно оставляя опцию "#auto br0" неактивной - это будет сделано позже написанным нами скриптом, вместе с объяснением причины.
Вручную переводим сетевую конфигурацию в активное состояние:
# ifconfig eno1 0.0.0.0 down
# ifconfig eno2 0.0.0.0 down
# ifup bond0
# ifup br0
# ifconfig eno2 0.0.0.0 down
# ifup bond0
# ifup br0
Явно добавляем к виртуальному коммутатору "мост" и агрегированный порт к таковому:
# ovs-vsctl add-br br0
# ovs-vsctl add-port br0 bond0
# ovs-vsctl add-port br0 bond0
На этом этапе сетевая схема уже должна заработать примерно в следующей конфигурации:
# ovs-vsctl show
Bridge "br0"
Port "br0"
Interface "br0"
type: internal
Port "bond0"
Interface "bond0"
ovs_version: "2.6.2"
Port "br0"
Interface "br0"
type: internal
Port "bond0"
Interface "bond0"
ovs_version: "2.6.2"
Возможно в списке виртуальных сетевых интерфейсов окажется созданный по умолчанию "vnet0", так его лучше удалить для исключения путаницы:
# ovs-vsctl del-port br0 vnet0
Ознакомится с действующей конфигураций "Open-vSwitch" можно следующим образом:
# ovsdb-client dump | less S
Чиним связку "Systemd" и "Open-vSwitch" в "Debian Stretch".
Как частенько случается с "этими вашими линуксами", в "Debian Stretch" слегка поломали связку "Systemd" и "Open-vSwitch", отчего виртуальный "мостовой" интерфейс попросту не создаётся на этапе загрузки системы. После загрузки всех компонентов сетевой подсистемы интерфейс "br0" активируется без проблем - но не в процессе. Полагаю, что дело в очерёдности инициирования компонентов, но разбираться в причине было некогда - оказалось быстрее добавить службу, загружаемую после инициирования сети и добавляющую все необходимые связи.
# mkdir -p /usr/local/etc/openvswitch && cd /usr/local/etc/openvswitch
# vi ./ovs-networking.sh && chmod +x ./ovs-networking.sh
# vi ./ovs-networking.sh && chmod +x ./ovs-networking.sh
#!/bin/bash
# Инициируем сетевые интерфейсы
/sbin/ifup bond0
/sbin/ifup br0
# Добавляем сетевые интерфейсы в OpenVSwitch
ovs-vsctl --may-exist add-br br0
ovs-vsctl --may-exist add-port br0 bond0
# Отключаем поддержку ненужного (как правило) STP/rSTP
ovs-vsctl set bridge br0 stp_enable=false
ovs-vsctl set bridge br0 rstp_enable=false
exit ${?}
# Инициируем сетевые интерфейсы
/sbin/ifup bond0
/sbin/ifup br0
# Добавляем сетевые интерфейсы в OpenVSwitch
ovs-vsctl --may-exist add-br br0
ovs-vsctl --may-exist add-port br0 bond0
# Отключаем поддержку ненужного (как правило) STP/rSTP
ovs-vsctl set bridge br0 stp_enable=false
ovs-vsctl set bridge br0 rstp_enable=false
exit ${?}
Создаём файл описания "юнита" запуска скрипта конфигурирования посредством Systemd:
# vi /etc/systemd/system/ovs-networking.service
[Unit]
Description=OpenVSwitsh custom networking
Requires=network-online.target networking.service openvswitch-switch.service
After=network-online.target networking.service openvswitch-switch.service
[Service]
User=root
Type=notify
RemainAfterExit=yes
ExecStart=/bin/bash /usr/local/etc/openvswitch/ovs-networking.sh
[Install]
WantedBy=multi-user.target
Description=OpenVSwitsh custom networking
Requires=network-online.target networking.service openvswitch-switch.service
After=network-online.target networking.service openvswitch-switch.service
[Service]
User=root
Type=notify
RemainAfterExit=yes
ExecStart=/bin/bash /usr/local/etc/openvswitch/ovs-networking.sh
[Install]
WantedBy=multi-user.target
# systemctl daemon-reload
# systemctl enable ovs-networking.service
# systemctl enable ovs-networking.service
Рекомендую сразу проконтролировать, верно ли настроены зависимости для запуска нашего скрипта:
# systemctl list-dependencies ovs-networking.service
ovs-networking.service
├─networking.service
├─openvswitch-switch.service
├─network-online.target
....
├─networking.service
├─openvswitch-switch.service
├─network-online.target
....
# systemctl start ovs-networking.service
# journalctl -u ovs-networking.service
# journalctl -u ovs-networking.service
Добавляем VLAN-ы в "Open-vSwitch".
С интерфейсами обслуживающими тегированный трафик (в рамках заданного VLAN-а) всё просто - это одна из основополагающих сущностей в "Open-vSwitch", так что инструментарий отлажен.
Например, добавляем в виртуальный коммутатор сетевой интерфейс "vlan450" (это произвольное имя), пропускающий только пакеты с 450-м тегом, в имеющийся мост "br0":
# ovs-vsctl add-br vlan450 br0 450
Установка "Qemu-KVM" и "LibVirt".
Публикация не про настройку системы виртуализации, так что здесь мы просто установим минимально необходимый набор пакетов, достаточный для проверки удалённого подключения:
# aptitude install qemu-kvm libvirt-daemon libvirt-daemon-system libvirt-clients
Разумеется, чтобы управлять виртуальными машинами последством "LibVirt", нужно иметь запущенным соответствующий сервис:
# systemctl status libvirtd
В качестве примера возможностей - так инструментами "LibVirt" можно узнать состояние имеющихся виртуальных машин:
# virsh list --all
Очистка от лишних подсистем "LibVirt".
Пакет приложений "LibVirt", используемый для управления средой виртуализации "Qemu-KVM", по умолчанию создаёт свои сетевые подсистемы, которые при использовании "Open-vSwitch" не нужны:
# virsh net-list --all
Name State Autostart Persistent
------------------------------------
default active no yes
------------------------------------
default active no yes
Удаляем лишнее:
# virsh net-destroy default
# virsh net-undefine default
# service libvirtd restart
# virsh net-undefine default
# service libvirtd restart
Определяем круг пользователей "LibVirt".
По умолчанию право на подключение к сервису "LibVirt" имеет член соответствующей группы:
Для "Linux Ubuntu":
# usermod -a -G libvirtd libvirt-user
Для "Linux Debian":
# usermod -a -G libvirt libvirt-user
Подключение к "LibVirt".
На рабочей станции администратора устанавливаем простенький GUI-интерфейс управления "LibVirt":
# aptitude install virt-manager
По умолчанию подключение к подсистеме управления виртуализацией разрешено только через локальный файловый сокет, а попадают на сервер виртуализации обычно через SSH-туннель. Соответственно, удобнее всего заранее завести SSH-ключи для прозрачной аутентификации.