Linux-контейнеры дома: зачем и как


53a6a22737462ac8612a9e0f3e1e7ef7.jpg

Рассуждения


При упоминании словосочетания «контейнерная виртуализация», многим на ум сразу же приходят Virtuozzo и OpenVZ, а также Docker. Ассоциируется же это все, в первую очередь, с хостингом, VPS и другими подобными вещами.

Дома, на личных компьютерах многие используют виртуальные машины: в основном, пожалуй, Virtualbox. Как правило, для того, чтобы работая под Linux, иметь под рукой Windows или наоборот. Однако, при наличии множества родственных Linux-операционок, я стал замечать, что использование виртуальных машин — это, мягко говоря, нерационально.
В первую очередь, очень быстро расходуется дисковое пространство. Каждой виртуальной машине нужно место, даже если несколько из них отличаются только конфигами. Особенно критично это на невеликих размеров SSD лаптопа. В принципе, Virtualbox умеет работать с raw-девайсами и, теоретически, машинам можно назначать rw LVM-снапшот, но тут опять же встают вопросы с изменением размера файловой системы в дальнейшем, автоматизацией клонирования, переноса, удаления и тому подобное.

Во вторую — это больший расход оперативной памяти. В третью — не самые удобные инструменты взаимодействия…

Потому, возникла идея опробовать в домашних условиях контейнерную виртуализацию. OpenVZ отмел сразу, по причине необходимости возиться с кастомным ядром. Выбор же пал на LXC, поставляющийся в репозитарии стабильного Debian'a.

Зачем это нужно:
— Для сборки ПО при нежелании захламлять разномастными *-dev пакетами основную рабочую систему.
— Потребность в другом дистрибутиве для запуска каких-либо специфических программ и, опять же, сборки.
— Изоляция потенциально небезопасного софта, вроде того же скайпа совершающего разные непонятные действия в домашнем каталоге пользователя и всяких сомнительных веб-технологий: уязвимость во флеше, в java, в обработчике pdf — это только то, что плавает на поверхности.
— Анонимность. Эдак можно банально остаться залогиненым в своей любимой социалочке, забыть подчистить куки или оказаться незнакомым с очередной новой веб-технологией вроде этой webrtc. Можно, конечно, держать несколько профилей браузера, но от перечисленных выше дыр и технологий это не защитит.

Итак, рассмотрим плюсы и минусы LXC:
+ Работает на ванильном ядре
+ Простота проброса устройств и каталогов хоста, так как работает это все через cgroups
+ Очень нетребовательно к ресурсам, в отличии от виртуальных машин типа Virtualbox или qemu

— Контейнеры будут работать на том же ядре, что и хост, хотя — это скорей особенность контейнерной виртуализации в целом.
— Некоторая недоделанность идущих в комплекте утилит.

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

В первую очередь, ставим пакет lxc и все необходимые утилиты:

sudo apt-get install lxc bridge-utils

Смотрим доступные группы томов LVM:

$sudo vgs
  VG         #PV #LV #SN Attr   VSize   VFree
  nethack-vg   1   6   0 wz--n- 119,00g 7,36g

sudo lxc-create -t debian -B lvm --vgname nethack-vg --fssize 2G -n deb_test

Указываем использовать LVM в качестве системы хранения, Volume Group ( в моем случае — nethack-vg) и размер 2 гигабайта, иначе по умолчанию будет создан одногиговый том. Хотя, если вдруг стало тесновато, можно будет сделать lvresize.

Смотрим:

65107073a7215506fd8af4f3e0e1b9a2.png

$sudo lvs
  LV   VG         Attr       LSize   Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  deb_test nethack-vg -wi-ao----   2,00g
  home     nethack-vg -wi-ao----  93,09g
  root     nethack-vg -wi-ao----   8,38g
  tmp      nethack-vg -wi-ao---- 380,00m
  var      nethack-vg -wi-ao----   2,79g
  vm       nethack-vg -wi-ao----   5,00g

Видим, что у нас появился том deb_test.

Типовой конфиг, созданный скриптом:

/var/lib/lxc/deb_test/config
# Template used to create this container: /usr/share/lxc/templates/lxc-debian
# Parameters passed to the template:
# For additional config options, please look at lxc.container.conf(5)
lxc.network.type = empty
lxc.rootfs = /dev/nethack-vg/deb_test

# Common configuration
lxc.include = /usr/share/lxc/config/debian.common.conf

# Container specific configuration
lxc.mount = /var/lib/lxc/deb_test/fstab
lxc.utsname = deb_test
lxc.arch = amd64
lxc.autodev = 1
lxc.kmsg = 0

Стартуем:

sudo lxc-start -n deb_test

8636ff17e2e01003971b9d6f8ec6bcaa.png
Залогинимся с указанным паролем. Для запуска в headless-режиме используется ключ -d, а рутовую консоль можно получить с помощью команды

sudo lxc-attach -n deb_test

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

На хосте прописываем в /etc/network/interfaces

auto lo br0

iface br0 inet static
   address 172.20.0.1
   netmask 255.255.255.0
   pre-up  /sbin/brctl addbr br0
   post-up /sbin/brctl setfd br0 0
   post-up iptables -t nat -A POSTROUTING -s 172.20.0.0/24 -j MASQUERADE
   post-up echo 1 > /proc/sys/net/ipv4/ip_forward
   pre-down /sbin/brctl delbr br0

В конфиг контейнера дописываем:

lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
lxc.network.hwaddr = 00:01:02:03:04:05

Чтобы сразу получить рабочую сеть и возможность установки пакетов apt'ом, допишем

lxc.network.ipv4 = 172.20.0.3
lxc.network.ipv4.gateway = 172.20.0.1


И выполним

echo "nameserver 192.168.18.1">/etc/resolv.conf

Понятно, что 192.168.18.1 — IP моего DNS.

Установим нужные пакеты:

#apt-get install vim openvpn zsh iftop

Дальше либо на хосте, либо на другой рабочей виртуалке можно получить список установленных пакетов и установить их все в нашем новом контейнере:

scp user@172.20.0.2:/etc/apt/sources.list /etc/apt/
scp -r user@172.20.0.2:/etc/apt/sources.list.d /etc/apt/
apt-get update
ssh user@172.20.0.2 'dpkg --get-selections|grep -v deinstall'|dpkg --set-selections
apt-get dselect-upgrade

Теперь можно по-человечески настроить сетевой интерфейс в контейнере, использовав любимый текстовый редактор:

/etc/network/interfaces:

auto lo eth0
iface lo inet loopback

iface eth0 inet static
address 172.20.0.3
netmask 255.255.255.0
gateway 172.20.0.1
dns-nameservers 192.168.18.1

Впрочем, это можно было сделать с хост-системы, например, смонтировав логический том. Способов много.

В принципе, в качестве DNS можно указать любой публичный, если не опасаетесь за свою приватность. Например, гугловские 8.8.8.8 и 8.8.4.4.

По доступу к устройствам хост-системы, я придерживаюсь политики «все, что не разрешено, запрещено». Добавим для этого следующую строчку в конфиг:

lxc.cgroup.devices.deny = a

Удаляем

lxc.include = /usr/share/lxc/config/debian.common.conf

Попробуем подключиться через OpenVPN. Сразу же получаем ошибку:

Thu Oct 15 16:39:33 2015 ERROR: Cannot open TUN/TAP dev /dev/net/tun: No such file or directory (errno=2)
Thu Oct 15 16:39:33 2015 Exiting due to fatal error

d8c3c13de22474b7fa96e7b684a1d796.png

Система пишет, что интерфейсы TUN/TAP недоступны по причине их отсутствия. Очевидно, что нужно разрешить гостевой системе использовать устройства хоста. Открываем конфигурационный файл контейнера, /var/lib/lxc/deb_test/config и добавляем туда строчку:

lxc.cgroup.devices.allow = c 10:200 rwm 

В контейнере выполняем:

root@deb_test:/# mkdir /dev/net; mknod /dev/net/tun c 10 200
root@deb_test:/# echo 'mkdir /dev/net; mknod /dev/net/tun c 10 200; exit 0'>/etc/rc.local

54908f7e23550a2a55be51559564c318.png

Обратим внимание на 10:200 — это идентификатор типа устройств. Если выполним на хосте:

$ls -l /dev/net/tun
crw-rw-rw- 1 root root 10, 200 окт 13 10:30 /dev/net/tun

То увидим идентификаторы 10, 200. По ним и будем ориентироваться, разрешая доступ к устройства, например камере — video0.

lxc.cgroup.devices.allow = c 81:* rwm

Точно также добавляем остальные нужные устройства:

# /dev/null and zero
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
# consoles
lxc.cgroup.devices.allow = c 5:1 rwm
lxc.cgroup.devices.allow = c 5:0 rwm
lxc.cgroup.devices.allow = c 4:0 rwm
lxc.cgroup.devices.allow = c 4:1 rwm
# /dev/{,u}random
lxc.cgroup.devices.allow = c 1:9 rwm
lxc.cgroup.devices.allow = c 1:8 rwm
lxc.cgroup.devices.allow = c 136:* rwm
lxc.cgroup.devices.allow = c 5:2 rwm
# rtc
lxc.cgroup.devices.allow = c 254:0 rm
#usb passthrough
lxc.cgroup.devices.allow = c 189:* rwm
#video
lxc.cgroup.devices.allow = c 81:* rwm
#sound
lxc.cgroup.devices.allow = c 116:* rwm
lxc.cgroup.devices.allow = c 14:* rwm

Для функционирования иксов и возможности их проброса через ssh, нужно добавить точку монтирования:

lxc.mount.entry = /tmp/.X11-unix/X0 tmp/.X11-unix/X0 none bind,optional,create=file

По аналогии можно примонтировать и другие, нужные каталоги и файлы:

lxc.mount.entry = /home/user/.vim home/user/.vim none bind,optional,create=dir 0 0 
lxc.mount.entry = /home/user/.vimrc home/user/.vimrc none bind,optional,create=file 0 0 

Для воспроизведения звука, можно разрешить доступ к звуковому устройству, если карта многопоточная (с однопоточной при использовании dmix возникают проблемы с блокировкой):

lxc.cgroup.devices.allow = c 116:* rwm
lxc.cgroup.devices.allow = c 14:* rwm
lxc.mount.entry = /dev/snd dev/snd none bind,optional,create=dir 0 0 

А можно настроить pulseaudio на воспроизведение звука по сети, как это описано здесь. Кратко:

Отредактировать на хосте /etc/pulse/default.pa, дописав туда:

load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;172.20.0.3 auth-anonymous=1

В итоге, у нас получается вот такой конифиг:

/var/lib/lxc/deb_test/config
lxc.network.type = empty
lxc.rootfs = /dev/nethack-vg/deb_test

lxc.mount = /var/lib/lxc/deb_test/fstab
lxc.utsname = deb_test
lxc.arch = amd64
lxc.autodev = 1
lxc.kmsg = 0

lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
lxc.network.hwaddr = 00:01:02:03:04:05

lxc.network.ipv4 = 172.20.0.3
lxc.network.ipv4.gateway = 172.20.0.1

#deny acces for all devices
lxc.cgroup.devices.deny = a

# /dev/null and zero
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
# consoles
lxc.cgroup.devices.allow = c 5:1 rwm
lxc.cgroup.devices.allow = c 5:0 rwm
lxc.cgroup.devices.allow = c 4:0 rwm
lxc.cgroup.devices.allow = c 4:1 rwm
# /dev/{,u}random
lxc.cgroup.devices.allow = c 1:9 rwm
lxc.cgroup.devices.allow = c 1:8 rwm
lxc.cgroup.devices.allow = c 136:* rwm
lxc.cgroup.devices.allow = c 5:2 rwm
# rtc
lxc.cgroup.devices.allow = c 254:0 rm

#sound
lxc.cgroup.devices.allow = c 116:* rwm
lxc.mount.entry = /dev/snd dev/snd none bind,optional,create=dir 0 0 
#tun/tap adapters
lxc.cgroup.devices.allow = c 10:200 rwm 
#video0
lxc.cgroup.devices.allow = c 81:* rwm
lxc.mount.entry = /dev/video0 dev/video0 none bind,optional,create=file

lxc.mount.entry = /dev/dri dev/dri none bind,optional,create=dir
lxc.mount.entry = /tmp/.X11-unix/X0 tmp/.X11-unix/X0 none bind,optional,create=file


Контейнер готов к использованию.

Использование

Доустановим, например, i2p с Tor'ом, если не сделали этого ранее, и сходу настроим privoxy:

wget -q https://geti2p.net/_static/i2p-debian-repo.key.asc -O- | sudo apt-key add -
echo "deb http://deb.i2p2.no/ jessie main" >/etc/apt/sources.list.d/i2p.list
echo "deb-src http://deb.i2p2.no/ jessie main" >>/etc/apt/sources.list.d/i2p.list
apt-get update
apt-get install privoxy i2p tor


/etc/privoxy/config
user-manual /usr/share/doc/privoxy/user-manual
confdir /etc/privoxy
logdir /var/log/privoxy
actionsfile user.action      # User customizations
filterfile default.filter
filterfile user.filter      # User customizations
logfile logfile
listen-address  localhost:8118
toggle  1
enable-remote-toggle  1
enable-remote-http-toggle  1
enable-edit-actions 1
enforce-blocks 0
buffer-limit 4096
enable-proxy-authentication-forwarding 0
forwarded-connect-retries  0
accept-intercepted-requests 0
allow-cgi-request-crunching 0
split-large-forms 0
keep-alive-timeout 5
tolerate-pipelining 1
socket-timeout 300
forward .i2p localhost:4444
forward-socks5 .onion localhost:9050 .

Запускать графические приложения вроде браузера удобнее всего через ssh:

ssh -Y 172.20.0.2 "PULSE_SERVER=172.20.0.1 http_proxy=127.0.0.1:8118 chromium"

59aeb58186a769847929e54881c8fb4b.png

Так же, разумеется, LXC предоставляет средства для клонирования контейнеров и снятия снапшотов.

Так, например, склонировать контейнер, файловая система которого будет являться LVM-снапшотом можно командой:

sudo lxc-clone -s -H -o deb_test -L 200M --new deb_test2

98a4dfed316b9ef6a16e303420622797.png

Будет создан контейнер deb_test2 с файловой системой, размещенной на LVM-снапшоте размером 200MB (под хранение diff'ов). Это будет точная копия deb_test, над которой можно провести пару экспериментов и, например, безболезненно удалить.

А вот lxc-snapshot с LVM в качестве хранилища, на версии lxc-1.0.6 почему-то не работает:

->sudo lxc-snapshot -n deb_test 
        lxc_container: deb_test's backing store cannot be backed up.
        lxc_container: Your container must use another backing store type.
        lxc_container: Error creating a snapshot

Проблема описывается и обсуждается здесь. Потому, снимки придется делать по старинке:

sudo lvcreate -L100M -s -n deb_test_before_rm_rf -p r /dev/nethack-vg/deb_test

В данном случае, создали read-only снапшот с именем deb_test_before_rm_rf размером 100MB. Что с ним делать дальше? Например, его можно сдампить посредством dd, перенести на другую машину вместе с конфигами контейнера, создать там том нужного размера и пролить тем же dd (cp, cat, итд) — эдакая «живая миграция».

Как писалось выше, областей применения контейнерам можно найти массу. Но главной, при домашнем использовании, на мой взгляд, является изоляция приложений.

На этом пока все.

© Habrahabr.ru