Настройка виртуальных серверов в OpenVZ с несколькими VLAN-устройствами (HN Debian Lenny)

vark аватар

Updated.

Первым делом, инсталлируем Linux на Hardware Node (в netinst режим expert, далее настраивался софтварный RAID1/mirror; после на одном из разделов - самом большом - настраивался LVM с разбиением на 3 логических lvm-раздела, примерно так: 2\3 - основной раздел, где будут размещены виртуалы, от 512 МБ до 1-2 ГБ - раздел без FS и точки монтирования, необходимый процессу vzdump, и остальное - под бэкапный раздел для тех виртуалов, которые есть смысл бэкапить целиком).
Далее - установка из репозитория ядра с поддержкой openvz (желательно 2.6.26-2; с 2.6.26-1 были бока) и утилит vzctl, vzquota, vzdump, vlan, bridge-utils. 2 последние понадобятся нам для работы с VLAN в HN и для создания бриджей, связывающих VLAN-девайс в HN с veth-девайсом виртуала.
К прочтению рекомендуется статья http://www.opennet.ru/docs/RUS/virtuozzo/index.html , несмотря на некоторые смешные ляпы перевода. Также советую почитать http://wiki.openvz.org/Virtual_Ethernet_device, единственное замечание - в нашей конфигурации нужно ВЫРУБИТЬ НАХРЕН!!! proxy-arp, причем везде. На сей предмет смотрим в /etc/sysctl.conf в первую очередь. Включите proxy-arp хоть на одном из бриджей или даже на veth-устройстве -- потом долго будете думать, откуда в подсетях берутся странные косяки. А вот форвардинг включить надо, проверяем в /etc/sysctl.conf строку "net.ipv4.conf.default.forwarding = 1".

Немного правим глобальный конфиг конфиг OpenVZ:

root@HN# vim /etc/vz/vz.conf

[...]
NEIGHBOUR_DEVS=all
[...]
VE_PRIVATE=/virts/$VEID
[...]

По поводу первого параметра - см.комментарий в конфиг.файле, по поводу второго - именно переменная VE_PRIVATE указывает, где будут располагаться VE-контейнеры. В данном случае это будет самый большой логический lvm-раздел с точкой монтирования /virts .

Поскольку нам нужны vlan-ы, то "особая опенвзшнная магия" в виде venet нам не нужна, будем использовать veth. Пока что создадим такой исполняемый скрипт:

root@HN# vim /etc/vz/vznet.conf

#!/bin/bash
EXTERNAL_SCRIPT="/usr/local/sbin/vznetcfg.custom"

Путь можете выбрать такой, какой вам больше нравится. Главное - наличие этого самого vznetcfg.custom-скрипта. Данный скрипт поднимает при старте\рестарте виртуала (в том числе при старте ОС нашей Hardware Node) первый из veth-интерфейсов, и только первый. Будем этот интерфейс считать основным сетевым интерфейсом виртуала (с маршрутом по умолчанию на нём). Для поднятия дополнительных интерфейсов в виртуале нарисуем ещё скрипты.

Перед тем, как вывести листинг скрипта vznetcfg.custom, давайте сначала поднимем в HN необходимые vlan-интерфейсы.

Во-первых, HN не обязательно иметь адреса на всех интерфейсах всех vlan, которые могут быть у виртуалов, главное - чтоб интерфейсы соответствующих vlan в HN (eth0.VLANnum) и соотв.бриджей (vzbrVLANnum) были подняты. Организуем в /etc/network/interfaces такой порядок подъёма интерфейсов, чтоб гарантированно поднялись все, даже те, для которых inet manual. Если самой хардварной ноде нужен адрес в каком-то vlan, будем цеплять этот адрес на соответствующий бридж.
Поэтому, пока прописываем всё что нужно в /etc/network/interfaces.

root@HN# vim /etc/network/interfaces

auto lo
iface lo inet loopback

auto eth0

auto eth0.525
iface eth0.525 inet manual
   vlan_raw_device eth0

