29 July 2014

OpenVZ + venet + vlan/адреса из разных сетей

Virtualization
Этот пост посвящён назначению контейнерам OpenVZ адресов из разных сетей на интерфейсе venet. Я решил написать этот пост потому, что видел как эту задачу решали другие специалисты неказистыми способами или же вовсе отказывались от использования venet.

Окружение


Мы имеем хостноду openvz с контейнерами, которым нужно назначить реальные адреса и адреса во внутренней сети. Притом, у контейнера может быть или реальный, или внутренний, или оба адреса сразу. Реальные адреса и внутренняя сеть доступны из хостноды через разные сетевые сегменты. В моём примере они в разных vlan-ах, а внутренняя сеть представлена блоком адресов 192.168.1/24.
ОС хостноды — CentOS 6.

Проблема


Если просто добавить внутренний и внешний адрес контейнеру, то сеть в контейнерах не будет работать как надо: для всех назначений будет всегда выбираться самый первый адрес в качестве исходящего, а при выборе маршрута на хостноде будет использована таблица маршрутизации, непригодная для внутренней (внешней) сети.
Документация и рассылки openvz склоняют к использованию veth для такой сетевой конфигурации. Однако стараюсь не использовать veth по следующим причинам:
  • производительность veth ниже
  • veth не накладывает на контейнер сетевых ограничений, это хуже в смысле безопасности
  • veth в основном случае предполагает использование моста. Когда состав интерфейсов-членов моста меняется (контейнеры запускаются или останавливаются), MAC-адрес моста выбирается минимальным по численному значению из MAC-адресов интерфейсов-членов. Если вы останавливаете запускаете или останавливаете контейнер и вам не посчастливилось иметь новый MAC-адрес меньше текущего или останавливать контейнер, MAC-адрес которого сейчас у интерфейса моста, связь с host-нодой по этому интерфейсу будет утеряна, пока не залёрнится новый MAC на свитче. Это может произойти через не один десяток секунд в ряде случаев. Я решал эту проблему назначением виртуальным интерфейсам заведомо высоких MAC-адресов, но я не был в восторге от этого.

Поэтому игра стоит свеч и хотелось бы даже в такой конфигурации иметь сеть на контейнерах на venet-интерфейсах.

Решение


Как говорилось выше, проблема с venet-интерфейсами в том, что на них приходит всё вместе, а при отправке нужно разграничить исходящие адреса и выбрать разные машруты для них. Администраторы решают эту проблему по-разному, подсовывая внутрь контейнера сбоку свои правила или меняя в ifup скриптах vz шаблон добавления адреса в контейнер. Я считаю, что сетевая конфигурация должна быть вне контейнера, чтобы не было проблем при миграции.

Выбираем нужный маршрут на хостноде

Действие происходит в /etc/sysconfig/network-scripts, если не оговорено другого.
Внутренний интерфейс:
[root@pve1 network-scripts]# cat ifcfg-vmbr0
DEVICE="vmbr0"
BOOTPROTO="static"
IPV6INIT="no"
DOMAIN="int"
DNS1="192.168.1.15"
DNS2="192.168.1.17"
GATEWAY="192.168.1.3"
IPADDR="192.168.1.142"
NETMASK="255.255.255.0"
ONBOOT="yes"
TYPE="Bridge"  

Внешний интерфейс:
[root@pve1 network-scripts]# cat ifcfg-vmbr1 
DEVICE="vmbr1"
BOOTPROTO="static"
IPADDR=200.200.100.6
NETMASK=255.255.254.0
IPV6INIT="no"
TYPE="Bridge"

Определим две дополнительные таблицы маршрутизации — для испускания виртуалками пакетов во внутреннюю сеть и во внешнюю. Две последние строки в файле ниже задают имена двум произвольно взятым свободным номерам для таблиц маршрутизации:
[root@pve1 network-scripts]# cat /etc/iproute2/rt_tables 
#
# reserved values
#
255     local
254     main
253     default
0       unspec
#
# local
#
#1      inr.ruhep
200     external
210     internal

