Обычно, когда встает задача поднять среднестатистический веб-сервер, администратор выбирает одну достаточно производительную виртуальную или физическую машину, которая способна справиться с ожидаемой нагрузкой, и поднимает на ней стек LAMP, включающий в себя Apache, PHP, MySQL, а также, возможно, memcached, nginx и реверс-прокси. Однако это далеко не самый эффективный и безопасный сценарий, лучшим решением будет разнести все компоненты стека по разным виртуальным машинам.

 

Введение

Стандартная инсталляция (L)AMP — это Linux/BSD-машина, на которой запущены все компоненты веб-сервера. Такую связку очень легко настроить, однако она имеет целый ряд недостатков. Во-первых, она небезопасна: взломав, например, веб-сервер, злоумышленник сможет получить доступ к файлам веб-сервера, базам MySQL, а если веб-сервер работает на физической машине без всякой изоляции, то и к самой машине.

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

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

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

 

Виртуальный LAMP

Идея виртуализации LAMP состоит в том, чтобы разнести Apache, MySQL, memcached в отдельные виртуальные серверы и добавить к ним еще один виртуальный сервер с реверс-прокси в лице nginx. За счет разделения сервисов такая конфигурация будет на порядок безопаснее, масштабируемее и удобнее в управлении.

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

Далее я пошагово опишу, как создать такую схему с использованием стандартных средств управления виртуализацией в Linux на базе libvirt и QEMU/KVM. Система управления ВМ libvirt используется во многих высокоуровневых облачных платформах, поэтому в будущем читатель сам сможет выбрать более подходящий для него инструмент управления и мониторинга.

 

Шаг 1. Установка libvirt и создание шаблонов ВМ

Итак, у нас есть машина под управлением Debian/Ubuntu Linux (на самом деле подойдет и Fedora, но придется учесть нюансы в различиях системы инициализации, конфигурирования и установки пакетов). Мы должны создать на ней инфраструктуру, с помощью которой сможем быстро и легко подготовить ферму виртуальных серверов требуемой конфигурации. Для этого необходимо сделать три вещи: создать виртуальный сетевой мост, который будет использоваться для коммуникации ВМ друг с другом и доступа реверс-прокси во внешний мир, установить и настроить libvirt и подготовить набор шаблонов ВМ.

Настройка сетевого моста в Ubuntu происходит следующим образом:

  1. Устанавливаем инструменты управления мостом:
    $ sudo apt-get install bridge-utils
    
  2. Останавливаем основной сетевой интерфейс и редактируем настройки сети:
    $ sudo ifdown eth0
    
    $ sudo vi /etc/network/interfaces
    auto lo
    iface lo inet loopback
    
    auto br0
    iface br0 inet static
        address 192.168.0.10
        network 192.168.0.0
        netmask 255.255.255.0
        broadcast 192.168.0.255
        gateway 192.168.0.1
        bridge_ports eth0
        bridge_fd 9
        bridge_hello 2
        bridge_maxage 12
        bridge_stp off
    

    В качестве IP-адреса и маски подсети здесь следует использовать физические адреса. Предполагается, что сервер находится в локальной сети, а доступ во внешний мир организован с помощью шлюза.

  3. Поднимаем наш бридж:
    $ sudo ifup br0
    
  4. Проверяем его работоспособность:
    $ sudo brctl show
    br0             8000.000e0cb30550       yes             eth0
    
Настраиваем виртуальный сетевой мост
Настраиваем виртуальный сетевой мост

Теперь следует установить libvirt:

$ sudo apt-get install kvm libvirt-bin virtinst

Проверяем работоспособность libvirt:

$ sudo virsh --connect qemu:///system version
Скомпилировано на базе библиотеки: libvirt 1.1.0
Используется библиотека: libvirt 1.1.0
...

Ну и последнее. Создаем виртуальную машину. Пока нам нужна только одна, она будет выполнять роль шаблона, который мы будем клонировать для создания новых ВМ:

$ sudo virt-install --connect qemu:///system \
    --name temp \
    --ram 1024 \
    --vnc \
    --os-type linux --os-variant virtio26 \
    --accelerate \
    --network=bridge:br0 \
    --disk path=/var/lib/libvirt/images/temp.img,size=100 \
    --cdrom /tmp/ubuntu-13.04-server-i386.iso \

Здесь все просто. Новая ВМ будет иметь имя temp, использовать 1 Гб памяти, диск размером 100 Гб (про запас, диск будет динамически расти), подключаться к сети через наш мост и загрузится с образа диска /tmp/ubuntu-13.04-server-i386.iso. Чтобы проверить, что машина действительно запустилась, используем команду virsh:

$ sudo virsh --connect qemu:///system list

Если ВМ под именем temp есть в списке, можно соединиться с ней при помощи virt-viewer. Это графический VNC/SPICE-клиент, поэтому если на сервере нет графического интерфейса, то можно использовать удаленную машину:

$ sudo apt-get install virt-viewer
$ virt-viewer -c qemu+ssh://root@IP-сервера/system temp

Далее следует просто установить дистрибутив, не указывая никаких специфических опций и назначив IP-адрес из подсети, прописанной в настройках моста. После установки завершаем работу машины.

Для мониторинга серверов удобнее всего использовать virt-manager
Для мониторинга серверов удобнее всего использовать virt-manager
 

Шаг 2. Поднимаем MySQL

Теперь следует использовать наш готовый шаблон для создания всех необходимых серверов из связки LAMP. Напомню, что нам нужны будут отдельные серверы для nginx, выступающего в роли реверс-прокси, для Apache/PHP, MySQL и memcached, если таковой необходим. Начнем с настройки Apache/PHP. Чтобы создать новую ВМ на базе уже существующей, используем команду virt-clone:

$ sudo virt-clone -o temp -n mysql -f /var/lib/libvirt/images/mysql.img

Так мы получим новую ВМ, аналогичную уже существующей. Теперь наша задача — запустить эту машину, зайти на нее с помощью все того же virt-viewer, а дальше — установить и запустить на ней связку Apache/PHP. Запускаем и входим:

$ sudo virsh start mysql
$ virt-viewer -c qemu+ssh://root@IP-сервера/system mysql

Вторую команду следует выполнять опять же с удаленной машины, имеющей графический интерфейс. Первое, что делаем, войдя на машину, — это изменяем ее IP-адрес, который достался в наследство от шаблонной ВМ:

$ vi /etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
    address 192.168.0.11
    network 192.168.0.0
    netmask 255.255.255.0
    broadcast 192.168.0.255
    gateway 192.168.0.1

Теперь устанавливаем и настраиваем MySQL:

$ sudo apt-get install mysql mysql-server
$ sudo vi /etc/my.cnf
...
[bind]
bind-address=192.168.0.11
...

Также сразу добавляем в файл необходимые строки конфигурации (обсуждение того, какие именно, выходит за рамки данной статьи) и перезапускаем сервер:

$ sudo service mysqld restart

Теперь можно создать пользователей и базы данных. В качестве завершающего шага сконфигурируем iptables так, чтобы он пропускал только пакеты для MySQL:

$ sudo iptables -A INPUT -j DROP
$ sudo iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT

Чтобы настройки вступили в силу после перезагрузки, эти строки следует добавить в/etc/rc.local (без sudo, естественно).

Перед использованием libvirt следует проверить возможности виртуализации машины с помощью команды virsh capabilities
Перед использованием libvirt следует проверить возможности виртуализации машины с помощью команды virsh capabilities
 

Шаг 3. Поднимаем memcached

Теперь создадим и поднимем сервер memcached. Возможно, он и не понадобится в твоей конкретной конфигурации, но преднастроенный сервер определенно стоит держать про запас. Если в какой-то момент потребуется использование агрессивного кеширования, все необходимое для этого уже будет в твоем арсенале, останется только запустить готовый сервер и подключить кеширование на стороне Apache/PHP.

Как и в случае с сервером MySQL, перво-наперво клонируем заранее подготовленный шаблон, запускаем виртуальный сервер и подключаемся к нему:

$ sudo virt-clone -o temp -n memcached -f /var/lib/libvirt/images/memcached.img
$ sudo virsh start memcached
$ virt-viewer -c qemu+ssh://root@IP-сервера/system memcached

Меняем IP-адрес, как показано выше, на этот раз можно использовать адрес 192.168.0.12 или любой другой на твой вкус. Главное — запомнить, что где находится. Далее устанавливаем сам сервер:

$ sudo apt-get install memcached

Открываем настройки и пишем:

PORT="11211";
USER="memcached";
MAXCONN="1024";
CACHESIZE="512";
OPTIONS="-l 192.168.0.12 -L"

Перезапускаем сервер:

$ sudo service restart memcached

Добавляем правило iptables, закрывающее все порты, кроме порта memcached:

$ sudo iptables -A INPUT -j DROP
$ sudo iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 11211 -j ACCEPT

Добавляем аналогичные строки в /etc/rc.local. Если в данный момент memcached не нужен, можно его остановить:

$ sudo virsh stop memcached

Когда сервер понадобится, достаточно будет отдать команду на запуск, и он появится в виртуальной сети.

 

Шаг 4. Поднимаем Apache/PHP

Предпоследний и самый важный шаг — это установка и настройка Apache/PHP. К сожалению, разнести эти два сервиса по разным серверам мы не можем в силу архитектуры PHP, выполненного в виде Apache-модуля. Поэтому они будут на одной виртуальной машине. Хотя если бы речь шла о Python, мы могли бы легко вынести uwsgi-сервер на отдельную машину.

Вновь клонируем и запускаем сервер:

$ sudo virt-clone -o temp -n apache -f /var/lib/libvirt/images/apache.img
$ sudo virsh start apache
$ virt-viewer -c qemu+ssh://root@IP-сервера/system apache

Правим сетевые конфиги. Пусть Apache будет у нас находиться по адресу 192.168.0.13. Далее ставим Apache/PHP и необходимые модули:

$ sudo apt-get install apache2
$ sudo apt-get install php5 libapache2-mod-php5 php5-mysql php5-memcache

Здесь мы устанавливаем только самые необходимые PHP-модули, нужные в нашей голой конфигурации: MySQL и memcache. Понадобятся ли другие модули, будет зависеть от потребностей конкретного веб-сайта. Далее создаем минимально необходимый конфиг веб-сервера:

$ sudo vi /etc/httpd/conf/httpd.conf
Listen 192.168.1.13:80
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
    Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    Options Indexes FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>

Настройки PHP5 оставляем пока как есть. Размещаем наш сайт в каталоге /var/www/html, перезапускаем Apache:

$ sudo service restart apache

Последним шагом добавляем правило iptables для пропуска трафика на 80-й порт:

$ sudo iptables -A INPUT -j DROP
$ sudo iptables -A INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT
 

Шаг 5. Поднимаем nginx в режиме реверс-прокси

Заключительная часть настройки — поднятие веб-сервера nginx в режиме реверс-прокси. По большому счету наша конфигурация могла бы обойтись и без него, и мы могли бы пробросить трафик с 80-го порта внешнего шлюза сразу на адрес 192.168.0.13. Однако nginx позволит создать задел для будущего расширения нашей конфигурации, выступая в роли балансировщика нагрузки на несколько серверов, а также защитит от ряда угроз, выступая в роли брандмауэра прикладного уровня.

Как всегда, первым делом клонируем шаблон и запускаем виртуальную машину:

$ sudo virt-clone -o temp -n nginx -f /var/lib/libvirt/images/nginx.img
$ sudo virsh start nginx
$ virt-viewer -c qemu+ssh://root@IP-сервера/system nginx

Далее правим сетевой конфиг (в этом примере я использую адрес 192.168.0.14) и устанавливаем nginx:

$ sudo apt-get install nginx

Конфигурация веб-сервера будет такой:

upstream apache {
    server 192.168.0.13:80;
}

