Инструменты пользователя

Инструменты сайта


aws:beanstalk-environment-options-precedence

Beanstalk: порядок и приоритет применения опций

Введение

На днях столкнулся с интересным поведением применения опций окружения в Beanstalk. Beanstalk - это PaaS(Platform as a Service), один из способов предоставления клиенту готовой программной среды. Одновременно предоставляются инструменты для тонкой настройки такой среды. Элементами PaaS является аппаратное обеспечение, операционная система, СУБД, промежуточное ПО, инструменты тестирования и разработки. AWS Elastic Beanstalk – это простой в использовании сервис для развертывания и масштабирования веб-приложений и сервисов, разработанных с помощью Java, .NET, PHP, Node.js, Python, Ruby, Go и Docker, на серверах Apache, Nginx, Passenger и IIS.

Т.е. по сути AWS предоставляет вам уже настроенную платформу для определенного вида окружений, например, Java/PHP/Python и т.п. И соответственно следит за ее обновлениями и совместимостью. Конечно не все так гладко, как хотелось бы, но в целом очень удобный сервис. Другими словами Beanstalk представляет набор сервисов AWS таких как EC2, ELB, Autoscaling, RDS, S3 и т.д. и выступает в неком роде оркестратором.

Для автоматизации деплоя мы используем Jenkins и одним из параметров сборки является тип EC2 инстанса, на котором необходимо запускать приложение. Одним из условий данной сборки является возможность динамически менять тип EC2 при каждой сборке и деплое.

Создание окружения

Мы используем Multicontainer docker environment, внутри которого запускаем java приложение, но в контексте данной статьи это не столь принципиально. Для описания того, как запускать docker образ и с какими параметрами используется специальный файл - Dockerrun.aws.json. Ниже привожу самый простой пример

{
    "AWSEBDockerrunVersion": 2,

    "containerDefinitions": [{
        "memory": 256,
        "cpu": 1,
        "name": "nginx",
        "image": "nginx",

        "portMappings": [{
            "hostPort": 80,
            "containerPort": 80
        }]
    }]
}

Это обычный json файл с довольно таки простым синтаксисом. Вышеприведенный файл запустит контейнер с nginx, выделит ему 256 Mb памяти и пробросит 80й порт с docker хоста внутрь контейнера. Для того, чтобы создать окружения на базе нашего Dockerrun.aws.json необходимо воспользоваться утилитой командной строки Beanstalk - aws eb cli. Поскольку написана она на python, то устанавливается она довольно таки просто - через pip.

Непосредственно перед созданием самого окружения, нам необходимо произвести инициализацию приложения.

# eb init TEST --profile personal --region us-west-1    
Application TEST has been created.

It appears you are using Multi-container Docker. Is this correct?
(Y/n): y

Select a platform version.
1) Multi-container Docker 17.03.2-ce (Generic)
2) Multi-container Docker 1.11.2 (Generic)
(default is 1): 1
Cannot setup CodeCommit because there is no Source Control setup, continuing with initialization
Do you want to set up SSH for your instances?
(Y/n): y

Select a keypair.
1) test
2) [ Create new KeyPair ]
(default is 1): 1

По сути, все что делает эта команда - создает служебную папку .elasticbeanstalk и конфигурационный файл config.yml внутри нее.

# tree -a
.
├── Dockerrun.aws.json
├── .elasticbeanstalk
│   └── config.yml
└── .gitignore

1 directory, 3 files

# cat .elasticbeanstalk/config.yml
branch-defaults:
  default:
    environment: null
    group_suffix: null
global:
  application_name: TEST
  branch: null
  default_ec2_keyname: test
  default_platform: Multi-container Docker 17.03.2-ce (Generic)
  default_region: us-west-1
  include_git_submodules: true
  instance_profile: null
  platform_name: null
  platform_version: null
  profile: personal
  repository: null
  sc: null
  workspace_type: Application

Если посмотреть в веб консоли самого Beanstalk, то там появится созданное нами приложение TEST

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

# eb create ebns-options-precedence --cname ebns-options-precedence \
--region us-west-1 --profile personal \
--vpc --vpc.id vpc-44bbbb21 --vpc.securitygroups sg-4b8fe72d \
--vpc.ec2subnets subnet-140fc770,subnet-1344584a \
--vpc.elbsubnets subnet-140fc770,subnet-1344584a \
--vpc.elbpublic --vpc.public \
--keyname test \
--instance_profile aws-elasticbeanstalk-ec2-role \
--instance_type t2.nano

Creating application version archive "app-171009_183531".
Uploading TEST/app-171009_183531.zip to S3. This may take a while.
Upload Complete.
WARNING: Uploaded SSH public key for "tkap" into EC2 for region us-west-1.
Environment details for: ebns-options-precedence
  Application name: TEST
  Region: us-west-1
  Deployed Version: app-171009_183531
  Environment ID: e-mxej3cvxn4
  Platform: arn:aws:elasticbeanstalk:us-west-1::platform/Multi-container Docker running on 64bit Amazon Linux/2.7.5
  Tier: WebServer-Standard
  CNAME: ebns-options-precedence.us-west-1.elasticbeanstalk.com
  Updated: 2017-10-09 15:35:38.704000+00:00
