Виртуализация второго порядка

UPDATE(2016-01-28): теперь для этого есть Docker.

Что делать, когда нужна куча маленьких и дешевых серверов для тестирования разных версий разных сайтов? Можно прикупить дедик и поставить на него OpenVZ. Хотя, OpenVZ будет как-то мелковато — памяти-то много. Лучше поставим XEN. Или KVM. Или даже VMWare.
И начнем всё это админить? — Конечно же, нет.

Виртуализация, любовь моя


Начну издалека, первый пост же. Больше шести лет назад я таки поменял один здоровенный и дорогущий железный сервер на несколько маленьких виртуальных. В то время мастерхост был еще торт, и виртуалки были у него только под Virtuozzo (впрочем, как и сейчас). Так и познакомились. Шло время, технология контейнеров очаровывала меня своей простотой: файловая система в простой папке, полные бекапы одним кликом, стабильные ядра от хостера и, главное, никаких заморочек с железом. Сбывшаяся мечта человека, никогда не собиравшего сервер.

Но когда появляется необходимость выйти за ограничения твух-трёх удобных хостеру тарифов, начинаются огорчения. В моём случае понадобилась масса создаваемых на лету маленьких серверов, которые запускают разные версии сайта для каждой ветки в репозитории. Памяти требуется чуть, диска тоже немного, процессор и сеть используются изредка. А главное, сервер надо уметь создавать из скрипта. И, лучше всего, быстро.

И с этим возникают сложности. Во-первых, серьёзные парни со своими API (Linode, Amazon) используют XEN от 20 баксов за сервер с кучей всего лишнего. Это не вариант. Во-вторых, дешевые хостинги постоянно лагают и отваливаются, даже если и научиться автоматом создавать у них серверы. Тоже не вариант. И всё это логично, ведь дорогие хостеры оттого и дорогие, что всё умеют и работают стабильно.

А что, если завести дешевые контейнеры на дорогом хостинге? Начиная с ubuntu 12.04 это сделать проще пареной репы: с помощью Linux Containers, которые уже несколько лет живут в ванильном ядре. В случае сервера от Linode, это пара apt-getов, маленький патч, перезагрузка и 300 Мб места.

Ванильное ядро


Начнем:
sudo apt-get update && sudo apt-get upgrade

Поставим скрипты LXC:
sudo apt-get install lxc

И проверим поддержку Linux Containers в ядре:
sudo lxc-checkconfig

Сегодняшнее дофолтное ядро у линоды поддерживает контейнеры очень частично. Поставим ванильное ядро от убунты:
sudo apt-get install linux-virtual

virtual потому, что нужна поддержка XEN.

Дальше двигаемся согласно кусочку инструкции по PV-GRUB:
sudo apt-get install grub-legacy-ec2

Когда установщик граба задавал вопрос «куда ставить», я выбирал xvda.

Далее пачим `/boot/grub/menu.lst` (ближе к концу файла):
- # defoptions=console=hvc0
+ # defoptions=console=hvc0 rootflags=nobarrier

и обновляем конфиг граба:
sudo update-grub-legacy-ec2


Всё, контейнер готов к загрузке ванильного ядра. Осталось только попросить наш linode загружаться в соответствии с конфигом граба. Для этого надо зайти в редактор профиля виртуального сервера, посмотреть там битность ядра (у меня Latest 32 bit) и выбрать там соответствующий pv-grub-* (у меня pv-grub-x86_32). Далее сохраняем профиль и перезагружаем сервер.

Контейнер


Теперь можно проверить поддержку LXC в ядре:
sudo lxc-checkconfig

и насладиться всеми зелененькими пунктами.

В системе появится новый сетевой интерфейс:
ifconfig | grep lxc

который будет объединять все контейнеры и выпускать их в интернет.

Наконец-то, создаем первый контейнер:
sudo lxc-create -t ubuntu -n demo1

В первый раз этот процесс затянется минут на пять, так как lxc-create соберёт новенькую убунту в папке /var/cache/lxc/, а потом скопирует ее в папку /var/lib/lxc/demo1/rootfs/, где и будет жить наш новый контейнер.

Запустить контейнер и попасть в его консоль можно вот так (логин и пароль ubuntu):
sudo lxc-start -n demo1

Выходить, предлагают, нажав Ctrl+A Q. Из lxc-start так выйти не удаётся, но можно просто закрыть окно терминала. В дальнейшем открыть консоль контейнера можно вот так (логин и пароль те же):
sudo lxc-console -n demo1

Осталось только настроить запуск контейнера при старте:
sudo ln -s /var/lib/lxc/demo1/config /etc/lxc/auto/demo1.conf


Окно в контейнер


