Тези 6 урока, свързани с cloudformation, усвоих за цял живот

Започнах да работя с cloudformation преди 4 години. Оттогава съм разрушил много инфраструктури, дори и такива, които вече бяха в продукция. Но всеки път, когато нещо се проваляше, научавах нещо ново. Благодарение на този опит, ще споделя някои от най-важните уроци, които научих.

Тези 6 урока, свързани с cloudformation, усвоих за цял живот

Урок 1: проверявайте промените преди да ги разгръщате

Научих този урок скоро след като започнах да работя с cloudformation. Не си спомням точно какво счупих тогава, но помня, че използвах командата aws cloudformation update. Тази команда просто разгръща шаблон без никаква проверка на промените, които ще бъдат разгръщани. Не мисля, че е нужно да обяснявам защо трябва да проверите всички промени преди да ги разгръщате.

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

# 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)

aws cloudformation wait 
    change-set-create-complete --change-set-name "$changeset_id"

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

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

Тази команда трябва да изведе резултат, подобен на следния:

--------------------------------------------------------------------
|                         DescribeChangeSet                        |
+---------+--------------------+----------------------+------------+
| Action  | ReplacementNeeded  |      Resource        | ResourceId |
+---------+--------------------+----------------------+------------+
|  Modify | True               |  AWS::ECS::Cluster   |  MyCluster |
|  Replace| True               |  AWS::RDS::DBInstance|  MyDB      |
|  Add    | None               |  AWS::SNS::Topic     |  MyTopic   |
+---------+--------------------+----------------------+------------+

Обърнете особено внимание на промените, при които Action е Replace, Delete или при които ReplacementNeeded — True. Това са най-опасните промени и обикновено водят до загуба на информация.

Когато промените са прегледани, те могат да бъдат разгръщани

aws cloudformation execute-change-set --change-set-name "$changeset_id"

operation_lowercase=$(echo "$OPERATION" | tr '[:upper:]' '[:lower:]')
aws cloudformation wait "stack-${operation_lowercase}-complete"
    --stack-name "$STACK_NAME"

Урок 2: използвайте stack policy, за да предотвратите замяна или изтриване на ресурси, като запазите състоянието

Понякога простото преглеждане на промените не е достатъчно. Всички ние сме хора и допускаме грешки. Скоро след като започнахме да използваме набори от промени, мой колега неволно направи разгръщане, което доведе до актуализация на базата данни. Нищо страшно не се случи, тъй като това беше тестова среда.

Въпреки че нашите скриптове показваха списък с промени и искаха потвърждение, промяната Replace беше пропусната, тъй като списъкът с промени беше толкова дълъг, че не влизаше на екрана. И тъй като това беше обикновена актуализация в тестова среда, на промените не беше отделено толкова внимание.

Има ресурси, които никога не искате да заменяте или изтривате. Това са statefull услуги, като инстанция на RDS база данни или клъстер Elasticsearch и т.н. Беше хубаво, ако AWS автоматично отказваше разгръщането, ако изпълняваната операция изискваше изтриването на такъв ресурс. За щастие, CloudFormation има вграден начин да направи това. Това се нарича stack policy, и можете да научите повече за това в документацията:

STACK_NAME=$1
RESOURCE_ID=$2

POLICY_JSON=$(cat <<EOF
{
    "Statement" : [{
        "Effect" : "Deny",
        "Action" : [
            "Update:Replace",
            "Update:Delete"
        ],
        "Principal": "*",
        "Resource" : "LogicalResourceId/$RESOURCE_ID"
    }]
}
EOF
)

aws cloudformation set-stack-policy --stack-name "$STACK_NAME" 
    --stack-policy-body "$POLICY_JSON"

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

