Балансировщик нагрузки для Amazon EC2 c автомасштабированием

Многие знают, что Amazon предоставляет возможность автоматически наращивать мощность вашего пула (увеличивать количество виртуальных серверов) в зависимости от нагрузки. Однако я не сумел найти в русскоязычном сегменте сети толкового описания практической реализации такой схемы. Рискну представить на суд общественности итог моих штудий на данную тему.

Итак, вводные данные. Наш сервер, судя по кривой посещаемости, в скором времени начнёт испытывать весьма суровые нагрузки, особенно в пиковые моменты. Для эффективной обработки трафика, а также во избежание отказов в обслуживании было решено использовать механизмы, предоставляемые Amazon, позволяющие в реальном времени запускать необходимое количество серверов. При этом, когда нагрузка спадает, получившийся пул должен «сбавлять обороты», автоматически уменьшаясь в размерах, и тем самым уменьшать финансовые затраты на проект.


Для всего этого мы подняли виртуальный сервер (EC2 instance в терминологии Amazon, далее я иногда буду использовать и вульгарную кальку с английского — «инстанс»), на котором настроили весь необходимый софт. Важно, чтобы всё было сделано так, чтобы ПО не нуждалось ни в какой дополнительной настройке, и таких одинаковых машин можно было бы запустить столько, сколько необходимо, не вызывая при этом никаких конфликтов, потому что стартует instance из готового зафиксированного слепка. Такой сервер у нас в проекте будет служить множественным front-end, на который и будет балансироваться нагрузка средствами Amazon, при этом база данных у нас хранится отдельно, а контент пользователей (например закачиваемые фотографии и аватары) — вообще на хранилище Amazon S3.

Что ж, считаем что эталонный front-end у нас готов, можем приступать.

Вот как будет выглядеть схема с некоторого отдаления:
image

1. Сначала снимем образ с эталонного instance


Быстрее всего сделать это средствами AWS management console, т.е. через веб-интерфейс. Для этого заходим в пункт меню ELASTIC BLOCK STORE -> Snapshots и делаем новый снимок (snapshot) с тома нашего front-end. Затем, щёлкнув правой кнопкой мыши по получившемуся снимку, выбираем пункт «Create image from snapshot» для получения образа (т.н. AMI) для дальнейшего клонирования. При создании AMI важно указать правильный (тот же, что и в эталонном instance) идентификатор ядра (Kernel ID), иначе есть шанс что AMI просто не будет запускаться.

2. Установим необходимый инструментарий для работы с балансировщиком и автомасштабированием


Собственно, управлять вашей фермой можно абсолютно с любого компьютера, мы выбрали для себя один из серверов с Ubuntu, поэтому привожу примеры настройки именно для этой OS.

2.1. Поставим java (если ещё не установлена) и пропишем переменную окружения JAVA_HOME

apt-get install default-jdk


Добавим в /etc/environment строку (в данном случае)
JAVA_HOME="/usr/lib/jvm/java-6-openjdk"


Далее ставим инструментарий Amazon. Я ставлю его в каталог /opt, и делаю символические ссылки, для облегчения поиска необходимых папок в дальнейшем, но это конечно дело вкуса.

2.2. Поставим инструментарий Amazon для настройки балансировщика

wget http://ec2-downloads.s3.amazonaws.com/ElasticLoadBalancing.zip
unzip ElasticLoadBalancing.zip
mv ElasticLoadBalancing-1.0.15.1 /opt/
ln -s /opt/ElasticLoadBalancing-1.0.15.1 /opt/ELB


Добавляем переменные окружения (я делаю это путём редактирования /etc/environment)
в переменную PATH необходимо добавить пути "/opt/ELB:/opt/ELB/bin"

Добавить туда же переменную AWS_ELB_HOME:
AWS_ELB_HOME="/opt/ELB"


Создать файл /opt/ELB/credential-file, куда прописать ваши AWS credentials (если не знаете что это — см. AWS management console -> Security credentials) в формате:
AWSAccessKeyId=xxx
AWSSecretKey=xxx



chmod 600 /opt/ELB/credential-file


Добавить в /etc/environment переменную AWS_CREDENTIAL_FILE:
AWS_CREDENTIAL_FILE="/opt/ELB/credential-file"


2.3. Поставим инструментарий Amazon для настройки автомасштабирования

wget http://ec2-downloads.s3.amazonaws.com/AutoScaling-2011-01-01.zip
unzip AutoScaling-2011-01-01.zip
mv AutoScaling-1.0.39.0 /opt/
ln -s /opt/AutoScaling-1.0.39.0/ /opt/AS


Добавляем переменные окружения в /etc/environment — в переменную PATH необходимо добавить пути "/opt/AS:/opt/AS/bin"

Добавить туда же переменную AWS_AUTO_SCALING_HOME:
AWS_AUTO_SCALING_HOME="/opt/AS"


2.4. Поставим инструментарий Amazon для настройки мониторинга

wget http://ec2-downloads.s3.amazonaws.com/CloudWatch-2010-08-01.zip
unzip CloudWatch-2010-08-01.zip
mv CloudWatch-1.0.12.1 /opt/
ln -s /opt/CloudWatch-1.0.12.1/ /opt/CW


Добавляем переменные окружения в /etc/environment — в переменную PATH необходимо добавить пути "/opt/CW:/opt/CW/bin"

Добавить туда же переменную AWS_CLOUDWATCH_HOME:
AWS_CLOUDWATCH_HOME="/opt/CW"


2.5. Проверка установленного инструментария

Для проверки переменных, установленных в /etc/environment можно перелогиниться или ещё каким-либо образом перечитать их.
Даём команды:
elb-cmd --help

as-cmd --help

mon-cmd --help

Каждая из них должна выдать справку, либо если были какие-то погрешности при установке — указать на них.

3. Поехали. Собственно настроим балансировщик нагрузки с автомасштабированием


3.1. Создаём балансировщик


elb-create-lb myLB --headers --listener "lb-port=80,instance-port=80,protocol=http" --region us-west-1 --availability-zones us-west-1c


Где myLB — имя нашего балансировщика
--listener «lb-port=80,instance-port=80,protocol=http» — слушаем порт 80, направляем трафик на порт 80 на целевом instance и используем протокол http
--region us-west-1 регион, где будет поднят балансировщик
--availability-zones us-west-1c зона, где будет поднят балансировщик

В ответ мы получим что-то вроде:

DNS_NAME  DNS_NAME
DNS_NAME myLB-108660279.us-west-1.elb.amazonaws.com

Это и есть адрес нашего будущего балансировщика

3.2. Создаём проверочный тест для балансировщика (healthcheck)

Это тест, благодаря которому балансировщик понимает, что целевой инстанс уже плотненько нагружен и переводит трафик на следующий инстанс.
elb-configure-healthcheck myLB --headers --target "HTTP:80/" --interval 30  --timeout 10 --unhealthy-threshold 2 --healthy-threshold 2 --region us-west-1


где myLB — имя нашего балансировщика
--headers — опциональный параметр, включающий вывод названий полей в описании данного healthchek
--target «HTTP:80/» — протокол: порт/URI для проверки
--interval 30 — интервал проверки (проверяем раз в 30 секунд)
--timeout 10 — таймаут, если превышен — инстанс чувствует себя плоховато
--unhealthy-threshold 2 — сколько проверок сделать, прежде чем окончательно убедиться, что инстанс нездоров
--healthy-threshold 2 — сколько проверок сделать, прежде чем окончательно убедиться, что инстанс таки здоров
--region us-west-1 — регион размещения

При удачном выполнении команды получим примерно такой вывод:
HEALTH_CHECK  TARGET  INTERVAL  TIMEOUT  HEALTHY_THRESHOLD  UNHEALTHY_THRESHOLD
HEALTH_CHECK  HTTP:80/index.html  30        10       2                  2


3.3. Создаём конфигурацию для запуска нового инстанса при автомасштабировании

as-create-launch-config myAS --image-id ami-fd015fb8 --kernel aki-9ba0f1de --key ubuntu --group ubuntu-new 	--region us-west-1 --instance-type m1.xlarge


Где myAS — имя конфигурации
--image-id XXX — ID слепка (AMI), созданного на шаге 1
--kernel XXX — Kernel ID для AMI, созданного на шаге 1
--key XXX — название вашего ключа доступа
--group ХХХ — название security group — набора правил брэндмауэра для доступа к данному инстансу
--instance-type XXX — тип запускаемого инстанса
--region us-west-1 — регион

В случае удачного создания конфигурации получаем такой вывод:
OK-Created launch config


3.4. Описываем свойства пула серверов при автомасштабировании, привязываем пул к балансировщику

as-create-auto-scaling-group myASG  --launch-configuration myAS --availability-zones us-west-1c --min-size 1 --max-size 20 --load-balancers myLB  --grace-period 500 --health-check-type ELB --region us-west-1


