J'ai appris ces 6 leçons de travail avec cloudformation pour le reste de ma vie.

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

J'ai appris ces 6 leçons de travail avec cloudformation pour le reste de ma vie.

Leçon 1 : Tester les modifications avant de les déployer

J'ai appris cette leçon peu de temps après avoir commencé à travailler avec nuage. Je ne me souviens pas exactement de ce que j'ai cassé à ce moment-là, mais je me souviens très bien que j'ai utilisé la commande mise à jour AWS Cloudformation. Cette commande déploie simplement le modèle sans aucune validation des modifications qui seront déployées. Je ne pense pas qu'aucune explication soit nécessaire pour expliquer pourquoi vous devez tester toutes les modifications avant de les déployer.

Après cet échec, j'ai immédiatement changé pipeline de déploiement, en remplaçant la commande update par la commande créer-changer-ensemble

# 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 qu'un ensemble de modifications est créé, il n'a aucun effet sur la pile existante. Contrairement à la commande update, l’approche changeset ne déclenche pas le déploiement réel. 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 que vous pouvez, 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 devrait produire une sortie similaire à celle-ci :

--------------------------------------------------------------------
|                         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 changements où l'action est remplacer, Supprimer ou où Remplacement nécessaire – Vrai. Ce sont les changements les plus dangereux et entraînent généralement une perte d’informations.

Une fois les changements examinés, ils peuvent être déployés

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 des ressources avec état

Parfois, il ne suffit pas simplement de visualiser 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 coéquipier a effectué sans le savoir un déploiement qui a entraîné une mise à jour de la base de données. Rien de grave ne s'est produit car c'était un environnement de test.

Même si nos scripts affichaient une liste de modifications et demandaient une confirmation, la modification Remplacer a été ignorée car la liste des modifications était si longue qu'elle ne tenait pas à l'écran. Et comme il s’agissait d’une mise à jour normale dans un environnement de test, peu d’attention a été accordée aux modifications.

Il existe des ressources que vous ne souhaitez jamais remplacer ou supprimer. Il s'agit de services avec état, comme une instance de base de données RDS ou un cluster elasticsearch, etc. Ce serait bien si aws refusait automatiquement le déploiement si l'opération en cours nécessitait la suppression d'une telle ressource. Heureusement, cloudformation dispose d'un moyen intégré pour ce faire. C'est ce qu'on appelle la politique de pile, et vous pouvez en savoir plus à ce sujet dans 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 : Utiliser UsePreviousValue lors de la mise à jour d'une pile avec des paramètres secrets

Lorsque vous créez une entité RDS mysql, AWS vous demande de fournir un MasterUsername et un MasterUserPassword. Comme il vaut mieux ne pas garder de secrets dans le code source et que je voulais absolument tout automatiser, j'ai implémenté un "mécanisme intelligent" où, avant le déploiement, les informations d'identification seront obtenues à partir de s3, et si les informations d'identification ne sont pas trouvées, de nouvelles informations d'identification sont générées et stocké dans s3 .

Ces informations d'identification seront ensuite transmises en tant que paramètres à la commande cloudformation create-change-set. Lors de l'expérimentation du script, il est arrivé que la connexion à s3 soit perdue et mon « mécanisme intelligent » l'a traité comme un signal pour générer de nouvelles informations d'identification.

Si je commençais à utiliser ce script en production et que le problème de connexion se reproduisait, il mettrait à jour la pile avec de nouvelles informations d'identification. Dans ce cas particulier, rien de grave ne se produira. Cependant, j'ai abandonné cette approche et j'ai commencé à en utiliser une autre, en fournissant les informations d'identification une seule fois : lors de la création de la pile. Et plus tard, lorsque la pile aura besoin d'être 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é a utilisé la fonction nuageappelé configuration de restauration. Je ne l'avais jamais rencontré auparavant et j'ai rapidement réalisé que cela rendrait le déploiement de mes piles encore plus cool. Maintenant, je l'utilise chaque fois que je déploie mon code sur lambda ou ECS à l'aide de cloudformation.

Comment ça marche : vous précisez Alarme CloudWatch en paramètre --rollback-configurationlorsque vous créez un ensemble de modifications. Plus tard, lorsque vous exécutez un ensemble de modifications, aws surveille l'alarme pendant au moins une minute. Il annule le déploiement si l'alarme change d'état en ALARM pendant cette période.

Vous trouverez ci-dessous un exemple d'extrait de modèle nuagedans lequel je crée alarme de surveillance des nuages, qui suit une métrique d'utilisateur du cloud sous la forme du nombre d'erreurs dans les journaux du cloud (la métrique est générée via FiltreMétrique):

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

tout de suite alarme peut être utilisé comme rollback déclencheur lors de l'exécution de la boîte à 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

Il est facile de déployer une version moins récente du modèle cloudformation, mais cela entraînerait beaucoup de dégâts. Cela nous est arrivé une fois : un développeur n'a pas poussé les dernières modifications de Git et a déployé sans le savoir une version précédente de la pile. Cela entraînait un temps d'arrêt pour l'application qui utilisait cette pile.

Quelque chose d'aussi simple que d'ajouter une vérification pour voir si la branche est à jour avant de s'y engager serait bien (en supposant que git soit 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

Cela peut sembler être un déploiement avec nuage - c'est facile. Vous avez juste besoin d'un tas de scripts bash exécutant 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 scénario n’était plus simple. Chaque leçon apprise rendait le scénario de plus en plus complexe. C'était non seulement difficile, mais aussi plein de bugs.

Je travaille actuellement dans un petit service informatique. L'expérience a montré que chaque équipe a sa propre manière de déployer les piles cloudformation. Et c'est mauvais. Il vaudrait mieux que tout le monde adopte la même approche. Heureusement, il existe de nombreux outils disponibles pour vous aider à déployer et configurer des piles cloudformation.

Ces leçons vous aideront à éviter les erreurs.

Source: habr.com

Ajouter un commentaire