Теперь надо как-то попасть на сайт внутри контейнера. Так как у него нет публичного IP, придется мудрить.
Можно поковырять iptables, так как контейнеры, всё же, на одной машине. Или отключить виртуализацию сети совсем, тогда у всех контейнеров будет общий сетевой интерфейс с виртуальной машиной. Но сегодня, я не готов курить такие крепкие маны. Лучше, поступим так же, как с любым другим приватным кластером: будем проксировать HTTP-запросы на внутренние ноды. Сегодня у меня есть опыт проксирования только с помощью nginx, его и опишу. Но если ваш сайт использует веб-сокеты, то проксировать надо с помощью HAproxy, так как nginx собирается обрести их поддержку только в начале 2013 года.

Итак, конфиг для nginx:
user  nobody nogroup;
worker_processes  2;
events { worker_connections  1024; }
http {
  keepalive_timeout 90;
  access_log off;
  
  server {
    server_name demo.example.com;
    location / {
      proxy_pass  10.3.0.2;
      proxy_redirect  http://localhost/  http://$host:$server_port/;
      proxy_buffering  off;
    }
  }
}

Тут всё просто: nginx получает запрос на 80 порт виртуальной машины и передает его на 80 порт контейнера с адресом 10.3.0.2. Ответ не буферизируем, а то nginx любит складывать картинки сначала на диск, а потом уже в сеть. Секцию server надо будет повторить для каждой пары сайт/контейнер, поменяв server_name и proxy_pass в соответствии с вашей ситуацией. Уверен, написать для этого скрипт сможет каждый.

Производительность


С одной стороны, о чем тут говорить? Это ж сервер для демок и девелоперских изысканий. С другой стороны, контейнеры тем и хороши, что не добавляют сложных абстракций. Процессор все получают в его первозданном виде, память тоже. Наверное, немного тратится на логику изоляции файловой системы, да и то, только при открытии файла. Вот сеть да, виртуальная, но всего лишь на уровне IP, никто же не эмулирует сетевую карту. В комментах inkvizitor68sl называет 1-2% потерь. Я верю, что для рядового сайта будет и того меньше. Конечно, если включить квотирование, то многое изменится. Но учет и ограничение потребления ресурсов всегда чего-то стоит. Потому мои контейнеры не ограничены ни в чем, они просто изолированы, чтобы удобнее было разворачивать кучу маленьких сайтов из скрипта. Вот и всё.

Глубокие выводы


Круто же!

Ах, да. Солярис и фря, сто лет уже как, умеют это из коробки. И то же самое можно сделать на OpenVZ (даже внутри LXC).
Поделиться публикацией

Комментарии 15

    +6
    Да прибудет с вами jail!
    0
    Очень любопытно посмотреть замеры производительности таких контейнеров относительно родительской виртуалки.
      0
      Оверхед — 1-2%, если ограничения по ресурсам контейнеру не выставлять.
        0
        Я думаю и 1% там не будет. Не должно быть.
      +1
      Кстати, Linode, как бы его не хвалили за его тех. поддержку, далеко не лучший в плане соотношения цена/качество — сравнить можно, например, тут: serverbear.com
        0
        Ну, честно говоря, большего аптайма и лучшего качества саппорта в сочетании с наличием дата-центра в Европе я ещё не видел.
          +1
          Железо у linode довольно старое и временами очень перегружено, я вот что имел в виду.
            0
            Да, перегруженость временами немного заметна на минимальных конфигурациях виртуальных машин. Но при этом, опять-таки, всё работает довольно стабильно, со вменяемой скоростью. Ну и прекрасный саппорт. И ещё можно добавить отличную панель управления инстансами и API.
        0
        Замечательно. Спасибо!
          0
          А можно чуть подробнее про проксирование nginx`ом внутрь контейнеров? Фрагментам конфигов сетей контейнеров и nginx`а на хосте был бы очень рад.
            0
            Пару мыслей и конфиг энжинкса добавил. А вот сети внутри контейнеров мне так и не пришлось трогать, скрипт lxc-create сам справился. В ранних версиях убунты надо было что-то подкручивать, теперь вот нет :)

            Если надо узнать какой у контейнера IP, можно заглянуть в его lxc-конфиг. Лежит он, если не ошибаюсь, тут: /var/lib/lxc/demo1/config. Дальше я пока не заходил.
            0
            ведь дорогие хостеры оттого и дорогие, что всё умеют и работают стабильно

            Золотые слова. Не всегда показатель денег решающий.
              0
              C тем же успехом можно использовать, например, openvz.
                0
                «OpenVZ будет как-то мелковато — памяти-то много.» — А какие ограничения по памяти есть у OpenVZ?
                «И то же самое можно сделать на OpenVZ (даже внутри LXC)» — это как? Я смогу внутри контейнера LXC запустить контейнер OpenVZ?

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

                Самое читаемое