Queste 6 lezioni apprese con CloudFormation le porterò con me per tutta la vita

Ho iniziato a lavorare con cloudformation 4 anni fa. Da allora ho danneggiato molte infrastrutture, anche quelle che erano già in produzione. Ma ogni volta che rompevo qualcosa, imparavo qualcosa di nuovo. Grazie a questa esperienza, condividerò alcune delle lezioni più importanti che ho appreso.

Queste 6 lezioni apprese con CloudFormation le porterò con me per tutta la vita

Lezione 1: controlla le modifiche prima di implementarle

Ho imparato questa lezione subito dopo aver iniziato a lavorare con cloudformation. Non ricordo cosa ho rovinato esattamente, ma ricordo benissimo che ho usato il comando aws cloudformation update. Questo comando applica semplicemente il modello senza alcun controllo delle modifiche che saranno implementate. Non penso che servano spiegazioni sul perché sia necessario controllare tutte le modifiche prima di implementarle.

Dopo quell'insuccesso, ho subito modificato il pipeline di deployment, sostituendo il comando update con il comando 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"

Quando viene creato un set di modifiche, non influisce sullo stack esistente. A differenza del comando update, l'approccio con il set di modifiche non attiva un effettivo deployment. Invece, crea un elenco di modifiche che puoi esaminare prima del deployment. Puoi visualizzare le modifiche nell'interfaccia della console AWS. Ma se preferisci automatizzare tutto ciò che è possibile, controllale nel 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

Questo comando dovrebbe restituire un output simile al seguente:

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

Fai particolare attenzione alle modifiche in cui l'Action è Replace, Elimina o dove ReplacementNeeded è True. Queste sono le modifiche più rischiose e di solito portano a una perdita di dati.

Una volta esaminate le modifiche, possono essere implementate.

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"

Lezione 2: utilizza la stack policy per evitare la sostituzione o la rimozione delle risorse mantenendo lo stato

A volte, semplicemente visualizzare le modifiche non è sufficiente. Siamo tutti esseri umani e commettiamo errori. Poco dopo aver iniziato a usare i set di modifiche, un mio collega ha involontariamente eseguito un deployment, causando un aggiornamento del database. Non è successo nulla di grave, poiché si trattava di un ambiente di test.

Anche se i nostri script mostravano un elenco di modifiche e richiedevano conferma, la modifica Replace è stata ignorata perché l'elenco delle modifiche era così lungo da non entrare nello schermo. E poiché si trattava di un aggiornamento usuale in un ambiente di test, non è stata prestata molta attenzione alle modifiche.

Ci sono risorse che non vorresti mai sostituire o eliminare. Si tratta di servizi stateful, come un'istanza di database RDS o un cluster Elasticsearch, ecc. Sarebbe utile se AWS rifiutasse automaticamente il deployment se l'operazione eseguita richiedesse l'eliminazione di tale risorsa. Fortunatamente, CloudFormation ha un modo integrato per farlo. Questo è chiamato stack policy, e puoi saperne di più in documentazione:

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"

Lezione 3: utilizza UsePreviousValue quando aggiorni lo stack con parametri segreti

Quando crei un'istanza RDS MySQL, AWS richiede di fornire MasterUsername e MasterUserPassword. Poiché è meglio non conservare segreti nel codice sorgente, e io volevo automatizzare tutto, ho implementato un "meccanismo intelligente", in cui le credenziali vengono recuperate da S3 prima del deployment, e se le credenziali non vengono trovate, nuove credenziali vengono generate e salvate in S3.

Successivamente, queste credenziali saranno passate come parametri al comando cloudformation create-change-set. Durante gli esperimenti con lo script, è successo che la connessione con s3 è stata persa, e il mio 'meccanismo intelligente' l'ha interpretato come un segnale per generare nuove credenziali.