Printing Status:
INFO: createEnvironment is starting.
INFO: Using elasticbeanstalk-us-west-1-012345678910 as Amazon S3 storage bucket for environment data.
INFO: Created security group named: sg-568fe730
INFO: Created load balancer named: awseb-e-m-AWSEBLoa-QZ2VZZD361DW
INFO: Created security group named: sg-2380e845
INFO: Created Auto Scaling launch configuration named: awseb-e-mxej3cvxn4-stack-AWSEBAutoScalingLaunchConfiguration-73VVARDND7Z9
INFO: Environment health has transitioned to Pending. Initialization in progress (running for 8 seconds). There are no instances.
INFO: Added instance [i-0353e83bb20fbbf2d] to your environment.
INFO: Created Auto Scaling group named: awseb-e-mxej3cvxn4-stack-AWSEBAutoScalingGroup-CK1YXIHAR1LC
INFO: Waiting for EC2 instances to launch. This may take a few minutes.
INFO: Created Auto Scaling group policy named: arn:aws:autoscaling:us-west-1:012345678910:scalingPolicy:d6b10534-9844-4256-b66a-68f344979553:autoScalingGroupName/awseb-e-mxej3cvxn4-stack-AWSEBAutoScalingGroup-CK1YXIHAR1LC:policyName/awseb-e-mxej3cvxn4-stack-AWSEBAutoScalingScaleDownPolicy-RZ43ZCFUSMO4
INFO: Created Auto Scaling group policy named: arn:aws:autoscaling:us-west-1:012345678910:scalingPolicy:bc8d32d4-5a90-4110-92cc-85a1874aacaf:autoScalingGroupName/awseb-e-mxej3cvxn4-stack-AWSEBAutoScalingGroup-CK1YXIHAR1LC:policyName/awseb-e-mxej3cvxn4-stack-AWSEBAutoScalingScaleUpPolicy-18QZ4TJ2YUWGU
INFO: Created CloudWatch alarm named: awseb-e-mxej3cvxn4-stack-AWSEBCloudwatchAlarmHigh-116GPNS213CY4
INFO: Created CloudWatch alarm named: awseb-e-mxej3cvxn4-stack-AWSEBCloudwatchAlarmLow-111DBMFL47DTV
INFO: Starting new ECS task with awseb-ebns-options-precedence-mxej3cvxn4:1.
INFO: ECS task: arn:aws:ecs:us-west-1:012345678910:task/c562d6ef-e9de-42e8-9335-b1da8fd2aa79 is RUNNING.
INFO: Successfully launched environment: ebns-options-precedence

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

# eb status -v
Environment details for: ebns-options-precedence
  Application name: TEST
  Region: us-west-1
  Deployed Version: app-171009_183531
  Environment ID: e-mxej3cvxn4
  Platform: arn:aws:elasticbeanstalk:us-west-1::platform/Multi-container Docker running on 64bit Amazon Linux/2.7.5
  Tier: WebServer-Standard
  CNAME: ebns-options-precedence.us-west-1.elasticbeanstalk.com
  Updated: 2017-10-09 15:39:11.239000+00:00
  Status: Ready
  Health: Green
  Running instances: 1
      i-0353e83bb20fbbf2d: InService
      
# curl -i http://ebns-options-precedence.us-west-1.elasticbeanstalk.com/
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Type: text/html
Date: Mon, 09 Oct 2017 15:56:00 GMT
ETag: "5989d7cc-264"
Last-Modified: Tue, 08 Aug 2017 15:25:00 GMT
Server: nginx/1.13.5
Content-Length: 612
Connection: keep-alive
...
...
...

Модификация окружения

Если вы обратили внимание, то при создании окружения мы использовали ключ –instance_type t2.nano, где явно задали какой тип инстанса необходимо создать в нашем окружении. И все бы хорошо, но теперь нам необходимо сделать возможность менять это значение. Beanstalk поддерживает несколько путей модификации окружений

  • через специальную папку .ebextensions, в том числе позволяет использовать Cloudformation шаблоны, правда со своими ограничениями
  • через api - update-environment
  • eb cli (eb config/eb upgrade)
  • Beanstalk Environment Management Console

Последние два способа нам не подходят, так как практически не поддаются автоматизации. Использование чистого api тоже довольно таки сложно автоматизировать, хотя и можно. А самый простой способ - использование .ebextensions. Все что нам нужно, создать папку .ebextensions и внутри нее добавить файлы с расширением config. И вот тут начинается самое интересное.

Мы, законопослушные граждане, читаем документацию и находим, что для изменения типа инстанса достаточно использовать т.н. namespace - aws:autoscaling:launchconfiguration. Создаем соответствующую структуру папок и сам файл

