J'ai commencé à travailler avec
cloudformation il y a 4 ans. Depuis lors, j'ai cassé de nombreuses infrastructures, même celles qui étaient déjà en production. Mais chaque fois que je gâtais quelque chose, j'apprenais de nouvelles choses. Grâce à cette expérience, je partagerai certaines des leçons les plus importantes que j'ai apprises.

Leçon 1: vérifier les modifications avant le déploiement
J'ai appris cette leçon dès que j'ai commencé à travailler avec
cloudformation . Je ne me souviens pas de ce que j'ai cassé à l'époque, mais je me souviens exactement que j'ai utilisé la
commande aws cloudformation update . Cette commande déploie simplement le modèle sans aucune vérification des modifications qui seront déployées. Je ne pense pas que des explications soient nécessaires, pour lesquelles toutes les modifications doivent être vérifiées avant de les déployer.
Après cet échec, j'ai immédiatement changé le
pipeline de déploiement , en remplaçant la commande update par la commande
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"
Lorsqu'un jeu de modifications est créé, il n'affecte pas la pile existante. Contrairement à la commande de mise à jour, l'approche d'ensemble de modifications ne se déploie pas réellement. Au lieu de cela, il crée une liste de modifications que vous pouvez consulter avant le déploiement. Vous pouvez afficher les modifications dans l'interface de la console aws. Mais si vous préférez automatiser tout ce qui est possible, vérifiez-les dans la 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
Cette commande doit produire une sortie similaire à la suivante:
-------------------------------------------------------------------- | DescribeChangeSet | +---------+--------------------+----------------------+------------+ | Action | ReplacementNeeded | Resource | ResourceId | +---------+--------------------+----------------------+------------+ | Modify | True | AWS::ECS::Cluster | MyCluster | | Replace| True | AWS::RDS::DBInstance| MyDB | | Add | None | AWS::SNS::Topic | MyTopic | +---------+--------------------+----------------------+------------+
Portez une attention particulière aux modifications dans lesquelles Action est
Remplacer ,
Supprimer ou où
ReplacementNeeded est Vrai . Ce sont les changements les plus dangereux et entraînent généralement une perte d'informations.
Lorsque les modifications sont affichées, elles peuvent être développées
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"
Leçon 2: utiliser la stratégie de pile pour empêcher le remplacement ou la suppression avec état des ressources
Parfois, regarder les changements ne suffit pas. Nous sommes tous humains et nous commettons tous des erreurs. Peu de temps après avoir commencé à utiliser des ensembles de modifications, mon coéquipier a inconsciemment effectué un déploiement, qui a conduit à une mise à niveau de la base de données. Rien de terrible ne s'est produit, car c'était un environnement de test.
Malgré le fait que nos scripts affichent une liste de modifications et demandent une confirmation, la modification Remplacer a été ignorée car la liste des modifications était si grande qu'elle ne pouvait pas tenir à l'écran. Et comme il s'agissait d'une mise à jour régulière dans l'environnement de test, peu d'attention a été accordée aux modifications.
Il y a des ressources que vous ne voudrez jamais remplacer ou supprimer. Ce sont des services d'état, comme une instance d'une base de données RDS ou un cluster elastichsearch, etc. Ce serait bien si aws refusait automatiquement de se déployer, si l'opération en cours nécessitait la suppression d'une telle ressource. Heureusement, la cloudformation a une manière intégrée de le faire. Cela s'appelle la stratégie de pile, et vous pouvez en savoir plus à ce sujet dans la
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"
Leçon 3: utilisez UsePreviousValue lors de la mise à jour d'une pile avec des paramètres secrets
Lorsque vous créez une entité RDS, mysql AWS vous oblige à fournir MasterUsername et MasterUserPassword. Puisqu'il vaut mieux ne pas garder de secrets dans le code source, et que je voulais automatiser absolument tout, j'ai implémenté un «mécanisme intelligent» dans lequel les informations d'identification sont obtenues de s3 avant le déploiement, et si les informations d'identification ne sont pas trouvées, de nouvelles informations d'identification sont générées et stockées dans s3 .
Ces informations d'identification seront ensuite transmises en tant que paramètres à la commande cloudformation create-change-set. Au cours des expériences avec le script, il est arrivé que la connexion à s3 soit perdue et mon «mécanisme intelligent» le considérait comme un signal pour générer de nouvelles informations d'identification.
Si je commençais à utiliser ce script dans un environnement de production et que le problème de connexion se posait à nouveau, cela mettrait à jour la pile avec de nouvelles informations d'identification. Dans ce cas particulier, rien de mauvais ne se produira. Cependant, j'ai abandonné cette approche et j'ai commencé à en utiliser une autre, en ne fournissant les informations d'identification qu'une seule fois - lors de la création de la pile. Et plus tard, lorsque la pile nécessite une mise à jour, au lieu de spécifier la valeur secrète du paramètre, j'utiliserais simplement
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"
Leçon 4: utiliser la configuration de restauration
Une autre équipe avec laquelle j'ai travaillé utilisait une fonction de
cloudformation appelée
configuration de restauration . Je ne l'ai jamais rencontrée auparavant et j'ai rapidement réalisé que cela rendrait le déploiement de mes piles encore meilleur. Maintenant, j'utilise chaque fois que je déploie mon code dans lambda ou ECS à l'aide de cloudformation.
Fonctionnement: vous spécifiez l'
arme d'alarme CloudWatch dans le paramètre
--rollback-configuration lorsque vous créez l'ensemble de modifications. Plus tard, lorsque vous avez terminé le jeu de modifications, aws suit l'alarme pendant au moins une minute. Il annule le déploiement si pendant ce temps l'alarme change d'état en ALARME.
Voici un exemple d'extrait du modèle
cloudformation dans lequel je crée une
alarme cloudwatch qui suit la métrique cloud de l'utilisateur comme le nombre d'erreurs dans les journaux cloud (la métrique est créée via
MetricFilter ):
Resources: # this metric tracks number of errors in the cloudwatch logs. In this # particular case it's assumed logs are in json format and the error logs are # identified by level "error". See 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
Désormais, l'
alarme peut être utilisée comme déclencheur de
restauration lors de l'exécution d'un ensemble d'outils:
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"
Leçon 5: Assurez-vous de déployer la dernière version du modèle
Ce n'est pas facile de déployer la dernière version du modèle de cloudformation, mais cela fera beaucoup de dégâts. Une fois qu'il était avec nous: le développeur n'a pas envoyé les dernières modifications de Git et déployé sans le savoir la version précédente de la pile. Cela a conduit à une application simple qui utilisait cette pile.
Quelque chose de simple, comme ajouter une vérification pour voir si une branche est pertinente avant le déploiement, serait bien (en supposant que git est votre outil de contrôle de version):
git fetch HEADHASH=$(git rev-parse HEAD) UPSTREAMHASH=$(git rev-parse master@{upstream}) if [[ "$HEADHASH" != "$UPSTREAMHASH" ]] ; then echo "Branch is not up to date with origin. Aborting" exit 1 fi
Leçon 6: ne réinventez pas la roue
Le déploiement avec
cloudformation peut sembler facile. Vous avez juste besoin d'un tas de scripts bash qui exécutent les commandes aws cli.
Il y a 4 ans, j'ai commencé avec des scripts simples appelés commande aws cloudformation create-stack. Bientôt, le script n'est plus simple. Chaque leçon apprise rendait le script de plus en plus complexe. Ce n'était pas seulement difficile, mais aussi avec un tas de bugs.
Maintenant, je travaille dans un petit département informatique. L'expérience a montré que chaque équipe a sa propre façon de déployer des piles de cloudformation. Et c'est mauvais. Il serait préférable que tout le monde utilise une seule approche. Heureusement, il existe de nombreux outils qui vous aident à déployer et à configurer des piles de cloudformation.
Ces leçons vous aideront à éviter les erreurs.