
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-у и Владу, за много чего :)
Комментарии
Справедливости для...
Оной скрипт был мною утащен уж не упомню где :)