Где myASG — имя пула
--launch-configuration myAS — имя конфигурации для запуска из предыдущего шага
--min-size 1 — минимальный размер пула
--max-size 20 -максимальный размер пула
--load-balancers myLB — привязка к балансировщику
--grace-period 300 — важный параметр, указывающий, через сколько секунд после старта нового инстанса на него можно подавать нагрузку. У меня например после старта происходит деплой приложения, поэтому этот период довольно большой
--health-check-type ELB — тип healthchek, в нашем случае — Elastic Load Balancer
--region us-west-1 — регион размещения
--availability-zones us-west-1c — зона

3.5. Описываем политику роста пула

as-put-scaling-policy myUp-policy -auto-scaling-group myASG --adjustment=1 --type ChangeInCapacity  --cooldown 300 --region us-west-1


Где myUp-policy — название политики
-auto-scaling-group myASG -название пула серверов, к которому будет относиться политика
--type ChangeInCapacity — тип политики. В данном случае — абсолютное изменение количества серверов пула. Может принимать также значения ExactCapacity (точное значение нового размера пула), PercentChangeInCapacity (процентное относительно текущего значение изменение размера пула).
--adjustment=1 — в данном случае мы прибавляем один сервер к пулу
--cooldown 600 — таймаут перед следующим изменением размера пула. Должно разумно коррелировать с параметром grace-period (как минимум быть не меньше), заданным на предыдущем шаге
--region us-west-1 регион размещения

В ответ мы получаем строку — дескриптор данной политики, примерно такого вида:
arn:aws:autoscaling:us-west-1:033313991904:scalingPolicy:5ae9e75d-4344-4b3f-abb0-c501c7221ecf:autoScalingGroupName/myASG:policyName/myUp-policy


3.6. Привязываем политику роста пула к мониторингу балансировщика

mon-put-metric-alarm MyHighLatAlarm --metric-name  Latency  --comparison-operator  GreaterThanThreshold  --evaluation-periods  1  --namespace  "AWS/ELB"  --period  60  --statistic Average --threshold  80 --alarm-actions arn:aws:autoscaling:us-west-1:033313991904:scalingPolicy:5ae9e75d-4344-4b3f-abb0-c501c7221ecf:autoScalingGroupName/myASG:policyName/myUp-policy --dimensions "LoadBalancerName=myELB"--unit Seconds --region us-west-1


Где MyHighLatAlarm — название привязки
--metric-name Latency — важный параметр. Мы показываем, что измеряем-то собственно задержку на балансировщике. Эта метрика была создана в момент создания балансировщика автоматически. (в чём можно убедится, проверив вывод команды mon-list-metrics).
--comparison-operator GreaterThanThreshold — оператор сравнения, в данном случае «больше порогового значения», может быть также «GreaterThanOrEqualToThreshold», LessThanThreshold" и «LessThanOrEqualToThreshold»
--evaluation-periods 1 — сколько раз проверить метрику, прежде чем сравнивать с порогом
--namespace «AWS/EC2» — пространство имён (обязательный параметр, зачем он — непонятно)
--period 60 период опроса
--statistic Average — как мы меряем порог. В данном случае — по среднему, а может быть также «SampleCount», «Sum», «Minimum», «Maximum»
--threshold 80 — собственно пороговое значение задержки.
--alarm-actions arn:aws:autoscaling:us-west-1:033313991904:scalingPolicy:5ae9e75d-4344-4b3f-abb0-c501c7221ecf:autoScalingGroupName/myASG:policyName/myUp-policy — говорим что по достижению порога срабатывания исполняем политику, описываемую данным дескриптором, которую мы создали на предыдущем шаге.
--dimensions «LoadBalancerName=myLB» — фильтруем алармы по данному признаку.
--unit Seconds — единицы измерения
--region us-west-1 — регион размещения.

Уфф. Теперь у нас есть пул, который будет реагировать на нагрузку, прибавляя по одному серверу. Однако хочется, чтобы пул «сдувался» в ответ на уменьшение нагрузки. Для этого нам надо описать политику уменьшения пула и привязать её к метрике балансировщика. Не буду повторять подробности, вроде бы тут всё ясно из синтаксиса:

3.7. Описываем политику уменьшения пула

as-put-scaling-policy myScaleDown-policy --auto-scaling-group myASG --adjustment=-1 --type ChangeInCapacity  --cooldown 600 --region us-west-1


