Немножко предыстории
В конце прошлого года, когда пришло время начинать писать диплом, я наткнулся на статью пользователя am83. Так как я крайне неравнодушен к виртуализации, хотелось придумать что-то относящееся к этой теме. И тут у меня родилась идея по поводу использования общей файловой системы для создания метода, который бы позволил сэкономить дисковое пространство при клонировании контейнеров LXC.
Создание основы файловой системы для контейнеров
Устанавливаем основные компоненты, которые понадобятся для создания общей файловой системы, командой:
$ apt-get install debootstrap lxc lxc-templates lxctl cgroup-lite rsync
Далее включим две управляющие опции.
В конфигурации сети включим сетевой мост для контейнеров:
$ nano /etc/init/lxc-net.conf
USE_LXC_BRIDGE="true"
А для автозапуска контейнеров при старте системы включим:
LXC_AUTO="true"
В файле lxc.conf, командой:
$ nano /etc/init/lxc.conf
Далее отредактируем конфигурационный файл:
$ nano /etc/default/lxc
И добавим в него следующие строки:
Введем переменную:
$ lxcRoot="/lxc"
Создадим каталог /lxc:
$ mkdir -p ${lxcRoot}
Создаем место где хранятся контейнеры и информация по их настройкам, а также место кеширования данных дистрибутивов для ускорения создания множества контейнеров:
$ mkdir /${lxcRoot}/lxclib /${lxcRoot}/lxccache
Удалим старый каталог:
$ rm -rf /var/lib/lxc /var/cache/lxc
Создадим ссылки на каталоги:
$ ln -s /${lxcRoot}/lxclib /var/lib/lxc
$ ln -s /${lxcRoot}/lxccache /var/cache/lxc
Создаем базовый LXC-контейнер на основе Ubuntu с именем hName и с версией Trusty:
$ lxc-create -t ubuntu -n hName -r trusty
Далее приступим к созданию неизменяемой части LXC-контейнера.
Переходим в каталог созданного контейнера:
$ cd /lxc/lxclib/hName/rootfs
В нем создаем каталог с общей частью, назовем его common:
$ mkdir common
Переносим в него неизменяемые каталоги:
$ mv bin lib lib64 sbin usr common/
Создаем символические ссылки на них:
$ ln -s common/bin
$ ln -s common/sbin
$ ln -s common/lib
$ ln -s common/lib64
$ ln -s common/usr
Создание контейнера LXC
После подготовки базового образа системы приступим непосредственно к созданию первого контейнера в системе. Назовем его просто «Node1»:
Создаем каталог для первого контейнера:
$ mkdir -p /lxc/lxclib/Node1/rootfs
Переходим в него:
$ cd /lxc/lxclib/Node1/rootfs
При помощи программы rsync копируем неизменяемую часть в первый контейнер:
$ rsync --exclude=/dev/* --exclude=/common/* -avz /lxc/lxclib/hName/rootfs/* ./
Далее для первого контейнера создаем два каталога для общей части и для устройств:
$ mkdir /lxc/lxclib/Node1/rootfs/common
$ mkdir /lxc/lxclib/Node1/rootfs/dev
Монтируем их при помощи программы Bind:
$ mount --bind /lxc/lxclib/hName/rootfs/dev /lxc/lxclib/Node1/rootfs/dev
$ mount --bind /lxc/lxclib/hName/rootfs/common /lxc/lxclib/Node1/rootfs/common
$ mount -o remount,ro /lxc/lxclib/Node1/rootfs/common
Копируем файл конфигурации и файл fstab из базового контейнера в первый:
$ cp /lxc/lxclib/hName/config /lxc/lxclib/Node1/
$ cp /lxc/lxclib/hName/fstab /lxc/lxclib/Node1/
Меняем имя в конфигурации первого контейнера на Node1, а также MAC-адрес:
$ nano /lxc/lxclib/Node1/config
Метод клонирования
Скрипт, который я написал выглядит следующим образом:
#!/bin/bash
echo "Введите имя нового контейнера: "
read Container
cp -a /lxc/lxclib/Node1 /lxc/lxclib/${Container}
mount --bind /lxc/lxclib/hName/rootfs/dev /lxc/lxclib/${Container}/rootfs/dev
mount --bind /lxc/lxclib/hName/rootfs/common /lxc/lxclib/${Container}/rootfs/common
mount -o remount,ro /lxc/lxclib/${Container}/rootfs/common
sed -i 's/Node1/'$Container'/g' /lxc/lxclib/${Container}/config
echo "Новый контейнер с именем" $Container "создан"
Сравнительные характеристики со стандартным методом lxc-clone
Что это нам дает?
Во-первых я хотел сэкономить дисковое пространство.
Узнаем размер, занимаемый на диске контейнером:
$ du –skh /lxc/lxclib/Имя_контейнера
На рисунке видно, что объем, занимаемый базовым контейнером составляет 395 мегабайт, тогда как объем, занимаемый контейнером, клонированным моим методом, составляет всего 141 мегабайт:
Исходя из вышеперечисленных данных – экономия дискового пространства с каждого контейнера выходит порядка 65%. То есть при применении данного метода, например, на серверной ферме с сотнями контейнеров экономия дискового пространства весьма заметна.
Во-вторых, не слишком впечатляющая, но все же скорость копирования контейнера.
Стандартный способ клонирования выполняется командой:
Замерим скорость клонирования командой time.
Time – выводит на экран время выполнения (в секундах) запускаемой команды. Подставляется перед любой выполняемой командой.
На рисунках представлено время выполнения команды стандартным способом клонирования контейнеров и моим:
Исходя из вышеперечисленных данных видно, что время выполнения клонирования контейнера стандартным способом занимает более 12 секунд, тогда как время выполнения клонирования моим способом, занимает около 6 секунд.
В итоге
Возможно, мою идею и не стоит использовать в продакшене на предприятии, но мне видится использование сего метода на мини-компьютерах. Ведь, поднимая несколько контейнеров на sd-карте, экономия пару сотен мегабайт с одного контейнера будет неплохим выигрышем.
Автор: Klimy