Se avessi iniziato a utilizzare questo script in un ambiente di produzione e si fosse ripresentato il problema di connessione, avrebbe aggiornato lo stack con nuove credenziali. In questo caso specifico, non succederebbe nulla di male. Tuttavia, ho abbandonato questo approccio e ho iniziato a usarne un altro, fornendo le credenziali solo una volta — al momento della creazione dello stack. E in seguito, quando lo stack richiamerà un aggiornamento, al posto di specificare il valore segreto del parametro, utilizzerei semplicemente 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"

Lezione 4: utilizza la rollback configuration

Un altro comando con cui ho lavorato utilizzava una funzione cloudformation, chiamata rollback configuration. Non l'avevo mai incontrata prima e ho subito capito che questo renderebbe il dispiegamento dei miei stack ancora più interessante. Ora la utilizzo ogni volta che dispiego il mio codice in lambda o ECS tramite cloudformation.

Ecco come funziona: si specifica l'arn dell'allerta di CloudWatch nel parametro —rollback-configuration, quando si crea un insieme di modifiche. In seguito, quando si esegue l'insieme di modifiche, aws monitora l'allerta per almeno un minuto. Ripristina il dispiegamento se durante questo periodo l'allerta cambia stato in ALARM.

Di seguito è riportato un esempio di un frammento di modello cloudformation, in cui creo un'allerta cloudwatch, che monitora una metrica personalizzata del cloud come numero di errori nei log del cloud (la metrica è creata tramite MetricFilter):

Risorse:
  # questo metrica tiene traccia del numero di errori nei log di CloudWatch. In questo
  # caso specifico si presume che i log siano in formato JSON e i log degli errori siano
  # identificati dal livello "error". Vedi FilterPattern
  ErrorMetricFilter:
    Tipo: AWS::Logs::MetricFilter
    Proprietà:
      LogGroupName: !Ref LogGroup
      FilterPattern: !Sub '{$.level = "error"}'
      MetricTransformations:
      - MetricNamespace: !Sub "${AWS::StackName}-log-errors"
        MetricName: Errors
        MetricValue: 1
        DefaultValue: 0

  ErrorAlarm:
    Tipo: AWS::CloudWatch::Alarm
    Proprietà:
      AlarmName: !Sub "${AWS::StackName}-errors"
      Namespace: !Sub "${AWS::StackName}-log-errors"
      MetricName: Errors
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      Period: 1 # 1 minuto
      EvaluationPeriods: 1
      Threshold: 0
      TreatMissingData: notBreaching
      ActionsEnabled: yes

Adesso allarme può essere usato come rollback trigger durante l'esecuzione di un insieme di strumenti:

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"

Lezione 5: assicurati di eseguire il deployment dell'ultima versione del template

È facile distribuire una versione non recente del modello cloudformation, ma ciò comporterebbe gravi conseguenze. Ci è già successo: uno sviluppatore non ha inviato le ultime modifiche da Git e ha inavvertitamente distribuito una versione precedente dello stack. Questo ha causato un'interruzione dell'applicazione che utilizzava quello stack.

Qualcosa di semplice, come aggiungere un controllo per verificare se il branch è aggiornato prima di eseguire la distribuzione, sarebbe utile (supponendo che git sia il vostro strumento di controllo versioni):

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

if [[ "$HEADHASH" != "$UPSTREAMHASH" ]] ; then
   echo "Il branch non è aggiornato con l'origin. Operazione annullata"
   exit 1
fi

Lezione 6: non reinventare la ruota

Potrebbe sembrare che distribuire con cloudformation sia facile. Hai solo bisogno di un sacco di script bash che eseguono comandi aws cli.

Quattro anni fa ho iniziato con semplici script che chiamano il comando aws cloudformation create-stack. Ben presto lo script non era più semplice. Ogni lezione appresa rendeva lo script sempre più complesso. Non era solo difficile, ma anche pieno di bug.

Attualmente lavoro in un piccolo dipartimento IT. L'esperienza dimostra che ogni team ha il proprio modo di implementare i stack CloudFormation. Questo è un problema. Sarebbe meglio se tutti usassero un approccio unificato. Fortunatamente, ci sono molti strumenti che aiutano a implementare e configurare gli stack CloudFormation.

Questi tutorial ti aiuteranno a evitare errori.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS | ProHoster