Определим содержимое этих таблиц:
[root@pve1 network-scripts]# cat route-vmbr0
192.168.1.0/24 dev vmbr0 table internal
default via 192.168.1.3 table internal
[root@pve1 network-scripts]# cat route-vmbr1
200.200.100.0/23 dev vmbr1 table external
default via 200.200.100.30 table external

И непосредственные подсети, и интернет доступны и с внутренних, и с внешних адресов, при этом внутренняя сеть связана с интернетом через NAT-шлюз (192.168.1.3). Как нам и надо.
Теперь нужно разделить, когда какие правила маршрутизации применять. По самим файлам трудно составить понимание, я дополню комментариями ниже.
[root@pve1 network-scripts]# cat rule-vmbr0
from 192.168.1.0/24 iif venet0 lookup internal
from 192.168.1.0/24 to 192.168.1.0/24 iif venet0 lookup main

[root@pve1 network-scripts]# cat rule-vmbr1
from 200.200.100.0/23 iif venet0 lookup external
from 200.200.100.0/23 to 200.200.100.0/23 iif venet0 lookup main

Эти правила PBR добавляются снизу вверх, таким образом вторая строка оказывается первой и наоборот, а сами правила связаны с интерфейсом только тем, что добавляются когда этот интерфейс поднимается. Результирующая таблица правил:
[root@pve1 network-scripts]# ip ru li
0:      from all lookup local 
32762:  from 200.200.100.0/23 to 200.200.100.0/23 iif venet0 lookup main 
32763:  from 200.200.100.0/23 iif venet0 lookup external 
32764:  from 192.168.1.0/24 to 192.168.1.0/24 iif venet0 lookup main 
32765:  from 192.168.1.0/24 iif venet0 lookup internal 
32766:  from all lookup main 
32767:  from all lookup default 

Здесь видно, что всё из внешней сети, полученное через venet, маршрутизируется по правилам внешней сети (таблица external). Один или несколько адресов из внешней сети 200.200.100.0/23 может быть поднят на соседней виртуалке на этой же машине и тогда связываться с ним нужно не через физический интерфейс, а тоже через виртуальный. Поэтому для случая отправки из 200.200.100.0/23 в 200.200.100.0/23 я полагаюсь на главную таблицу маршрутизации, куда openvz добавляет соответствующие /32 маршруты, и в которой есть маршрут 200.200.100.0/23 через физический интерфейс для всего остального.
Аналогично и для внутренней сети.

Теперь наша хостнода в состоянии понять, что гасить сразу в интернет пакетами из серой сети и всё остальное в таком же духе не нужно.

Подсказываем контейнеру, какой выбирать исходящий адрес

Здесь всё просто, на venet можно добавлять не только /32 адреса в контейнере, но и адреса с обозначенной маской подсети. Это даёт ядру подсказку, что адреса из этого блока соседние, и что при отправке на них предпочтитетельнее использовать такой src:
[root@pve1 network-scripts]# fgrep IP /etc/vz/conf/138.conf
IP_ADDRESS="200.200.100.12/23 192.168.1.100/24"

[root@pve1 network-scripts]# vzctl exec 138 ip r
192.168.1.0/24 dev venet0  proto kernel  scope link  src 192.168.1.100 
200.200.100.0/23 dev venet0  proto kernel  scope link  src 200.200.100.12 
default dev venet0  scope link 

По умолчанию будет выбираться первый адрес. Таким образом, если я поменяю местами адреса в конфиге контейнера, то контейнер будет стараться выбирать внутренний IP и хостнода будет направлять его в интернет через NAT-шлюз.

Заключение


На мой взгляд venet это сильная сторона OpenVZ и по возможности лучше стараться пользоваться именно им. Решение выше позволяет контейнеру использовать сетевые адреса способом, абстрагированным от сетевой конфигурации.
Кроме того, я надеюсь, что помимо основного назначения пост послужит кому-то ещё и иллюстрацией использования policy based routing в Linux.
Tags:openvzvlanразные подсетиvenetPBRip rule
Hubs: Virtualization
+17
10.3k 88
Comments 15
Top of the last 24 hours