auto vzbr525
iface vzbr525 inet static
   address 100.100.232.14
   netmask 255.255.255.240
   network 100.100.232.0
   broadcast 100.100.232.15
   gateway 100.100.232.4
   dns-nameservers 100.100.232.34
   bridge_maxwait 0
   bridge_ports none
   post-up /usr/sbin/brctl addif vzbr525 eth0.525
   post-up /sbin/ifconfig eth0.525 up

auto eth0.200
iface eth0.200 inet manual
   vlan_raw_device eth0

auto vzbr200
iface vzbr200 inet static
   address 192.168.50.3
   netmask 255.255.255.0
   network 192.168.50.0
   broadcast 192.168.50.255
   bridge_maxwait 0
   bridge_ports none
   post-up /usr/sbin/brctl addif vzbr200 eth0.200
   post-up /sbin/ifconfig eth0.200 up

auto eth0.304
iface eth0.304 inet manual
   vlan_raw_device eth0

auto vzbr304
iface vzbr304 inet manual
   bridge_maxwait 0
   bridge_ports none
   post-up /usr/sbin/brctl addif vzbr304 eth0.304
   post-up /sbin/ifconfig eth0.304 up

auto eth0.543
iface eth0.543 inet manual
   vlan_raw_device eth0

auto vzbr543
iface vzbr543 inet manual
   bridge_maxwait 0
   bridge_ports none
   post-up /usr/sbin/brctl addif vzbr543 eth0.543
   post-up /sbin/ifconfig eth0.543 up

В данном примере видно, что у HN есть адреса в vlan525 и vlan200, причем эти адреса присвоены бридж-устройствам, все остальные vlan-ы, которые могут понадобиться, также определены в конфиге, вместе с парными им бриджами.
Обратите внимание, что мы сначала создаем vlan, а уже потом добавляем данный vlan в бридж, подняв при этом сам vlan-интерфейс. Насколько данная конструкция сообразна с самой идеологией VLAN, а также какие можно поиметь проблемы в плане безопасности - я пока без понятия, но подозрения есть. Ждём комментариев.

Хорошо, теперь можно создать наш первый openvz-контейнер (виртуала то бишь):

root@HN# vzctl create 101 --config vps.basic --ostemplate debian-lenny-minimal-i386

Шаблон можно не задавать, тогда будет применен дефолтовый шаблон, прописанный в vz.conf в переменной DEF_OSTEMPLATE.
Внимание! Шаблоны должны находиться в директории /var/lib/vz/template/cache/ и кроме вас, их туда никто не положит. Ищем шаблоны на странице http://wiki.openvz.org/Download/template/precreated , это не единственный источник, но на первое время хватит :)

Далее все изменения будем вносить прямо в конфиг виртуала:

root@HN# vim /etc/vz/conf/101.conf

ONBOOT="yes"
[...]
HOSTNAME="backup.domain.ua"
NAME="backup"

BRIDGEDEV="vzbr525"
VETH_IP_ADDRESS="100.100.232.12"
VE_DEFAULT_GATEWAY="100.100.232.4"
MASK="255.255.255.240"
NETIF="ifname=eth101.525,mac=00:18:51:31:A2:D8,host_ifname=veth101.525,host_mac=00:18:51:C7:C4:6E"
NAMESERVER="100.100.232.34"
SEARCHDOMAIN="domain.ua"
VLAN="525"

ADDITIONAL_VLANS="304 543"

NETIF_304="ifname=eth101.304,bridge=vzbr304,mac=00:18:51:31:A2:D7,host_ifname=veth101.304,host_mac=00:18:51:C7:C4:6D"
VETH_IP_ADDRESS_304="192.168.2.4"
MASK_304="24"

NETIF_543="ifname=eth101.543,bridge=vzbr543,mac=00:18:51:31:A2:D6,host_ifname=veth101.543,host_mac=00:18:51:C7:C4:6C"
VETH_IP_ADDRESS_543="100.100.232.36"
MASK_543="27"

