Site icon 8HOST.COM

Обслуживание нескольких сайтов с помощью Nginx, HAProxy и LXD в Ubuntu 16.04

Контейнеры Linux (Linux containers) – это группа процессов, изолированных от остальной системы с помощью функций безопасности ядра Linux (пространств имён, контрольных групп и т. п.). Эта конструкция похожа на очень лёгкую виртуальную машину; в контейнерах нет расходов на запуск дополнительного ядра или симулирование аппаратного обеспечения. Это значит, что один сервер может легко поддерживать несколько контейнеров. С помощью контейнеров Linux можно запустить несколько экземпляров разных операционных систем на одном сервере, а также объединить приложение и его зависимости и изолировать их от остальной среды.

Предположим, у вас есть сервер, на котором нужно запустить несколько сервисов и веб-сайтов. В стандартной установке все сайты будут представлены виртуальными хостами, принадлежащими одному экземпляру Apache или Nginx. А контейнеры Linux позволяют изолировать каждый сайт и обеспечить ему собственный экземпляр веб-сервера.

Для создания и работы с контейнерами используется сервис LXD – гипервизор для управления всем жизненным циклом контейнеров Linux.

Данное руководство научит использовать LXD для обслуживания двух сайтов на основе Nginx в рамках одного сервера; каждый сайт будет помещен в отдельный контейнер. Также вы узнаете, как запустить обратный прокси-сервер HAProxy в третьем контейнере и перенаправить на него трафик.

Требования

1: Добавление пользователя в группу lxd

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

sudo usermod --append --groups lxd 8host

Выйдите из системы и войдите снова, чтобы обновить SSH-сессию.

2: Настройка LXD

Самым важным здесь является выбор типа бэкэнда для хранения контейнеров. Данные LXD рекомендуется хранить в файловой системе ZFS, размещенной в предварительно выделенном файле или в блочном хранилище. Чтобы включить поддержку ZFS в LXD, установите zfsutils-linux:

sudo apt-get update
sudo apt-get install zfsutils-linux

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

Настройка предварительно выделенного файла

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

sudo lxd init

Команда предложит ввести некоторую информацию (как показано ниже). Можно оставить значения по умолчанию (размер указывается в строке loop device).

Name of the storage backend to use (dir or zfs) [default=zfs]: zfs
Create a new ZFS pool (yes/no) [default=yes]? yes
Name of the new ZFS pool [default=lxd]: lxd
Would you like to use an existing block device (yes/no) [default=no]? no
Size in GB of the new loop device (1GB minimum) [default=15]: 15
Would you like LXD to be available over the network (yes/no) [default=no]? no
Do you want to configure the LXD bridge (yes/no) [default=yes]? yes
Warning: Stopping lxd.service, but it can still be activated by:
lxd.socket
LXD has been successfully configured.

После этого указанный объём будет автоматически выделен из дискового пространства сервера.

Настройка блочного хранилища

Найдите устройство, которое указывает на созданный том хранилища, чтобы добавить его в конфигурации LXD.

Примечание: В данном случае устройство будет называться /dev/disk/by-id/scsi-0D0_Volume_volume-fra1-01. Название устройства может отличаться.

Определив том, вернитесь в терминал и выполните следующую команду, чтобы инициализировать LXD:

sudo lxd init

Команда задаст ряд вопросов. Ответы на них указаны ниже:

Name of the storage backend to use (dir or zfs) [default=zfs]: zfs
Create a new ZFS pool (yes/no) [default=yes]? yes
Name of the new ZFS pool [default=lxd]: lxd

Когда команда предложит указать блочное устройство, выберите yes и укажите путь к этому устройству:

Would you like to use an existing block device (yes/no) [default=no]? yes
Path to the existing block device: /dev/disk/by-id/scsi-0DO_Volume_volume-fra1-01

На остальные вопросы можно ответить по умолчанию:

Would you like LXD to be available over the network (yes/no) [default=no]? no
Do you want to configure the LXD bridge (yes/no) [default=yes]? yes
Warning: Stopping lxd.service, but it can still be activated by:
lxd.socket
LXD has been successfully configured.

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

Процесс инициализации представит ряд экранов, которые помогут настроить сетевой мост для контейнеров, чтобы они могли получить внутренние IP-адреса, взаимодействовать друг с другом и подключаться к Интернету.

Используйте значение по умолчанию для каждого параметра, кроме сети IPv6: здесь нужно выбрать No (в этом руководстве IPv6 не используется).

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

Настройка LXD успешно завершена. Теперь можно создать контейнеры с помощью команды lxc.

Запросите список доступных установленных контейнеров:

lxc list

Команда вернёт:

Generating a client certificate. This may take a minute...
If this is your first time using LXD, you should also run: sudo lxd init
To start your first container, try: lxc launch ubuntu:16.04
+------+-------+------+------+------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+-------+------+------+------+-----------+

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

Создайте три контейнера: два для веб-серверов и один для обратного прокси-сервера. Цель обратного прокси-сервера – перенаправить входящие из Интернета соединения на правильный веб-сервер в контейнере.

С помощью команды lxc launch создайте и запустите контейнер Ubuntu 16.04 по имени web1. Символ x в части команды ubuntu:x – это сокращение от Xenial, кодового имени дистрибутива Ubuntu 16.04; ubuntu: – это идентификатор предварительно настроенного репозитория образов LXD.

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

lxc image list ubuntu:

Список образов для других дистрибутивов покажет команда:

lxc image list images:

Чтобы создать необходимые контейнеры, введите:

lxc launch ubuntu:x web1
lxc launch ubuntu:x web2
lxc launch ubuntu:x haproxy

При создании первого контейнера команда загружает образ контейнера из Интернета и кэширует его. Следующие два контейнера будут созданы значительно быстрее.

Вывод после создания первого контейнера:

Creating web1
Retrieving image: 100%
Starting web1

Теперь у вас есть три пустых контейнера. Чтобы получить информацию о них, введите:

lxc list

На экране появится таблица, в которой указано имя, состояние, IP и тип каждого контейнера, а также наличие снапшотов контейнеров.

+---------+---------+-----------------------+------+------------+-----------+
|  NAME   |  STATE  |         IPV4          | IPV6 |    TYPE    | SNAPSHOTS |
+---------+---------+-----------------------+------+------------+-----------+
| haproxy | RUNNING | 10.10.10.10 (eth0)    |      | PERSISTENT | 0         |
+---------+---------+-----------------------+------+------------+-----------+
| web1    | RUNNING | 10.10.10.100 (eth0)   |      | PERSISTENT | 0         |
+---------+---------+-----------------------+------+------------+-----------+
| web2    | RUNNING | 10.10.10.200 (eth0)   |      | PERSISTENT | 0         |
+---------+---------+-----------------------+------+------------+-----------+

Запишите имена контейнеров и их адреса IPv4.

4: Настройка контейнеров Nginx

Подключитесь к контейнеру web1 и настройте первый веб-сервер. Для этого используйте команду lxc exec. В ней укажите имя контейнера и команду, которую в нём нужно выполнить.

lxc exec web1 -- sudo --login --user ubuntu

Строка — указывает, где заканчиваются параметры lxc и начинаются команды, которые нужно выполнить в контейнере. Команда sudo –login –user ubuntu предоставит оболочку входа в предварительно настроенный аккаунт ubuntu в контейнере.

Примечание: Чтобы подключиться к контейнеру как root, введите:

lxc exec web1 -- /bin/bash

Командная строка в контейнере выглядит так:

ubuntu@web1:~$

Пользователь ubuntu в контейнере имеет доступ к sudo и может запускать команды с sudo, не указывая пароля.

Эта оболочка ограничена пределами контейнера. Все команды, запущенныев этой оболочке, останутся в контейнере и не повлияют на хост-сервер.

