LXC-контейнеры для домашнего сервера (часть первая)

LXC на домашнем сервере

Вместо вступления

Контейнерная виртуализация переживает настоящий бум именно сейчас. Увлечение Docker с целью или без оной не оставило равнодушным ни одного более-менее адекватного "ойтишнега", который хочет быть "в тренде" и тщетно прокачивает скиллы в надежде на светлое будущее. LXC (Linux Containers) плетутся (или бегут ?) где-то рядом.

Дальнейший текст - лишь результат эксперимента, проведенного на домашнем сервере под управлением Linux. Он не претендует на полноту и точность, и, совершенно точно, можно сделать лучше. Но всем, вобщем-то пофигу.

В чём проблема, бро ?

Есть домашний сервер, который работает качалкой торрентов, хостингом OwnCloud, TOR-middlebox'ом и помимо этого тащит на себе TiddlyWiki@node.js и пару других чахлых web-приложений. Задача - изолировать приложения друг от друга и от хост-системы. Зачем ? Ответов несколько:

  • Поддерживать хост (bare metal) систему в рабочем предсказуемом состоянии не загаживая её кучей зависимостей, а тем более - собранных из исходников пакетов. Место на диске считаем условно бесконечным, учитывая цену за гигабайт.
  • Пусть каждое приложение работает в окружении, которое рекомендовано производителем. То есть, если кто-то любит Lighttpd, а для OwnCloud готовые решения хорошо документированы при использовании nginx, ради бога - пусть это будет nginx.
  • Настроить бэкап так, чтобы инфраструктуру можно было восстановить "обратно" не тратя времени на повторную настройку и сборку граблей.
  • Потому что хочется (это, кстати, самый веский аргумент)

Как будем разруливать ?

Довольно очевидным способом, а именно создадим набор контейнеров, каждый из которых будет иметь собственный внутренний IP-адрес, выделенный кусочек LVM, и сильно популярный дистрибутив, дабы не искать выход из тупика на говнофорумах расейских пользователей типа LOR, а пользоваться уже накопленными и систематизированными знаниями в кормушках типа LinuxQuestions и StackOverflow. Как уже говорилось - на сегодня у нас 2 мощнейших кандидата - Docker и LXC. Для моих задач больше подходит LXC - Docker всё-таки больше подходит под deploy и "одно приложение-один контейнер". Ну и заморочки с persitent тоже время отожрут немалое, а толку от них в домашнем хозяйстве не очень много.

Небольшое замечание - я использовал привилегированные контейнеры, т.е. работающие от root. Смысл пользовательских контейнеров в ещё более "сильной изоляции" и безопасности, но они требуют немного больше усилий в установке.

Docker faggots

Подготовка

Все действия производились на Cubieboard 2 под управлением Armbian и ядра 4.х. Тестирование proof-of-concept проводилось на Андроид-стике с ядром 3.0.36 (на Linux, если что), так что решение заработает на любом ущербном китайском устройстве, даже если нет "последнего ядра", лишь бы все нужные флаги в ядре присутствовали (поддержка cgroups, например). Ну за этим - в документацию. Там всё хорошо описано.

Для начала нам нужно будет создать ещё одну подсеть, в которой будут жить наши контейнеры. Шаги:

  • Установим lxc, он "потащит" за собой нужные пакеты.
  • Дальше - настроим сеть.
  • Ещё нужно позаботиться о дисковом пространстве. Я использую LVM в качестве storage backend, но можно обойтись без него и файловая система "ляжет" в обычную директорию.

Сеть

Нам потребуется "поднять" bridge-интерфейс. При этом я не привязывал его к конкретному сетевому адаптеру, чтобы работало даже на устройствах, где кроме беспроводного интерфейса ничего нет.

Этот трюк почерпнут из Archlinux Wiki - дело в том, что полноценный бридж на беспроводном адаптере не создать, зато можно настроить masquerading и получить доступ к интернету из контейнера. Может оно и к лучшему.

Вот примерно так выглядит /etc/network/interfaces:

#empty bridge auto br0 iface br0 inet static address 192.168.242.1 netmask 255.255.255.0 network 192.168.242.0 bridge_ports none

Итак, сеть вида 192.168.242.x будет отдана под контейнеры.

На сетевой карте, которая "смотрит" в интернет нужно включить masquerading с помощью iptables, ну и не забыть бы forwarding в настройках ядра. Делается примерно так, правда нужно сохранить настройки, чтобы переживать перезагрузку (способов много):

iptables --table nat --append POSTROUTING --out-interface eth0 -j MASQUERADE /sbin/sysctl -w net.ipv4.ip_forward=1

Ещё я использую пакет dnsmasq для обеспечения доступа к DNS своих контейнеров. После установки нужно будет добавить в конфигурационный файл /etc/dnsmasq.conf такие строчки:

interface=br0 no-dhcp-interface=br0

Мы говорим, что DNS доступен на интерфейсе br0, и мы используем статические IP для контейнера.

Диск

Как уже говорилось - в случае использования LVM должно быть достаточное количество дискового пространства. По-умолчанию контейнер выделяет себе гигабайт. Как показала практика - это не очень много :)

Если LVM не используется - вы ограничены только свободным местом на основном диске.

На самом деле - гонять контейнер из директории идейка "так себе", особенно, если корневая файловая система живет на microSD. В этом случае надо хорошо подумать об оптимизации флеш-карты. Да и LVM оказался побыстрее.

Первый контейнер

В принципе, можно создавать первый контейнер. Делается это командой lxc-create, в моем случае это выглядит вот так:

sudo lxc-create -t download -n applications -B lvm --vgname vg0 --fssize 2G --lvname applications

Рассмотрим подробнее:

  • Мы будем использовать LVM для storage backend.
  • Мы будем скачивать готовый rootfs с сайта LXC (кстати, он иногда любит упасть). Выберем его позднее в интерактивном режиме выполнения lxc-create.
  • Контейнер будет называться "applications"
  • Он будет создан в Volume Group vg0 (/dev/vg0)
  • Логический том будет иметь то же имя (applications)
  • Мы выделяем под него 2 гигабайта места

В случае не-использования lvm - решительно убираем опции -B, --vgname, --fssize, --lvname.

После запуска нам предлагают кучу дистрибутивов на выбор:

Downloading the image index

---

DIST RELEASE ARCH VARIANT BUILD

---

centos 6 amd64 default 20160811_02:16 centos 6 i386 default 20160811_02:16 centos 7 amd64 default 20160811_02:16 debian jessie amd64 default 20160810_22:42 debian jessie arm64 default 20160811_03:58 ..... ubuntu yakkety armhf default 20160811_03:49 ubuntu yakkety i386 default 20160811_03:49 ubuntu yakkety powerpc default 20160811_03:49 ubuntu yakkety ppc64el default 20160811_03:49 ubuntu yakkety s390x default 20160811_03:49

Напомню, что весь процесс у нас происходит на процессоре ARM (CubieBoard2, Orange Pi), поэтому архитектуру мы будем использовать armhf.

Кстати, об образах - в LXC 2.0.x их немного больше и доступен Alpine Linux - отличный легковесный дистр, как будто созданный для контейниризации. LXC 2.0 доступен из бэкпортов Jessie, его можно установить вместо дефолтного 1.0. Но концепция озвученная ранее - "только дефолтные пакеты на базовой системе" и общая лень не дали мне этого сделать.

Вообще, лучше выбирать какой-нибудь известный дистрибутив. Я использвал ubunty trusty для armhf - это всё-таки LTS, хоть и старый, да и под настройку всего полно туториалов.

После ответа на вопросы начнется закачка, или, если это не первый контейнер - будет использована закэшированная копия.

После окончания процесса получаем примерно такое сообщение:

You just created an Ubuntu container (release=trusty, arch=armhf, variant=default) To enable sshd, run: apt-get install openssh-server For security reason, container images ship without user accounts and without a root password.
Use lxc-attach or chroot directly into the rootfs to set a root password or create user accounts.

Не торопитесь запуcкать контейнер. Для начала его нужно настроить - создать пароль root'а по крайней мере, да и сеть тоже не будет лишней.

Установка пароля root в контейнере

Вот тут начинается самое главное. Конечно, можно воспользоваться советом системы и запустить lxc-attach, но у меня лично на старом тестовом ядре не заработал "как надо". Так что предлагаю смонтировать lvm в директорию, и сделать туда chroot перед запуском, чтобы установить пароль root и настроить сеть.

Вот настройка настройка сети для использования статического адреса в контейнере с Ubuntu 14.04:

auto eth0 iface eth0 inet static address 192.168.242.120 netmask 255.255.255.0 gateway 192.168.242.1 dns-nameservers 192.168.242.1

Команды, которые нужно выполнить для chroot в контейнер и настройки сети

mount /dev/vg0/applications /mnt chroot /mnt passwd vi /etc/network/interfaces (....тут настраиваем сеть....) exit umount /mnt

Подготовка конфигурационного файла

Вторым шагом перед стартом контейнера - его нужно донастроить. Конфигурационный файл в нашем случае - /var/lib/lxc/applications/config

#Distribution configuration lxc.include = /usr/share/lxc/config/ubuntu.common.conf #Container specific configuration lxc.rootfs = /dev/vg0/applications lxc.utsname = applications lxc.arch=armhf lxc.autodev=1 lxc.kmsg=0 #Network configuration lxc.network.type = veth lxc.network.flags = up lxc.network.link = br0 lxc.network.hwaddr = f8:7a:15:0c:f6:4f lxc.network.script.up = /usr/local/bin/lxc_applications.sh lxc.mount.entry = /var/lvm/data/downloads var/downloads none bind 0 0 #Autostart lxc.start.auto = 1 lxc.start.order = 0 lxc.start.delay = 10

Возможно, для начала, секцию Autostart можно выкинуть (и включить, когда все уже настроено).

В конфигурации сети нужно указать тип сети (veth), линк (br0), и мак-адрес. Его можно сгенерить с помощью массы онлайн сервисов. Напомню, что сам IP адрес "прописан" в самом контейнере - как на "настоящей" системе.

Еще присутствует некий скрипт, который будет выполняться на хосте при старте контейнера - но об этом во второй части заметки, равно как и подключение "внешней" директории через директиву lxc.mount.entry.

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

Для первого старта:

lxc-start -n applications

Если всё хорошо - мы увидим стартующие сервисы и сможем попасть внутрь контейнера, используя пароль root.

Если что-то пошло не так - проверяем, что мак-адрес валиден (можно просто перегенерить) и что мы действительно установили пароль. Ну или пользуемся lxc-attach, если ядро позволяет.

В LXC 1.0 - контейнер по-умолчанию стартует в foreground mode, в 2.0 - в background.

Чтобы запустить 1.0 контейнер в background mode, нужно использовать опцию "-d" команды lxc-start.

Итак, если все хорошо, то будет доступна сеть и можно ставить нужные приложения!

To Be Continued...

Окончание этой санта-барбары

На корм коту

Магазин открыт!

ещё