ROUTE01="target_type=net,target_addr=100.100.235.192/26,gw=192.168.2.10"
ROUTE02="target_type=host,target_addr=192.168.20.253,gw=100.100.232.14"
STATIC_ROUTES="ROUTE01 ROUTE02"

Теперь пояснения.
Если мы хотим, чтоб виртуал автоматически поднимался при старте HN, то значение переменной ONBOOT должно быть "yes" (скорее всего, по умолчанию это и прописано в конфиге вашего виртуала).
Пропускаем автоматически сгенеренную на основе выбранного конфиг-файла секцию с назначением виртуалу разнообразных ресурсов, и после неё определяем хостнейм и имя виртуала.
Далее следует блок переменных, необходимых для того, чтоб скрипт vznetcfg.custom поднял первичный vlan-интерфейс нашего виртуала.
BRIDGEDEV - интерфейс бриджа в HN, к которому добавим из HN veth-интерфейс,
VLAN - номер vlan-а,
VETH_IP_ADDRESS - IP, присваиваемый виртуалу в данном vlan,
VE_DEFAULT_GATEWAY - IP шлюза в данном vlan, заметьте - это НЕ адрес соответствующего бриджа (vzbr525) в HN (хотя можно и его подставить, + форвардинг в HN вы разрешили глобально),
MASK - маска подсети в данном vlan,
NAMESERVER - DNS-сервер, будет внесен в resolv.conf в виртуале,
SEARCHDOMAIN - опциональный параметр для resolv.conf,
NETIF - очень важный набор параметров. Именно здесь мы определяем:
a) ifname - имя интерфейса внутри виртуала,
b) mac - MAС-адрес интерфейса внутри виртуала,
c) host_ifname - имя связующего veth-интерфейса в хост-системе (то бишь в HN), именно этот интерфейс мы добавляем в бридж, определенный в переменной BRIDGEDEV,
d) host_mac - MAС-адрес связующего veth-интерфейса.
Очень важно, что бы эти 2 MAC-адреса были уникальны не только в пределах HN и самого виртуала; для генерации виртуальных MAC-адресов рекомендуется применять утилиту easymac.sh, которая гарантированно сгенерирует такой адрес, который точно не совпадет ни с одним из MAC-ов любого реального железа. Ссылка на данный скрипт есть выше, в вики OpenVZ, но приведу её тут отдельно: http://www.easyvmx.com/software/easymac.sh

STATIC_ROUTE="ROUTENAME1 ROUTENAME2" - если нужно добавить статические маршруты внутрь виртуала, то в это переменной приводим список имён маршрутов, сами маршруты определяем в виде
ROUTENAME="target_type=net,target_addr=100.100.235.192/26,gw=192.168.2.10"
где:
a) target_type = net (если задаём маршрут к сети) или host (путь к определённому хосту),
b) target_addr записываем в виде ip/masken,
c) gw - прописываем ip шлюза, через который маршрутизируется target_addr.

А теперь поехали разбирать скрипты.

root@HN# cat /etc/vz/dists/scripts/vznetcfg.custom

#!/bin/bash
GLOBALCONFIGFILE=/etc/vz/vz.conf
VECONFIGFILE=/etc/vz/conf/$VEID.conf
vzctl=/usr/sbin/vzctl
ip=/sbin/ip
. $GLOBALCONFIGFILE
. $VECONFIGFILE