# tree -a
.
├── Dockerrun.aws.json
├── .ebextensions
│   └── ec2-settings.config
├── .elasticbeanstalk
│   └── config.yml
└── .gitignore

2 directories, 4 files

# cat .ebextensions/ec2-settings.config
option_settings:
    aws:autoscaling:launchconfiguration:
        InstanceType: t2.micro

Напоминаю, что изначально мы создали окружение с t2.nano, а сейчас хотим перейти на более мощный инстанс t2.micro. После этого обновляем наше окружение.

# eb deploy
Creating application version archive "app-171009_200401".
Uploading TEST/app-171009_200401.zip to S3. This may take a while.
Upload Complete.
INFO: Environment update is starting.
INFO: Deploying new version to instance(s).
INFO: Stopping ECS task arn:aws:ecs:us-west-1:012345678910:task/c562d6ef-e9de-42e8-9335-b1da8fd2aa79.
INFO: ECS task: arn:aws:ecs:us-west-1:012345678910:task/c562d6ef-e9de-42e8-9335-b1da8fd2aa79 is STOPPED.
INFO: Starting new ECS task with awseb-ebns-options-precedence-mxej3cvxn4:3.
INFO: ECS task: arn:aws:ecs:us-west-1:012345678910:task/4d0ea10e-68d1-4990-872b-7fb0e85685b5 is RUNNING.
INFO: New application version was deployed to running EC2 instances.
INFO: Environment update completed successfully.

Проверяем тип инстанса

# aws ec2 describe-instances --profile cardpool-dev --region us-west-1 \
--instance-ids i-0353e83bb20fbbf2d  --query "Reservations[].Instances[].InstanceType"
[
    "t2.nano"
]

Странно, но как мы видим, тип не изменился. Пробуем изменить напрямую через aws api

# aws elasticbeanstalk update-environment --environment-name ebns-options-precedence \
--profile personal --region us-west-1 \
--option-settings Namespace=aws:autoscaling:launchconfiguration,OptionName=InstanceType,Value=t2.small

# eb status -v | tail -2
  Running instances: 1
      i-049d31ef7196304a1: InService

Как мы видим по статусу id у нашего инстанса изменился, а значит и тип скорее всего. Проверяем

# aws ec2 describe-instances --instance-ids i-049d31ef7196304a1 \
--profile personal --region us-west-1 --query "Reservations[].Instances[].InstanceType"
[
    "t2.small"
]

Да, так и есть. Тип инстанса поменялся. Но каждый раз вызывать из Jenkins aws elasticbeanstalk update-environment не очень удобно, к тому же нам надо будет отслеживать поменялся ли тип инстанса по сравнению с предыдущим деплоем, что лишь усложнит сам процесс деплоя. Через Beanstalk Webconsole тип инстанса так же меняется без проблем.

Автоматизируем обновление настроек

Снова обращаемся к документации и находим там раздел Precedence

Итак, во время создания и/или обновления окружения опции применяется в следующем приоритете. Список приведен от наиболее приоритетных к менее приоритетным.

  • Настройки непосредственно применяемые к окружению через Elastic Beanstalk API любым из клиентов, включая AWS Management Console, EB CLI, AWS CLI и SDKs. AWS Management Console и EB CLI так же применяют рекомендуемые значения для некоторых настроек.
  • Saved Configurations – настройки для любых опций, которые не были заданы явно и загружаются из т.н. сохраненных конфигураций.
  • Конфигурационные файлы (.ebextensions) – настройки, которые не были заданы явно, а так же не были заданы в сохраненных конфигурациях с предыдущего пункта.
  • Значения по умолчанию (Default Values) – если какой-либо параметр имеет значение по умолчанию, то оно будет применятся только, если сама опция не была задана ни на одном из предыдущих уровней.

Теперь становится понятно, почему значение InstanceType из нашего файла .ebextensions/ec2-settings.config не имеет эффекта. Но что же тогда делать, оказывается выход есть. После создания окружения нам надо лишь удалить соответствующий параметр на уровне API и тогда начнет действовать значение параметров из .ebextensions

# aws elasticbeanstalk update-environment --profile personal --region us-west-1 \
--environment-name ebns-options-precedence \
--options-to-remove Namespace=aws:autoscaling:launchconfiguration,OptionName=InstanceType 

Данный вызов необходимо выполнить только один раз, после первоначального создания окружения. Как по мне - то такое поведение является очень не удобным и не очевидным. Получается, что с помощью eb cli вы можете задать тип инстанса, но сменить его в дальнейшем вы уже не можете.

Так же стоит учитывать, что если после удаления данной опции, кто либо поменяет ее через Web Console и/или через aws api cli, то настройки из .ebextensions опять перестанут применяться из-за приоритета и нужно будет заново выполнить options-to-remove

aws/beanstalk-environment-options-precedence.txt · Последние изменения: 2017/10/09 23:16 — root