Настройка Elastic Beanstalk для повышения отказоустойчивости и автоматического масштабирования
Всем привет! Сегодняшняя статья, как вы увидели в заголовке, посвящена настройке Elastic Beanstalk, а конкретно — балансировщика AWS и количества инстансов TagretGroup в зависимости от нужных нам условий.
Введение в Elastic Beanstalk
pic 0. Elastic Beans
Elastic Beanstalk представляет собой высокоуровневую сервисную платформу, которая автоматизирует развертывание приложений в облаке. EB автоматически настраивает необходимую инфраструктуру и ресурсы, такие как экземпляры EC2 (виртуальные серверы), группы безопасности, объекты S3 для хранения файлов версии приложения, базы данных RDS, балансировщики нагрузки Elastic Load Balancing и многое другое, чтобы обеспечить запуск приложения с минимальными затратами времени и усилий со стороны разработчика.
Ключевыми компонентами Elastic Beanstalk являются:
Приложение (Application) является основной единицей. Сначала вы создаете приложение, которое служит контейнером для ваших версий и окружений.
Окружение (Environment) представляет собой версию приложения, которая развертывается на конкретной инфраструктуре AWS и служит выполнения определенной роли, например, тестового или продакшн окружения.
Ресурсы, с которыми мы будем иметь дело:
EC2 (Elastic Compute Cloud) — это виртуальные серверы, которые можно арендовать в облаке AWS для запуска приложений.
Target Group — это концепция, используемая в AWS для управления трафиком на уровне приложений. Target Group позволяют указать куда именно должен быть направлен трафик, поступающий на балансировщик нагрузки (Load Balancer). Каждая Target Group ассоциирована с определёнными ресурсами, например с EC2 либо лямбда-функциями.
Auto Scaling Groups дают возможность автоматического масштабирования количества экземпляров EC2 в ответ на изменения нагрузки/возникновения других факторов
После небольшого описания EB, приступим к теме статьи.
Вступление и проблемы
pic 1. EB Environment Events
Наше приложение было запущено на одном экземпляре c5.xlarge и все status checks от AWS выдавали »Success», машина была в состоянии »Running», однако приложение не отвечало. Посмотрев логи сервера, мы поняли, что проблема крылась в перезапуске IIS (эта процедура стандартная — обычно это происходит каждые 29 часов, но также можно настроить период индивидуально).
Однако выяснилось, что сервер перезапустился только через 4 (!) минуты. В контексте нашего приложения 4 минуты неработоспособности веб-сервера недопустимо.
К сожалению, такого рода падения сервера мы не смогли определить с помощью системы мониторинга NewRelic. Всё что мы получили из его логов — что сервер не отвечал на запросы, факты увеличения времени отклика и пр. показатели.
Поэтому в итоге мы решили использовать стандартные фичи ElasticBeanstalk для того чтобы повысить отказоустойчивость нашего приложения. И тут возникли следующие мысли — следует не только заменять неработающие инстансы, но и при большой нагрузке увеличивать их число, либо уменьшать при малой.
Elastic Beanstalk
EB не заточен под работу с одной машиной, поэтому после разговора с заказчиком мы сделали такой сэтап:
2x c5.large (c5.large стоит ровно в 2 раза дешевле, чем c5.xlarge)
если в течении 5 минут нагрузка на процессор >80%, мы добавляем еще одни инстанс
если в течении 5 минут нагрузка на процессор <40%, мы убираем один инстанс
pic 2. configuration
Конфигурация
Resources:
AWSEBAutoScalingGroup:
Type: "AWS::AutoScaling::AutoScalingGroup"
Properties:
MinSize: 2
MaxSize: 3
AWSEBCloudwatchAlarmHigh:
Type: "AWS::CloudWatch::Alarm"
Properties:
EvaluationPeriods: 1
MetricName: CPUUtilization
Namespace: AWS/EC2
Period: 300
Statistic: Average
ComparisonOperator: GreaterThanOrEqualToThreshold
Threshold: 80
Unit: Percent
AlarmActions:
- Ref: "AWSEBAutoScalingScaleUpPolicy"
AWSEBCloudwatchAlarmLow:
Type: "AWS::CloudWatch::Alarm"
Properties:
EvaluationPeriods: 1
MetricName: CPUUtilization
Namespace: AWS/EC2
Period: 300
Statistic: Average
ComparisonOperator: LessThanOrEqualToThreshold
Threshold: 40
Unit: Percent
AlarmActions:
- Ref: "AWSEBAutoScalingScaleDownPolicy"
AWSEBAutoScalingScaleUpPolicy:
Type: "AWS::AutoScaling::ScalingPolicy"
Properties:
AdjustmentType: ChangeInCapacity
AutoScalingGroupName:
Ref: "AWSEBAutoScalingGroup"
Cooldown: 360
ScalingAdjustment: 1
Главная проблема такого решения в том, что оно не будет работать как задумано — по умолчанию AWS считает, что если все Status Check на инстансе проходят, то на него можно пускать траффик. Что неверно для нашего кейса — нам нужно добавить к этим проверкам наш собственный HealthCheck, установленный в .NET приложении.
Начнем!
Конфигурация
Для конфигурации нам понадобиться Environment ID и вот эти вкладки в сайдбаре EC2:
pic 3. EC2 Sidebar
Пойдем по порядку:
1) Target Groups
Здесь нам нужно в соответствием с нашими критериями uptime’а задать конфигурацию TG.
Для нашем случае мы посчитали достаточным обнаружение unhealthy машины через 1 минуту:
3 запроса каждые 15 секунд + таймауты (5 сек) = 1 минута
pic 4. Tagret Group health check settings
Также во вкладке Attributes есть важный параметр — Deregistration delay. Когда инсанc перестает отвечать по /hc, то он должен быть заменен за время, указанное в этом параметре:
pic 5. TG Deregistration Delay
Конфигурация
Resources:
AWSEBV2LoadBalancerTargetGroup:
Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
Properties:
HealthCheckIntervalSeconds: 15
HealthCheckPath: /hc
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 5
UnhealthyThresholdCount: 3
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: '20'
2) Auto Scaling Groups
2.1 Enable HealthChecks & Set grace period
Самый важный параметр, без которого все конфигурации выше не будут применены:
pic. 6
Чтобы добавить проверку по ELB, нужно нажать на »Turn on Elastic Load Balancing health checks» и установить Health check grace period.
Health check grace period — это время ожидания, в течение которого AWS не будет предпринимать никаких действий по удалению или замене только что созданных (или запущенных) инстансов в рамках Auto Scaling Group, даже если они не проходят health check.
Например, мы только запустили своё новое приложение и ему нужно некоторое время, чтобы полностью загрузиться и начать работать правильно. Если бы AWS сразу же начинал проверять, работает ли оно как надо, и при этом видел, что что-то не так (потому что приложению просто еще нужно время, чтобы завершить все процессы запуска), то AWS маркировал бы его как unhealthy и попытаться перезапустить его или создать новый экземпляр.
pic. 7
Подбор оптимального grace period — задача не самая тривиальная. Мы просто замерили сколько в среднем времени нужно новому приложению, чтобы оно начало отдавать 200-е запросы. Для нашего приложения это менее 8 минут, поэтому мы выставили с запасом значение 500 секунд.
Следует учитывать разные факторы, которые могут увеличить время загрузки приложения — миграции БД или крупные обновления приложения. В таких случаях потребуется увеличить grace period вручную
Конфигурация
Resources:
AWSEBAutoScalingGroup:
Type: "AWS::AutoScaling::AutoScalingGroup"
Properties:
HealthCheckType: ELB
HealthCheckGracePeriod: 500
2.2 Instance maintenance policy
Также нам нужно разрешить ELB превышать лимит в N инстансов в случае, если один из них не отвечает, но еще не удален из EB. То есть мы хотим разрешить превентивно добавлять 1 инстанс, пока происходит удаление нерабочего. В итоге мы будем одномоментно иметь на 1 инстанс больше, чем разрешено в EB. Для этого в Auto Scaling Group выбираем параметр »Instance maintenance policy» и устанавливаем для него значение Launch before terminating:
pic 8. ASG Instance maintenance policy (example for 2 instances)
Эксперимент
Давайте проверим, как будет вести себя балансировщик, если мы целенаправленно остановим наше приложение на одном инстансе (например командой Stop-Service)
Немного изменим нашу конфигурация для эксперимента — максимальное количество инстансов сделаем равным 2, оставив такие же параметры CPU:
pic 9. Demo configuration
Cтоит упомянуть, что в нашем кейсе также настроен Blue/Green Deployment, но его конфигурация выходит за рамки этой статьи.
Видим рабочие инстансы:
pic 10. Running instances
Останавливаем сервис на одном из них:
pic 11. Stopping the service
В течении одной минуты (по нашим настройкам TargetGroups) мы увидим следующую картину:
pic 12. Severe Status
Инстанс закономерно не прошел HealthCheck и в теперь весь траффик идет на единственный оставшийся инстанс.
И в тоже время (как мы помним, в течении 20 секунд) стартует запуск нового инстанса, даже пока предыдущий не удалён! В результате мы видим 3 инстанса (конечно, рабочих инстансов всегда будет всего 2):
pic 13. 3 Instances
После удаления нерабочего инстанса наша система возвращается в стабильное состояние из двух рабочих машин.
Что можно увидеть и в EB Envorinment Events:
pic 14. Final EB Envorinment Events
Заключение
На этом всё, надеемся что описание нашего кейса сэкономило вам немало времени и вы узнали больше о настройке Elastic Beanstalk в AWS.