AWS ElasticBeanstalk — PaaS на базе инфраструктуры AWS. На мой взгляд значительное преимущество этого сервиса — возможность прямого доступа к элементам инфраструктуры (балансировщики, инстансы, очереди и т.п.) В этой статье решил собрать некоторые трюки, позволяющие решить типичные проблемы при использовании ElasticBeanstalk. Буду дополнять по мере нахождения новых. Вопросы и предложения в комментариях приветствуются.
На мой взгляд очевидный недостаток платформы — невнятный механизм хранения конфигурации. Поэтому я применяю следующие методы добавления конфигурации.
Самый очевидный и нативный для ElasticBeanstalk — установка через переменные окружения. Внутри инстанса эти переменные окружения доступны не как обычно, а исключительно в environment приложения. Для установки этих параметров наиболее удобно использовать команду eb setenv из пакета awsebcli, который используется для развертывания приложения (подходит для небольших проектов), либо API AWS.
Второй вариант — когда конфиг инжектится внутрь созданной версии приложения. Для этого надо пояснить, как происходить процесс развертывания. Вручную или скриптом создается zip архив, содержащий код приложения, выкладывается на специальный S3 bucket, уникальный для каждого региона ( вида elasticbeanstalk-<region_name>-<my_account_id> — не пытайтесь использовать другой, работать не будет — проверено). Можно создавать данный пакет вручную, либо редактировать программно. Я предпочитаю использовать альтернативный вариант развертывания, когда вместо awsebcli используется собственный код создания пакета версии.
Третий вариант — подгрузка конфигурации удаленно на этапе развертывания из внешней базы конфигурации. ИМХО наиболее правильный подход, однако выходит за рамки данной статьи. Я использую схему с хранением конфигов на S3 и проксированием запросов к S3 через API Gateway — это позволяет наиболее гибко управлять конфигами. S3 также поддерживает версионность.
ElasticBeanstalk поддерживает создание заданий для планировщика, используя файл cron.yaml. Однако этот конфиг работает только для worker environment — конфигурации, используемой для обработки очереди задач / периодических задач. Для решения этой задачи в окружении WebServer добавляем в каталоге проекта .ebextensions файл со следующим содержимым:
Добавляем в файл конфига в .ebextensions:
Аналогичным образом применяются миграции alembic; для того, чтобы избежать применения миграций на каждом инстансе autoscaling группы, указывается параметр leader_only
Создавая скрипты в каталоге /opt/elasticbeanstalk/hooks/, можно добавлять различные управляющие скрипты, в частности, модифицировать процесс развертывания приложения. Скрипты, исполняющиеся перед развертыванием, лежат в каталоге /opt/elasticbeanstalk/hooks/appdeploy/pre/*, во время — в /opt/elasticbeanstalk/hooks/appdeploy/enact/*, и после — в /opt/elasticbeanstalk/hooks/appdeploy/post/*. Скрипты исполняются в алфавитном порядке, благодаря этому можно выстроить правильную последовательность развертывания приложений.
Кстати, я использовал экспериментальную возможность взять в качестве брокера для Celery SQS и это вполне себя оправдало; правда, flower еще не имеет поддержки такой схемы.
Используется такая вот добавка к конфигу Apache внутри ElasticBeanstalk
Amazon предоставляет возможность владельцам доменов бесплатно использовать SSL сертификаты, в том числе и wildcard, однако только внутри самого AWS. Для использования нескольких доменов с SSL на одном environment получаем сертификат через AWS Certificate Manager, добавляем еще ELB балансировщик и настраиваем на нем SSL. Можно использовать и полученные у другого поставщика сертификаты.
UPDATE Ниже в комментарии уважаемый darken99 привел еще пару полезных фишек, позволю добавить их здесь с некоторыми пояснениями
Выключаем environment по расписанию
В данном случае в зависимости от указанного временного диапазона количество инстансов в autoscaling группе уменьшается с 1 до 0.
Замена Apache на Nginx
Варианты добавления конфигурации приложения
На мой взгляд очевидный недостаток платформы — невнятный механизм хранения конфигурации. Поэтому я применяю следующие методы добавления конфигурации.
Самый очевидный и нативный для ElasticBeanstalk — установка через переменные окружения. Внутри инстанса эти переменные окружения доступны не как обычно, а исключительно в environment приложения. Для установки этих параметров наиболее удобно использовать команду eb setenv из пакета awsebcli, который используется для развертывания приложения (подходит для небольших проектов), либо API AWS.
eb setenv RDS_PORT=5432 PYTHONPATH=/opt/python/current/app/myapp:$PYTHONPATH RDS_PASSWORD=12345 DJANGO_SETTINGS_MODULE=myapp.settings RDS_USERNAME=dbuser RDS_DB_NAME=appdb RDS_HOSTNAME=dbcluster.us-east-1.rds.amazonaws.com
Второй вариант — когда конфиг инжектится внутрь созданной версии приложения. Для этого надо пояснить, как происходить процесс развертывания. Вручную или скриптом создается zip архив, содержащий код приложения, выкладывается на специальный S3 bucket, уникальный для каждого региона ( вида elasticbeanstalk-<region_name>-<my_account_id> — не пытайтесь использовать другой, работать не будет — проверено). Можно создавать данный пакет вручную, либо редактировать программно. Я предпочитаю использовать альтернативный вариант развертывания, когда вместо awsebcli используется собственный код создания пакета версии.
Третий вариант — подгрузка конфигурации удаленно на этапе развертывания из внешней базы конфигурации. ИМХО наиболее правильный подход, однако выходит за рамки данной статьи. Я использую схему с хранением конфигов на S3 и проксированием запросов к S3 через API Gateway — это позволяет наиболее гибко управлять конфигами. S3 также поддерживает версионность.
Включаем задания в crontab
ElasticBeanstalk поддерживает создание заданий для планировщика, используя файл cron.yaml. Однако этот конфиг работает только для worker environment — конфигурации, используемой для обработки очереди задач / периодических задач. Для решения этой задачи в окружении WebServer добавляем в каталоге проекта .ebextensions файл со следующим содержимым:
files:
"/etc/cron.d/cron_job":
mode: "000644"
owner: root
group: root
content: |
#Add comands below
15 10 * * * root curl www.google.com >/dev/null 2>&1<code>
"/usr/local/bin/cron_job.sh":
mode: "000755"
owner: root
group: root
content: |
#!/bin/bash
/usr/local/bin/test_cron.sh || exit
echo "Cron running at " `date` >> /tmp/cron_job.log
# Now do tasks that should only run on 1 instance ...
"/usr/local/bin/test_cron.sh":
mode: "000755"
owner: root
group: root
content: |
#!/bin/bash
METADATA=/opt/aws/bin/ec2-metadata
INSTANCE_ID=`$METADATA -i | awk '{print $2}'`
REGION=`$METADATA -z | awk '{print substr($2, 0, length($2)-1)}'`
# Find our Auto Scaling Group name.
ASG=`aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" --region $REGION --output text | awk '/aws:autoscaling:groupName/ {print $5}'`
# Find the first instance in the Group
FIRST=`aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $ASG --region $REGION --output text | awk '/InService$/ {print $4}' | sort | head -1`
# Test if they're the same.
[ "$FIRST" = "$INSTANCE_ID" ]
commands:
rm_old_cron:
command: "rm *.bak"
cwd: "/etc/cron.d"
ignoreErrors: true
Автоматическое применение миграций Django и сборка статики при развертывании
Добавляем в файл конфига в .ebextensions:
container_commands:
01_migrate:
command: "python manage.py migrate --noinput"
leader_only: true
02_collectstatic:
command: "./manage.py collectstatic --noinput"
Аналогичным образом применяются миграции alembic; для того, чтобы избежать применения миграций на каждом инстансе autoscaling группы, указывается параметр leader_only
Использование hooks при развертывании приложений
Создавая скрипты в каталоге /opt/elasticbeanstalk/hooks/, можно добавлять различные управляющие скрипты, в частности, модифицировать процесс развертывания приложения. Скрипты, исполняющиеся перед развертыванием, лежат в каталоге /opt/elasticbeanstalk/hooks/appdeploy/pre/*, во время — в /opt/elasticbeanstalk/hooks/appdeploy/enact/*, и после — в /opt/elasticbeanstalk/hooks/appdeploy/post/*. Скрипты исполняются в алфавитном порядке, благодаря этому можно выстроить правильную последовательность развертывания приложений.
Добавление демона Celery в уже имеющийся конфиг supervisor
files:
"/opt/elasticbeanstalk/hooks/appdeploy/post/run_supervised_celeryd.sh":
mode: "000755"
owner: root
group: root
content: |
#!/usr/bin/env bash
# Get django environment variables
celeryenv=`cat /opt/python/current/env | tr '\n' ',' | sed 's/export //g' | sed 's/$PATH/%(ENV_PATH)s/g' | sed 's/$PYTHONPATH//g' | sed 's/$LD_LIBRARY_PATH//g'`
celeryenv=${celeryenv%?}
# Create celery configuraiton script
celeryconf="[program:celeryd]
; Set full path to celery program if using virtualenv
command=/opt/python/run/venv/bin/celery worker -A yourapp -B --loglevel=INFO -s /tmp/celerybeat-schedule
directory=/opt/python/current/app
user=nobody
numprocs=1
stdout_logfile=/var/log/celery-worker.log
stderr_logfile=/var/log/celery-worker.log
autostart=true
autorestart=true
startsecs=10
; Need to wait for currently executing tasks to finish at shutdown.
; Increase this if you have very long running tasks.
stopwaitsecs = 600
; When resorting to send SIGKILL to the program to terminate it
; send SIGKILL to its whole process group instead,
; taking care of its children as well.
killasgroup=true
; if rabbitmq is supervised, set its priority higher
; so it starts first
priority=998
environment=$celeryenv"
# Create the celery supervisord conf script
echo "$celeryconf" | tee /opt/python/etc/celery.conf
# Add configuration script to supervisord conf (if not there already)
if ! grep -Fxq "[include]" /opt/python/etc/supervisord.conf
then
echo "[include]" | tee -a /opt/python/etc/supervisord.conf
echo "files: celery.conf" | tee -a /opt/python/etc/supervisord.conf
fi
# Reread the supervisord config
supervisorctl -c /opt/python/etc/supervisord.conf reread
# Update supervisord in cache without restarting all services
supervisorctl -c /opt/python/etc/supervisord.conf update
# Start/Restart celeryd through supervisord
supervisorctl -c /opt/python/etc/supervisord.conf restart celeryd
Кстати, я использовал экспериментальную возможность взять в качестве брокера для Celery SQS и это вполне себя оправдало; правда, flower еще не имеет поддержки такой схемы.
Автоматическая переадресация HTTP на HTTPS
Используется такая вот добавка к конфигу Apache внутри ElasticBeanstalk
files:
"/etc/httpd/conf.d/ssl_rewrite.conf":
mode: "000644"
owner: root
group: root
content: |
RewriteEngine On
<If "-n '%{HTTP:X-Forwarded-Proto}' && %{HTTP:X-Forwarded-Proto} != 'https'">
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
</If>
Использование нескольких SSL доменов
Amazon предоставляет возможность владельцам доменов бесплатно использовать SSL сертификаты, в том числе и wildcard, однако только внутри самого AWS. Для использования нескольких доменов с SSL на одном environment получаем сертификат через AWS Certificate Manager, добавляем еще ELB балансировщик и настраиваем на нем SSL. Можно использовать и полученные у другого поставщика сертификаты.
UPDATE Ниже в комментарии уважаемый darken99 привел еще пару полезных фишек, позволю добавить их здесь с некоторыми пояснениями
Выключаем environment по расписанию
В данном случае в зависимости от указанного временного диапазона количество инстансов в autoscaling группе уменьшается с 1 до 0.
option_settings:
- namespace: aws:autoscaling:scheduledaction
resource_name: Start
option_name: MinSize
value: 1
- namespace: aws:autoscaling:scheduledaction
resource_name: Start
option_name: MaxSize
value: 1
- namespace: aws:autoscaling:scheduledaction
resource_name: Start
option_name: DesiredCapacity
value: 1
- namespace: aws:autoscaling:scheduledaction
resource_name: Start
option_name: Recurrence
value: "0 9 * * 1-5"
- namespace: aws:autoscaling:scheduledaction
resource_name: Stop
option_name: MinSize
value: 0
- namespace: aws:autoscaling:scheduledaction
resource_name: Stop
option_name: MaxSize
value: 0
- namespace: aws:autoscaling:scheduledaction
resource_name: Stop
option_name: DesiredCapacity
value: 0
- namespace: aws:autoscaling:scheduledaction
resource_name: Stop
option_name: Recurrence
value: "0 18 * * 1-5"
Замена Apache на Nginx
option_settings:
aws:elasticbeanstalk:environment:proxy:
ProxyServer: nginx
Поделиться с друзьями
darken99
Там возможностей миллион и маленькая тележка
random1st
Я же не ссылку на доку привел, а те вещи, которые там не описаны, или не так очевидны, с упором на сам сервис и его возможности в плане архитектуры.
darken99
В статье описано только применительно к Python и в общем довольно очевидные вещи.
Например при использовании Tomcat либо Docker solution никакого supervisor не будет.
Certificate manager притянут за уши.
Бакеты можно использовать с любым названием, главное чтобы в том же регионе был.
random1st
А я, собственно говоря, и не скрывал, что пишу применительно к развертыванию Python приложений. Буду рад почитать, если напишите что-то менее очевидное, информации в целом не так уж и много, исключая официальный форум и документацию.
darken99
В заголовке и названии статьи это не упомянуто.
Из полезного я бы добавил:
random1st
С Вашего разрешения добавил в статью. Что касается открытия портов — я так понимаю, Вы добавляете открытый порт на самом инстансе, а не на балансировщике?
darken99
Тут зависит от того, какой solution используется и какой тип — load balanсed либо standalone instance.
Если точно известно, что приложению нужен какой-то порт, который Elastc Beanstalk не открывает автоматом, можно через ebextensions, его открыть автоматически при деплое. Это избавит от необходимости вручную искать нужный Security Group и его туда добавлять.
darken99
А по сути Elastic Beanstalk в зависимости от solution использует разные Cloudformation templates, с четко определенными параметрами, их можно даже посмотреть в соответствующем разделе AWS Console.
yurtaev
Рекомендую https://github.com/briandilley/ebs-deploy Упрощает деплой + легче поддерживать сразу несколько окружений (dev/beta/prod)
random1st
Чем он лучше чем штатный awsebcli? Я существенной разницы не увидел.
yurtaev
Возможность в одном файле описать всё что нужно, при этом для разных окружений не описывать полную конфигурацию, а расширять базовую.