Aceste 6 lecții despre cloudformation le-am învățat pentru toată viața

Am început să lucrez cu cloudformation în urmă cu 4 ani. De atunci, am distrus multe infrastructuri, chiar și cele care erau deja în producție. Dar de fiecare dată când am stricat ceva, am învățat ceva nou. Datorită acestei experiențe, voi împărtăși câteva dintre cele mai importante lecții pe care le-am învățat.

Aceste 6 lecții despre cloudformation le-am învățat pentru toată viața

Lecția 1: verificați modificările înainte de a le desfășura

Am învățat această lecție imediat după ce am început să lucrez cu cloudformation. Nu-mi amintesc exact ce am stricat atunci, dar îmi amintesc clar că am folosit comanda aws cloudformation update. Această comandă desfășoară pur și simplu șablonul fără a verifica modificările care vor fi desfășurate. Nu cred că este nevoie de explicații despre de ce este important să verificați toate modificările înainte de a le desfășura.

După acest eșec, am schimbat imediat pipeline-ul de desfășurare, înlocuind comanda update cu comanda 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"

Atunci când setul de modificări este creat, acesta nu afectează în niciun fel stiva existentă. Spre deosebire de comanda update, abordarea folosind un set de modificări nu declanșează desfășurarea efectivă. În schimb, creează o listă de modificări pe care o puteți revizui înainte de desfășurare. Puteți vizualiza modificările în interfața consolei aws. Dar dacă preferați să automatizați tot ce se poate, verificați-le în 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

Această comandă ar trebui să returneze un rezultat similar cu următorul:

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

Acordați o atenție deosebită modificărilor unde Action este Replace, Delete sau unde ReplacementNeeded — True. Acestea sunt cele mai periculoase modificări și de obicei duc la pierderea informațiilor.

După revizuirea modificărilor, acestea pot fi desfășurate

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"

Lecția 2: utilizați politicile de stivă pentru a preveni înlocuirea sau ștergerea resurselor păstrând starea

Uneori, doar vizualizarea modificărilor nu este suficientă. Suntem cu toții oameni și facem greșeli. La scurt timp după ce am început să folosim seturile de modificări, colegul meu de echipă a efectuat fără să-și dea seama o desfășurare care a dus la actualizarea bazei de date. Nu s-a întâmplat nimic grav, deoarece era un mediu de testare.

Deși scripturile noastre afișau lista modificărilor și cereau confirmări, modificarea Replace a fost omisă deoarece lista modificărilor era atât de mare încât nu încăpea pe ecran. Și deoarece aceasta era o actualizare obișnuită în mediul de testare, modificărilor nu li s-a acordat prea multă atenție.

Există resurse pe care nu doriți niciodată să le înlocuiți sau să le eliminați. Acestea sunt servicii stateful, cum ar fi o instanță de bază de date RDS sau un cluster Elasticsearch etc. Ar fi bine dacă AWS ar refuza automat desfășurarea dacă operațiunea executată ar necesita eliminarea unei astfel de resurse. Din fericire, CloudFormation are o modalitate încorporată de a face acest lucru. Se numește politică de stack și se poate afla mai multe despre ea în documentation:

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"

Lectia 3: utilizați UsePreviousValue atunci când actualizați un stack cu parametrii secreți

Atunci când creați o entitate RDS MySQL, AWS vă cere să furnizați MasterUsername și MasterUserPassword. Deoarece este mai bine să nu stocați secrete în codul sursă și voiam să automatizez totul, am implementat un „mecanism inteligent”, prin care înainte de desfășurare acreditivile sunt obținute din S3, iar dacă acreditivele nu sunt găsite, se generează noi acreditive care sunt stocate în S3.

Apoi aceste acreditive vor fi transmise ca parametrii comenzii cloudformation create-change-set. În timpul experimentării cu scriptul, s-a întâmplat ca conexiunea cu S3 să fie pierdută, iar „mecanismul meu inteligent” a considerat aceasta un semnal pentru a genera noi acreditive.

Dacă aș începe să folosesc acest script într-un mediu de lucru, iar problema de conectare ar apărea din nou, acesta ar actualiza stiva cu noi acreditive. În acest caz specific, nimic rău nu s-ar întâmpla. Cu toate acestea, am renunțat la acest mod de abordare și am început să folosesc altul, furnizând acreditivele o singură dată - la crearea stivei. Și mai târziu, când stiva ar necesita actualizare, aș folosi în locul specificării valorii secrete a parametrului pur și simplu 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"

Lectia 4: folosiți rollback configuration

O altă comandă cu care am lucrat a folosit funcția cloudformation, numită rollback configuration. Nu m-am întâlnit cu asta înainte și am realizat rapid că va face desfășurarea stivelor mele și mai grozavă. Acum o folosesc de fiecare dată când desfășor codul meu în lambda sau ECS cu ajutorul cloudformation.

Cum funcționează: specificați CloudWatch alarm arn în parametrul --rollback-configuration, când creați un set de schimbări. Mai târziu, când veți executa setul de schimbări, aws monitorizează alarmă timp de minimum un minut. Întoarce desfășurarea înapoi dacă în acest interval alarmă își schimbă starea în ALARM.

Mai jos este un exemplu de fragment de șablon cloudformation, în care creez cloudwatch alarm, care monitorizează o metrica personalizată a cloudului sub formă de număr de erori în jurnalele cloud (metrica este creată prin MetricFilter):

Resources:
  # această metrică urmărește numărul de erori în jurnalele cloudwatch. În acest
  # caz particular se presupune că jurnalele sunt în format json și jurnalele de erori sunt
  # identificate prin nivelul "error". Consultați 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

Acum alarm poate fi utilizat ca rollback trigger atunci când executați un set de instrumente:

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"

Lecția 5: asigurați-vă că desfășurați cea mai recentă versiune a șablonului

Este ușor să desfășurați o versiune anterioară a șablonului cloudformation, dar acest lucru poate provoca daune semnificative. Odată s-a întâmplat: un dezvoltator nu a trimis cele mai recente modificări din Git și a desfășurat involuntar o versiune anterioară a stivei. Acest lucru a dus la o întrerupere a aplicației care utiliza acea stivă.

Ceva simplu, cum ar fi adăugarea unei verificări pentru a vedea dacă ramura este actualizată înainte de a desfășura, ar fi bine (presupunând că git este instrumentul dvs. de control al versiunilor):

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

if [[ "$HEADHASH" != "$UPSTREAMHASH" ]] ; then
   echo "Ramura nu este actualizată cu origin. Aborting"
   exit 1
fi

Lecția 6: nu reinventați roata

Poate părea că desfășurarea cu cloudformation — este simplă. Tot ce aveți nevoie sunt o mulțime de scripturi bash care execută comenzi aws cli.

Acum 4 ani am început cu scripturi simple care apelau comanda aws cloudformation create-stack. În scurt timp, scriptul nu a mai fost atât de simplu. Fiecare lecție învățată a făcut ca scriptul să devină din ce în ce mai complex. Nu era doar complicat, ci și plin de bug-uri.

Acum lucrez într-un departament IT mic. Experiența arată că fiecare echipă are propria modalitate de a desfășura stive cloudformation. Și acest lucru este problematic. Ar fi mai bine dacă toată lumea ar folosi o abordare unificată. Din fericire, există multe instrumente care ajută la desfășurarea și configurarea stivelor cloudformation.

Aceste lecții vă vor ajuta să evitați greșelile.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster