Ik begon te werken met CloudFormation 4 jaar geleden. Sindsdien heb ik veel infrastructuur gebroken, zelfs diegene die al in productie was. Maar elke keer dat ik iets kapot maakte, leerde ik iets nieuws. Vanwege deze ervaring deel ik enkele van de belangrijkste lessen die ik heb geleerd.

Les 1: controleer wijzigingen voordat je ze implementeert
Ik leerde deze les vroeg toen ik begon te werken met CloudFormation. Ik kan me niet herinneren wat ik toen precies kapot maakte, maar ik herinner me zeker dat ik het commando aws cloudformation update. Dit commando voert eenvoudigweg de sjabloon uit zonder enige controle van de wijzigingen die geĆÆmplementeerd zullen worden. Ik denk niet dat uitleg nodig is waarom je alle wijzigingen moet controleren voordat je ze implementeert.
Na deze mislukking veranderde ik meteen deployment pipeline, waarbij ik het update-commando verving door het commando 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" Wanneer een wijzigingenpakket is aangemaakt, heeft dit geen invloed op de bestaande stack. In tegenstelling tot het update-commando, veroorzaakt de benadering met een wijzigingenpakket geen daadwerkelijke implementatie. In plaats daarvan creƫert het een lijst van wijzigingen die je kunt bekijken voordat je deze implementeert. Je kunt de wijzigingen bekijken in de AWS-console. Maar als je alles wilt automatiseren, controleer ze dan in de 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 tableDit commando zou een uitvoer moeten genereren die erop lijkt:
--------------------------------------------------------------------
| DescribeChangeSet |
+---------+--------------------+----------------------+------------+
| Actie | Vervanging Nodig | Resource | ResourceId |
+---------+--------------------+----------------------+------------+
| Wijzig | Waar | AWS::ECS::Cluster | MyCluster |
| Vervang| Waar | AWS::RDS::DBInstance| MyDB |
| Voeg toe| Geen | AWS::SNS::Topic | MyTopic |
+---------+--------------------+----------------------+------------+Let op de wijzigingen waarbij Actie is Vervangen, Delete of waar Vervanging Nodig ā Waar. Dit zijn de gevaarlijkste wijzigingen en leiden meestal tot gegevensverlies.
Wanneer de wijzigingen zijn beoordeeld, kunnen ze worden geĆÆmplementeerd
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"Les 2: gebruik stack policy om het vervangen of verwijderen van bronnen met behoud van staat te voorkomen
Soms is het eenvoudigweg bekijken van de wijzigingen niet genoeg. We zijn allemaal mensen en maken fouten. Kort nadat we met wijzigingssets begonnen te werken, voerde mijn teamgenoot onbewust een implementatie uit, wat leidde tot een database-update. Er gebeurde niets ernstigs, omdat het een testomgeving was.
Ondanks dat onze scripts een lijst van veranderingen toonden en om bevestiging vroegen, werd de wijziging 'Vervangen' gemist, omdat de lijst zo groot was dat deze niet op het scherm paste. En omdat dit een gebruikelijke update in de testomgeving was, kreeg de wijziging niet veel aandacht.
Er zijn middelen die je nooit wilt vervangen of verwijderen. Dit zijn stateful services, zoals een RDS-database-instantie of een Elasticsearch-cluster, enzovoort. Het zou mooi zijn als AWS automatisch een uitrol zou weigeren als de uitgevoerde bewerking de verwijdering van zo'n bron vereist. Gelukkig heeft CloudFormation een ingebouwde manier om dit te doen. Dit wordt een stack policy genoemd, en hier kun je meer over leren. :
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"Les 3: gebruik UsePreviousValue bij het bijwerken van de stack met geheime parameters.
Wanneer je een RDS MySQL-entiteit aanmaakt, vraagt AWS je om MasterUsername en MasterUserPassword te verstrekken. Omdat het beter is om geheimen niet in de broncode op te slaan en ik alles volledig wilde automatiseren, implementeerde ik een 'slimme mechanisme' waarbij de inloggegevens voorafgaand aan de uitrol uit S3 worden gehaald, en als de inloggegevens niet worden gevonden, worden nieuwe inloggegevens gegenereerd en in S3 opgeslagen.
Deze inloggegevens worden vervolgens als parameters doorgegeven aan de opdracht cloudformation create-change-set. Tijdens experimenten met het script verloor ik de verbinding met S3, en mijn 'slimme mechanisme' beschouwde dit als het signaal om nieuwe inloggegevens te genereren.
Als ik dit script in een productieomgeving zou beginnen te gebruiken, en het verbindingsprobleem zich opnieuw zou voordoen, zou het de stack bijwerken met nieuwe inloggegevens. In dit specifieke geval zou er niets slechts gebeuren. Ik heb echter deze aanpak afgewezen en ben overgestapt op een andere, waarbij ik inloggegevens slechts ƩƩn keer verstrek ā bij het maken van de stack. En later, wanneer de stack een update vereist, zou ik in plaats van de geheime waarde van de parameter op te geven, gewoon gebruiken 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"Les 4: gebruik rollbackconfiguratie
Een ander commando waarmee ik werkte, maakte gebruik van een functie CloudFormation, genaamd rollbackconfiguratie. Ik was daar voorheen niet mee in aanraking gekomen en begreep snel dat dit mijn stack-implementaties nog beter zou maken. Nu gebruik ik het elke keer wanneer ik mijn code in Lambda of ECS implementeer met behulp van CloudFormation.
Hoe het werkt: je geeft de CloudWatch alarm arn op in de parameter ārollback-configuration, wanneer je een change set maakt. Later, wanneer je de change set uitvoert, houdt AWS de alarm gedurende minstens ƩƩn minuut in de gaten. Het trekt de implementatie terug als de alarm in die tijd zijn status wijzigt naar ALARM.
Hieronder staat een voorbeeld van een sjabloonfragment CloudFormation, waarin ik maak cloudwatch alarm, dat een aangepaste cloud metriek bijhoudt in de vorm van het aantal fouten in de cloudlogboeken (de metriek wordt gemaakt via MetricFilter):
Resources:
# deze metriek houdt het aantal fouten in de cloudwatch logs bij. In dit
# specifieke geval wordt aangenomen dat logs in json-indeling zijn en dat de foutlogs
# zijn geĆÆdentificeerd door niveau "error". Zie 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 minuut
EvaluationPeriods: 1
Threshold: 0
TreatMissingData: notBreaching
ActionsEnabled: yesNu alarm kan worden gebruikt als rollback trigger bij het uitvoeren van de change set:
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"Les 5: zorg ervoor dat je de meest recente versie van de sjabloon uitrolt
Het is eenvoudig om een oudere versie van de cloudformation-sjabloon uit te rollen, maar dat kan aanzienlijke schade aanrichten. Een keer gebeurde het bij ons: een ontwikkelaar had de laatste wijzigingen niet naar Git gepusht en rolde onbewust de vorige versie van de stack uit. Dit leidde tot uitvaltijd van de applicatie die deze stack gebruikte.
Iets eenvoudigs, zoals het toevoegen van een controle om te verifiƫren of de tak actueel is voordat je gaat uitrollen, zou goed zijn (ervan uitgaande dat git je versiebeheertool is):
git fetch
HEADHASH=$(git rev-parse HEAD)
UPSTREAMHASH=$(git rev-parse master@{upstream})
if [[ "$HEADHASH" != "$UPSTREAMHASH" ]] ; then
echo "Branch is niet up to date met origin. Afgebroken"
exit 1
fiLes 6: uitvinding is niet nodig
Het lijkt misschien eenvoudig om uit te rollen met CloudFormation ā het is makkelijk. Je hebt gewoon een stel bash-scripts nodig die aws cli-commando's uitvoeren.
Vier jaar geleden begon ik met eenvoudige scripts die de aws cloudformation create-stack opdracht aanriepen. Al snel was het script niet meer zo eenvoudig. Elke les die ik leerde maakte het script steeds ingewikkelder. Het was niet alleen complex, maar ook vol met bugs.
Nu werk ik in een klein IT-team. De ervaring leert dat elk team zijn eigen manier heeft om cloudformation stacks uit te rollen. En dat is slecht. Het zou beter zijn als iedereen een uniforme aanpak gebruikte. Gelukkig zijn er verschillende tools die helpen bij het uitrollen en configureren van cloudformation-stacks.
Deze lessen helpen je om fouten te voorkomen.
Bron: habr.com
