...

суббота, 24 октября 2015 г.

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


Рассуждения


При упоминании словосочетания «контейнерная виртуализация», многим на ум сразу же приходят 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.

Смотрим:

$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


Залогинимся с указанным паролем. Для запуска в 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

Система пишет, что интерфейсы 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

Обратим внимание на 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 http://ift.tt/1R1Uu8T -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"

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

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

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

Будет создан контейнер 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, итд) — эдакая «живая миграция».

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

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

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Комментариев нет:

Отправить комментарий