Глава 6. Построение кластеров и горизонтальное масштабирование при помощи LXC

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

Горизонтальное масштабирование является неким споособом добавления вычислительной мощности в некий кластер или какую- то группу ресурсов, которая выполняет общую задачу. Это обычно выполняется путём добавления большего числа серверов или виртуальных машин, или, в случае LXC, большего числа контейнеров для работы приложений. В противоположность ему вертикальное масштабирование выполняется путём добавления большего объёма оборудования или виртуальных ресурсов, таких как ЦПУ и оперативной памяти в физические серверы, ВМ или контейнеры.

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

  • Создать некий протой кластер Apache, запустить на исполнение LXC на некоторой минимальной корневой файловой системе при помощи libvirt.

  • Реализовать некий веб кластер со множеством узлов Apache и HAProxy с применением Open vSwitch и некоторой сетки туннелей GRE

  • Продемонстрировать как добавлять больше контейнеров путйм повторного использования имеющейся файловой системы существующих экземпляров LXC.

Масштабирование приложений с применением LXC

LXC достаточно хорошо приспособлен для замены виртуальных машин в том смысле, что он может содержать некую завершённую корневую файловую систему для какого- то дистрибутива Linux, причём в этом случае единственным совместным с ОС хоста компонентом является его ядро. Приложения могут быть установлены в корневой файловой системе такого контейнера таким образом, что сам хост и все прочие контейнеры не могут совместно использовать их. Такая изоляция полезна если вы желаем исполнять различные версии одного и того же приложения и его зависимости, или различные дистрибутивы Linux все вместе.

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

В последующих двух разделах мы изучим оба сценария. Мы начнём с построения минимальной корневой файловой системы контейнеров Apache с libvirt и балансировки их нагрузки при помощи HAProxy, а затем перемещать для построения некоторого кластера Apache при помощи LXC, а также выделенные файловые системы и сетевую изоляцию посредством Open vSwitch с некоторой сеткой из туннелей GRE.

Масштабирование Apache в минимальной файловой системе корня с использованием LXC libvirt

В данном разделе мы продемонстрируем как исполнять множество серверов Apache на одном и том же хосте с применением libvirt LXC и некоторой минимальной файловой корневой системы для кажодого контейнера. При этом все двоичные файлы и библиотеки Apache будут совместно испорльзоваться всеми контейнерами. Даже если такой подход будет не самым удобным для Apache, хотя и предоставляя более простые однопоточные процессы, он поможет нам продемонстрировать обсуждаемую концепцию наиболее практичным образом.

Для данного примера мы воспользуемся Ubuntu, хотя те же самые инструкции применимы и к CentOS, как мы это продемонстрировали в Главе 2, Установка и исполнение LXC в системах Linux. Ниже приводятся необходимые для данного примера шаги:

  1. Давайте начнём с обновления имеющейся ОС и проверки того, что она работает с самой последней версией:

    
    root@ubuntu:~# apt-get update && apt-get upgrade --yes && reboot
    root@lxc:~# lsb_release -a 2>/dev/null
    Distributor ID: Ubuntu
    Description: Ubuntu 16.04.1 LTS
    Release: 16.04
    Codename: xenial
    root@ubuntu:~#
    
    root@ubuntu:~# uname -r
    4.4.0-38-generic
    root@ubuntu:~#
     	   
  2. Здесь показан самый последний пакет libvirt на Ubuntu Xenial на момент написания данной книги:

    
    root@ubuntu:~# apt-cache policy libvirt-bin
    libvirt-bin:
    Installed:
    Candidate: 1.3.1-1ubuntu10.5
    Version table:
    *** 1.3.1-1ubuntu10.5 500
          500 http://rackspace.clouds.archive.ubuntu.com/ubuntu
          xenial-updates/main amd64 Packages
          100 /var/lib/dpkg/status
       1.3.1-1ubuntu10 500
          500 http://rackspace.clouds.archive.ubuntu.com/ubuntu
          xenial/main amd64 Packages
    root@ubuntu:~#
     	   
  3. Далее установим сами пакеты libvirt:

    
    root@ubuntu:~# apt-get install libvirt-bin virtinst
    root@ubuntu:~# dpkg --list | grep libvirt
    ii  libvirt-bin      1.3.1-1ubuntu10.5       amd64
    programs for the libvirt library
    ii  libvirt0:amd64     1.3.1-1ubuntu10.5     amd64
    library for interfacing with different virtualization systems
    ii  python-libvirt      1.3.1-1ubuntu1       amd64
    libvirt Python bindings
    root@ubuntu:~#
     	   
  4. Что удобно, libvirt создаёт для нас необходимый мост:

    
    root@ubuntu:~# brctl show
    bridge name      bridge id          STP enabled  interfaces
    virbr0           8000.5254003d3c43  yes          virbr0-nic
    root@ubuntu:~#
     	   
  5. .

    Мы будем применять сетевую среду default libvirt, давайте убедимся что она присутствует:

    
    root@ubuntu:~# export LIBVIRT_DEFAULT_URI=lxc:///
    root@ubuntu:~# virsh net-list --all
    Name              State      Autostart     Persistent
    ----------------------------------------------------------
    default           active     yes           yes
    root@ubuntu:~#
     	   
  6. Чтобы проверить эту сетевую среду default и шлюз, которые будет использовать libvirt, выполните следующую команду:

    
    root@ubuntu:~# virsh net-dumpxml default
    <network>
      <name>default</name>
      <uuid>6585ac5b-3d81-4071-bb61-3aa22007834e</uuid>
      <forward mode='nat'>
        <nat>
          <port start='1024' end='65535'/>
        </nat>
      </forward>
      <bridge name='virbr0' stp='on' delay='0'/>
      <mac address='52:54:00:3d:3c:43'/>
      <ip address='192.168.122.1' netmask='255.255.255.0'>
        <dhcp>
          <range start='192.168.122.2' end='192.168.122.254'/>
        </dhcp>
      </ip>
    </network>
    root@ubuntu:~#
     	   
  7. Инструментарий libvirt также запускает dnsmasq, который назначит все сетевые установки нуждающимся в них контейнерам LXC если мы настроим их на использование DHCP:

    
    root@ubuntu:~# ps axfww
    5310 ? Ssl 0:00 /usr/sbin/libvirtd
    5758 ? S 0:00 /usr/sbin/dnsmasq
    --conf-file=/var/lib/libvirt/dnsmasq/default.conf
    --leasefile-ro
    --dhcp-script=/usr/lib/libvirt/libvirt_leaseshelper
    5759 ? S 0:00 \_ /usr/sbin/dnsmasq
    --conf-file=/var/lib/libvirt/dnsmasq/default.conf
    --leasefile-ro
    --dhcp-script=/usr/lib/libvir/libvirt_leaseshelper
     	   
  8. Мы будем применять назначаемые по умолчанию настройки dnsmasq, однако давайте обеспечим соответствие отведённого диапазона DHCP тому, о чём знает libvirt-net из предыдущего вывода:

    
    root@ubuntu:~# cat /var/lib/libvirt/dnsmasq/default.conf | grep -vi "#"
    strict-order
    user=libvirt-dnsmasq
    pid-file=/var/run/libvirt/network/default.pid
    except-interface=lo
    bind-dynamic
    interface=virbr0
    dhcp-range=192.168.122.2,192.168.122.254
    dhcp-no-override
    dhcp-lease-max=253
    dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
    addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
    root@ubuntu:~#
     	   

