Главное Авторские колонки Вакансии Образование
Выбор редакции:
😼
Выбор
редакции
302 0 В избр. Сохранено
Авторизуйтесь
Вход с паролем

Эти 6 уроков работы с cloudformation я усвоил на всю жизнь

Cloudformation - один из лучших инструментов для развертывания приложения в облаке AWS. Он позволяет создавать и пересоздавать инфраструктуру и приложения без необходимости выполнять ручные действия или писать собственные скрипты.
Мнение автора может не совпадать с мнением редакции

Я начал работать с cloudformation 4 года назад. С тех пор я сломал много инфраструктур, даже те, которые уже были в продукции. Но каждый раз, когда я что-то портил, узнавал новое. Благодаря этому опыту, я поделюсь некоторыми из самых важных уроков, которые я выучил.

b_5ca745ba9bd6d.jpg

Урок 1: проверяйте изменения перед тем, как развернуть их

Я усвоил этот урок вскоре как начал работать с cloudformation. Не помню, что именно я тогда сломал, но точно помню, что использовал команду aws cloudformation update. Эта команда просто выкатывает шаблон без какой-либо проверки изменений, которые будут развернуты. Не думаю, что требуются объяснения, для чего нужно проверить все изменения перед тем, как развернуть их.

После этого провала, я сразу же изменил deployment pipeline, заменив команду update командой create-change-set

<code># OPERATION is either "UPDATE" or "CREATE"changeset_id=$(aws cloudformation create-change-set \ —change-set-name "$CHANGE_SET_NAME" \ —stack-name "$STACK_NAME" \ —template-body "$TPL_PATH" \ —change-set-type "$OPERATION" \ —parameters "$PARAMETERS" \ —output text \ —query Id)</code>
<code>aws cloudformation wait \ change-set-create-complete —change-set-name "$changeset_id"</code>

Когда набор изменений создан, он никак не влияет на существующий стек. В отличие от команды update, подход с использованием набора изменений не вызывает фактического развертывания. Вместо этого он создает список изменений, которые вы можете просмотреть до развертывания. Вы можете просмотреть изменения в интерфейсе консоли aws. Но если вы предпочитаете автоматизировать всё, что только можно, то проверяйте их в CLI:

<code># this command is presented only for demonstrational purposes.# the real command should take pagination into accountaws cloudformation describe-change-set \ —change-set-name "$changeset_id" \ —query 'Changes[*].ResourceChange.{Action:Action,Resource:ResourceType,ResourceId:LogicalResourceId,ReplacementNeeded:Replacement}' \ —output table</code>

Эта команда должна выдать вывод, похожий на следующий:

b_5ca7464584f08.jpg

Обратите особое внимание на изменения, где Action это Replace,Delete или где ReplacementNeeded — True. Это самые опасные изменения и обычно они приводят к потере информации.

Когда изменения просмотрены, они могут быть развернуты

<code>aws cloudformation execute-change-set —change-set-name "$changeset_id"</code>
<code>operation_lowercase=$(echo "$OPERATION" | tr '[:upper:]' '[:lower:]')aws cloudformation wait "stack-${operation_lowercase}-complete" \ —stack-name "$STACK_NAME"</code>

Урок 2: используйте stack policy для предотвращения замены или удаления ресурсов с сохранением состояния

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

Несмотря на то, что наши скрипты отображали список изменений и просили подтверждения, изменение Replace было пропущено, потому что список изменений был настолько большим, что не помещался на экране. И поскольку это было обычное обновление в среде тестирования, изменениям уделялось не так много внимания.

Есть ресурсы, которые вы никогда не захотите заменить или удалить. Это statefull сервисы, такие как экземпляр базы данных RDS или кластер elastichsearch и т. д. Было бы хорошо, если бы aws автоматически отказывал в развертывании, если выполняемая операция потребует удаления такого ресурса. К счастью, cloudformation имеет встроенный способ сделать это. Это называется stack policy, и об этом можно больше узнать в документации:

<code>STACK_NAME=$1RESOURCE_ID=$2</code>
<code>POLICY_JSON=$(cat <<EOF{ "Statement" : [{ "Effect" : "Deny", "Action" : [ "Update:Replace", "Update:Delete" ], "Principal": "*", "Resource" : "LogicalResourceId/$RESOURCE_ID" }]}EOF)</code>
<code>aws cloudformation set-stack-policy —stack-name "$STACK_NAME" \ —stack-policy-body "$POLICY_JSON"</code>

Урок 3: используйте UsePreviousValue, когда обновляете стек с секретными параметрам

Когда вы создаете сущность RDS mysql AWS требует от вас предоставить MasterUsername и MasterUserPassword. Поскольку лучше не хранить секреты в исходном коде, и я хотел автоматизировать абсолютно все, я реализовал «умный механизм», при котором перед развертыванием учетные данные будут получены из s3, и если учетные данные не будут найдены, новые учетные данные генерируются и хранятся в s3.

Затем эти учетные данные будут переданы в качестве параметров команде cloudformation create-change-set. Во время экспериментов со скриптом случилось, что соединение с s3 было потеряно, и мой «умный механизм» рассматривал его как сигнал для генерации новых учетных данных.