Получаем дескриптор политики:
arn:aws:autoscaling:us-west-1:033313991904:scalingPolicy:a764b4d4-aff5-4061-ba3a-c35c05ad1d25:autoScalingGroupName/myASG:policyName/myScaleDown-policy


3.8. Привязываем политику уменьшения пула к мониторингу балансировщика

mon-put-metric-alarm MyLowLatAlarm  --comparison-operator  LessThanThreshold --evaluation-periods  1 --metric-name  Latency --namespace  "AWS/EC2"  --period  600  --statistic Average  --threshold  20  --alarm-actions arn:aws:autoscaling:us-west-1:033313991904:scalingPolicy:a764b4d4-aff5-4061-ba3a-c35c05ad1d25:autoScalingGroupName/myASG:policyName/myScaleDown-policy ---dimensions "LoadBalancerName=myLB" --unit Seconds --region us-west-1


4. Обращаемся к балансировщику по имени-отчеству


Здорово. Теперь можно потестировать всё это дело, обращаясь к нашему балансировщику по его публичному DNS имени, полученному на шаге 3.1., в нашем случае — myLB-108660279.us-west-1.elb.amazonaws.com

Амазон советует затем завести запись DNS типа CNAME, показывающую на данное имя для домена вашего сайта:
www.site.com IN CNAME myLB-108660279.us-west-1.elb.amazonaws.com

Тогда можно будет попадать на балансировщик, просто обращаясь к www.site.com

5. Как нам от всего этого избавиться?


Теперь пару слов о том, как разобрать сию конструкцию (когда наиграетесь с тестовой версией например), с тем чтобы она зазря не кушала денежки (пусть и небольшие, но всё же). Это внезапно оказалось нетривиальной задачей, т.к. балансер с масштабированием не давал себя разбирать, постоянно плодя новые инстансы и не давая гасить балансировщик, пока за ним эти самые инстансы висели. Вобщем правильная последовательность действий получилась такая:

получим список наших алармов:
mon-describe-alarms --region us-west-1


Вычистим их по одному:
mon-delete-alarms --region  us-west-1 --alarm-name MyHighLatAlarm
mon-delete-alarms --region  us-west-1 --alarm-name MyLowLatAlarm


Получим список наших политик:
as-describe-policies --region us-west-1


Вычистим их по одной:
as-delete-policy myScaleDown-policy  --auto-scaling-group myASG --region us-west-1
as-delete-policy myUp-policy --auto-scaling-group myASG --region us-west-1


Вот она, хитрость-то. Сначала оказывается надо размер группы выставить в ноль, чтобы инстансы не подрывались:
as-update-auto-scaling-group myASG --min-size 0 --max-size 0 --region us-west-1


Теперь подождём минут пять, пока все инстансы не погаснут и удаляем пул:
as-delete-auto-scaling-group myASG --region us-west-1


Удаляем конфигурацию:
as-delete-launch-config myAS --region us-west-1