Когато създавате RDS MySQL инстанция, AWS изисква от вас да предоставите MasterUsername и MasterUserPassword. Понеже е по-добре да не съхранявате тайни в изходния код, а аз исках да автоматизирам абсолютно всичко, реализирах «умен механизъм», при който преди разгръщането, удостоверенията ще се извлекат от S3, и ако удостоверенията не бъдат намерени, ще се генерират нови удостоверения и ще се съхраняват в S3.

След това тези удостоверения ще бъдат предадени като параметри на командата cloudformation create-change-set. По време на експериментите с скрипта се случи, че връзката с S3 беше изгубена, и моят «умен механизъм» го разглеждаше като сигнал за генериране на нови удостоверения.

Ако започна да използвам този скрипт в работна среда и отново възникне проблем с връзката, той ще актуализира стекa с нови удостоверения. В този конкретен случай няма да се случи нищо лошо. Въпреки това, се отказах от такъв подход и започнах да използвам друг, предоставяйки удостоверения само веднъж — при създаването на стека. А по-късно, когато стекът се наложи да бъде актуализиран, вместо да посоча секретната стойност на параметъра, просто ще използвам UsePreviousValue=true:

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"

Урок 4: използвайте конфигурация за откат

Друга команда, с която работих, използваше функция cloudformation, наречена rollback configuration. Никога не бях виждал това преди и бързо разбрах, че ще направи разполагането на моите стекове още по-яко. Сега го използвам всеки път, когато разполагам кода си в lambda или ECS с помощта на cloudformation.

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

По-долу е пример на шаблон cloudformation, в който създавам cloudwatch alarm, проследяващ потребителска метрика на облака под формата на броя на грешките в облачните журнали (метриката се създава чрез MetricFilter):

Resources:
  # тази метрика проследява броя на грешките в облачните журнали. В този
  # конкретен случай се предполага, че журналите са във формат json и грешките са
  # идентифицирани по ниво "error". Вижте 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

  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 минута
      EvaluationPeriods: 1
      Threshold: 0
      TreatMissingData: notBreaching
      ActionsEnabled: yes

Сега алармата може да бъде използвана като rollback триггер при изпълнение на набора от инструменти:

ALARM_ARN=$1

ROLLBACK_TRIGGER=$(cat <<EOF
{
  "RollbackTriggers": [
    {
      "Arn": "$ALARM_ARN",
      "Type": "AWS::CloudWatch::Alarm"
    }
  ],
  "MonitoringTimeInMinutes": 1
}
EOF
)

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"

Урок 5: уверете се, че разгръщате най-новата версия на шаблона

Лесно е да разгрънете не най-новата версия на шаблона cloudformation, но това може да доведе до сериозни проблеми. Веднъж се случи, че разработчик не изпрати последните промени от Git и неволно разгради по-стара версия на стека. Това доведе до просто приложение, което използваше този стек.

Нещо просто, например добавянето на проверка дали клонът е актуален преди да изпълните разгръщането, би било добре (ако приемем, че git е вашият инструмент за контрол на версиите):

git fetch
HEADHASH=$(git rev-parse HEAD)
UPSTREAMHASH=$(git rev-parse master@{upstream})

if [[ "$HEADHASH" != "$UPSTREAMHASH" ]] ; then
   echo "Клонът не е актуален с origin. Прекратяване"
   exit 1
fi

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

Може да изглежда, че разгръщането с cloudformation е лесно. Просто ви трябват куп скриптове bash, изпълняващи команди aws cli.

Преди 4 години започнах с прости скриптове, които наричаха командата aws cloudformation create-stack. Скоро скриптът вече не беше прост. Всеки научен урок направи скрипта все по-сложен. Не само, че беше сложно, но и имаше много бъгове.

Сега работя в малък ИТ екип. Опитът показва, че всяка команда има свой собствен начин на разгръщане на стека cloudformation. И това е лошо. Беше по-добре, ако всички използваха единен подход. За щастие, има много инструменти, които помагат за разгръщането и конфигурирането на стека cloudformation.

Тези уроци ще ви помогнат да избегнете грешки.

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster