Иногда возникает необходимость размещать несколько проектов на одном физическом сервере. Для того, чтобы отделить проекты друг от друга, защитить от проблем, уязвимостей и конфликтов зависимостей, можно использовать виртуализацию или контейнеризацию.
В 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-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, а также как запретить контейнерам из разных сетей общаться друг с другом.