Контейнерная виртуализация при помощи OpenVZ

3 ноября 2015

Сейчас наблюдается большой ажиотаж в отношении технологий контейнерной виртуализации. И почему-то в этом контексте особенно часто упоминается Docker, словно это вообще единственное решение для создания контейнеров. А тем временем существует технология, которая не только ничем не уступает Docker, но и по некоторым оценкам опережает ее как минимум лет на пять. Технология эта называется OpenVZ. О причинах малой популярности OpenVZ мне остается только гадать. Однако, надеюсь, что данная статья хотя бы отчасти исправит положение дел.

Мотивация

В отношении OpenVZ справедливо следующее:

  • С точки зрения пользователя контейнеры выглядят, как полноценные VDS со своими дисками и сетевыми интерфейсами. Никаких «слоеных» ФС, никаких коллизий номеров портов на хост-системе, и так далее. Действительно очень удобно;
  • Накладные расходы на контейнеризацию оцениваются в 1-3%, что очень мало, особенно если вспомнить о накладных расходах в Docker при работе с диском;
  • Очень гибкая настройка того, какому контейнеру сколько и каких ресурсов выделяется. CPU, память, диск, сеть — все под вашим контролем;
  • Существуют веб-панели, например, OpenVZ Web Panel и Proxmox (UPD: Proxmox перешел на LXC), благодаря которым можно за пол дня поднять AWS на собственном железе. Это может быть оправдано не только с точки зрения безопасности, но и в связи с последними изменениями в российском законодательстве. Плюс вы сможете менять все квоты на лету, у вас не будет ограничений в стиле «держите все данные на сетевых дисках (EBS)» и так далее;
  • OpenVZ уже много лет используется в Parallels, Яндексе, FastVPS, Atlassian, Pixar, Travis CI, WarGaming и ряде других компаний;

Как любая технология контейнерной виртуализации, OpenVZ имеет и ряд ограничений. Вы можете запускать в контейнерах различные дистрибутивы Linux, но не можете запустить Winodws или FreeBSD. Также, например, нельзя загрузить в контейнере произвольный модуль ядра. Такова плата за малые накладные расходы на виртуализацию.

Установка OpenVZ

Официальный сайт рекомендует ставить OpenVZ на СentOS или RHEL. Debian или Ubuntu также поддерживаются, но далее мы будем следовать рекомендации и воспользуемся CentOS 6. Должен предостеречь вас от желания поставить OpenVZ на ноутбук с Ubuntu. В OpenVZ используется собственная сборка ядра Linux, в результате чего, скажем, может отвалиться какое-то железо. Хотя попробовать ничего не мешает, так как вы всегда сможете переключиться на старое ядро. Кстати, чтобы поиграться с OpenVZ, совсем не обязательно использовать отдельный комьютер. OpenVZ отлично работает под Vagrant.

OpenVZ хранит все контейнеры в каталоге /vz. Допустим, есть контейнер с номером 100. Сам контейнер будет лежать в каталоге /vz/private/100/. Когда контейнер запущен, его файловую систему можно найти в каталоге /vz/root/100. А вот настройки контейнеров лежат не в /vz, для контейнера 100 их следует искать в файле /etc/vz/conf/100.conf. Таким образом, перед установкой OpenVZ рекомендуется соответствующим образом разбить диск, ну или хотя бы создать линк /vz туда, где побольше свободного места.

Далее говорим:

sudo wget -P /etc/yum.repos.d/ http://ftp.openvz.org/openvz.repo
sudo rpm --import http://ftp.openvz.org/RPM-GPG-Key-OpenVZ
sudo yum install vzkernel
sudo yum install vzctl vzquota ploop

Правим /etc/sysctl.conf:

# On Hardware Node we generally need
# packet forwarding enabled and proxy arp disabled
net.ipv4.ip_forward = 1
net.ipv6.conf.default.forwarding = 1
net.ipv6.conf.all.forwarding = 1
net.ipv4.conf.default.proxy_arp = 0

# Enables source route verification
net.ipv4.conf.all.rp_filter = 1

# Enables the magic-sysrq key
kernel.sysrq = 1

# We do not want all our interfaces to send redirects
net.ipv4.conf.default.send_redirects = 1
net.ipv4.conf.all.send_redirects = 0

SELinux нужно отключить, правим /etc/sysconfig/selinux:

SELINUX=disabled

Перезагружаемся:

sudo reboot

После перезагрузки uname -a покажет ту же версию ядра, что мы видели при установке пакета vzkernel. У меня это 2.6.32-042stab111.12.

Вот и все! Не так уж и сложно, согласны?

Основные команды

Создать контейнер:

sudo vzctl create 100 --ostemplate centos-6-x86_64 \
  --hostname test-container

Важно! Контейнеры должны иметь номера 100 или больше. Прочие номера зарезервированы. В частности, с ними может отказаться работать утилита vzdump, речь о которой пойдет ниже.

Полный список доступных шаблонов можно найти здесь. Первая загрузка шаблона занимает время, 219 Мб как-никак.

Запустить контейнер:

sudo vzctl start 100

Список контейнеров:

sudo vzlist -a

Выполнить команду в контейнере:

sudo vzctl exec 100 ps

Зайти в контейнер:

sudo vzctl enter 100

Сменить пароль пользователя:

sudo vzctl set 100 --userpasswd root:PASSWORD

Перезапустить контейнер:

sudo vzctl restart 100

Остановить контейнер:

sudo vzctl stop 100

Приостановить/возобновить работу контейнера:

sudo vzctl suspend 100
sudo vzctl resume 100

Примонтировать/отмонтировать файловую систему контейнера:

sudo vzctl mount 100
sudo ls /vz/root/100
sudo vzctl umount 100

Удалить контейнер:

sudo vzctl delete 100

Основные настройки контейнера

Запускать контейнер при старте системы:

sudo vzctl set 100 --save --onboot yes

Количество ядер:

sudo vzctl set 100 --save --cpus 1

Выделяем 512 Мб памяти:

sudo vzctl set 100 --save --ram 512M

Разрешаем использовать 2 Гб под своп:

sudo vzctl set 100 --save --swap 2048M

Выделяем под контейнер 20 Гб на диске:

sudo vzctl set 100 --save --diskspace 20G

Меняем имя хоста:

sudo vzctl set 100 --save --hostname centos.example.com

Меняем IP и маску сети:

sudo vzctl set 100 --save --ipadd 10.110.0.13/24

Меняем DNS:

sudo vzctl set 100 --save --nameserver 8.8.8.8 --nameserver 8.8.4.4

Резервное копирование, восстановление и клонирование

Утилиты vzdump и vzrestore устанавливаются отдельно. Понадобится скачать два RPM пакета — первый, второй. Затем говорим:

sudo yum install perl-LockFile-Simple.noarch
sudo rpm -i cstream-2.7.4-3.el5.rf.x86_64.rpm
sudo rpm -i vzdump-1.2-4.noarch.rpm

Резервное копирование контейнера:

sudo vzdump --compress 100

Резервная копия будет называтьcя как-то так:

/vz/dump/vzdump-openvz-100-2015_01_02-12_13_14.tgz

Восстановление контейнера с присвоением ему номера 101:

sudo vzrestore /path/to/archive.tgz 101

Таким же образом можно клонировать контейнеры.

Использование veth-интерфейса

OpenVZ может создавать два типа сетевых интерфейсов — venet (Virtual Network) и veth (Virtual Ethernet). Подробно различия описаны здесь. Если в двух словах:

  • venet используется по умолчанию. Он чуть быстрее veth, но требует вручную указывать сетевые настройки контейнера и не позволяет использовать broadcast сообщения из контейнера. Также venet безопаснее (см далее);
  • veth разрешает использовать broadcast сообщения из контейнера, что, помимо прочего, позволяет назначать контейнеру сетевые настройки при помощи DHCP. Но veth при этом менее безопасен, чем venet. Поимев рута внутри контейнера, можно получить такой же доступ к сетевому интерфейсу, как и из хост-системы. Это позволяет, например, снифать весь трафик, идущий через интерфейс;

Рассмотрим, как при помощи veth-интерфейса можно пробрасывать трафик из контейнеров в локальную сеть, а также использовать в контейнерах DHCP этой локальной сети.

Примечание: Аналогичную инструкцию для Debian и Ubuntu вы найдете в заметке Туториал по контейнеризации при помощи LXC.

На хост-системе ставим bridge-utils, если пакет еще не установлен:

sudo yum install bridge-utils

Далее создаем файл /etc/sysconfig/network-scripts/ifcfg-vmbr0. Здесь и далее нужно использовать имя vmbr0, а не какое-то другое. Это имя используется в скрипте vznetaddbr, речь о котором пойдет далее.

Итак, если хост использует DHCP, в файле пишем:

DEVICE="vmbr0"
TYPE="Bridge"
BOOTPROTO="dhcp"
IPV6INIT="no"
IPV6_AUTOCONF="no"
ONBOOT="yes"

Если же IP у хоста статический, пишем что-то вроде:

DEVICE="vmbr0"
TYPE="Bridge"
BOOTPROTO="static"
IPADDR="10.110.0.12"
NETMASK="255.255.255.0"
DELAY="0"
IPV6INIT="no"
IPV6_AUTOCONF="no"
ONBOOT="yes"

В случае со статическим IP в файле /etc/sysconfig/network также указываем шлюз:

GATEWAY="10.110.0.1"

Теперь правим /etc/sysconfig/network-scripts/ifcfg-eth0, где eth0 — ваш сетевой интерфейс, а 08:00:27:2D:82:3A — его MAC-адрес:

DEVICE="eth0"
HWADDR="08:00:27:2D:82:3A"
ONBOOT="yes"
BRIDGE="vmbr0"

Применяем новые настройки сети:

sudo service network restart

В ifconfig теперь должны увидеть vmbr0.

Далее создаем файл /etc/vz/vznet.conf такого содержания:

EXTERNAL_SCRIPT="/usr/sbin/vznetaddbr"

Добавляем в контейнер veth-интерфейс:

sudo vzctl set 100 --netif_add eth0 --save

Заходим в контейнер, правим /etc/sysconfig/network-scripts/ifcfg-eth0:

DEVICE="eth0"
TYPE="Ethernet"
BOOTPROTO="dhcp"
IPV6INIT="no"
IPV6_AUTOCONF="no"
ONBOOT="yes"

Рестартуем контейнер:

sudo vzctl restart 100

Если все было сделано правильно, должны увидеть строчку:

Adding interface veth100.0 to bridge vmbr0 on CT0 for CT100

При этом внутри контейнера по DHCP подхватятся настройки сети.

Заключение

В качестве источников дополнительной информации можно рекомендовать:

Лично я считаю, что OpenVZ — прекрасная технология. И мне бесконечно грустно от того, что так много людей прутся от убогого Docker просто потому что сейчас он в моде, и возможно даже не подозревают о существовании куда более зрелых альтернатив.

Метки: , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.