server {
    listen       192.168.0.14:80;
    server_name  www.example.com;

    access_log  /var/log/nginx/log/www.example.access.log main;
    error_log  /var/log/nginx/log/www.example.error.log;
    root   /usr/share/nginx/html;
    index  index.html index.htm;

    location / {
        proxy_pass  http://apachep;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;
        proxy_buffering off;
        proxy_set_header Host            $host;
        proxy_set_header X-Real-IP       $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Ничего особенного, самая стандартная конфигурация прокси, который отдает все полученные запросы указанному серверу, в нашем случае Apache с IP-адресом 192.168.0.13. Ее более чем достаточно для 90% задач. Перезагружаем nginx, чтобы изменения вступили в силу:

$ sudo service nginx reload
 

Дальнейшие шаги

Теперь у нас есть готовая инфраструктура, состоящая как минимум из четырех виртуальных серверов: MySQL с адресом 192.168.0.11, memcached — 192.168.0.12, Apache — 192.168.0.13, nginx — 192.168.0.14. Чтобы проверить, что все виртуальные машины корректно функционируют, воспользуемся virsh:

$ sudo virsh list

Или с удаленной машины:

$ sudo virsh --connect qemu+ssh://root@192.168.0.10/system list

Повторюсь, что это лишь базовая конфигурация, которая на данном этапе обеспечивает только работоспособность связки nginx + Apache. Далее следует корректным образом отконфигурировать Apache и PHP и поднять сайт, указав в его настройках адреса и порты серверов MySQL и memcached (если он будет использован). После проверки работоспособности всей связки следует настроить маскарадинг пакетов, пришедших на порт 80 внешнего шлюза, так, чтобы они перенаправлялись на адрес 192.168.0.14, то есть на адрес nginx. Он и будет точкой входа на сайт.

Кроме того, рекомендую подправить настройки iptables всех серверов так, чтобы каждый из них мог общаться только с тем, с которым действительно необходимо. Например, nginx должен принимать пакеты только с адреса внешнего шлюза, а отдавать только на адрес сервера Apache, порт 80. Таким образом ты полностью изолируешь серверы друг от друга и в случае, если будет взломан, например, тот же nginx, его взломщик, не получив права root, вообще не сможет выбраться из песочницы. Дополнительным уровнем защиты станет настройка правил брандмауэра на стороне самого сервера, тогда взломщику не помогут даже права root, он окажется замкнут внутри виртуального сервера.

 

INFO

Живая миграция с помощью libvirt выполняется в одну команду: sudo virsh migrate —live имя_вм qemu+ssh://host2.com/system.

Для libvirt есть прекрасный графический инструмент управления под названием virt-manager. Он позволяет выполнить все описанное в статье, просто кликая мышкой.

Чтобы защититься от возможных проблем с конфигурацией и прочих сбоев, сразу после настройки окончательной конфигурации следует сделать клоны всех серверов. В этом случае сбойный сервер можно будет быстро восстановить из априори работоспособной копии. Apache и MySQL таким образом, конечно, не восстановишь, так как они содержат динамически генерируемые данные; эти данные можно заранее расположить на втором виртуальном диске, который достаточно будет подключить после восстановления рабочей копии.

Сделать это можно с помощью все того же virt-clone, указав дополнительный диск с помощью опции -f. Например (apache_base — заранее сохраненная рабочая копия):

$ sudo virt-clone -o apache_base -n apache -f /var/lib/libvirt/images/apache.img -f /var/lib/libvirt/images/www-data.img

Естественно, следует заранее внести необходимые строки в /etc/fstab. Кстати, идею с вынесением данных виртуальных машин на отдельные диски можно использовать для шаринга данных между несколькими виртуальными машинами, например в конфигурации с использованием дополнительного сервера nginx для отдачи статики. В таком случае доступ к данным веб-сервера должны иметь сразу две виртуальные машины: Apache и nginx. Чтобы это организовать, можно использовать отдельный диск для данных и подключать его к обеим ВМ. Правда, в этом случае придется настроить блокировку диска; как это сделать, описано в официальной документации.

Что касается ресурсов виртуальных машин, а точнее, их правильного распределения между ВМ, то здесь ситуация следующая. Два главных ресурса — это память и используемые ядра процессора. Libvirt позволяет гибко управлять ими и изменять без необходимости пересоздавать и перезапускать ВМ. Например, для изменения количества выделенной виртуальной машине памяти можно использовать такую команду:

$ virsh setmem memcached 1048576 --live --config

Она установит количество выделенной памяти для ВМ memcached равным 1 Гб и запишет это значение в конфиг ВМ (флаг ‘—config’). Количество выделенных процессоров можно изменить так:

$ virsh setvcpus apache 2 --live --config

Правильный выбор количества ресурсов для каждой виртуальной машины в нашей конфигурации будет зависеть от ситуации, однако следует иметь в виду, что это лишь ограничение, поэтому вполне возможно выделить, например, по 6 Гб каждой ВМ на сервере с 6 Гб физической памяти и положиться на подсистему VM, которая сама распределит ресурсы между машинами. Далее останется только мониторить работу виртуальных окружений и подстраивать ограничения для достижения лучшего баланса.

Virsh — мощнейший инструмент управления ВМ
Virsh — мощнейший инструмент управления ВМ
VNC-клиент
VNC-клиент
 

Выводы

Мы получили LAMP-сервер, в котором все сервисы полностью изолированы друг от друга. Он удобен в управлении, гибок в настройке и безопасен, можно переносить без остановки его сервисы на другие физические машины и добавлять новые сервисы, лишь немного изменяя конфигурацию. И вся эта настройка была выполнена за пару часов.

Оставить мнение

Check Also

Конкурс хаков: пишем на PowerShell скрипт, который уведомляет о днях рождения пользователей Active Directory

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