Настройка LXC в ручном режиме
N.B.
Информация актуальна для lxc и штатного ПО, поставляемого в slackware-15.0.
N.B. 2
Для некоторых настроек придётся модифицировать штатные стартовые скрипты из /etc/rc.d.
Исходим из предпосылок:
Cеть с контейнерами | 10.200.0.0/24 |
GW, через который ходят конты во вне | 10.200.0.1 |
DNS resolver | 10.200.0.1 |
Интерфейс, в который "воткнуты" сетевушки контейнеров | lxcbr0 |
Подготовка среды
Настройка elogind для нормального запуска контейнеров с systemd
Надо пропатчить /etc/rc.d/rc.elogind вот таким образом:
--- rc.elogind.orig 2022-08-20 15:26:00.000000000 +0300
+++ rc.elogind 2022-11-04 14:00:00.000000000 +0300
@@ -21,11 +21,14 @@
if [ -x /lib64/elogind/elogind ]; then
if [ ! -d /run/user ]; then
mkdir -p /run/user
+ chmod 1777 /run/user
fi
if [ ! -d /run/systemd ]; then
- mkdir -p /run/elogind /sys/fs/cgroup/elogind
- ( cd /run; rm -rf systemd; ln -sf elogind systemd; )
- ( cd /sys/fs/cgroup; rm -rf systemd; ln -sf elogind systemd; )
+ mkdir -p /run/systemd /sys/fs/cgroup/systemd
+ mount -t cgroup -o rw,nosuid,nodev,noexec,none,name=systemd none /sys/fs/cgroup/systemd
+ ( cd /run; rm -rf elogind; ln -sf systemd elogind; )
+ #( cd /sys/fs/cgroup; rm -rf elogind; ln -sf systemd elogind; )
+ ( cd /sys/fs/cgroup; mkdir -p elogind; mount -t cgroup -o rw,nosuid,nodev,noexec,none,name=elogind none /sys/fs/cgroup/elogind)
fi
if pgrep -l -F /run/elogind.pid 2>/dev/null | grep -q elogind; then
echo "Elogind is already running"
Иначе контейнеры с systemd не смогут стартовать, будут жаловаться на чтьо-то странное, а на самом деле им только-то нужен /sys/fs/cgroup/systemd смонтированный правильным образом.
Настройка сетевого моста для lxc
Чтобы нам собрать lxc-контейнеры в местную локальную сеть, нам нужен сетевой мост. В его качестве будет выступать классический linux-овый bridge.
Тут начинается кастом в /etc/rc.d.
Для создания сетевого моста, нам надо подгрузить пару модулей в ядро. Модуль dummy для "прогрева" моста (чтобы не было накладок с arp-ом). И, собственно, модуль bridge.
Для этого нам надо дописать в /etfc/rc.d/rc.modules.local вот такие строки:
if [ -x /etc/rc.d/rc.netdevice ]; then
/etc/rc.d/rc.netdevice start
fi
А уже в файле /etc/rc.d/rc.netdevice мы будем настраивать наши виртуальные сетевые адаптеры:
#!/bin/sh
PATH='/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin'
case "$1" in
"start")
# setup device dummy0
modprobe dummy
ifconfig dummy0 up
# setup bridge
modprobe bridge
brctl addbr lxcbr0
brctl addif lxcbr0 dummy0
brctl stp lxcbr0 off
brctl setfd lxcbr0 0
ifconfig lxcbr0 up
ethtool -K lxcbr0 tx off >/dev/null 2&>1
;;
"stop")
ifconfig lxcbr0 down
brctl delif lxcbr0 dummy0
brctl delbr lxcbr0
ifconfig docker0 down
brctl delbr docker0
ifconfig docker1 down
brctl delbr docker1
modprobe -r bridge
ifconfig dummy0 down
modprobe -r dummy
;;
*)
echo "Usage: $0 start|stop"
;;
esac
Остаётся самая малость - нам надо настроить ip адрес нашего gw, через который во вне будут ходить наши контейнеры.
Это делается в файле /etc/rc.d/rc.inet1.conf, просто настраиваем ещё один интерфейс (например, второй по счёту):
# IPv4 config options for eth1:
IFNAME[1]="lxcbr0"
IPADDRS[1]="10.200.0.1/24"
USE_DHCP[1]=""
# IPv6 config options for eth1:
IP6ADDRS[1]=""
USE_SLAAC[1]=""
USE_DHCP6[1]=""
# Generic options for eth1:
DHCP_HOSTNAME[1]=""
Включение ip_forward
Здесь всё просто, для работы интернетов и вообще внешних (по отношению к стенду lxc) сетей, нужно настроить форвардинг пакетов между сетевыми интерфейсами.
chmod +x /etc/rc.d/rc.ip_forward
Настройка NAT
Чтобы уметь ходить за пределы localhost-а lxc-машинками, от имени ip-адреса этого localhost-а надо научиться в NAT.
Для этого надо создать файрволл - создаём файлик /etc/rc.d/rc.firewall и делаем на него
chmod +x /etc/rc.d/rc.firewall
в сам файл записываем:
#!/bin/bash
if [[ "$1" == "stop" ]]; then
iptables-restore < /etc/default/iptables-empty
elif [[ "$1" == "start" ]]; then
iptables-restore < /etc/default/iptables
else
echo "Usage: $0 start|stop"
fi
и нам потребуется создать пару файлов с пустым файрволлом и с настройками для lxc.
Пустой файрволл /etc/default/iptables-empty:
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
файл с настройками файрволла для lxc:
# Generated by iptables-save v1.8.7 on Mon Aug 15 23:07:58 2022
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lxcbr0 -j ACCEPT
-A OUTPUT -o lxcbr0 -j ACCEPT
-A FORWARD -i lxcbr0 -j ACCEPT
-A FORWARD -o lxcbr0 -j ACCEPT
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [8:416]
:POSTROUTING ACCEPT [8:416]
-A POSTROUTING -s 10.200.0.0/24 -j MASQUERADE
COMMIT
# Completed on Mon Aug 15 23:07:58 2022
Настройка dhcpd
Для автоматической конфигурации сетевых настроек lxc-контейнеров, нам нужен сервис dhcpd. Он будет раздавать виртуалкам заданные адреса, маршрут по-умолчанию, список dns-серверов и hostname-ы. А также добавлять/удалять/обновлять записи в dns-е о хостах, получивших адреса.
Конфиг сервиса /etc/dhcpd.conf:
default-lease-time 600;
max-lease-time 7200;
authoritative;
ignore unknown-clients;
log-facility local7;
# DDNS statements
ddns-updates on;
ddns-update-style interim;
allow client-updates;
update-static-leases on;
update-conflict-detection false;
subnet 10.200.0.0 netmask 255.255.255.0 {
ignore unknown-clients;
range 10.200.0.200 10.200.0.250;
option domain-name-servers 10.200.0.1;
option domain-name "lxc";
option routers 10.200.0.1;
option broadcast-address 10.200.0.255;
default-lease-time 600;
max-lease-time 7200;
}
# Ip addresses for known hosts
host mygrav { hardware ethernet 00:00:00:00:00:03; fixed-address 10.200.0.3; }
# DDNS, for named/bind zones autoupdate
zone lxc. {
primary 127.0.0.1;
}
zone 0.200.10.in-addr.arpa. {
primary 127.0.0.1;
}
СтОит заметить, что по-умолчанию сервис dhcpd в Slackware не запускается автоматически. Совсем, никак. Это, конечно, большой промах.
Его можно исправить, добавив сервису стартовый скрипт и прописав его запуск в /etc/rc.d/rc.local:
if [ -x /etc/rc.d/rc.dhcpd ]; then
/etc/rc.d/rc.dhcpd start
fi
И сам стартовый скрипт:
!/bin/sh
# /etc/rc.d/rc.dhcpd - start/stop the dhcpd daemon
# To change the default options, edit /etc/default/dhcpd.
if [ -r /etc/default/dhcpd ]; then
. /etc/default/dhcpd
fi
start_dhcpd() {
if ! /usr/bin/pgrep --ns $$ --euid root -f "^/usr/sbin/dhcpd" 1> /dev/null 2> /dev/null ; then
echo "Starting dhcpd: /usr/sbin/dhcpd $DHCPD_OPTS"
/usr/sbin/dhcpd -t
if [ $? -gt 0 ]; then
echo "Config /etc/dhcpd.conf is incorrect. Refuse to start dhcpd."
exit 1
fi
/usr/sbin/dhcpd $DHCPD_OPTS
fi
}
stop_dhcpd() {
echo "Stopping dhcpd."
/usr/bin/pkill --ns $$ --euid root -f "^/usr/sbin/dhcpd" 2> /dev/null
}
restart_dhcpd() {
/usr/sbin/dhcpd -t
if [ $? -gt 0 ]; then
echo "Config /etc/dhcpd.conf is incorrect. Refuse to restart dhcpd."
exit 1
fi
stop_dhcpd
sleep 1
start_dhcpd
}
case "$1" in
'start')
start_dhcpd
;;
'stop')
stop_dhcpd
;;
'restart')
restart_dhcpd
;;
*)
echo "usage $0 start|stop|restart"
esac
Настройки для него /etc/default/dhcpd:
DHCPD_OPTS="-4 -q lxcbr0"
Настройка логгирования сообщений от dhcpd в отдельный файл - это домашнее задание :)
Настройка Bind/Named
Сразу стОит отметить, что я использую named в том числе как локальный ресолвер (то есть для резолва не только зоны lxc, но и всего интернета). То есть в нём прописаны форвардеры и зоны.
Итак, для работы named нам надо настроить его общий конфиг и зоны, которые будет апдэйтить dhcpd.
Основной файл конфига у нас находится в /etc/named.conf:
options {
directory "/var/named";
forwarders {
1.1.1.1;
1.0.0.1;
8.8.8.8;
8.8.4.4;
208.67.222.222;
208.67.220.220;
9.9.9.9;
149.112.112.112;
209.244.0.3;
209.244.0.4;
74.82.42.42;
};
allow-query { any; };
allow-recursion { any; };
allow-transfer { any; };
allow-query-cache { any; };
dnssec-validation yes;
dnssec-enable yes;
};
//
// a caching only nameserver config
//
zone "." IN {
type hint;
file "caching-example/named.root";
};
zone "localhost" IN {
type master;
file "caching-example/localhost.zone";
allow-update { none; };
};
zone "0.0.127.in-addr.arpa" IN {
type master;
file "caching-example/named.local";
allow-update { none; };
};
zone "lxc" {
type master;
allow-update { any; }; # ip of dhcp server
file "lxc.zone";
notify yes;
};
// reverse zone
zone "0.200.10.in-addr.arpa" {
type master;
allow-update { any; }; # ip of dhcp server
file "0.200.10.in-addr.arpa.zone";
notify yes;
};
Настройки запуска named-а /etc/default/named:
# User to run named as:
NAMED_USER=named
# Group to use for chowning named related files and directories.
# By default, named will also run as the primary group of $NAMED_USER,
# which will usually be the same as what's listed below, but not
# necessarily if something other than the default of "named" is used.
NAMED_GROUP=named
# Options to run named with. At least -u $NAMED_USER is required, but
# additional options may be added if needed.
NAMED_OPTIONS="-u $NAMED_USER -4"
также надо убедиться, что /var/named действительно принадлежит пользователю named:
chown -R named:named /var/named
Reboot и проверка, что всё нужное запустилось
На этом моменте нужно удостовериться, что в каталоге /var/lib/lxc ничего нету (если есть, то надо это дело "подвинуть" в сторонку).
После ребута должны появиться процессы dhcpd, named, а также инрфейсы dummy0 и lxcbr0, кроме того, должны прмениться правила iptables.
Создание тестовой машинки
Нам понадобится утилита lxc-create, с её помощью надо бутстрапнуть любой доступный дистрибутив. И для примера назвать контейнер mygrav.
Однако, торопиться запускать контейнер не стОит. Нам нужно подправить его конфиг. Приведём его к такому виду:
# Template used to create this container: /usr/share/lxc/templates/lxc-slackware
# Parameters passed to the template:
# For additional config options, please look at lxc.conf(5)
lxc.arch = amd64
lxc.uts.name = mygrav
lxc.start.auto = 0
lxc.rootfs.path = dir:/var/lib/lxc/mygrav/rootfs
lxc.mount.entry = lxcpts dev/pts devpts defaults,newinstance 0 0
lxc.mount.entry = none proc proc defaults 0 0
lxc.mount.entry = none sys sysfs defaults 0 0
lxc.mount.entry = none run tmpfs defaults,mode=0755 0 0
lxc.mount.entry = none run tmpfs defaults,mode=0755 0 0
lxc.mount.entry = none dev/shm tmpfs nodev,nosuid,noexec,mode=1777,create=dir 0 0
lxc.mount.entry = none tmp tmpfs defaults,mode=1777 0 0
lxc.net.0.type = veth
lxc.net.0.veth.pair = mygrav
lxc.net.0.flags = up
lxc.net.0.link = lxcbr0
lxc.net.0.name = eth0
lxc.net.0.hwaddr = 00:00:00:00:00:03
#lxc.net.0.ipv4.address = 10.200.0.3/24
#lxc.net.0.ipv4.gateway = 10.200.0.1
#lxc.net.0.ipv6.address = fd00::1:3
#lxc.net.0.ipv6.gateway = fd00::1:0
lxc.tty.max = 1
lxc.pts.max = 1024
lxc.autodev = 1
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 rwm
# we don't trust even the root user in the container, better safe than sorry.
# comment out only if you know what you're doing.
#lxc.cap.drop = sys_module mknod mac_override mac_admin sys_time setfcap setpcap
#lxc.cap.drop = sys_module mknod mac_override mac_admin sys_time
# you can try also this alternative to the line above, whatever suits you better.
#lxc.cap.drop=sys_admin
#lxc.hook.autodev=/var/lib/lxc/mygrav/autodev
Тут для контейнеров с systemd надо убедиться, что все записи с lxc.cap.drop закоментированы, так как systemd для запуска нужны некоторые Linux Capabilities.
Запуск контейнера
Для пробного запуска контейнера, нам понадобится 2 консоли.
В первой консоле от рута делаем:
lxc-start -F -n mygrav
и смотрим, как происходит инициализация контейнера.
Во второй консоле можно попробоватьи прыгнуть внутрь контейнера, как только процесс его запуска дойёт до финальной точки.
lxc-attach -n mygrav
Если визуально с контейнером всё хорошо, надо проверить, что унего настроена сеть по dhcp и прописался resolver.