Если бы я начал использовать этот скрипт в рабочей среде, и проблема с подключением возникла бы снова, он бы обновил стек новыми учетными данными. В этом конкретном случае ничего плохого не произойдет. Однако я отказался от такого подхода и начал использовать другой, предоставляя учетные данные только один раз — при создании стека. И позже, когда стек потребует обновления, я бы вместо указания секретного значения параметра просто использовал UsePreviousValue=true:

<code>aws cloudformation create-change-set \ —change-set-name "$CHANGE_SET_NAME" \ —stack-name "$STACK_NAME" \ —template-body "$TPL_PATH" \ —change-set-type "UPDATE" \ —parameters "ParameterKey=MasterUserPassword,UsePreviousValue=true"</code>

Урок 4: используйте rollback configuration

Другая команда, с которой я работал, использовала функцию cloudformation, называемую rollback configuration. Я не встречался с ней раньше и быстро понял, что это сделает развертывание моих стеков еще круче. Теперь я использую каждый раз, когда разворачиваю свой код в lambda или ECS с помощью cloudformation.

Как это работает: вы указываете CloudWatch alarm arn в параметре — rollback-configuration, когда создаете набор изменений. Позже, когда вы выполните набор изменений, aws отслеживает alarm не менее одной минуты. Он откатывает развертывание обратно, если в течение этого времени alarm изменяет состояние на ALARM.

Ниже приведен пример отрывка шаблона cloudformation, в котором я создаю cloudwatch alarm, отслеживающий пользовательскую метрику облака в виде числа ошибок в журналах облака (метрика создается через MetricFilter):

<code>Resources: # this metric tracks number of errors in the cloudwatch logs. In this # particular case it's assumed logs are in json format and the error logs are # identified by level "error". See FilterPattern ErrorMetricFilter: Type: AWS::Logs::MetricFilter Properties: LogGroupName: !Ref LogGroup FilterPattern: !Sub '{$.level = "error"}' MetricTransformations: - MetricNamespace: !Sub "${AWS::StackName}-log-errors" MetricName: Errors MetricValue: 1 DefaultValue: 0</code>
<code> ErrorAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: !Sub "${AWS::StackName}-errors" Namespace: !Sub "${AWS::StackName}-log-errors" MetricName: Errors Statistic: Maximum ComparisonOperator: GreaterThanThreshold Period: 1 # 1 minute EvaluationPeriods: 1 Threshold: 0 TreatMissingData: notBreaching ActionsEnabled: yes</code>

Сейчас alarm может быть использован как rollback триггер при выполнении набора инструментов:

<code>ALARM_ARN=$1</code>
<code>ROLLBACK_TRIGGER=$(cat <<EOF{ "RollbackTriggers": [ { "Arn": "$ALARM_ARN", "Type": "AWS::CloudWatch::Alarm" } ], "MonitoringTimeInMinutes": 1}EOF)</code>
<code>aws cloudformation create-change-set \ —change-set-name "$CHANGE_SET_NAME" \ —stack-name "$STACK_NAME" \ —template-body "$TPL_PATH" \ —change-set-type "UPDATE" \ —rollback-configuration "$ROLLBACK_TRIGGER"</code>

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

Легко развернуть не самую последнюю версию шаблона cloudformation, но это нанесет большой ущерб. Однажды так было у нас: разработчик не отправил последние изменения из Git и неосознанно развернул предыдущую версию стека. Это привело к простою приложения, которое использовало этот стек.

Что-то простое, например, добавление проверки, актуальна ли ветка, прежде чем выполнять развертывание, будет хорошо (если предположить, что git — это ваш инструмент контроля версий):

<code>git fetchHEADHASH=$(git rev-parse HEAD)UPSTREAMHASH=$(git rev-parse master@{upstream})</code>
<code>if [[ "$HEADHASH" != "$UPSTREAMHASH" ]] ; then echo "Branch is not up to date with origin. Aborting" exit 1fi</code>

Урок 6: не изобретайте велосипед

Может показаться, что развертывание с cloudformation — это легко. Вам просто нужна куча скриптов bash, выполняющих команды aws cli.

4 года назад я начинал с простых скриптов, которые называют aws cloudformation create-stack командой. Вскоре скрипт уже не был простым. Каждый выученный урок делал скрипт все более и более сложным. Было не только сложно, но и с кучей багов.

Сейчас я работаю в небольшом ИТ-отделе. Опыт показывает, что у каждой команды есть свой способ развертывания стеков cloudformation. И это плохо. Было бы лучше, если бы все использовали единый подход. К счастью, существует множество инструментов, которые помогают развернуть и настроить стеки cloudformation.

Эти уроки помогут вам избежать ошибок.

Перевод статьи Lessons learned from 4 years of working with cloudformation от Digital Skynet

0
В избр. Сохранено
Авторизуйтесь
Вход с паролем
Комментарии
Выбрать файл
Блог проекта
Расскажите историю о создании или развитии проекта, поиске команды, проблемах и решениях
Написать
Личный блог
Продвигайте свои услуги или личный бренд через интересные кейсы и статьи
Написать

Spark использует cookie-файлы. С их помощью мы улучшаем работу нашего сайта и ваше взаимодействие с ним.