Обновите индекс пакетов в контейнере и установите Nginx:

sudo apt-get update
sudo apt-get install nginx

Отредактируйте веб-страницу по умолчанию для этого сайта и добавьте текст, который сообщит, что этот сайт размещен в контейнере web1. Откройте файл /var/www/html/index.nginx-debian.html:

sudo nano /var/www/html/index.nginx-debian.html

Внесите в файл следующие изменения:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container web1!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx on LXD container web1!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
...

Теперь в файле есть строка:

on LXD container web1

Сохраните и закройте файл.

Чтобы выйти из контейнера, введите:

logout

Повторите этот процесс в контейнере web2. Войдите в контейнер, установите веб-сервер и отредактируйте файл /var/www/html/index.nginx-debian.html (укажите web2 вместо web1). Затем закройте контейнер web2.

С помощью curl протестируйте веб-серверы в контейнерах. Для этого понадобятся IP-адреса контейнеров.

curl http://10.10.10.100/
http://10.10.10.100/" command
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container web1!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx on LXD container web1!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
...

Аналогичным образом проверьте работу второго контейнера. Для этого укажите в команде curl его IP-адрес.

5: Настройка контейнера HAProxy

Теперь нужно настроить контейнер обратного прокси-сервера HAProxy.

Читайте также: Основы работы с HAProxy и базовые понятия балансировки нагрузки

Направьте трафик в контейнеры с помощью доменных имен.

Примечание: В руководстве используются условные домены. Первый веб-сайт доступен по доменам example.com и www.example.com, а второй сайт – по www2.example.com.

Войдите в контейнер haproxy:

lxc exec haproxy -- sudo --login --user ubuntu

Обновите список пакетов и установите HAProxy:

sudo apt-get update
sudo apt-get install haproxy

Затем нужно настроить HAProxy. Конфигурационный файл HAProxy находится в /etc/haproxy/haproxy.cfg.

sudo nano /etc/haproxy/haproxy.cfg

Внесите пару поправок в раздел defaults. Добавьте опцию forwardfor, чтобы сохранить исходный IP клиента. Опция http-server-close позволяет повторно использовать сессии и уменьшить задержку.

global
...
defaults
log global
mode    http
option  httplog
option  dontlognull
option   forwardfor
option   http-server-close
timeout connect 5000
timeout client  50000
timeout server  50000
...

Затем нужно направить фронтэнд на контейнеры бэкэнда. Добавьте раздел www_frontend:

frontend www_frontend
bind *:80     # Bind to port 80 (www) on the container
# It matches if the HTTP Host: field mentions any of the hostnames (after the '-i').
acl host_web1 hdr(host) -i example.com www.example.com
acl host_web2 hdr(host) -i web2.example.com
# Redirect the connection to the proper server cluster, depending on the match.
use_backend web1_cluster if host_web1
use_backend web2_cluster if host_web2

Команды acl указывают имена хостов веб-серверов и перенаправляют запросы в соответствующий раздел backend.

Затем добавьте разделы backend для веб-серверов (они будут называться web1_cluster и web2_cluster).

backend web1_cluster
balance leastconn
# We set the X-Client-IP HTTP header. This is useful if we want the web server to know the real client IP.
http-request set-header X-Client-IP %[src] # This backend, named here "web1", directs to container "web1.lxd" (hostname).
server web1 web1.lxd:80 check
backend web2_cluster
balance leastconn
http-request set-header X-Client-IP %[src] server web2 web2.lxd:80 check

Параметр balance определяет стратегию балансировки нагрузки. В этом случае выбрано наименьшее количество подключений. Опция http-request устанавливает HTTP-заголовок с реальным IP-адресом веб-клиента. Без этого заголовка веб-сервер записывал бы IP-адрес HAProxy в качестве исходного IP всех соединений, что затрудняло бы анализ происхождения трафик. Опция server  задает имя сервера (web1), за которым следует имя хоста и порт сервера.

