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

Традиционно эту задачу решают средствами виртуализации, но, к сожалению, наши процессоры еще не поддерживают её. Альтернативным решением является контейнеризация. Изучив доступные варианты, которые можно реализовать на ОС Эльбрус, мы остановились на LXC как стабильном решении. В данной статье я хочу рассказать как пользоваться LXC в ОС Эльбрус.

Что мы еще рассматривали
  • chroot - имеет право на жизнь, но хочется аналог виртуальных машин, что даст нам больше гибкости.

  • docker - есть только в экспериментальной версии, возможно вернемся к нему, но хочется сосредоточиться на работе, а не выяснении почему все упало.

  • В ОС Эльбрус есть собранный Bochs, но он не умеет эмулировать e2k архитектуру разработка под которую является целевой для нас.

  • QEMU - в версии ядра 2.6 для ОС Эльбрус работал QEMU в режиме паравиртуализации, потом его убирали на доработку. В пакетах ОС Эльбрус он есть, но его работоспособность мной не проверялась (как проверим, обязательно расскажем).

Исходные данные

Итак, для начала у нас имеется:

  • сервер на базе процессоров Эльбрус-8С;

  • на сервер установлена ОС Эльбрус версии 6.0.1

  • LXC версии 2.0.8

  • в качестве rootfs бэкенда в LXC мы будем использовать каталог (опция lxc.rootfs.backend = dir), это даст нам дополнительные возможности, о которых я расскажу ниже.

Подготовка и настройка сервера

Устанавливаем пакеты lxc и lxcfs командой:

sudo apt install lxc lxcfs

Настраиваем сеть для контейнеров, для этого создаем файл /etc/default/lxc-net следующего содержания:

USE_LXC_BRIDGE="true"
LXC_BRIDGE="lxcbr0"
LXC_ADDR="192.168.103.1"
LXC_NETMASK="255.255.255.0"
LXC_NETWORK="192.168.103.0/24"
LXC_DHCP_RANGE="192.168.103.2,192.168.103.254"
LXC_DHCP_MAX="253"
#LXC_DHCP_CONFILE=""
LXC_DOMAIN=""

Для применения сетевых настроек ко вновь создаваемым контейнерам вносим изменения в файл /etc/lxc/default.conf:

lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.flags = up
lxc.network.hwaddr = 00:16:3e:xx:xx:xx

Для создания сетевого моста нужно запустить службу lxc-net:

sudo service lxc start

Ну и включаем необходимые службы для автоматического запуска при старте операционной системы:

sudo chkconfig lxc-net on
sudo chkconfig lxc on

Создание первого контейнера

С ОС Эльбрус идет шаблон osl для создания контейнера из образа установочного диска. Для создания нашего первого контейнера копируем на сервер iso-образ (в моем случае я его залил в /opt/iso) и монтируем его:

sudo mkdir -p /mnt/cdrom
sudo mount -t iso9660 -o loop /opt/iso/el-6.0.1-e8c-boot.iso /mnt/cdrom

Следует заметить, что монтировать нужно строго в /mnt/cdrom так как этот путь зашит в скрипте шаблона.

Создаем контейнер командой:

sudo lxc-create -t osl -n osl-test

Ждем окончания развертывания, запускаем контейнер и подключаемся к нему:

sudo lxc-start -n osl-test
sudo lxc-attach -n osl-test

У нас есть работающий контейнер, который уже можно использовать как замену виртуальной машины. В нем потребуется еще произвести настройку и установку необходимых для работы пакетов.

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

Шаблон для создания контейнера из тарбола

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

За основу нового шаблона был взят аналогичный шаблон из SaltStack с небольшими доработками под специфику ОС Эльбрус.

Первая правка которую нам нужно внести касается задания hostname, в ОС Эльбрус имя хоста задается в файле /etc/sysconfig/network для этого в файл шаблона в конец функции deploy_tar() дописываем:

if [ -f "${rootfs_path}/etc/sysconfig/network" ]; then
    OLD_HOSTNAME=$(grep HOSTNAME ${rootfs_path}/etc/sysconfig/network)
    sed -i "s/$OLD_HOSTNAME/HOSTNAME=$name/" ${rootfs_path}/etc/sysconfig/network
fi

Мы не планируем настраивать сеть из шаблона, для этого убираем строки, отвечающие за настройки настройки сети:

lxc_network_type="veth"
lxc_network_link="br0"
...
-t|--network_type)  lxc_network_type=${2}; shift 2;;
-l|--network_link)  lxc_network_link=${2}; shift 2;;
-r|--root_passwd)   root_passwd=${2}; shift 2;;
...
if [ ! -e /sys/class/net/${lxc_network_link} ]; then
    echo "network link interface does not exist"
    exit 1
fi

Задаем новое имя нашему файлу, в LXC имена шаблонов имеют вид lxc-<имя шаблона>, где часть <имя шаблона> потом используется в команде lxc-create, для файла выставляем права на запуск и копируем его в /usr/share/lxc/templates/. Свой шаблон я назвал osl-img:

mv salt_tarball lxc-osl-img
chmod +x lxc-osl-img
TODO: уточнить права на шаблонах
chown root:root lxc-osl-img
sudo mv lxc-osl-img /usr/share/lxc/templates/

Создаем шаблонный образ rootfs

Прежде всего создаем контейнер по первой части нашей инструкции и проводим настройку контейнера.

Исправление ошибок при выполнении команды chkconfig

Данные действия можно произвести без запуска контейнера, напрямую отредактировав файлы. По умолчанию LXC создает новые контейнеры в /var/lib/lxc/<имя вашего контейнера> (далее пути будут указаны относительно этого пути).

Нам нужно отредактировать файл rootfs/etc/init.d/sysklogd вписав после строки

### BEGIN INIT INFO

строку:

# Provides:          sysklogd

Удаляем файл rootfs/etc/rcsysinit.d/S05mknod и создаем файл rootfs/etc/init.d/mknod со следующим содержимым:

#!/bin/sh
### BEGIN INIT INFO
# Provides:          mknod
# Required-Start:    mountkernfs
# Required-Stop:     mountkernfs
# Default-Start:     S
# Default-Stop:      0 6
# Short-Description: 
# Description:       
### END INIT INFO

. /etc/sysconfig/rc
. ${rc_functions}

case "${1}" in
    start)
        mknod -m 660 /dev/loop0 b 7 0
        mknod -m 660 /dev/loop1 b 7 1
        mknod -m 660 /dev/loop2 b 7 2
        mknod -m 660 /dev/loop3 b 7 3

        (exit ${failed})
        evaluate_retval
        ;;
    *)
        echo "Usage: ${0} {start}"
        exit 1
        ;;
esac

Настройка сети

В своих тарболах я использую назначение адресов по DHCP, если требуются другие настройки, то это уже конфигурируется для конкретных контейнеров. Настройки сетевых интерфейсов в ОС Эльбрус располагаются в /etc/sysconfig/network-devices/ifconfig.<имя интерфейса>/ipv4, для интерфейса eth0 создаем файл rootfs/etc/sysconfig/network-devices/ifconfig.eth0/ipv4 следующего содержания:

BOOTPROTO=dhcp
ONBOOT=yes
SERVICE=dhclient

Создаем служебного пользователя

Я являюсь противником работы от пользователя root, поэтому создадим служебного пользователя под которым можно будет производить дальнейшие настройки.

Стартуем наш контейнер lxc-start -n <имя контейнера>, и подключаемся к нему lxc-attach -n <имя контейнера>. Создаем пользователя admin:

useradd -m admin
passwd admin
usermod -a -G wheel admin

Редактируем файл /etc/sudoers разрешая членам группы wheel выполнять команды через sudo. Для этого раскомментируем строку:

%wheel ALL=(ALL) ALL

Не лишним будем установить новый пароль пользователю root:

passwd root

Автозапуск служб

Для корректной работы контейнера нам требуется выставить автозагрузку некоторых служб, для этого выполним внутри контейнера следующие команды:

chkconfig devpts on
chkconfig network on
chkconfig mknod on

Упаковка rootfs в тарбол

Останавливаем контейнер командой lxc-stop -n <имя контейнера>. И выполняем команду:

tar -cvzf osl-template.tar.gz -C /var/lib/lxc/<имя контейнера>/rootfs/ .

Получившийся архив перемещаем в удобное для хранения место.

Как пользоваться?

Свой шаблон я назвал osl-img, поэтому в примерах он будет фигурировать с таким именем. Что-бы развернуть новый контейнер нам потребуется выполнить команду:

sudo lxc-create -t osl-img -n tarball-test -- --imgtar /path/to/osl-template.tar.gz 

Если все сделали правильно, то мы получим новый контейнер, который даже работает.

Доступ в контейнеры по SSH

Подключаться к контейнеру через lxc-attach конечно хорошо, но не очень удобно, тем более некоторые IDE позволяют производить компиляцию на удаленном хосте через SSH, что было бы очень удобно при портировании и разработке под Эльбрус.

Мною опробованы два способа. Первый способ, работает при использовании моста на сетевой интерфейс, в этом случае хостовую систему можно использовать как jump host и настроить ssh клиента для работы через него. Для этого нужно внести в файл ~/.ssh/config следующие строки:

Host e2k-proxy
    HostName <доменное имя или ip вашего хоста>
    ForwardAgent yes

Host 192.168.103.*
    ProxyCommand ssh e2k-proxy -W [%h]:%p

После этих настроек все соединения на хосты с адресами 192.168.103.1/24 (сеть которую мы настраивали ранее в файле /etc/default/lxc-net) будут осуществляться через хостовую систему.

Второй вариант, выставить контейнер наружу сетевым интерфейсом. Такое возможно, если вместо veth мы будем использовать macvlan, для этого в конфигурацию контейнера (/var/lib/lxc/<имя контейнера>/config) нужно внести следующие изменения:

lxc.network.type = macvlan
lxc.network.macvlan.mode = bridge
lxc.network.link = eth0

Если ваш контейнер был запущен, то перезапускаем его. Подключаемся к нему и настраиваем сетевой интерфейс. Пример файла ipv4 для статического адреса выглядит так:

IP=192.168.103.2
GATEWAY=192.168.103.1
PREFIX=24
CHECK_LINK=yes
ONBOOT=yes
TYPE=Ethernet
SERVICE=ipv4-static

Вместо заключения

В статье не затронуто много аспектов работы с LXC, я постарался сделать больше упор на специфику ОС Эльбрус. Как вы уже поняли, работа с LXC на Эльбрусе не отличается от таковой в других дистрибутивах Linux и на других архитектурах процессоров.

По первым результатам работы коллег, у нас уже появились планы по улучшению текущей системы:

  • запустить альтернативные операционные системы в контейнерах (под Эльбрус есть как минимум еще Альт и Astra Linux)

  • создать контейнеры с GUI для случаев когда требуется посмотреть как это будет работать у пользователя

  • ну и мы уже начали работу по автоматизации, там тоже поле не паханое и очень интересное.