De 6 lektioner, jeg har lært om cloudformation, vil jeg huske for livet

Jeg begyndte at arbejde med cloudformation for 4 år siden. Siden da har jeg brudt mange infrastrukturer, selv dem, der allerede var i produktion. Men hver gang jeg ødelagde noget, lærte jeg noget nyt. Takket være denne erfaring vil jeg dele nogle af de mest vigtige lektioner, jeg har lært.

De 6 lektioner, jeg har lært om cloudformation, vil jeg huske for livet

Lektion 1: Tjek ændringerne, før du ruller dem ud

Jeg lærte denne lektion snart efter, at jeg begyndte at arbejde med cloudformation. Jeg kan ikke huske, hvad jeg har ødelagt dengang, men jeg husker tydeligt, at jeg brugte kommandoen aws cloudformation update. Denne kommando ruller bare skabelonen ud uden at kontrollere de ændringer, der vil blive implementeret. Jeg tror ikke, jeg behøver at forklare, hvorfor det er nødvendigt at tjekke alle ændringer, før de implementeres.

Efter det nederlag ændrede jeg straks deployment pipeline, og erstattede kommandoen update med kommandoen 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"

Når ændringssættet er oprettet, påvirker det ikke den eksisterende stak. I modsætning til kommandoen update forårsager tilgangen med et ændringssæt ikke en faktisk implementering. I stedet opretter det en liste over ændringer, som du kan se før implementeringen. Du kan se ændringerne i AWS-konsollens grænseflade. Men hvis du foretrækker at automatisere alt, så tjek dem i 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

Denne kommando skal producere output, der ligner følgende:

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

Vær særligt opmærksom på ændringer, hvor Action er Replace, Slet eller hvor ReplacementNeeded — True. Disse er de mest risikable ændringer og fører som regel til datatab.

Når ændringerne er blevet gennemgået, kan de implementeres

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"

Lektion 2: Brug stackpolicies for at forhindre erstatning eller sletning af ressourcer med bibeholdelse af tilstand

Nogle gange er det kun utilstrækkeligt at gennemgå ændringer. Vi er alle mennesker, og vi laver alle fejl. Kort efter at vi begyndte at bruge ændringssæt, udførte en af mine teamkolleger utilsigtet en implementering, som førte til en opdatering af databasen. Der skete ikke noget alvorligt, fordi det var et testmiljø.

Selvom vores scripts viste en liste over ændringer og bad om bekræftelse, blev ændringen Replace overset, fordi ændringslisten var så lang, at den ikke kunne være på skærmen. Og da det drejede sig om en rutinemæssig opdatering i testmiljøet, blev ændringerne ikke taget så seriøst.

Der er ressourcer, som du aldrig vil ønsker at erstatte eller slette. Det er stateful tjenester som en RDS-databaseinstans eller en Elasticsearch-klynge osv. Det ville være godt, hvis AWS automatisk nægtede implementeringen, hvis den udførte operation krævede sletning af en sådan ressource. Heldigvis har CloudFormation en indbygget måde at gøre dette på. Det kaldes stackpolitik, og du kan lære mere om det i dokumentationen:

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"

Lektion 3: brug UsePreviousValue, når du opdaterer stakken med hemmelige parametre.

Når du opretter en RDS MySQL-enhed, beder AWS dig om at angive MasterUsername og MasterUserPassword. Da det er bedst ikke at gemme hemmeligheder i kildekoden, og jeg ønskede at automatisere alt, implementerede jeg en 'smart mekanisme', hvor legitimationsoplysningerne hentes fra S3 før implementeringen, og hvis legitimationsoplysningerne ikke findes, genereres nye legitimationsoplysninger og gemmes i S3.

Derefter vil disse legitimationsoplysninger blive sendt som parametre til kommandoen cloudformation create-change-set. Under eksperimenterne med scriptet skete det, at forbindelsen til S3 blev tabt, og min 'smarte mekanisme' betragtede dette som et signal til at generere nye legitimationsoplysninger.

Hvis jeg begyndte at bruge dette script i et produktionsmiljø, og forbindelsesproblemet skulle opstå igen, ville det opdatere stakken med nye loginoplysninger. I dette specifikke tilfælde ville der ikke ske noget dårligt. Jeg har imidlertid afvist en sådan tilgang og begyndt at bruge en anden, hvor jeg kun angiver loginoplysninger én gang — når jeg opretter stakken. Og senere, når stakken har brug for en opdatering, ville jeg i stedet for at angive en hemmelig parameterbare værdi blot bruge 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"

Lektion 4: brug rollback-konfiguration

Et andet kommando, jeg arbejdede med, brugte en funktion cloudformation, kaldet rollback-konfiguration. Jeg har ikke set det før og indså hurtigt, at det ville gøre implementeringen af mine stakker endnu bedre. Nu bruger jeg det hver gang, jeg implementerer min kode i lambda eller ECS via cloudformation.

Sådan fungerer det: du angiver CloudWatch alarm arn i parameteren —rollback-configuration, når du opretter et ændringssæt. Senere, når du udfører ændringssættet, overvåger aws alarmen i mindst et minut. Den ruller implementeringen tilbage, hvis alarmen ændrer status til ALARM i løbet af denne tid.

Her er et eksempel på en skabelonuddrag cloudformation, hvor jeg opretter cloudwatch alarm, der overvåger en brugerdefineret måling af skyen i form af antallet af fejl i cloudlogs (målingen oprettes gennem MetricFilter):

Resources:
  # denne måling sporer antallet af fejl i cloudwatch logs. I dette
  # specifikke tilfælde antages det, at loggene er i json-format, og fejlloggene identificeres
  # ved niveauet "error". Se 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 minut
      EvaluationPeriods: 1
      Threshold: 0
      TreatMissingData: notBreaching
      ActionsEnabled: yes

Nu alarm kan bruges som rollback trigger, når du udfører et ændringssæt:

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"

Lektion 5: Sørg for, at du implementerer den nyeste version af skabelonen

Det er let at implementere en gammel version af cloudformation-skabelonen, men det kan forårsage store problemer. En gang oplevede vi: udvikleren glemte at sende de seneste ændringer fra Git og implementerede ubevidst en tidligere version af stakken. Det førte til nedetid for applikationen, der benyttede denne stak.

Noget så simpelt som at tilføje en kontrol for, om grenen er aktuel, før du udfører implementeringen, vil være en god idé (forudsat at git er dit versionsstyringsværktøj):

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

if [[ "$HEADHASH" != "$UPSTREAMHASH" ]] ; then
   echo "Grenen er ikke opdateret med origin. Afbryder"
   exit 1
fi

Lektion 6: Opfind ikke hjulet på ny

Det kan synes, at implementering med cloudformation er let. Du har bare brug for en masse bash-scripts, der kører aws cli-kommandoer.

For 4 år siden startede jeg med simple scripts, der kaldte aws cloudformation create-stack-kommandoen. Snart blev scriptet ikke længere simpelt. Hver lært lektion gjorde scriptet mere og mere komplekst. Det var ikke kun svært, men også fyldt med fejl.

Nu arbejder jeg i en lille IT-afdeling. Erfaringen viser, at hver gruppe har sin egen måde at implementere cloudformation-stakke på. Og det er dårligt. Det ville være bedre, hvis alle brugte en ensartet tilgang. Heldigvis findes der mange værktøjer, der hjælper med at implementere og konfigurere cloudformation-stakke.

Disse lektioner vil hjælpe dig med at undgå fejl.

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster