Оркестрация проектов с помощью менеджера контейнеров LXD

10 min read

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

В Linux существует технология LXC, которая предоставляет производительный и гибко настраиваемый механизм контейнеризации. Использовать LXC напрямую не очень удобно, поэтому существуют различные интерфейсы, например, LXD. Сегодня он поддерживается всеми современными Linux-дистрибутивами. 

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

Установка

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

apt update apt upgrade -y

Установим пакет zfsutils, чтобы в контейнерах можно было использовать файловую систему ZFS

apt install zfsutils-linux

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

apt install bridge-utils

Для того, чтобы установить самую свежую версию lxd из репозиториев snap, удалим текущую версию, если она есть в системе

apt remove --purge lxd lxd-client

Установим менеджер пакетов snap

apt install snapd

Теперь можно установить пакет lxd

snap install lxd

Теперь с помощью следующей команды можно увидеть, что на данный момент на сервере нет ни одного контейнера. 

lxc list

Система может сказать, что команды «lxc» нет. В таком случае проверьте, существует ли файл lxc в директории /snap/bin.

Если есть, то перезагрузите сервер, например, с помощью reboot или выполните следующую команду. 

source /etc/profile.d/apps-bin-path.sh

После этого директории snap должны добавиться в переменную окружения PATH.

Настройка NAT

Для того, чтобы внутри контейнеров был доступ в интернет, необходимо настроить NAT. Он служит для подмены адресов, отправляя запросы в интернет от имени сервера. Также NAT отвечает за обратную подмену адресов, определяя какой пакет какому контейнеру вернуть.

Для переадресации запросов будем использовать iptables. Включаем механизм роутинга следующей командой

sysctl -w net.ipv4.ip_forward=1

Активируем NAT

iptables --table nat --append POSTROUTING --out-interface ens3 -j MASQUERADE

При перезагрузке сервера настройки iptables сбрасываются. Чтобы не вводить их каждый раз заново, установим утилиту iptables-persistent

apt install iptables-persistent

После её установки нам будет предложено сохранить текущие параметры в файл. Соглашаемся.

Если в будущем потребуется сохранить изменения iptables, выполните команду

iptables-save > /etc/iptables/rules.v4

Настройка LXD

Теперь приступим к инициализации LXD. Выполните следующую команду.

lxd init

Вам будут заданы вопросы. Чтобы применять настройки по умолчанию, нажимайте Enter.

Do you want to configure a new storage pool (yes/no) [default=yes]?

Да, мы хотим сконфигурировать новое хранилище.

Name of the new storage pool [default=default]:

Оставляем для него имя default.

Name of the storage backend to use (dir, btrfs, ceph, lvm, zfs) [default=zfs]:

Теперь у нас спрашивают тип файловой системы этого хранилища. Оставляем ZFS.

Create a new ZFS pool (yes/no) [default=yes]?

Хотим ли мы создать новый ZFS пул? Да, у нас пока нет ни одного.

Would you like to use an existing block device (yes/no) [default=no]?

Нет, мы не хотим использовать дополнительное устройство, где будет находиться хранилище.

Size in GB of the new loop device (1GB minimum) [default=15GB]:

Теперь нужно задать объем этого хранилища. 15 ГБ для начала устроит.

Would you like LXD to be available over the network (yes/no) [default=no]?

Нет, не нужно делать контейнеры доступными из сети. Мы сами настроим это позже.

Would you like stale cached images to be updated automatically (yes/no) [default=yes]?

Нужно ли автоматически обновлять базовые образы систем, из которых создаются контейнеры? Пусть обновляются.

Would you like to create a new network bridge (yes/no) [default=yes]? no

Тут отвечаем «no». Не нужно создавать мост, потому что мы это сделаем сами.

Диалог с системой

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

Чтобы создать контейнер, нужно выполнить следующую команду

lxc init ubuntu:16.04 container-alice -p default

В качестве базовой системы для контейнера будет взята Ubuntu 16.04. Контейнер получит имя «container-alice» и будет размещен в хранилище default.

Перед запуском контейнера, необходимо настроить сеть, в которой он будет работать.

Создаем сеть alice-br 10.0.1.0/24

lxc network create alice-br ipv6.address=none ipv4.address=10.0.1.1/24 ipv6.nat=false ipv4.nat=false ipv4.firewall=false ipv6.firewall=false

Основной сервер будет иметь адрес 10.0.1.1

Привяжем контейнер к сети alice-br и зададим ему адрес 10.0.1.2

lxc network attach alice-br container-alice eth0 lxc config device set container-alice eth0 ipv4.address 10.0.1.2

Теперь можно запустить контейнер следующей командой

lxc start container-alice

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

В контейнерах можно выполнять любые команды с помощью lxc exec

lxc exec <container_name> <command>

Подключимся к терминалу container-alice, выполнив следующую команду

lxc exec container-alice /bin/bash

Теперь можно настроить контейнер как самостоятельную систему

apt update apt upgrade -y

Установим nginx, который в будущем может отвечать каким-нибудь сайтом из этого контейнера  

apt install nginx -y

Теперь отредактируем дефолтный конфиг для сервера

nano /etc/nginx/sites-enabled/default

Стираем всё, что там находится, и добавляем следующие строки

server { listen 80; add_header Content-Type text/plain; return 200 'Hello! My name is Alice!'; }

По этим настройкам сервер будет слушать порт 80 и отвечать на запросы текстом «Hello! My name is Alice!». 

Это самый простой nginx-конфиг, который умеет отвечать только одной фразой. Чтобы запустить настоящий сайт, вам понадобятся более сложные настройки. Например, конфигурационный файл сервера для проекта Capella выглядит так.

Чтобы сохранить файл и выйти из nano, нажмите Ctrl+x, нажмите Y, а затем Enter.

Перезагрузите nginx, чтобы подключить обновленный конфиг

service nginx restart

На этом этапе завершаем настройку контейнера и возвращаемся на уровень выше — на основной сервер

exit

Проксирование запросов

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

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

apt install nginx -y

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

nano /etc/nginx/sites-available/container-alice

И добавим туда следующее

server { listen 80; server_name alice.myserver.com; include proxy_params; location / { proxy_pass http://10.0.1.2/; } }

Сервер будет слушать порт 80, а этот хост будет отзываться на имя alice.myserver.com. Вместо этого адреса подставьте поддомен своего сервера. Также будут добавлены параметры из файла /etc/nginx/proxy_params, которые проставят необходимые для проксирования заголовки. Все запросы будут отправляться на контейнер по адресу 10.0.1.2.

Чтобы активировать конфиг хоста, нужно добавить символическую ссылку на этот файл в папку sites-enabled

ln -s /etc/nginx/sites-available/container-alice /etc/nginx/sites-enabled/

Перезагружаем nginx

service nginx restart

Теперь, если вы попробуйте открыть в браузере сайт, который указали в конфиге, то увидите приветствие от container-alice

Как создавать новые контейнеры

Если вы захотите создать ещё один контейнер, то следуйте инструкциям по созданию первого, но внимательно следите за названиями сетей и адресов. 

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

lxc init ubuntu:16.04 container-bob -p default

Вместо «container-bob» вы можете задать любое другое имя.

Если новый контейнер, будет работать в отдельной сети, то создайте её. Для этого нужно выбрать имя сети (например, bob-br) и задать адрес и маску сети (например, 10.0.2.1/24).

lxc network create bob-br ipv6.address=none ipv4.address=10.0.2.1/24 ipv6.nat=false ipv4.nat=false ipv4.firewall=false ipv6.firewall=false

Можно не создавать новую сеть, а добавить контейнер в уже существую. Для этого нужно выдать ему соответсвующий адрес. Например, если у нас есть сеть alice-br 10.0.1.0, где 10.0.1.1 — это основной сервер, а 10.0.1.2 — это container-alice, то новый контейнер может получить адрес 10.0.1.3.

Теперь подключаем контейнер к сети и задаем ему адрес

lxc network attach bob-br container-bob eth0 lxc config device set container-bob eth0 ipv4.address 10.0.2.2

И запускаем новый контейнер

lxc start container-bob

Теперь вы можете подключиться к нему с помощью lxc exec и произвести настройку. Если в container-bob тоже будет развернут какой-то сайт, то не забудьте создать nginx-конфиг на основном сервере для этого контейнера.

Подведем итоги

Мы научились создавать LXC-контейнеры и управлять ими при помощи менеджера контейнеризации LXD. Затем настроили веб-сервер внутри контейнера и предоставили к нему доступ извне при помощи NAT и проксирования Nginx.

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