# Define management interface with default gateway
NETIF_OPTIONS=`echo $NETIF | sed 's/,/\n/g'`
for str in $NETIF_OPTIONS; do \
   echo $str
   # getting 'ifname' parameter value
   if [[ "$str" =~ ^ifname= ]]; then
     # remove the parameter name from the string (along with '=')
     VEIFNAME=${str#*=};
   fi
   # getting 'host_ifname' parameter value
   if [[ "$str" =~ ^host_ifname= ]]; then
     # remove the parameter name from the string (along with '=')
     VZHOSTIF=${str#*=};
   fi
done

if [ ! -n "$VETH_IP_ADDRESS" ]; then
   echo "According to $CONFIGFILE VE$VEID has no veth IPs configured."
   exit 1
fi

if [ ! -n "$VZHOSTIF" ]; then
   echo "According to $CONFIGFILE VE$VEID has no veth interface configured."
   exit 1
fi

if [ ! -n "$VEIFNAME" ]; then
   echo "Corrupted $CONFIGFILE: no 'ifname' defined for host_ifname $VZHOSTIF."
   exit 1
fi

for IP in $VETH_IP_ADDRESS; do
   echo "Initializing interface $VZHOSTIF for VE$VEID."
   /sbin/ifconfig $VZHOSTIF 0
done

if [ -n "$BRIDGEDEV" ]; then
   echo "Adding interface $VZHOSTIF to the bridge $BRIDGEDEV."
   /usr/sbin/brctl addif $BRIDGEDEV $VZHOSTIF
   echo 1 > /proc/sys/net/ipv4/conf/$VZHOSTIF/forwarding
   echo 1 > /proc/sys/net/ipv4/conf/$BRIDGEDEV/forwarding
fi

# Up the interface $VEIFNAME link in VE$VEID
$vzctl exec $VEID $ip link set $VEIFNAME up
echo "Adding an IP $VETH_IP_ADDRESS to the $VEIFNAME for VE$VEID."
$vzctl exec $VEID $ip address add $VETH_IP_ADDRESS/$MASK dev $VEIFNAME

if [ -n "$VE_DEFAULT_GATEWAY" ]; then
   echo "Setting $VE_DEFAULT_GATEWAY as a default gateway for VE$VEID."
   $vzctl exec $VEID \
     $ip route add default via $VE_DEFAULT_GATEWAY dev $VEIFNAME
fi

exit 0

Немного bash-магии в области регэкспов (скрипт был выдан Erraen-ом и очень так слегка модифицирован - особенно 2 предпоследние строчки :) - разбор по опциям содержимого переменной NETIF; в скрипте нет явного указания применять прописанные MAC-адреса, однако они применяются. По-моему, всё достаточно прозрачно - после проверки на наличие необходимых параметров добавляем veth-интерфейс в бридж, разрешаем форвардинг, поднимаем интерфейс внутри виртуала, добавляем айпишники и маршруты.

Далее, теперь костыль для поднятия всех дополнительных vlan-интерфейсов (если они определены в конфиге) в каждом виртуале. Добавление нужного кода в скрипт vznetcfg.custom результата не дало - не поднимаются, хоть ты тресни! Зато этот же код, запущенный с небольшой паузой после того, как отработает /etc/rcN.d/S20vz , прекрасно выполнил свою задачу. решено было оформить этот код как новый init-скрипт, стартующий после init-скрипта vz (поместим его на уровнях 2-5 под номером S30, kill-скрипт не нужен).

root@HN# cat /etc/init.d/vzvlans

#!/bin/bash
# OpenVZ startup-2 script, for setting up additional veth intefaces (vlans and bridges)

###
# chkconfig: 2345 96 88
# description: OpenVZ startup-2 script.
###

### BEGIN INIT INFO
# Provides: vzvlans
# required-start: $vz
# required-stop: $network $remote_fs $syslog
# Should-Start: sshd
# Should-Stop: sshd
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: OpenVZ startup-2 script
# Description: OpenVZ startup-2 script.
### END INIT INFO

sleep 10

GLOBALCONFIGFILE=/etc/vz/vz.conf
vzctl=/usr/sbin/vzctl
ip=/sbin/ip
. $GLOBALCONFIGFILE
IS_RUN=`/usr/sbin/vzlist -aH | /bin/grep running | /usr/bin/awk '{print $1}'`
for VEID in `echo $IS_RUN`; do
   VECONFIGFILE=/etc/vz/conf/$VEID.conf
   . $VECONFIGFILE
   # Setting up additional vlans
   if [ -n "$ADDITIONAL_VLANS" ]; then
     for VLANID in `echo $ADDITIONAL_VLANS`; do
       eval NET_IF=\$NETIF_${VLANID}
       NETIF_OPTIONS=`echo $NET_IF | sed 's/,/\n/g'`
       for str in $NETIF_OPTIONS; do
         if [[ "$str" =~ ^ifname= ]]; then
           VEIFNAME=${str#*=}
         fi
       if [[ "$str" =~ ^host_ifname= ]]; then
           VZHOSTIF=${str#*=}
         fi
         if [[ "$str" =~ ^bridge= ]]; then
           BRIDGEDEV=${str#*=}
         fi
         if [[ "$str" =~ ^mac= ]]; then
           VEIFMAC=${str#*=}
         fi
         if [[ "$str" =~ ^host_mac= ]]; then
           VZHOSTMAC=${str#*=}
         fi
       done
       ## for vzctl version 3.0.23-8
       $vzctl set $VEID --netif_add $VEIFNAME,$VEIFMAC,$VZHOSTIF,$VZHOSTMAC,$BRIDGEDEV
      ##and for vzctl version 3.0.22-14
      #$vzctl set $VEID --netif_add $VEIFNAME,$VEIFMAC,$VZHOSTIF,$VZHOSTMAC
       /sbin/ifconfig -v $VZHOSTIF 0
       /usr/sbin/brctl addif $BRIDGEDEV $VZHOSTIF
       echo 1 > /proc/sys/net/ipv4/conf/$VZHOSTIF/forwarding
       echo 1 > /proc/sys/net/ipv4/conf/$BRIDGEDEV/forwarding
       $vzctl exec $VEID $ip link set $VEIFNAME up
       eval VETH_IP=\$VETH_IP_ADDRESS_${VLANID}
       eval VETH_MASK=\$MASK_${VLANID}
       echo "For vlan $VLANID IP-address is $VETH_IP, mask is $VETH_MASK"
       $vzctl exec $VEID $ip address add $VETH_IP/$VETH_MASK dev $VEIFNAME
     done
   else
     echo "No additional VLANS in VE$VEID."
   fi

# Setting up static routes
   if [ -n "$STATIC_ROUTES" ]; then
     for ROUTENAME in $STATIC_ROUTES; do
       ROUTE=${!ROUTENAME}
       ROUTE_PARMS=`echo $ROUTE | sed 's/,/\n/g'`
       for str in $ROUTE_PARMS; do
         if [[ "$str" =~ ^target_type= ]]; then
           TARGET_TYPE=${str#*=}
         fi
         if [[ "$str" =~ ^target_addr= ]]; then
           TARGET_ADDR=${str#*=}
         fi
         if [[ "$str" =~ ^gw= ]]; then
           GATEWAY=${str#*=}
         fi
       done
       $vzctl exec $VEID /sbin/route add -$TARGET_TYPE ${TARGET_ADDR} gw ${GATEWAY}
     done
   else
     echo "No static routes in VE$VEID config file!"
   fi

done

Таким образом, этот скрипт делает то же самое, что и предыдущий, только обращается к переменным, сгруппированным по суффиксу в имени, в виде номера vlan-а. Ну и явное "выковыривание" обоих нужных MAC-адресов с последующим их задействованием в конструкции vzctl set ... --netif_add (без такого явного указания всех 5-ти параметров доп.интерфейсы не поднимаются; почему - к теоретикам, а не ко мне, я не разработчик OpenVZ. И ещё на заметку хозяйке - для vzctl версии 3.0.23-8 необходимо указание 5-го параметра - bridge, тогда как для версии 3.0.22-14 необходимо ровно 4 параметра, иначе не поднимется).

В следующем выпуске - дополнительные скрипты, для добавления вланов\бриджей\интерфейсов-в-виртуале вручную, удаления ненужных интерфейсов, а также рестарта виртуала с переподнятием всех его дополнительных интерфейсов (команда vzctl restart VLANID переподнимет только основной интерфейс виртуала).

P.S. Огромное спасибо Erraen-у и Владу, за много чего :)

Комментарии

Erraen аватар

Справедливости для...

Оной скрипт был мною утащен уж не упомню где :)