Diese 6 Lektionen über die Arbeit mit CloudFormation habe ich für mein ganzes Leben gelernt.

Ich habe vor cloudformation vor 4 Jahren angefangen zu arbeiten. Seitdem habe ich viele Infrastrukturen kaputtgemacht, sogar solche, die schon in der Produktion waren. Aber jedes Mal, wenn ich etwas kaputtgemacht habe, habe ich etwas Neues gelernt. Aufgrund dieser Erfahrung möchte ich einige der wichtigsten Lektionen, die ich gelernt habe, teilen.

Diese 6 Lektionen über die Arbeit mit CloudFormation habe ich für mein ganzes Leben gelernt.

Lektionen 1: Überprüfen Sie Änderungen, bevor Sie sie implementieren

Ich habe diese Lektion bald gelernt, nachdem ich angefangen habe, mit cloudformationzu arbeiten. Ich erinnere mich nicht genau, was ich damals kaputtgemacht habe, aber ich erinnere mich genau, dass ich den Befehl aws cloudformation updateverwendet habe. Dieser Befehl setzt einfach das Template um, ohne irgendwelche Änderungen zu überprüfen, die implementiert werden. Ich denke, es bedarf keiner Erklärung, warum es wichtig ist, alle Änderungen zu überprüfen, bevor Sie sie implementieren.

Nach diesem Misserfolg habe ich sofort die deployment pipelinegeändert, indem ich den Befehl update durch den Befehl 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"

ersetzt habe. Wenn ein Änderungsset erstellt wird, hat es keine Auswirkungen auf den bestehenden Stack. Im Gegensatz zum Befehl update führt der Ansatz mit einem Änderungsset nicht zu einer tatsächlichen Implementierung. Stattdessen wird eine Liste von Änderungen erstellt, die Sie vor der Implementierung überprüfen können. Sie können die Änderungen im aws-Konsole-Interface überprüfen. Aber wenn Sie alles automatisieren möchten, überprüfen Sie sie im 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

Dieser Befehl sollte eine Ausgabe ähnlich der folgenden erzeugen:

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

Achten Sie besonders auf Änderungen, bei denen Action Replace, Delete ist oder wo ReplacementNeeded — True. Dies sind die gefährlichsten Änderungen, die normalerweise zu Datenverlust führen.

Nachdem die Änderungen überprüft wurden, können sie implementiert werden

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: Verwenden Sie eine Stack-Policy, um die Ersetzung oder Löschung von Ressourcen im Zustand zu verhindern

Manchmal reicht es nicht aus, die Änderungen nur zu betrachten. Wir sind alle Menschen und machen Fehler. Kurz nachdem wir mit der Verwendung von Änderungssätzen begonnen hatten, führte mein Teamkollege unbeabsichtigt ein Deployment durch, was zu einem Update der Datenbank führte. Es ist nichts Schlimmes passiert, da es sich um eine Testumgebung handelte.

Obwohl unsere Skripte eine Liste der Änderungen anzeigten und um Bestätigung baten, wurde die Änderung Replace übersehen, da die Liste so lang war, dass sie nicht auf den Bildschirm passte. Und da es sich um ein reguläres Update in der Testumgebung handelte, wurde den Änderungen nicht so viel Aufmerksamkeit geschenkt.

Es gibt Ressourcen, die Sie niemals ersetzen oder löschen möchten. Dazu gehören zustandsbehaftete Dienste wie eine RDS-Datenbankinstanz oder ein Elasticsearch-Cluster usw. Es wäre gut, wenn AWS das Deployment automatisch verweigern würde, wenn die ausgeführte Operation die Löschung solcher Ressourcen erfordert. Glücklicherweise hat CloudFormation einen integrierten Weg, dies zu tun. Das nennt sich Stack-Policy, und dazu kann man mehr erfahren in Dokumentation:

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: Verwenden Sie UsePreviousValue, wenn Sie einen Stack mit geheimen Parametern aktualisieren.

Wenn Sie eine RDS MySQL-Entität erstellen, verlangt AWS von Ihnen, MasterUsername und MasterUserPassword anzugeben. Da es besser ist, Geheimnisse nicht im Quellcode zu speichern, und ich alles absolut automatisieren wollte, habe ich einen „intelligenten Mechanismus“ implementiert, bei dem vor dem Deployment die Anmeldedaten aus S3 abgerufen werden. Wenn die Anmeldedaten nicht gefunden werden, werden neue Anmeldedaten erstellt und in S3 gespeichert.

