J'ai commencé à travailler avec cloudformation il y a 4 ans. Depuis, j'ai cassé beaucoup d'infrastructures, même celles déjà en production. Mais chaque fois que je dégradais quelque chose, j'apprenais quelque chose de nouveau. Grâce à cette expérience, je vais partager certaines des leçons les plus importantes que j'ai apprises.

Leçon 1 : vérifiez les modifications avant de les déployer
J'ai appris cette leçon peu après avoir commencé à travailler avec cloudformation. Je ne me souviens pas exactement de ce que j'ai cassé à l'époque, mais je me rappelle très bien avoir utilisé la commande aws cloudformation update. Cette commande déploie simplement le modèle sans aucune vérification des modifications qui seront effectuées. Je ne pense pas qu'il soit nécessaire d'expliquer pourquoi il est important de vérifier toutes les modifications avant de les déployer.
Après cet échec, j'ai immédiatement changé ma pipeline de déploiement, 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" Une fois le jeu de modifications créé, il n'affecte pas le stack existant. Contrairement à la commande update, l'approche utilisant le jeu de modifications ne déclenche pas de déploiement réel. Il crée plutôt une liste de modifications que vous pouvez examiner avant le déploiement. Vous pouvez visualiser les modifications dans l'interface de la console AWS. Mais si vous préférez automatiser tout ce qui peut l'être, vérifiez-les dans le 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 tableCette commande devrait produire une sortie ressemblant à ceci :
--------------------------------------------------------------------
| DescribeChangeSet |
+---------+--------------------+----------------------+------------+
| Action | ReplacementNeeded | Resource | ResourceId |
+---------+--------------------+----------------------+------------+
| Modify | True | AWS::ECS::Cluster | MyCluster |
| Replace| True | AWS::RDS::DBInstance| MyDB |
| Add | None | AWS::SNS::Topic | MyTopic |
+---------+--------------------+----------------------+------------+Faites particulièrement attention aux modifications où Action est Remplacer, Supprimer ou où ReplacementNeeded — True. Ce sont les modifications les plus dangereuses et elles entraînent généralement une perte de données.
Une fois les modifications examinées, elles peuvent être déployé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 : utilisez une politique de stack pour éviter le remplacement ou la suppression de ressources tout en préservant l'état
Parfois, il ne suffit pas de simplement consulter les modifications. Nous sommes tous humains et nous faisons tous des erreurs. Peu de temps après avoir commencé à utiliser les ensembles de modifications, mon collègue a involontairement effectué un déploiement, ce qui a entraîné une mise à jour de la base de données. Rien de grave ne s'est produit, car il s'agissait d'un environnement de test.
Bien que nos scripts affichent une liste de modifications et demandent une confirmation, le changement de remplacement a été omis parce que la liste de modifications était si longue qu'elle ne tenait pas à l'écran. Et comme il s'agissait d'une mise à jour ordinaire dans l'environnement de test, les modifications n'ont pas reçu beaucoup d'attention.
Il existe des ressources que vous ne voudrez jamais remplacer ou supprimer. Ce sont des services statefull, comme une instance de base de données RDS ou un cluster Elasticsearch, etc. Ce serait bien qu'AWS refuse automatiquement le déploiement si l'opération effectuée nécessite la suppression de telles ressources. Heureusement, CloudFormation dispose d'un moyen intégré de le faire. Cela s'appelle une politique de pile, et vous pouvez en apprendre davantage à ce sujet dans :
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 exige que vous fournissiez MasterUsername et MasterUserPassword. Comme il est préférable de ne pas stocker les secrets dans le code source, et que je voulais automatiser absolument tout, j'ai implémenté un « mécanisme intelligent », où les identifiants sont récupérés depuis S3 avant le déploiement, et si les identifiants ne sont pas trouvés, de nouveaux identifiants sont générés et stockés dans S3.
Ensuite, ces identifiants seront transmis en tant que paramètres à la commande CloudFormation create-change-set. Lors des expérimentations avec le script, il est arrivé que la connexion à S3 soit perdue, et mon « mécanisme intelligent » a interprété cela comme un signal pour générer de nouveaux identifiants.
Si j'avais commencé à utiliser ce script dans un environnement de travail et qu'un problème de connexion survenait à nouveau, il mettrait à jour la pile avec de nouvelles informations d'identification. Dans ce cas précis, rien de grave ne se produirait. Cependant, j'ai renoncé à cette approche et j'ai commencé à utiliser une autre méthode, en fournissant les informations d'identification une seule fois - lors de la création de la pile. Plus tard, lorsque la pile nécessitera une mise à jour, je n'indiquerai pas la valeur secrète en tant que paramètre, mais utiliserai plutôt 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 : utilisez la configuration de rollback
Une autre commande avec laquelle j'ai travaillé utilisait une fonction cloudformation, appelée rollback configuration. Je ne l'avais jamais rencontrée auparavant et j'ai rapidement compris que cela rendrait le déploiement de mes piles encore plus incroyable. Je l'utilise maintenant chaque fois que je déploie mon code dans lambda ou ECS avec cloudformation.
Comment cela fonctionne : vous spécifiez CloudWatch alarm arn dans le paramètre --rollback-configuration, lors de la création du ensemble de changements. Plus tard, lorsque vous exécuterez l'ensemble de changements, AWS surveillera l'alarme pendant au moins une minute. Elle annule le déploiement si, durant ce temps, l'alarme change d'état en ALARM.
Voici un exemple d'extrait de modèle cloudformation, où je crée cloudwatch alarm, surveillant une métrique personnalisée du cloud sous la forme d'un nombre d'erreurs dans les journaux du cloud (la métrique est créée via MetricFilter):
Resources:
# cette métrique suit le nombre d'erreurs dans les journaux cloudwatch. Dans ce
# cas particulier, on suppose que les journaux sont au format json et que les journaux d'erreurs sont
# identifiés par le niveau "error". Voir 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: yesMaintenant l'alarme peut être utilisée comme rollback déclencheur lors de l'exécution de l'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"Cours 5 : assurez-vous de déployer la version la plus récente du modèle
Il est facile de déployer une version obsolète du modèle cloudformation, mais cela peut causer de gros dommages. Une fois, nous avons eu ce problème : un développeur n'a pas poussé les dernières modifications depuis Git et a involontairement déployé une version précédente de la pile. Cela a entraîné un temps d'arrêt de l'application utilisant cette pile.
Une simple vérification, comme s'assurer que la branche est à jour avant d'exécuter le déploiement, serait bénéfique (si l'on suppose que git est votre outil de gestion de version) :
git fetch
HEADHASH=$(git rev-parse HEAD)
UPSTREAMHASH=$(git rev-parse master@{upstream})
if [[ "$HEADHASH" != "$UPSTREAMHASH" ]] ; then
echo "La branche n'est pas à jour avec l'origine. Abandon"
exit 1
fiCours 6 : ne réinventez pas la roue
Cela peut sembler facile de déployer avec cloudformation — il vous suffit d'une multitude de scripts bash exécutant des commandes aws cli.
Il y a 4 ans, je commençais avec des scripts simples qui lançaient la commande aws cloudformation create-stack. Rapidement, le script n'était plus simple. Chaque leçon apprise rendait le script de plus en plus complexe. Ça devenait non seulement compliqué, mais aussi rempli de bugs.
Aujourd'hui, je travaille dans une petite équipe informatique. L'expérience montre que chaque équipe a sa propre méthode de déploiement des piles cloudformation. Et c'est problématique. Il serait préférable que tout le monde utilise une approche uniforme. Heureusement, il existe de nombreux outils qui aident à déployer et à configurer des piles cloudformation.
Ces leçons vous aideront à éviter des erreurs.
Source : habr.com