LXD предоставляет DNS-сервер для контейнеров. Потому web1.lxd разрешается на IP-адрес, связанный с контейнером web1. У других контейнеров есть свои имена хостов (web2.lxd и haproxy.lxd).

С помощью параметра check HAPRoxy поддерживает проверки состояния веб-серверов.

Убедитесь, что настройка работает должным образом:

/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c

Команда должна вернуть:

Configuration file is valid

Перезапустите HAProxy:

sudo systemctl reload haproxy

Закройте контейнер и вернитесь в среду хоста.

logout

Теперь HAProxy работает как обратный прокси-сервер, который передаёт соединения, поступающие на порт 80, соответствующему контейнеру. Убедитесь, что haproxy может перенаправлять соединения.

curl --verbose --header 'Host: web2.example.com' http://10.10.10.10

Эта команда создаст запрос HAProxy и установит HTTP-заголовок host, который HAProxy должен использовать для перенаправления соединения на соответствующий веб-сервер.

Команда должна вернуть:

'Host: web2.example.com' http://10.10.10.10" command
...
> GET / HTTP/1.1
> Host: web2.example.com
> User-Agent: curl/7.47.0
> Accept: */*
>
...
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container web2!</title>
<style>
...

Прокси-сервер HAProxy правильно понял запрос и отправил его в контейнер web2. Там веб-сервер обслужил страницу по умолчанию, которую вы отредактировали ранее, и вывел соответствующий текст. Теперь нужно направить внешние запросы в HAProxy, чтобы внешние клиенты могли получить доступ к веб-сайтам.

6: Перенаправление входящих подключений в контейнер HAProxy

Осталось только подключить обратный прокси-сервер к сети Интернет.

Сервер должен передавать все запросы из Интернета на порт 80 контейнера haproxy.

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

В команде iptables нужно указать два IP-адреса: внешний IP сервера (укажите его вместо your_server_ip) и внутренний IP контейнера haproxy (вместо your_haproxy_ip), который можно узнать с помощью команды lxc list.

Чтобы добавить правило, введите:

sudo iptables -t nat -I PREROUTING -i eth0 -p TCP -d your_server_ip/32 --dport 80 -j DNAT --to-destination your_haproxy_ip:80

Читайте также:

Чтобы сохранить правила iptables, установите пакет iptables-persistent.

sudo apt-get install iptables-persistent

Во время установки будет предложено сохранить текущие правила.

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

Чтобы убедиться, что оба веб-сервера действительно доступны из Интернета, попробуйте получить доступ к каждому из них с помощью команды curl:

curl --verbose --header 'Host: example.com' 'http://your_server_ip'
curl --verbose --header 'Host: web2.example.com' 'http://your_server_ip'

Эти команды выполняют HTTP-подключение к внешнему IP-адресу сервера и добавляют поле заголовка HTTP с помощью опции –header, которую HAProxy будет использовать для обработки запроса.

Вывод первой команды выглядит так:

*   Trying your_server_ip...
* Connected to your_server_ip (your_server_ip) port 80 (#0)
> GET / HTTP/1.1
> Host: example.com
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.10.0 (Ubuntu)
...
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container web1!</title>
<style>
body {
...

Вывод второй команды:

*   Trying your_server_ip...
* Connected to your_server_ip (your_server_ip) port 80 (#0)
> GET / HTTP/1.1
> Host: web2.example.com
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.10.0 (Ubuntu)
...
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container web2!</title>
<style>
body {
...

Заключение

Вы поместили два сайта в индивидуальные контейнеры, а также настроили контейнер HAProxy, который управляет трафиком. Чтобы добавить на сервер другие веб-сайты в индивидуальных контейнерах, достаточно повторить вышеописанный процесс.

Также вы можете добавить контейнер MySQL и установить CMS (например, WordPress) для управления каждым сайтом.

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

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