Ну и наконец гасим балансировщик:
elb-delete-lb myLB --region us-west-1
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 9

    +4
    ето скорее свободньій пересказ документации Amazon, но мне понравилось что вся цепочка описана в одном документе. Мне в свое время пришлось написать похожую простьіню для себя :)

    Немного помогу — у вас там ошибки в определении метрик "--metric-name Latency --namespace «AWS/EC2» --period 600 --statistic SampleCount --threshold 20":

    1. --metric-name Latency относится к пространству имен «AWS/ELB» а не «AWS/EC2».

    2. --dimensions «AutoScalingGroupName=myASG». сдесь нужно указьівать ваш LB а не AS Group, --dimensions «LoadBalancerName=myLB».

    3. --statistics SampleCount. если вам нужно знать именно задержку (latency) в мс, тогда нужно использовать "--statistic Average", так так SampleCount даст вам количество запросов к LB за указаньій период "--period 600", тоесть фактически будет равен значению "--statistics RequestCount".

      0
      Да, со всем абсолютно согласен, и это как раз и есть моя «простыня», которую делал вообщем-то для себя изначально.
      Исправил всё, как вы посоветовали. По п.2. — можно и так и так, но пожалуй правильней действительно будет мерять на ELB. Хотя в амазоновской документации везде как раз фильтруется по ASG почему-то.
      +1
      п.2 — можно по разному, но правильно будет все равно по-моему :)

      Метрика Latency доступна только в namespace «AWS/ELB», для которого, в свою очередь, доступньі только два dimension: «LoadBalancerName, AvailabilityZone».

      При создании mon-put-metric-alarm — metric-name, namespace и dimensions должньі соответствовать друг другу, иначе вьі получите в ответ значение 'unknown' и Alarm state 'INSUFFICIENT_DATA'.

      Посмотрите сдесь (хороший документ, кстати) docs.amazonwebservices.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html

      В документации приводится использование метрики CPUUtilization — вот для нее-то как раз --namespace «AWS/EC2» и --dimensions «AutoScalingGroupName=MyASG»

        0
        Интересует, как Вы собираетесь обновлять код проекта на уже запущенной группе, хотя бы из двух инстансов?

        Тоже сейчас изучаю варианты масштабирования проекта в AWS. Добавлю несколько ссылок:
        docs.amazonwebservices.com/gettingstarted/latest/wah-linux/web-app-hosting-intro.html
        cloudblog.8kmiles.com/2011/12/09/autoscale-wordpress-with-ease-using-amazon-cloud/
        cloudblog.8kmiles.com/2011/04/20/auto-scaling-using-amazon-web-services/
          +1
          Ну собственно не такая большая проблема. Код нужно обновлять в двух случаях — когда стартует новый инстанс (вдруг что-то изменилось), и когда собственно программисты решают задеплоить новый код.
          В первом случае деплой прописывается в автозагрузку, а во втором всё решает скрипт, который дёргает Amazon API, смотрит какие инстансы сейчас у нас запущены на Amazone и деплоит на них по списку. Конечно есть разные инстансы с разными ролями, но мы на те инстансы, на которые не надо деплоить вешаем специальный тег (так получилось что они у нас фиксированные).
          Для того чтобы у инстанса было время на деплой приложения при стартапе, мы указываем ключ --grace-period при создании политики роста пула, и в это время инстанс не дёргается через балансировщик.

            0
            В принципе для первого случая, после обновления системы можно обновлять снепшот AMI. В таком случае при старте нового инстанса ничего проверять не нужно.
            Интересный вариант во второй моей ссылке — код проекта монтируется при старте инстанса по сети. Из плюсов обновлять надо только один инстанс, изменения сразу же доступны на всех запущенных инстансах.
            В данной схеме смущает только один момент, насколько медленней будет чтение с сетевой ФС особенно при росте кол-ва инстансов в группе. Как вариант вместо сетевой ФС можно монтировать S3 бакет с помощью s3fs, но думаю будет медленно и дорого.
            Возможно остановлюсь и на Вашем варианте, спасибо.
              0
              второй вариант ето, в случае c облаком, антипаттерн.
              Идея auto-scaling том чтобьі обеспечить вьісокую производительность и(или) доступность.
              А мьі, желая упростить жизнь себе, добавляем еще один SPOF (у нас ведь уже есть MySQL) к архитектуре, понижая доступность, и в какой-то мемент упремся в сеть, которая, к слову сказать, имеет непостоянную производительность.

              На инстансьі нужно смотреть как на расходньій материал. Упал? Недоступен? ну и хрен с ним, некогда разбираться, прикончили его бьістренько (terminate),
              и запустили новьй, которьій при старте прочел настройки (user-data, cloud-init) и сам себя задеплоил (chef/puppet/mycustomscript).

              Обновление кода на работающих инстансах — все просто, используем АРІ для получения списка инстансов (например список всех инстансов в конкретном лоадбалансере у которьіх статус InService

              # elb-describe-instance-health myLB --headers
              INSTANCE_ID INSTANCE_ID STATE DESCRIPTION REASON-CODE
              INSTANCE_ID i-742e0216 InService N/A N/A
              INSTANCE_ID i-4e23352e UnHealthy N/A N/A
              Дополнительно можно использовать тег, например если нужно проапдейтить не все инстансьі а определенную часть.

              Конечно же, можно делать снепшот AMI каждьій раз если вам так удобно или необходимо. Только нужно еще дополнительно cоздавать launch-configuration и обновлять auto-scaling group property каждьій раз.
              Иногда такой подход оправдан, но если есть возможность — лучше все-таки стремиться к автоматическому deployment при старте инстанса.
          0
          Помимо амазоновского сервиса автомасштабирования, есть (как минимум) 2 сервиса, которые позволяют это делать на амазоне сильно умнее (scalr.com, alestic.com). Умнее в том, что есть интеграция с ПО на инстансах (если это mysql, то будет подниматься репликация, если apache, то автоматически будет добавляться к фронтенду в список бэкендов)
            0
            вьі наверно имели ввиду ylastic.com?

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

            Самое читаемое