Saltar al contenido principal

Включение internal fabric

Internal fabric — сеть, по которой диск ВМ отдаётся по NVMe/TCP с узла, где импортирован её пул, на узел, где ВМ запущена. Без фабрики кластер 3.2 работает, ВМ на NVMe, но каждая запускается только на узле своего пула. С фабрикой Center запускает ВМ на любом узле кластера.

Страница — для кластера, где узлы и Center уже на 3.2, а фабрика не включена. На кластере, установленном с 3.2, её включает инсталлятор.

Кластер обновляется с Center старой линейки

Подготовку удобно совместить с обновлением по странице «Обновление до 3.2, Center старой линейки: шаги»:

  • до окна A — шаги 1 и 2. Узлы и Center в это время ещё 3.0 и старой линейки, поэтому в шаге 1 проверяются только vlan40 и подсеть; остальные проверки шага 1 — перед шагом 5;
  • в окне A — шаг 3 на каждом узле перед его перезагрузкой (после шага 22 «Шагов»). Отдельная перезагрузка (шаг 4) тогда не нужна;
  • после окна B — шаги 5–9, пока клиентам не объявлено об окончании работ.

Шаги идут строго по порядку. Если результат другой — СТОП: дальше не идти, записать, что видно, обратиться в техническую поддержку vStack.

Где выполнять:

  • [раб] — рабочая машина инженера (нужны sh, ssh, curl, jq); команды — из каталога, где распакован архив со скриптами (шаг 1);
  • [mgmt] — mgmtVM по ssh;
  • [узел] — узел по ssh. У root на узле оболочка csh, а команды написаны для sh: войдя на узел, сначала наберите sh.

Обозначения: <N> — номер узла в ccrd_cli; zNN — пул; <MGMT> — внешний адрес mgmtVM; <vxlan> — первые три октета подсети VXLAN кластера (vxlan_vlan_network в /usr/local/etc/vstack-env.conf, например 192.0.2); <узлы> — адреса управления всех узлов через пробел.

1. Кластер подходит [узел], [mgmt], [раб]​

freebsd-version -k # [узел] 14.2-RELEASE-vstack-3.2.0-…
pgrep -lf '^/usr/local/bin/(spdk|nvmf)_tgt' # [узел] пусто
grep -E 'vxlan_vlan_(id|network)' /usr/local/etc/vstack-env.conf # [узел] VLAN и подсеть VXLAN
ifconfig vlan40 | grep 'inet ' # [узел] адрес узла в подсети VXLAN
test -f /usr/local/etc/vstack/settings.yml && echo settings-OK # [mgmt]
cd /tmp; su -m postgres -c "psql -At -d vstack -c \"select value from management.cluster_options where an_option = 'feature.nvmf'\"" # [mgmt] пусто или 0

Должно получиться: узлы 3.2.0, spdk_tgt и nvmf_tgt не запущены, Center 3.2 (settings-OK), флаг feature.nvmf не включён; у узла есть интерфейс vlan40 с адресом из vxlan_vlan_network, подсеть — /24. Интерфейс называется vlan40 на всех кластерах (его имя задаёт инсталлятор), номер VLAN на коммутаторе — vxlan_vlan_id. Запишите <vxlan>. spdk_tgt запущен и feature.nvmf = 1 — фабрика уже работает. nvmf_tgt, нет vlan40 или подсеть не /24 — СТОП, порядок включения — с поддержкой.

Скрипты и учётка [раб] — тот же архив, что для обновления с Center старой линейки:

curl -O https://docs.vstack.com/os/3.2.0/files/upgrade-3.0-to-3.2/upgrade-3.0-to-3.2-tools.tar.gz
curl -O https://docs.vstack.com/os/3.2.0/files/upgrade-3.0-to-3.2/SHA256SUMS
grep tools.tar.gz SHA256SUMS | shasum -a 256 -c -
tar xzf upgrade-3.0-to-3.2-tools.tar.gz
printf '%s\n%s\n' '<логин cluster master>' '<пароль>' > center.cred; chmod 600 center.cred
export VAPI_URL=https://<MGMT> VAPI_CRED=$PWD/center.cred
export NODES="<узлы>"
sh tools/vapi.sh vm-list '{}' | jq '.result.data | length'