Создание минимальной файловой системы корня для имеющихся контейнеров

Для данного примера мы теперь собираемся использовать предоставленные шаблоны или применить команду debootstrap для построения полностью завершённой файловой системы нашего контейнера вместо того, чтобы создавать некую минимальную структуру каталога в хосте для необходимых файлов настройки для Apache и имеющегося контейнера. Всё остальное будет в том же самом пространстве имён mount, что и у ОС самого хоста, за исключением нескольких каталогов, которые мы собираемся привязать к данному контейнеру.

Для создания необходимой минимальной корневой файловой системы для данного контейнера проследйте приведёнными ниже шагами:

  1. Давайте начнём с создания всех каталогов; скопируем необходимые файлы и установим Apache на самом хосте:

    
    root@ubuntu:~# cd /opt/
    root@ubuntu:/opt# mkdir -p containers/http1/etc
    root@ubuntu:/opt# mkdir -p containers/http1/var/www/html
    root@ubuntu:/opt# apt-get install --yes apache2
    ...
    root@ubuntu:/opt# cp -r /etc/apache2/ /opt/containers/http1/etc/
    root@ubuntu:/opt# cp -r /etc/passwd /opt//containers/http1/etc/
    root@ubuntu:/opt# cp -r /etc/shadow /opt//containers/http1/etc/
    root@ubuntu:/opt# cp -r /etc/group /opt//containers/http1/etc/
    root@ubuntu:/opt# cp -r /etc/mime.types /opt//containers/http1/etc/
    root@ubuntu:/opt# cp -r /etc/init.d/ /opt//containers/http1/etc/
    root@ubuntu:/opt# cp -r /etc/resolv.conf /opt/containers/http1/etc/
    root@ubuntu:/opt# cp -r /etc/fstab /opt/containers/http1/etc/
    root@ubuntu:/opt# cp -r /etc/apache2/ /opt/containers/http1/etc/
    root@ubuntu:/opt# cp -r /etc/network/ /opt/containers/http1/etc/
     	   
  2. Далее создадим для Apache страницу index.html и настроим этот веб сервер с его собственным уникальным файлом PID, который впоследствии позволит нам запустить множество процессов Apache:

    
    root@ubuntu:/opt# echo &qout;Apache in LXC http1&qout; > /opt/containers/http1/var/www/html/index.html
    root@ubuntu:/opt# cd containers/
    root@ubuntu:/opt/containers# sed -i
    's/\${APACHE_PID_FILE}/\/var\/run\/apache2\/apache2_http1.pid/g'
    http1/etc/apache2/apache2.conf
     	   
  3. Настройте все сетевые файлы на применение DHCP с тем, чтобы мы могли усилить сервер dnsmasq, который libvirtd запустил ранее:

    
    root@ubuntu:/opt/containers# cat http1/etc/network/interfaces
    auto lo
    iface lo inet loopback
    auto eth0
    iface eth0 inet dhcp
    root@ubuntu:/opt/containers#
     	   

Определение контейнера libvirt Apache

Чтобы построить контейнеры LXC при помощи libvirt, нам необходимо создать некий файл настроек, который содержит все атрибуты данных контейнеров. Применив имеющуюся структуру каталога, помещённую на своё место на предыдущих шагах, а также установленный на данном хосте Apache, мы можем определить такой файл настроек наших контейнеров. Мы уже видели некие аналогичные настройки, когда мы обсуждали libvirt в Главе 3, Операции командной строки с использованием инструментов Native и Libvirt. Чтобы просмотреть эти настройки выполните следующее:


root@ubuntu:/opt/containers# cat http1.xml
<domain type='lxc'>
  <name>http1</name>
  <memory>102400</memory>
  <os>
    <type>exe</type>
    <init>/opt/containers/startup.sh</init>
  </os>
  <vcpu>1</vcpu>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>
  <devices>
    <emulator>/usr/lib/libvirt/libvirt_lxc</emulator>
    <filesystem type='mount'>
      <source dir='/opt/containers/http1/etc/apache2/'/>
      <target dir='/etc/apache2'/>
    </filesystem>
    <filesystem type='mount'>
      <source dir='/opt/containers/http1/var/www/html/'/>
      <target dir='/var/www/html'/>
    </filesystem>
    <filesystem type='mount'>
      <source dir='/opt/containers/http1/etc/'/>
      <target dir='/etc'/>
    </filesystem>
    <interface type='network'>
    <source network='default'/>
    </interface>
    <console type='pty'/>
  </devices>
</domain>
root@ubuntu:/opt/containers#
 	   

Что является новым в предыдущих настройках, так это то, что вместо описания /sbin/init в качестве init системы, мы настраиваем чтобы libvirt применял персональный сценарий - startup.sh. Этот сценарий может быть чем угодно что мы пожелаем; в данном случае мы запустим построение сетевой среды в данном контейнере, настройку его оболочки, исполнение dhclient для получения необходимых сетевых установок от dnsmasq, а затем запуска Apache и bash:


root@ubuntu:/opt/containers# cat startup.sh
#!/bin/bash

export
PATH=$PATH:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
export PS1=&qout;[\u@\h \W]\\$&qout;
echo &qout;Starting Networking&qout; >> /var/log/messages
/etc/init.d/networking start
/sbin/dhclient eth0
echo &qout;Starting httpd&qout; >> /var/log/messages
/etc/init.d/apache2 start
/bin/bash
root@ubuntu:/opt/containers#
 	   

Далее сделаем этот сценарий исполняемым:


root@ubuntu:/opt/containers# chmod u+x startup.sh
root@ubuntu:/opt/containers#
 	   

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


root@ubuntu:/opt/containers# ls -la http1
total 16
drwxr-xr-x 4 root root 4096 Oct 31 17:25 .
drwxr-xr-x 3 root root 4096 Oct 31 17:32 ..
drwxr-xr-x 4 root root 4096 Oct 31 17:27 etc
drwxr-xr-x 3 root root 4096 Oct 31 17:25 var
root@ubuntu:/opt/containers#
 	   

Имеются всего два каталога! Теперь мы готовы к определению самого контейнера:


root@ubuntu:/opt/containers# virsh define http1.xml
Domain http1 defined from http1.xml
root@ubuntu:/opt/containers#
 	   

Запуск контейнера libvirt Apache

Имея на своём месте все необходимые компоненты, давайте запустим данный контейнер и убедимся что он исполняется:


root@ubuntu:/opt/containers# virsh start http1
Domain http1 started
root@ubuntu:/opt/containers# virsh list --all
 Id    Name                           State
----------------------------------------------------
19032  http1                          running
root@ubuntu:/opt/containers# ps axfww
...
10592 ?      S     0:00 /usr/lib/libvirt/libvirt_lxc --name http1
--console 23 --security=apparmor --handshake 26 --veth vnet1
10594 ?      S     0:00  \_ /bin/bash /opt/containers/startup.sh
10668 ?      Ss    0:00      \_ /sbin/dhclient eth0
10694 ?      Ss    0:00      \_ /usr/sbin/apache2 -k start
10698 ?      Sl    0:00      |   \_ /usr/sbin/apache2 -k start
10699 ?      Sl    0:00      |   \_ /usr/sbin/apache2 -k start
10697 ?      S     0:00      \_ /bin/bash
root@ubuntu:/opt/containers#
 	   

При выводе списка всех процессов данного хоста отметьте как все контейнеры запускались из нашего сценария libvirt_lxc, который является предком процесса данного сценария startup.sh, который в конечном счёте запускает Apache.

Подключитесь к контейнеру и убедитесь что он способен получить некий IP адрес и шлюз по умолчанию от dnsmasq:


root@ubuntu:/opt/containers# virsh console http1
[root@ubuntu /]#ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group
default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
        valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
        valid_lft forever preferred_lft forever
14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
state UP group default qlen 1000
    link/ether 52:54:00:92:cf:12 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.122.216/24 brd 192.168.122.255 scope global eth0
        valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe92:cf12/64 scope link
        valid_lft forever preferred_lft forever
[root@ubuntu /]#ip r s
default via 192.168.122.1 dev eth0
192.168.122.0/24 dev eth0 proto kernel scope link src 192.168.122.10
[root@ubuntu /]# Ctrl + ]
 	   

Давайте подключимся к Apache из ОС хоста:


root@ubuntu:/opt/containers# curl 192.168.122.216
Apache in LXC http1
root@ubuntu:/opt/containers#
 	   
[Замечание]Замечание

IP адрес 192.168.122.70 является тем, что dnsmasq назначил нашему контейнеру; вам может понадобиться замените его каким- то правильным адресом в вашей системе.

Масштабирование Apache при помощи LXC libvirt и HAProxy

Выполните следующие шаги для масштабирования Apache при помощи libvirt LXC и HAProxy:

  1. При наличии одного исполняющегося контейнера Apache давайте быстро создадим некий второй, скопировав имеющуюся простую структуру каталога и настройки libvirt из этого контейнера http1:

    
    root@ubuntu:/opt/containers# cp -r http1 http2
    root@ubuntu:/opt/containers# cp http1.xml http2.xml
    root@ubuntu:/opt/containers#
     	   
  2. Всё что нам требуется изменить, это само имя данного контейнера, его PID файл для Apache, а также его индексный файл:

    
    root@ubuntu:/opt/containers# sed -i 's/http1/http2/g'http2.xml
    root@ubuntu:/opt/containers# sed -i 's/http1/http2/g'http2/etc/apache2/apache2.conf
    root@ubuntu:/opt/containers# echo "Apache in LXC http2" > /opt/containers/http2/var/www/html/index.html
     	   
  3. определите такой новый контейнер и проверьте его структуры каталога которая содержит необходимую корневую файловую систему и все файлы настроек для обоих контейнеров:

    
    root@ubuntu:/opt/containers# virsh define http2.xml
    Domain http2 defined from http2.xml
    root@ubuntu:/opt/containers# ls -la
    total 28
    drwxr-xr-x 4 root root 4096 Oct 31 19:57 .
    drwxr-xr-x 3 root root 4096 Oct 31 17:32 ..
    drwxr-xr-x 4 root root 4096 Oct 31 17:25 http1
    -rw-r--r-- 1 root root 868 Oct 31 17:31 http1.xml
    drwxr-xr-x 4 root root 4096 Oct 31 19:56 http2
    -rw-r--r-- 1 root root 868 Oct 31 19:57 http2.xml
    -rwxr--r-- 1 root root 418 Oct 31 19:44 startup.sh
    root@ubuntu:/opt/containers#
     	   
  4. Давайте запустим полученный новый контейнер и убедимся что оба экземпляра работают:

    
    root@ubuntu:/opt/containers# virsh start http2
    Domain http2 started
    root@ubuntu:/opt/containers# virsh list --all
    Id    Name                           State
    ----------------------------------------------------
    10592 http1                          running
    11726 http2                          running
     	   
  5. Для получения дополнительной информации обо всех контейнерах Apache выполните следующую команду:

    
    root@ubuntu:~# virsh dominfo http1
    Id:             15720
    Name:           http1
    UUID:           defd17d7-f220-4dca-9be9-bdf40b4d9164
    OS Type:        exe
    State:          running
    CPU(s):         1
    CPU time:       35.8s
    Max memory:     102400 KiB
    Used memory:    8312 KiB
    Persistent:     yes
    Autostart:      isable
    Managed save:   no
    Security model: apparmor
    Security DOI:   0
    Security label: libvirt-defd17d7-f220-4dca-9be9-bdf40b4d9164 
    (enforcing)
    
    root@ubuntu:~# virsh dominfo http2
    Id:             16126
    Name:           http2
    UUID:           a62f9e9d-4de3-415d-8f2d-358a1c8bc0bd
    OS Type:        exe
    State:          running
    CPU(s):         1
    CPU time:       36.5s
    Max memory:     102400 KiB
    Used memory:    8300 KiB
    Persistent:     yes
    Autostart:      disable
    Managed save:   no
    Security model: apparmor
    Security DOI:   0
    Security label: libvirt-a62f9e9d-4de3-415d-8f2d-358a1c8bc0bd
    (enforcing)
    root@ubuntu:~#
     	   
    [Замечание]Замечание

    Вывод dominfo предоставляет полезную информацию об имеющейся у данного контейнера памяти и использовании ЦПУ, которую вы можете применять для мониторинга, выдачи сообщений и автоматического масштабирования, как мы это увидим в Главе 7, Мониторинг и резервное копирование в мире с контейнерами. Отметим, что OS Type установлен в exe, так как системой init контейнера является некий сценарий.

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

    
    root@ubuntu:/opt/containers# curl 192.168.122.242
    Apache in LXC http2
    root@ubuntu:/opt/containers#
     	   
  7. Оба процесса Apache доступны из данного хоста благодаря имеющемуся у них общему мосту, к которому они подключены. Чтобы осуществить к нему доступ извне ОС данного хоста мы можем установить HAProxy на данном сервере, причём с адресами IP этих контейнеров в качестве лежащих в его основе серверов:

    
    root@ubuntu:~# echo "nameserver 8.8.8.8" > /etc/resolv.conf
    root@ubuntu:~# apt-get install --yes haproxy
    root@ubuntu:~# cat /etc/haproxy/haproxy.cfg
    global
          log /dev/log  local0
          log /dev/log  local1 notice
          chroot /var/lib/haproxy
          stats socket /run/haproxy/admin.sock mode 660 level admin
          stats timeout 30s
          user haproxy
          group haproxy
          daemon
          ca-base /etc/ssl/certs
          crt-base /etc/ssl/private
          ssl-default-bind-ciphers
          ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
          ssl-default-bind-options no-sslv3
    defaults
          log global
          mode http
          option httplog
          option dontlognull
          timeout connect 5000
          timeout client  50000
          timeout server  50000
    frontend http
          bind :80
    reqadd X-Forwarded-Proto:\ http
    default_backend http_nodes
    backend http_nodes
          mode http
          balance roundrobin
          option httpclose
          option forwardfor
          option redispatch
          option httpchk GET /
          cookie JSESSIONID prefix
          server http1 192.168.122.216:80 check inter 5000
          server http1 192.168.122.242:80 check inter 5000
    root@ubuntu:~#
     	   

    Указанные IP адреса определяют в строках данного сервера в его разделе backend имеющихся настроек HAProxy те, которые принадлежат нашим контейнерам LXC libvirt.

    В разделе frontend данных настроек мы сообщаем HAProxy выполнять ожидание (listen) на порту 80 связываться со всеми интерфейсами. В соответствующем разделе backend мы определяем необходимые IP обоих контейнеров LXC. Вам может понадобиться замена обозначенных IP контейнеров на те, которые dnsmasq предоставляет в вашей системе.

  8. Перезапустите HAProxy, так как в Ubuntu он автоматически запускается сразу после установки пакета:

    
    root@ubuntu:~# service haproxy restart
    root@ubuntu:~#
     	   
  9. Затем убедитесь что HAProxy запущен и выполняет ожидание по порту 80 в данном хосте:

    
    root@ubuntu:~# pgrep -lfa haproxy
    1957 /usr/sbin/haproxy-systemd-wrapper -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
    1958 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
    1960 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
    root@ubuntu:~#
    
    root@ubuntu:~# netstat -antup | grep -i listen | grep -w 80
    tcp      0     0 0.0.0.0:80      0.0.0.0:*
    LISTEN 1960/haproxy
    tcp6       0      0 :::80        :::*
    LISTEN 9693/apache2
    root@ubuntu:~#
     	   
  10. Мы настроили HAProxy на использование карусельного метода (round robin) для выбора серверных узлов; давайте свяжемся с ними несколько раз и убедимся что мы соединились с Apache в каждом контейнере LXC.

    
    root@ubuntu:~# curl localhost
    Apache in LXC http1
    
    root@ubuntu:~# curl localhost
    Apache in LXC http2
    root@ubuntu:~#
     	   
  11. Наконец, мы можем остановить один из конейнеров и убедиться что HAProxy удалит его из карусели:

    
    root@ubuntu:~# virsh destroy http2
    Domain http2 destroyed
    root@ubuntu:~# virsh list --all
    Id    Name                           State
    ----------------------------------------------------
    15720 http1                          running
    -     http2                          shut off
    root@ubuntu:~# curl localhost
    Apache in LXC http1
    root@ubuntu:~# curl localhost
    Apache in LXC http1
    root@ubuntu:~#
     	   

Apache может быть не лучшим приложением для исполнения во множестве контейнеров в одном и том же хосте. Тем не менее, это помогло нам продемонстрировать как просто масштабировать приложения, исполняющиеся в минимальных контейнерах с применением LXC libvirt, полагаясь на HAProxy, или как построить некую среду со множеством арендаторов. Дополнительным преимуществом при совместном использовании двоичных кодов всеми контейнерами состоит в том, что их обновление не требует изменений для каждого экземпляра LXC, вместо этого вы выполняете изменения в ОС хоста, которые будут видны из всех контейнеров данного сервера. Предыдущая установка может выглядеть простой, однако она предоставляет некий мощный способ масштабирования служб в LXC контейнерах с малым весом, которые не требуют большого дискового пространства.

Масштабирование Apache при помощи полной файловой системы корня LXC и туннелей OVS GRE

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

 

Рисунок 1



Мы собираемся использовать три сервера - lxc-lb, lxc-node-01 и lxc-node-02. Хост lxc-lb будет размещать некий контейнер исполняющий HAProxy, а далее HAProxy на самом таком сервере. Серверы lxc-node-01 и lxc-node-02 будут иметь контейнеры, исполняющие Apache. Все экземпляры LXC будут взаимодействовать в некоторой выделенной частной сетевой среде через сетку туннелей GRE, подключённых к OVS. Данная сетка GRE OVS создаст сетевую изоляцию между всеми контейнерами и самим хостом, а также потенциально и прочими контейнерами и их сетевыми средами. Все контейнеры получат свои собственные сетевые настройки от dnsmasq, исполняемого в хосте lxc-lb.

Для данного развёртывания мы применяем три экземпляра EC2 из ASW, работающих под управлением самого последнего выпуска Ubuntu Xenial.

Настройка хоста балансировки нагрузки

Чтобы настроить необходимый хост балансировки нагрузки выполните следующие шаги:

  1. Давайте запустим сам сервер lxc-lb. Проверим что версии LXC доступны и установим самую последнюю:

    
    root@lxc-lb:~# apt-get update && apt-get upgrade --yes && reboot
    root@lxc-lb:~# apt-cache policy lxc
    lxc:
      Installed: (none)
      Candidate: 2.0.5-0ubuntu1~ubuntu16.04.2
      Version table:
          2.0.5-0ubuntu1~ubuntu16.04.2 500
              500 http://us-east-1.ec2.archive.ubuntu.com/ubuntu
              xenial-updates/main amd64 Packages
          2.0.0-0ubuntu2 500
               500 http://us-east-1.ec2.archive.ubuntu.com/ubuntu
               xenial/main amd64 Packages
    root@lxc-lb:~#
    
    root@lxc-lb:~# apt-get install --yes lxc
    ...
    
    root@lxc-lb:~# dpkg --list | grep lxc
    ii          liblxc1      2.0.5-0ubuntu1~ubuntu16.04.2
    amd64       Linux Containers userspace tools (library)
    ii          lxc          2.0.5-0ubuntu1~ubuntu16.04.2
    all         Transitional package for lxc1
    ii          lxc-common   2.0.5-0ubuntu1~ubuntu16.04.2
    amd64       Linux Containers userspace tools (common tools)
    ii          lxc-templates  2.0.5-0ubuntu1~ubuntu16.04.2
    amd64       Linux Containers userspace tools (templates)
    ii          lxc1         2.0.5-0ubuntu1~ubuntu16.04.2
    amd64       Linux Containers userspace tools
    ii          lxcfs        2.0.4-0ubuntu1~ubuntu16.04.1
    amd64       FUSE based filesystem for LXC
    ii          python3-lxc  2.0.5-0ubuntu1~ubuntu16.04.2
    
    amd64
    Linux       Containers userspace tools (Python 3.x bindings)
    root@lxc-lb:~#
    
    root@lxc-lb:~# lxc-create --version
    2.0.5
    root@lxc-lb:~# 	   
  2. После установки необходимых пакетов и шаблонов LXC мы завершаем также с имеющимся мостом Linux, причём мы не собирваемся его применять:

    
    root@lxc-lb:~# brctl show
    bridge name       bridge id STP       enabled interfaces
    lxcbr0            8000.000000000000   no
    root@lxc-lb:~#
     	   
  3. Далее установите OVS и создайте неций новый мост с именем lxcovsbr0:

    
    root@lxc-lb:~# apt-get install --yes openvswitch-switch
    ...
    root@lxc-lb:~# ovs-vsctl add-br lxcovsbr0
    root@lxc-lb:~# ovs-vsctl show
    482cf359-a59e-4482-8a71-02b0884d016d
        Bridge "lxcovsbr0"
            Port "lxcovsbr0"
                Interface "lxcovsbr0"
                    type: internal
        ovs_version: "2.5.0"
    root@lxc-lb:~#
     	   
  4. Сетевая среда LXC по умолчанию использует свою подсеть 10.0.3.0/24; мы собираемся заменить её на 192.168.0.0/24. Это поможет нам в тех случаях, когда мы уже имеем некую существующую сетевую среду LXC и мы желаемя запустить некую новую, а также изолировать определённые наборы контейнеров и к тому же поможет нам проедмонстрировать данную концепцию:

    
    root@lxc-lb:~# cat /etc/default/lxc-net | grep -vi &qout;#&qout;
    USE_LXC_BRIDGE=&qout;true&qout;
    LXC_BRIDGE=&qout;lxcbr0&qout;
    LXC_ADDR=&qout;10.0.3.1&qout;
    LXC_NETMASK=&qout;255.255.255.0&qout;
    LXC_NETWORK=&qout;10.0.3.0/24&qout;
    LXC_DHCP_RANGE=&qout;10.0.3.2,10.0.3.254&qout;
    LXC_DHCP_MAX=&qout;253&qout;
    
    root@lxc-lb:~# cat /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
    root@lxc-lb:~#
     	   
  5. Заменим имя имеющегося моста Linux на соотвестующее имя только что созданного нами моста OVS и заменим эту сетевую среду:

    
    root@lxc-lb:~# sed -i 's/lxcbr0/lxcovsbr0/g' /etc/default/lxc-net
    root@lxc-lb:~# sed -i 's/10.0.3/192.168.0/g' /etc/default/lxc-net
    root@lxc-lb:~# sed -i 's/lxcbr0/lxcovsbr0/g' /etc/lxc/default.conf
     	   
  6. Служба dnsmasq настроена для сети 10.0.3.0/24, однако после любого перезапуска она должна ожидать определённую ранее нами новую подсеть. Давайте перезанрузим данный сервер чтобы убедиться что эти изменения вступили в силу:

    
    root@lxc-lb:~# pgrep -lfa dnsmasq
    10654 dnsmasq -u lxc-dnsmasq --strict-order --bind-interfaces
    --pid-file=/run/lxc/dnsmasq.pid --listen-address 10.0.3.1
    --dhcp-range 10.0.3.2,10.0.3.254 --dhcp-lease-max=253
    --dhcp-no-override --except-interface=lo --interface=lxcbr0
    --dhcp-leasefile=/var/lib/misc/dnsmasq.lxcbr0.leases
    --dhcp-authoritative
    root@lxc-lb:~#
    root@lxc-lb:~# reboot
     	   
  7. Как и ожидалось, dnsmasq теперь будет предлагать IP для подсети 192.168.0.0/24:

    
    root@lxc-lb:~# pgrep -lfa dnsmasq
    1354 dnsmasq -u lxc-dnsmasq --strict-order --bind-interfaces
    --pid-file=/run/lxc/dnsmasq.pid --listen-address 192.168.0.1
    --dhcp-range 192.168.0.2,192.168.0.254 --dhcp-lease-max=253
    --dhcp-no-override --except-interface=lo --interface=lxcovsbr0
    --dhcp-leasefile=/var/lib/misc/dnsmasq.lxcovsbr0.leases
    --dhcp-authoritative
    root@lxc-lb:~#
     	   
  8. Проверим имеющийся мост OVS; он должен быть поднятым и настроенным на некий IP:

    
    root@lxc-lb:~# ip a s lxcovsbr0
    4: lxcovsbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc
    noqueue state UNKNOWN group default qlen 1
        link/ether ee:b0:a2:42:22:4e brd ff:ff:ff:ff:ff:ff
        inet 192.168.0.1/24 scope global lxcovsbr0
           valid_lft forever preferred_lft forever
        inet6 fe80::ecb0:a2ff:fe42:224e/64 scope link
           valid_lft forever preferred_lft forever
    root@lxc-lb:~#
     	   

Создание контейнера балансировки нагрузки

Для создания требуемого контейнера балансировки нагрузки следуйте данным шагам:

  1. Мы намереваемся применять имеющиеся шаблоны Ubuntu для создания необходимой корневой файловой системы требующегося нам контейнера HAProxy:

    
    root@lxc-lb:~# lxc-create --name haproxy --template ubuntu
    root@lxc-lb:~# lxc-start --name haproxy
    root@lxc-lb:~#
     	   
  2. Наш мост OVS теперь должен иметь все интерфейсы контейнеров добавленными в качестве порта - для данного примера vethUY97FY:

    
    root@lxc-lb:~# ovs-vsctl show
    482cf359-a59e-4482-8a71-02b0884d016d
        Bridge &qout;lxcovsbr0&qout;
            Port &qout;lxcovsbr0&qout;
                Interface &qout;lxcovsbr0&qout;
                    type: internal
            Port &qout;vethUY97FY&qout;
                Interface &qout;vethUY97FY&qout;
        ovs_version: &qout;2.5.0&qout;
    root@lxc-lb:~#
    
    root@lxc-lb:~# ip a s vethUY97FY
    6: vethUY97FY@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc
    noqueue master ovs-system state UP group default qlen 1000
        link/ether fe:d1:f3:ca:9e:83 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet6 fe80::fcd1:f3ff:feca:9e83/64 scope link
            valid_lft forever preferred_lft forever
    root@lxc-lb:~#
     	   
  3. Подключитесь к только что созданному новоме контейнеру и убедитесь что он получил некий IP адрес от работающего на том же самом хосте DHCP:

    
    root@lxc-lb:~# lxc-attach --name haproxy
    root@haproxy:~# ifconfig
    eth0      Link encap:Ethernet HWaddr 00:16:3e:76:92:0a
              inet addr:192.168.0.26 Bcast:192.168.0.255
              Mask:255.255.255.0
              inet6 addr: fe80::216:3eff:fe76:920a/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
              RX packets:16 errors:0 dropped:0 overruns:0 frame:0
              TX packets:12 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000
              RX bytes:1905 (1.9 KB) TX bytes:1716 (1.7 KB)
    lo        Link encap:Local Loopback
              inet addr:127.0.0.1 Mask:255.0.0.0
              inet6 addr: ::1/128 Scope:Host
              UP LOOPBACK RUNNING MTU:65536 Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1
              RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
    root@haproxy:~# route -n
    Kernel IP routing table
    Destination   Gateway       Genmask       Flags Metric Ref  Use Iface
    0.0.0.0       192.168.0.1   0.0.0.0       UG    0      0    0   eth0
    192.168.0.0   0.0.0.0       255.255.255.0 U     0      0    0   eth0
    root@haproxy:~#
     	   
  4. Данный контейнер должен иметь связь с имеющимся хостом и всемирным Интернентом. Давайте проверим это прежде чем двинемся далее:

    
    root@haproxy:~# ping -c 3 192.168.0.1
    PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
    64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=0.218 ms
    64 bytes from 192.168.0.1: icmp_seq=2 ttl=64 time=0.045 ms
    64 bytes from 192.168.0.1: icmp_seq=3 ttl=64 time=0.046 ms
    --- 192.168.0.1 ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 2000ms
    rtt min/avg/max/mdev = 0.045/0.103/0.218/0.081 ms
    root@haproxy:~# ping google.com -c 3
    PING google.com (216.58.217.110) 56(84) bytes of data.
    64 bytes from iad23s42-in-f14.1e100.net (216.58.217.110): icmp_seq=1 ttl=48 time=2.55 ms
    64 bytes from iad23s42-in-f14.1e100.net (216.58.217.110): icmp_seq=2 ttl=48 time=2.11 ms
    64 bytes from iad23s42-in-f14.1e100.net (216.58.217.110): icmp_seq=3 ttl=48 time=2.39 ms
    --- google.com ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 2002ms
    rtt min/avg/max/mdev = 2.113/2.354/2.555/0.191 ms
    root@haproxy:~# exit
    exit
    root@lxc-lb:~#
     	   

Если связь не работает, проверьте что ваш сервер dnsmasq назначает IP адреса как положено и что данный контейнер подключен к своему мосту OVS, а также что имеющийся интерфейс моста поднять с неким IP адресом у него самого.

Построение туннеля GRE

GRE (Generic Routing Encapsulation, Общая инкапсуляция маршрутов) является протоколом туннелирования, который делает возможным построение сетей точка-точка поверх IP (Internet Protocol). Мы можем применить его для построения некоторой сетки между имеющимися коммутаторами OVS на своих трёх хостах, таким образом соединив все контейнеры LXC в некую изолированную сетевую среду. Каждый сервер (или экземпляр EC2 в нашем примере) будет соединён друг с другом. Имеющиеся OVS предоставляют некий удобный метод для установления таких туннелей GRE.

Всё ещё находясь на своём хосте балансировки нагрузки, создайте два туннеля GRE к другим двум серверам, в случае необходимости заменив указанные IP:


root@lxc-lb:~# ovs-vsctl add-port lxcovsbr0 gre0 -- set interface gre0 type=gre options:remote_ip=10.1.34.124
root@lxc-lb:~# ovs-vsctl add-port lxcovsbr0 gre1 -- set interface gre1 type=gre options:remote_ip=10.1.34.57
root@lxc-lb:~#
 	   
[Замечание]Замечание

Отметим, что предыдущие адреса являются адресами реалных серверов, в не рассматриваемых контейнеров.

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


root@lxc-lb:~# ovs-vsctl show
482cf359-a59e-4482-8a71-02b0884d016d
    Bridge &qout;lxcovsbr0&qout;
        Port &qout;gre1&qout;
            Interface &qout;gre1&qout;
                type: gre
                options: {remote_ip=&qout;10.1.34.57&qout;}
        Port &qout;vethRIC2BJ&qout;
            Interface &qout;vethRIC2BJ&qout;
        Port &qout;lxcovsbr0&qout;
            Interface &qout;lxcovsbr0&qout;
                type: internal
        Port &qout;gre0&qout;
            Interface &qout;gre0&qout;
                type: gre
                options: {remote_ip=&qout;10.1.34.124&qout;}
    ovs_version: &qout;2.5.0&qout;
root@lxc-lb:~#
 	   

Так как мы создали некую сетку между OVS, могут возникнуть некие зацикливания пакетов. Для предотвращения топологических циклов нам необходимо включить в OVS STP (Spanning Tree Protocol, Протокол остовного дерева). STP является протоколом второго уровня, который предотвращает сетевые циклы при создании избыточных и взаимосвязанных соединений между коммутаторами. Чтобы включить его в имеющемся коммутаторе OVS выполните следующую команду:


root@lxc-lb:~# ovs-vsctl set bridge lxcovsbr0 stp_enable=true
root@lxc-lb:~#
 	   

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

Настройка узлов Apache

Для настройки своих узлов Apache выполните эти шаги:

  1. На первом узле Apache установите LXC и OVS и создаёте требующийся мост:

    
    root@lxc-node-01:~# apt-get update && apt-get --yes upgrade && reboot
    root@lxc-node-01:~# apt-get install --yes lxc
    root@lxc-node-01:~# apt-get install --yes openvswitch-switch
    root@lxc-node-01:~# ovs-vsctl add-br lxcovsbr0
    root@lxc-node-01:~# ifconfig lxcovsbr0 up
     	   
  2. Поменяйте имеющееся имя этого моста и измените его подсеть:

    
    root@lxc-node-01:~# sed -i 's/lxcbr0/lxcovsbr0/g'
    /etc/lxc/default.conf
    root@lxc-node-01:~# sed -i 's/lxcbr0/lxcovsbr0/g'
    /etc/default/lxc-net
    root@lxc-node-01:~# sed -i 's/10.0.3/192.168.0/g' 
    /etc/default/lxc-net
     	   
  3. Создайте необходимые туннели GRE к остальным двум серверам, заменив при необходимости их IP:

    
    root@lxc-node-01:~# ovs-vsctl add-port lxcovsbr0 gre0 -- set
    interface gre0 type=gre options:remote_ip=10.1.34.23
    root@lxc-node-01:~# ovs-vsctl add-port lxcovsbr0 gre1 -- set
    interface gre1 type=gre options:remote_ip=10.1.34.57
    root@lxc-node-01:~# ovs-vsctl show
    625928b0-b57a-46b2-82fe-77d541473f29
        Bridge &qout;lxcovsbr0&qout;
            Port &qout;gre0&qout;
                Interface &qout;gre0&qout;
                    type: gre
                    options: {remote_ip=&qout;10.1.34.23&qout;}
            Port &qout;gre1&qout;
                Interface &qout;gre1&qout;
                    type: gre
                    options: {remote_ip=&qout;10.1.34.57&qout;}
            Port &qout;lxcovsbr0&qout;
                Interface &qout;lxcovsbr0&qout;
                    type: internal
        ovs_version: &qout;2.5.0&qout;
    root@lxc-node-01:~#
     	   
  4. Включите на самом мосту STP:

    
    root@lxc-node-01:~# ovs-vsctl set bridge lxcovsbr0 stp_enable=true
    root@lxc-node-01:~#
     	   
  5. Code

    
    New-VM -Name VM01 -Generation 2
     	   
  6. Затем давайте создадим некий контейнер с именем apache:

    
    root@lxc-node-01:~# lxc-create --name apache --template ubuntu
    root@lxc-node-01:~# lxc-start --name apache
     	   
  7. Самое время для создания окончательного узла аналогичным образом:

    
    root@lxc-node-02:~# apt-get update && apt-get upgrade --yes && reboot
    root@lxc-node-02:~# apt-get install --yes lxc
    root@lxc-node-02:~# apt-get install --yes openvswitch-switch
    root@lxc-node-02:~# ovs-vsctl add-br lxcovsbr0
    root@lxc-node-02:~# ifconfig lxcovsbr0 up
    root@lxc-node-02:~# sed -i 's/lxcbr0/lxcovsbr0/g'
    /etc/lxc/default.conf
    root@lxc-node-02:~# sed -i 's/lxcbr0/lxcovsbr0/g'
    /etc/default/lxc-net
    root@lxc-node-02:~# sed -i 's/10.0.3/192.168.0/g'
    /etc/default/lxc-net
     	   
  8. Создадим туннели GRE:

    
    root@lxc-node-02:~# ovs-vsctl add-port lxcovsbr0 gre0 -- set
    interface gre0 type=gre options:remote_ip=10.1.34.23
    root@lxc-node-02:~# ovs-vsctl add-port lxcovsbr0 gre1 -- set
    interface gre1 type=gre options:remote_ip=10.1.34.124
    root@lxc-node-02:~# ovs-vsctl show
    7b8574ce-ed52-443e-bcf2-6b1ddbedde4c
        Bridge &qout;lxcovsbr0&qout;
            Port &qout;gre0&qout;
                Interface &qout;gre0&qout;
                    type: gre
                    options: {remote_ip=&qout;10.1.34.23&qout;}
            Port &qout;gre1&qout;
                Interface &qout;gre1&qout;
                    type: gre
                    options: {remote_ip=&qout;10.1.34.124&qout;}
            Port &qout;lxcovsbr0&qout;
                Interface &qout;lxcovsbr0&qout;
                    type: internal
        ovs_version: &qout;2.5.0&qout;
    root@lxc-node-02:~#
     	   
  9. Также включим на этом коммутаторе GRE:

    
    root@lxc-node-02:~# ovs-vsctl set bridge lxcovsbr0 stp_enable=true
    root@lxc-node-02:~#
     	   
  10. Наконец, создадим и запустим свой контейнер Apache:

    
    root@lxc-node-02:~# lxc-create --name apache --template ubuntu
    root@lxc-node-02:~# lxc-start --name apache
     	   

Установка Apache и HAProxy с тестированием связи

Имея настроенными все серверы, запущенными все контейнеры и установленными все туннели GRE давайте проверим имеющиеся связи между всеми экземплярами LXC. Так как все контейнеры являются частью одной и той же сетевой среды, причём соединёнными друг с другом посредством коммутаторов OVS при помощи туннелей GRE, они должны иметь возможность для взаимодейсвия друг с другом. Что ещё более важно, все контейнеры Apache будут получать свои сетевые настройки через DHCP от исполняемой на нашем сервере lxc-lb службы dnsmasq.

Чтобы проверить, что все контейнеры сданы внаём, мы можем проверить имеющийся файл владения dnsmasq выполнив следующую команду:


root@lxc-lb:~# cat /var/lib/misc/dnsmasq.lxcovsbr0.leases
1478111141 00:16:3e:84:cc:f3 192.168.0.41 apache *
1478111044 00:16:3e:74:b3:8c 192.168.0.165 * *
1478110360 00:16:3e:76:92:0a 192.168.0.26 haproxy *
root@lxc-lb:~#
 	   
  1. Выдача в аренду может занять несколько секунд, вам может понадобиться проверить этот файл нескоько раз прежде чем какие- то IP запушутся. Когда все контейнеры получат IP, нам следует иметь возможность просмотреть их при перечислении всех контейнеров во всех серверах:

    
    root@lxc-lb:~# lxc-ls -f
    NAME    STATE   AUTOSTART GROUPS IPV4         IPV6
    haproxy RUNNING 0         -      192.168.0.26 -
    root@lxc-lb:~#
    root@lxc-node-01:~# lxc-ls -f
    NAME    STATE   AUTOSTART GROUPS IPV4          IPV6
    apache  RUNNING 0         -      192.168.0.165 -
    root@lxc-node-01:~#
    root@lxc-node-02:~# lxc-ls -f
    NAME    STATE   AUTOSTART GROUPS IPV4          IPV6
    apache  RUNNING 0         -      192.168.0.41  -
    root@lxc-node-02:~#
     	   
  2. Далее давайте установим HAProxy в имеющемся контейнере haproxy в узле сервера lxc-lb и проверим связи между контейнерами:

    
    root@lxc-lb:~# lxc-attach --name haproxy
    root@haproxy:~# ping -c3 192.168.0.165
    PING 192.168.0.165 (192.168.0.165) 56(84) bytes of data.
    64 bytes from 192.168.0.165: icmp_seq=1 ttl=64 time=0.840 ms
    64 bytes from 192.168.0.165: icmp_seq=2 ttl=64 time=0.524 ms
    64 bytes from 192.168.0.165: icmp_seq=3 ttl=64 time=0.446 ms
    --- 192.168.0.165 ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 1999ms
    rtt min/avg/max/mdev = 0.446/0.603/0.840/0.171 ms
    root@haproxy:~# ping -c3 192.168.0.41
    PING 192.168.0.41 (192.168.0.41) 56(84) bytes of data.
    64 bytes from 192.168.0.41: icmp_seq=1 ttl=64 time=1.26 ms
    64 bytes from 192.168.0.41: icmp_seq=2 ttl=64 time=0.939 ms
    64 bytes from 192.168.0.41: icmp_seq=3 ttl=64 time=1.05 ms
    --- 192.168.0.41 ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 2001ms
    rtt min/avg/max/mdev = 0.939/1.086/1.269/0.142 ms
    root@haproxy:~#
    root@haproxy:~# apt-get update && apt-get install haproxy
    ...
     	   
    [Замечание]Замечание

    Если вы строите данный пример развёртывания в некотором облаке поставщика и подвис apt-get update, попытайтесь уменьшить установку MTU своего интерфейса eth0 внутри всех контейнеров LXC следующим образом: ifconfig eth0 mtu 1400.

  3. Давайте просмотрим имеющийся файл настроек haproxy.cfg:

    
    root@haproxy:~# cat /etc/haproxy/haproxy.cfg
    global
          log /dev/log local0
          log /dev/log local1 notice
          chroot /var/lib/haproxy
          stats socket /run/haproxy/admin.sock mode 660 level admin
          stats timeout 30s
          user haproxy
          group haproxy
          daemon
    
          ca-base /etc/ssl/certs
    
          crt-base /etc/ssl/private
          ssl-default-bind-ciphers
          ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+
          AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:
          RSA+3DES:!aNULL:!MD5:!DSS
          ssl-default-bind-options no-sslv3
    defaults
          log global
          mode http
          option httplog
          option dontlognull
          timeout connect 5000
          timeout client 50000
          timeout server 50000
    frontend http
          bind :80
          reqadd X-Forwarded-Proto:\ http
          default_backend http_nodes
          backend http_nodes
          mode http
          balance roundrobin
          option httpclose
          option forwardfor
          option redispatch
          option httpchk GET /
          cookie JSESSIONID prefix
          server http1 192.168.0.165:80 check inter 5000
          server http1 192.168.0.41:80 check inter 5000
    root@haproxy:~#
     	   

    Все настройки HAProxy почти идентичны тем, которые мы применяли ранее в данной главе.

    [Замечание]Замечание

    Отметим, что все IP адреса в соответствующих разделах backend в файле настроек данного HAProxy те же самые, что и у соответствующих контейнеров, исполняющихся в наших серверах lxcnode-01/02.

  4. Перезапустите HAProxy и убедитесь что он работает:

    
    root@haproxy:~# service haproxy restart
    root@haproxy:~# ps axfww
      PID TTY      STAT   TIME COMMAND
        1 ?        Ss     0:00 /sbin/init
       38 ?        Ss     0:00 /lib/systemd/systemd-journald
       59 ?        Ss     0:00 /usr/sbin/cron -f
       62 ?        Ssl    0:00 /usr/sbin/rsyslogd -n
      143 ?        Ss     0:00 /sbin/dhclient -1 -v -pf
      /run/dhclient.eth0.pid
      -lf /var/lib/dhcp/dhclient.eth0.leases -I -df
      /var/lib/dhcp/dhclient6.eth0.leases eth0
      167 ?        Ss     0:00 /usr/sbin/sshd -D
      168 pts/2    Ss+    0:00 /sbin/agetty --noclear
      --keep-baud pts/2 115200 38400 9600 vt220
      169 lxc/console Ss+ 0:00 /sbin/agetty --noclear
      --keep-baud console 115200 38400 9600 vt220
      412 ?        Ss     0:00 /usr/sbin/haproxy-systemd-wrapper -f
      /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
      413 ?        S      0:00  \_ /usr/sbin/haproxy -f
      /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
      414 ?        Ss     0:00       \_ /usr/sbin/haproxy -f
      /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
    root@haproxy:~#
     	   

    Отметите внимание, что в предыдыдущем выводе, в отличии от приведённого ранее в данной главе примера с libvirt LXC, тепрерь основным родительским процессом у контейнера является сам процесс init.

  5. Установите в двух других контейнерах Apache и создайте для каждого по некоторой странице index.html:

    
    root@lxc-node-01:~# lxc-attach --name apache
    root@apache:~# apt-get install --yes apache2
    root@apache:~# echo &qout;Apache on LXC container on host lxc-node-01&qout; > /var/www/html/index.html
    root@lxc-node-02:~# lxc-attach --name apache
    root@apache:~# apt-get install --yes apache2
    root@apache:~# echo &qout;Apache on LXC container on host lxc-node-02&qout; > /var/www/html/index.html
     	   
  6. Внутри самого контейнера haproxy подключитесь к порту 80, по которому проводит прослушивание HAProxy и имеющийся балансировщик нагрузки должет перенаправлять все запросы на наши контейнеры Apache:

    
    root@haproxy:~# apt-get install --yes curl
    root@haproxy:~# curl localhost
    Apache on LXC container on host lxc-node-01
    root@haproxy:~# curl localhost
    Apache on LXC container on host lxc-node-02
    root@haproxy:~# exit
    exit
    root@lxc-lb:~#
     	   
  7. Мы также должны иметь возможность соединиться с HAProxy из самого хоста lxc-lb, так как ОС этого хоста может общаться со всеми имеющимися контейнерами через свой коммутатор OVS:

    
    root@lxc-lb:~# apt-get install curl
    root@lxc-lb:~# curl 192.168.0.26
    Apache on LXC container on host lxc-node-01
    root@lxc-lb:~# curl 192.168.0.26
    Apache on LXC container on host lxc-node-02
    root@lxc-lb:~#
     	   

    Предыдущий адрес 192.168.0.26 является адресом IP самого контейнера haproxy; замените его на какой- то назначенный dnsmasq в вашей системе.

  8. Наконец мы можем установит HAProxy на самом сервере lxc-lb , что позволит нам соединяться со всеми серверами Apache из внешнего мира если, например, данный хост lxc-lb имеет некий общедоступный IP адрес. В данном случае мы не должны совсем иметь исполняемым HAProxy в определённом контейнере, хотя мы и можем повторно воспользоваться теми же самыми настройками:

    
    root@lxc-lb:~# apt-get install haproxy
    ...
    root@lxc-lb:~#
     	   

    Скопируйте все настройки из имеющегося контейнера (перечисленные ранее в данном разделе) и перезапустите сервер HAProxy:

    
    root@lxc-lb:~# service haproxy restart
    root@lxc-lb:~# curl 10.1.34.23
    Apache on LXC container on host lxc-node-01
    root@lxc-lb:~# curl 10.1.34.23
    Apache on LXC container on host lxc-node-02
    root@lxc-lb:~#
     	   