Diese Anmeldedaten werden dann als Parameter an den Befehl cloudformation create-change-set übergeben. Bei Experimenten mit dem Skript kam es vor, dass die Verbindung zu S3 unterbrochen wurde, und mein „intelligenter Mechanismus“ betrachtete dies als Signal, neue Anmeldedaten zu generieren.

Wenn ich dieses Skript in einer Produktionsumgebung verwenden würde und das Verbindungsproblem erneut auftreten würde, würde es den Stack mit neuen Anmeldeinformationen aktualisieren. In diesem speziellen Fall würde nichts Schlimmes passieren. Ich habe mich jedoch gegen diesen Ansatz entschieden und began, einen anderen zu verwenden, bei dem ich die Anmeldeinformationen nur einmal angebe - bei der Erstellung des Stacks. Später, wenn der Stack ein Update benötigt, würde ich anstelle der Angabe des geheimen Wertes einfach verwenden 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: Verwenden Sie die Rollback-Konfiguration

Ein weiteres Kommando, mit dem ich gearbeitet habe, verwendete eine Funktion cloudformation, genannt Rollback-Konfiguration. Ich hatte davor noch nicht davon gehört und verstand schnell, dass es meine Stacks noch cooler machen würde. Jetzt benutze ich es jedes Mal, wenn ich meinen Code in Lambda oder ECS mit CloudFormation bereitstelle.

So funktioniert es: Sie geben an CloudWatch Alarm ARN im Parameter —rollback-configuration, wenn Sie ein Änderungsset erstellen. Später, wenn Sie das Änderungsset ausführen, überwacht AWS den Alarm mindestens eine Minute. Es wird das Deployment zurücksetzen, wenn der Alarm in diesem Zeitraum den Status auf ALARM ändert.

Hier ist ein Beispielausschnitt des Templates cloudformation, in dem ich erstelle CloudWatch Alarm, das eine benutzerdefinierte Cloud-Metrik in Form von Fehlerzahlen in Cloud-Protokollen überwacht (die Metrik wird über MetricFilter):

Ressourcen:
  # Diese Metrik verfolgt die Anzahl der Fehler in den CloudWatch-Protokollen. In diesem
  # speziellen Fall wird angenommen, dass die Protokolle im JSON-Format vorliegen und die Fehlerprotokolle
  # durch das Level "error" identifiziert werden. Siehe 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 Minute
      EvaluationPeriods: 1
      Threshold: 0
      TreatMissingData: notBreaching
      ActionsEnabled: yes

Jetzt Alarm kann als Rollback Trigger beim Ausführen eines Änderungssets verwendet werden:

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: Stellen Sie sicher, dass Sie die neueste Version der Vorlage bereitstellen

Es ist leicht, nicht die neueste Version der CloudFormation-Vorlage bereitzustellen, aber das kann großen Schaden anrichten. Einmal war es bei uns so: Ein Entwickler hatte die letzten Änderungen nicht aus Git übertragen und stellte unbewusst eine vorherige Version des Stacks bereit. Das führte zu Ausfallzeiten der Anwendung, die diesen Stack verwendete.

Eine einfache Lösung, wie das Hinzufügen einer Überprüfung, ob der Branch aktuell ist, bevor Sie die Bereitstellung durchführen, wäre sinnvoll (vorausgesetzt, git ist Ihr Versionskontrollwerkzeug):

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

if [[ "$HEADHASH" != "$UPSTREAMHASH" ]] ; then
   echo "Branch ist nicht aktuell mit dem Ursprung. Abbruch"
   exit 1
fi

Lektion 6: Erfinden Sie das Rad nicht neu

Es mag einfach erscheinen, mit cloudformation bereitstellen. Man braucht nur eine Menge Bash-Skripte, die AWS-CLI-Befehle ausführen.

Vor 4 Jahren begann ich mit einfachen Skripten, die den Befehl aws cloudformation create-stack aufriefen. Bald war das Skript nicht mehr so einfach. Jede gelernte Lektion machte das Skript immer komplexer. Es war nicht nur schwierig, sondern auch voller Fehler.

Heute arbeite ich in einer kleinen IT-Abteilung. Meine Erfahrung zeigt, dass jedes Team seine eigene Methode hat, CloudFormation-Stacks bereitzustellen. Und das ist problematisch. Es wäre besser, wenn jeder einen einheitlichen Ansatz verwenden würde. Zum Glück gibt es viele Werkzeuge, die helfen, CloudFormation-Stacks bereitzustellen und zu konfigurieren.

Diese Lektionen helfen Ihnen, Fehler zu vermeiden.

Quelle: habr.com

60GB SSD 8Gb DDR4