Должно получиться: OK, каталог tools/, число ВМ кластера. Если архив уже скачан для обновления, — тот же каталог и те же переменные.

2. Адреса фабрики в конфиге ccrd [раб]​

Фабрика работает в VLAN подложки VXLAN (vlan40), как на кластере, установленном с 3.2: новый VLAN на коммутаторах не нужен. Каждому пулу — свой адрес в этой подсети: пул zNN → <vxlan>.(200+NN). Адрес поднимает ccrd на узле, где пул, и переносит вместе с пулом. Узлы .(100+N) в этой подсети уже есть.

Конфиг ccrd одинаков на всех узлах — проверить, взять копию, добавить адреса:

for h in $NODES; do printf '%s ' $h; ssh -n root@$h 'md5 -q /usr/local/etc/ccrd/config.json'; done # у всех одинаковый
scp root@<любой узел>:/usr/local/etc/ccrd/config.json ccrd-config.orig.json
jq --arg pfx <vxlan> -f tools/nvmf-add.jq ccrd-config.orig.json > ccrd-config.new.json
jq -r '.services[] | select(.name|test("^zpool_z")) | .name as $n | .ext_ips[] | select(.label=="NVMf") | "\($n)\t\(.interface)\t\(.ip)"' ccrd-config.new.json | tee nvmf-addresses.tsv

md5 разный — СТОП: конфиги разошлись, сначала разобраться. В nvmf-addresses.tsv — по строке на пул: zpool_zNN vlan40 <vxlan>.2NN. Если у пулов уже были адреса с меткой NVMf (так ставит инсталлятор), скрипт их не трогает — в списке будут они.

Адреса свободны (ответ есть только у уже прописанных пулам):

cut -f3 nvmf-addresses.tsv | while read a; do ssh -n root@<любой узел> "ping -c 1 -t 1 $a" >/dev/null && echo "$a ЗАНЯТ"; done

Занят адрес, которого не было в исходном конфиге, — СТОП: в подсети VXLAN кто-то ещё; адреса выбирать с поддержкой.

Разложить на все узлы:

jq -e . ccrd-config.new.json >/dev/null && echo json-OK
for h in $NODES; do
ssh -n root@$h 'cp -p /usr/local/etc/ccrd/config.json /usr/local/etc/ccrd/config.json.pre-nvmf'
scp ccrd-config.new.json root@$h:/usr/local/etc/ccrd/config.json
done
for h in $NODES; do printf '%s ' $h; ssh -n root@$h 'md5 -q /usr/local/etc/ccrd/config.json'; done # у всех одинаковый, новый

ccrd не перезапускать — он прочтёт файл сам при перезагрузке узла (шаг 4 или перезагрузка в обновлении). nvmf-addresses.tsv понадобится в шагах 4 и 8.

3. Пакеты и память под SPDK [раб], каждый узел​

ssh root@<узел> 'sh -s' <<'EOF'
set -e
pkg info -e vStack-spdk vStack-nvmf-initiator || pkg install -y -r vstack-os vStack-spdk vStack-nvmf-initiator
cp -p /boot/loader.conf.local /boot/loader.conf.local.pre-contigmem-$(date +%Y%m%d%H%M%S)
sed -i '' -e '/^contigmem_load=/d' -e '/^hw.contigmem./d' /boot/loader.conf.local
cat >> /boot/loader.conf.local <<'L'
contigmem_load="YES"
hw.contigmem.num_buffers="8"
hw.contigmem.buffer_size="1073741824"
L
pkg info -e vStack-spdk vStack-nvmf-initiator && echo pkgs-OK
grep contigmem /boot/loader.conf.local
EOF

Должно получиться: pkgs-OK и три строки contigmem. 8 ГиБ на узел: без этих строк фабрика на узле не запустится, а ни update.sh, ни Center их не пишут. Эта память уходит из памяти под ВМ — при планировании переноса пулов (шаг 4) учитывать.

4. Перезагрузить узлы — по одному​

Нужен, только если шаги 2 и 3 сделаны не перед перезагрузками обновления. Строки contigmem и адреса из конфига ccrd вступают в силу только при загрузке узла.

Для каждого узла: освободить его — пулы на соседей, ВМ с пометкой remote на узлы своих пулов (Center новой линейки — разделы «Перенос сервисов пула» и «Освобождение узла с удалённо запущенными ВМ»; после обновления с Center старой линейки — шаги 16–18 «Шагов»). Затем [узел]:

ccrd_cli --node --node_id <N> --command disable
pgrep -lf '^bhyve: [0-9]'; zpool list # пусто / no pools available
sync; shutdown -r now

С консолью BMC перед глазами. После загрузки [узел]:

sysctl -n kern.bootfile # /boot/kernel.GENERIC_PROD/kernel
sysctl hw.contigmem.num_buffers # 8
ccrd_cli --node --node_id <N> --command enable

Вернуть пулы на узел тем же способом. Адрес фабрики каждого вернувшегося пула (из nvmf-addresses.tsv) поднят: [узел] ifconfig vlan40 | grep 'inet '. zpool status -x — healthy, resilver не идёт. Следующий узел — только после этого.

5. Фильтр портов [раб], каждый узел​

cp tools/pf-fabric.conf pf-fabric-<кластер>.conf

В копии заменить ЗАМЕНИТЬ_интерфейс_фабрики на vlan40, ЗАМЕНИТЬ_подсеть_фабрики/24 на <vxlan>.0/24. Порты 7000 и 4420 к адресам узла будут пропускаться только из подсети VXLAN; трафик ВМ фильтр не затрагивает. Порт 7000 (управление SPDK) работает без пароля, снаружи его закрывают. На каждый узел:

scp pf-fabric-<кластер>.conf root@<узел>:/root/pf-fabric.conf
ssh root@<узел> 'sh -s' <<'EOF'
test -e /etc/pf.conf && { echo "/etc/pf.conf уже есть — СТОП"; exit 1; }
grep -q ЗАМЕНИТЬ /root/pf-fabric.conf && { echo "заглушки не заменены — СТОП"; exit 1; }
install -m 0600 /root/pf-fabric.conf /etc/pf.conf
kldload -n pf
pfctl -nf /etc/pf.conf && pfctl -f /etc/pf.conf && pfctl -e
sysrc pf_enable=YES pf_rules=/etc/pf.conf
pfctl -sr
EOF

Должно получиться: pf enabled, четыре правила. Если на узле уже есть свой /etc/pf.conf — правила из pf-fabric.conf добавить в него вручную.

6. Настройки Center и роль nodes [mgmt]​

cp -p /usr/local/etc/vstack/settings.yml /root/settings.yml.pre-nvmf

В /usr/local/etc/vstack/settings.yml: vstack_node_install_nvmf: true и nvmf_net: "<vxlan>.0/24" (подсеть VXLAN, как в шаге 2). Проверочный прогон роли:

cd /usr/local/share/vstack-center
umask 077
ANSIBLE_CONFIG=$PWD/ansible.cfg ansible-playbook main.yml -t nodes -e @/usr/local/etc/vstack/settings.yml --check --diff > /var/log/nodes-check.log 2>&1; echo rc=$?
sed -n '/PLAY RECAP/,$p' /var/log/nodes-check.log
grep -E '^[-+](Password|password)' /var/log/nodes-check.log | wc -l # 0

Прогон занимает 2–5 минут; после строки PLAY RECAP процесс может висеть до получаса — тогда Ctrl+C. Должно получиться: у каждого узла unreachable=0 failed=0, меняется только задача Enable NVMe-oF services (по одному changed на узел). nvmf_tgt в выводе, меняются сетевые интерфейсы, конфиг ccrd или пароли — СТОП.

Боевой прогон:

cat > /root/nodes-run.sh <<'X'
#!/bin/sh
umask 077
cd /usr/local/share/vstack-center
ANSIBLE_CONFIG=$PWD/ansible.cfg ansible-playbook main.yml -t nodes -e @/usr/local/etc/vstack/settings.yml --diff > /var/log/nodes-run.log 2>&1
echo "rc=$?" >> /var/log/nodes-run.log
X
daemon -f sh /root/nodes-run.sh

Через 2–5 минут: sed -n '/PLAY RECAP/,$p' /var/log/nodes-run.log — у каждого узла unreachable=0 failed=0.

7. Запустить на узлах [раб], каждый узел​

ssh root@<узел> 'sh -s' <<'EOF'
pgrep -qf '^/usr/local/bin/nvmf_tgt' && { echo "СТОП: nvmf_tgt"; exit 1; }
pgrep -qf '^/usr/local/bin/spdk_tgt' || { nohup service spdk_tgt start >/dev/null 2>&1 </dev/null & }
sleep 8
pgrep -qf vstack-nvmf-initiator || { nohup service vstack_nvmf_initiator start >/dev/null 2>&1 </dev/null & }
sleep 5
pgrep -lf '^/usr/local/bin/spdk_tgt|nvmf-initiator'
sockstat -4l | grep spdk_tgt
EOF
nc -z -w3 <адрес узла> 7000 && echo OPEN || echo blocked # с рабочей машины: blocked

Должно получиться: оба процесса, слушатель *:7000, с рабочей машины — blocked. OPEN — СТОП: фильтр не работает.

8. Адреса пулов в Center [раб], [mgmt]​

Адрес каждого пула поднят на узле, где пул сейчас [раб]:

for h in $NODES; do ssh -n root@$h 'ifconfig vlan40'; done | grep -c "inet <vxlan>\.2" # = числу пулов

SQL с адресами из шага 2 [раб]:

V=$(awk -F'\t' '{sub("zpool_","",$1); printf "%s(\x27%s\x27,\x27%s\x27)", (NR>1?", ":""), $1, $3}' nvmf-addresses.tsv)
cat > nvmf-enable.sql <<SQL
\\set ON_ERROR_STOP 1
BEGIN;
CREATE TEMP TABLE want(name text PRIMARY KEY, inet inet UNIQUE);
INSERT INTO want VALUES $V;
DO \$\$ BEGIN
IF (SELECT count(*) FROM storage.pool) <> (SELECT count(*) FROM want)
OR EXISTS (SELECT 1 FROM storage.pool p LEFT JOIN want w USING (name) WHERE w.name IS NULL)
THEN RAISE EXCEPTION 'список пулов не совпадает с storage.pool'; END IF;
END \$\$;
UPDATE storage.pool p SET nvmf_inet = w.inet FROM want w WHERE w.name = p.name;
INSERT INTO management.cluster_options (an_option, value) VALUES ('feature.nvmf','1')
ON CONFLICT (an_option) DO UPDATE SET value = EXCLUDED.value;
SELECT name, nvmf_inet, current_node FROM storage.pool ORDER BY name;
COMMIT;
SQL
grep 'INSERT INTO want' nvmf-enable.sql
scp nvmf-enable.sql root@<MGMT>:/root/

[mgmt]:

cd /tmp
su -m postgres -c "psql -X -At -d vstack -c \"copy (select name, nvmf_inet from storage.pool order by name) to stdout\"" > /root/pool-nvmf-pre.tsv
rc=0; su -m postgres -c "psql -X -v ON_ERROR_STOP=1 -d vstack" < /root/nvmf-enable.sql || rc=$?
echo rc=$rc
[ $rc = 0 ] && service vstack_center_dispatcher restart

Должно получиться: rc=0, у каждого пула свой адрес. Ошибка список пулов не совпадает — транзакция откатилась, ничего не изменено; сверить nvmf-addresses.tsv с пулами кластера. С этого момента Center запускает ВМ на любом узле.

9. Проверка на одной Linux-ВМ [раб], [узел]​

sh tools/vapi.sh vms-stop '{"id":<id>,"hard_timeout":90}'; sh tools/vapi.sh wait <id> 1
sh tools/vapi.sh vms-start '{"id":<id>,"node_id":<чужой узел>}'; sh tools/vapi.sh wait <id> 3
sh tools/vapi.sh vm-list '{}' | jq '.result.data[] | select(.vm_id==<id>) | {node,rstart}' # rstart: 1

<чужой узел> — id в Center любого узла, кроме того, где пул ВМ. На чужом узле: sockstat -4 -c | grep ':4420' — есть соединения к адресу пула. В консоли гость загрузился. Затем vms-stop и vms-start с узлом-владельцем пула — ВМ снова на своём узле. Не прошло — откат.

Windows-ВМ запускаются на чужом узле только после перевода на NVMe — «Windows на NVMe».

Откат​

  1. Все ВМ — на узлы своих пулов: у кого в vapi.sh vm-list rstart: 1 — vms-stop, затем vms-start с узлом-владельцем.
  2. [mgmt] — адреса пулов и флаг, как было до шага 8:
cd /tmp
{ cat <<'SQL'
\set ON_ERROR_STOP 1
BEGIN;
CREATE TEMP TABLE was(name text PRIMARY KEY, inet inet);
COPY was FROM STDIN;
SQL
cat /root/pool-nvmf-pre.tsv; echo '\.'
cat <<'SQL'
UPDATE storage.pool p SET nvmf_inet = w.inet FROM was w WHERE w.name = p.name;
UPDATE management.cluster_options SET value = '0' WHERE an_option = 'feature.nvmf';
COMMIT;
SQL
} > /root/nvmf-rollback.sql
rc=0; su -m postgres -c "psql -X -v ON_ERROR_STOP=1 -d vstack" < /root/nvmf-rollback.sql || rc=$?
echo rc=$rc
[ $rc = 0 ] && service vstack_center_dispatcher restart
  1. [mgmt] cp -p /root/settings.yml.pre-nvmf /usr/local/etc/vstack/settings.yml; роль после этого не запускать.
  2. На узлах: sysrc -f /etc/rc.conf.local spdk_tgt_enable=NO vstack_nvmf_initiator_enable=NO, service spdk_tgt stop, service vstack_nvmf_initiator stop. Фильтр pf можно оставить; снять — pfctl -d; sysrc pf_enable=NO.

Почему так​

  • Перезагрузка. Адреса пулов в конфиге ccrd и contigmem вступают в силу только при загрузке узла. Поэтому их готовят заранее, а при обновлении — до перезагрузок узлов. Само включение (шаги 5–9) перезагрузок не требует.
  • Фабрика на vlan40. Так её настраивает инсталлятор 3.2: адреса NVMf пулов — .(200+NN) в подсети VXLAN на vlan40, где уже есть адреса узлов .(100+N). Отдельный VLAN и работы на коммутаторах не нужны. NVMe/TCP работает и при MTU 1500; при MTU 9000 на lagg и коммутаторе — быстрее.
  • Порт 7000 (управление SPDK) работает без пароля и слушает все интерфейсы. Привязать его к одному адресу нельзя: узел, где запускается ВМ, обращается к SPDK владельца по адресу пула, а этот адрес переезжает вместе с пулом. Поэтому порт закрывает фильтр pf (шаг 5). Правила в нём — с no state: правило с keep state пропускает только новые соединения и оборвало бы уже открытые сессии между узлами.
  • Включение — в окне работ. С feature.nvmf = 1 Center сразу начинает ставить ВМ клиентов на чужие узлы; откат требует вернуть их все. Проще, пока клиентам не объявлено об окончании работ.
  • ACL дисков. Диски ВМ раздаются с allow_any_host. Ограничение по Host NQN есть (NVMfAllowAnyHost в vstack-vm.config), но порядок его включения на работающем кластере не описан — не включать.

Что проверено​

На тестовом кластере из 4 узлов, сразу после обновления узлов 3.0 и Center старой линейки до 3.2.0, фабрика — на отдельных портах lagg20:

  • роль nodes с vstack_node_install_nvmf, ручной старт spdk_tgt, feature.nvmf, запуск 468 ВМ на чужом узле и возврат;
  • фильтр pf на всех узлах: снаружи порт 7000 закрыт, запуск ВМ на чужом узле в обе стороны, работающие удалённые ВМ не пострадали;
  • Linux-гость с драйвером nvme в initramfs и Windows, переведённая на NVMe, загружаются на чужом узле.

Не проверялось:

  • фабрика на vlan40 (схема инсталлятора 3.2): команды те же, меняются интерфейс и подсеть. На первом кластере шаг 9 обязателен до объявления об окончании работ;
  • фильтр pf после перезагрузки узла (включается штатным pf_enable);
  • включение на кластере с Center новой линейки.

Время: шаги 5–9 — около 10 минут, без простоя ВМ. Отдельная перезагрузка (шаг 4) — плюс перенос пулов каждого узла туда и обратно.