[Замечание]Замечание

Отметим, что IP адрес 10.1.34.23 является адресом сервера lxc-lb из нашего примера. Если ваш сервер имеет более одного IP адреса или некий общедоступный адрес, вы можете применять их, так как мы настроили HAProxy на привязку ко всем интерфейсам.

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

Масштабирование службы Apache

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

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

  1. Для начала остановите один из экземпляров Apache:

    
    root@lxc-node-01:~# lxc-ls -f
    NAME   STATE   AUTOSTART GROUPS IPV4 IPV6
    apache RUNNING 0         -      -    -
    root@lxc-node-01:~# lxc-stop --name apache
     	   
  2. Затем скопируйте его корневую файловую систему и имеющийся файл настроек LXC:

    
    root@lxc-node-01:~# cd /var/lib/lxc
    root@lxc-node-01:/var/lib/lxc# ls -alh
    total 12K
    drwx------  3 root root 4.0K Nov 2 16:53 .
    drwxr-xr-x 43 root root 4.0K Nov 2 15:02 ..
    drwxrwx---  3 root root 4.0K Nov 2 16:53 apache
    root@lxc-node-01:/var/lib/lxc# cp -rp apache/ apache_new
    root@lxc-node-01:/var/lib/lxc#
     	   
  3. Измените соответствующее имя имя на имя нового контейнера и удалите все имеющиеся MAC адреса из данного файла настроек. LXC динамически назначит некие новые при запуске данного контейнера:

    
    root@lxc-node-01:/var/lib/lxc# sed -i 's/apache/apache_new/g'
    apache_new/config
    root@lxc-node-01:/var/lib/lxc# sed -i '/lxc.network.hwaddr/d'
    apache_new/config
    root@lxc-node-01:/var/lib/lxc#
     	   
  4. Теперь у нас имеются два контейнера на этом хосте:

    
    root@lxc-node-01:/var/lib/lxc# lxc-ls -f
    NAME       STATE   AUTOSTART GROUPS IPV4 IPV6
    apache     STOPPED 0         -      -    -
    apache_new STOPPED 0         -      -    -
    root@lxc-node-01:/var/lib/lxc#
     	   
  5. Давайте запустим их оба:

    
    root@lxc-node-01:/var/lib/lxc# lxc-start --name apache
    root@lxc-node-01:/var/lib/lxc# lxc-start --name apache_new
    root@lxc-node-01:/var/lib/lxc# lxc-ls -f
    NAME       STATE   AUTOSTART GROUPS IPV4 IPV6
    apache     RUNNING 0         -      -    -
    apache_new RUNNING 0         -      -    -
    root@lxc-node-01:/var/lib/lxc#
     	   
  6. Так как мы скопировали корневую фаловую систему целиком из своего первоначального контейнера, имеющаяся служба Apache уже установлена в этом новом экземпляре. Теперь запустите её и убедитесь что она исполняется:

    
    root@lxc-node-01:/var/lib/lxc# lxc-attach --name apache_new
    root@apache:/# /etc/init.d/apache2 start
    [ ok ] Starting apache2 (via systemctl): apache2.service.
    root@apache:/# ps ax
      PID TTY      STAT   TIME COMMAND
        1 ?        Ss     0:00 /sbin/init
       37 ?        Ss     0:00 /lib/systemd/systemd-journald
       50 ?        Ss     0:00 /sbin/ifup -a --read-environment
       60 ?        Ss     0:00 /usr/sbin/cron -f
       62 ?        Ssl    0:00 /usr/sbin/rsyslogd -n
       97 ?        S      0:00 /bin/sh -c /sbin/dhclient -1 -v -pf
       /run/dhclient.eth0.pid -lf
       /var/lib/dhcp/dhclient.eth0.leases -I
       -df /var/lib/dhcp/dhclient6.eth0.leases eth0 ?
       98 ?        S      0:00 /sbin/dhclient -1 -v -pf
       /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases
       -I -df /var/lib/dhcp/dhclient6.eth0.leases eth0
       125 pts/3   Ss     0:00 /bin/bash
       168 ?       Ss     0:00 /usr/sbin/apache2 -k start
       171 ?       Sl     0:00 /usr/sbin/apache2 -k start
       172 ?       Sl     0:00 /usr/sbin/apache2 -k start
       246 pts/3   R+     0:00 ps ax
    root@apache:/#
     	   

Аналогичный данному процесс может быть полностью автоматизирован при помощи такой службы как Jenkins и распространён по различным серверам для достижения некоторого уровня автоматического масштабирования. Мы изучим такую настройку далее более подробно в Главе 7, Мониторинг и резервное копирование в мире с контейнерами.

Выводы

Применение LXC для масштабирования различных рабочих нагрузок требует служб посредников, таких как HAProxy или Nginx и исполнения всех реальных служб в контейнера. Сетевая связность и сегментация может достигаться с применением программно определяемых сетевых сред с использованием OVS и туннелей GRE.

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

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