Estas 6 lecciones sobre el trabajo con CloudFormation las he aprendido para toda la vida.

Comencé a trabajar con cloudformation hace 4 años. Desde entonces, he roto muchas infraestructuras, incluso aquellas que ya estaban en producción. Pero cada vez que rompía algo, aprendía algo nuevo. Gracias a esta experiencia, compartiré algunas de las lecciones más importantes que he aprendido.

Estas 6 lecciones sobre el trabajo con CloudFormation las he aprendido para toda la vida.

Lección 1: verifica los cambios antes de desplegarlos

Aprendí esta lección poco después de comenzar a trabajar con cloudformation. No recuerdo exactamente qué rompí en ese momento, pero sí recuerdo que utilicé el comando aws cloudformation update. Este comando simplemente despliega la plantilla sin ninguna verificación de los cambios que se van a realizar. No creo que necesite explicaciones sobre por qué es necesario verificar todos los cambios antes de desplegarlos.

Después de ese fracaso, inmediatamente cambié mi pipeline de despliegue, reemplazando el comando update con el comando 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"

Cuando se crea un conjunto de cambios, no afecta al stack existente. A diferencia del comando update, el enfoque de usar un conjunto de cambios no provoca un despliegue real. En su lugar, genera una lista de cambios, que puedes revisar antes del despliegue. Puedes ver los cambios en la interfaz de la consola de aws. Pero si prefieres automatizar todo lo que sea posible, revísalos en 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

Este comando debería generar una salida similar a la siguiente:

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

Presta especial atención a los cambios donde Action es Replace, Eliminar o donde ReplacementNeeded — True. Estos son los cambios más peligrosos y generalmente conducen a la pérdida de información.

Una vez revisados los cambios, pueden ser desplegados

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"

Lección 2: usa una política de stack para evitar la reemplazo o eliminación de recursos mientras se conserva el estado

A veces, simplemente revisar los cambios no es suficiente. Todos somos humanos y todos cometemos errores. Poco después de comenzar a usar los conjuntos de cambios, mi compañero de equipo realizó un despliegue sin darse cuenta, lo que llevó a una actualización de la base de datos. No pasó nada grave porque era un entorno de pruebas.

A pesar de que nuestros scripts mostraban una lista de cambios y pedían confirmación, el cambio Replace se pasó por alto porque la lista de cambios era tan extensa que no cabía en la pantalla. Y dado que era una actualización común en el entorno de pruebas, no se prestó mucha atención a los cambios.

Hay recursos que nunca querrías reemplazar o eliminar. Son servicios con estado, como una instancia de base de datos RDS o un clúster de Elasticsearch, etc. Sería bueno que AWS rechazara automáticamente el despliegue si la operación requiriera eliminar tal recurso. Afortunadamente, CloudFormation tiene una manera incorporada de hacer esto. Se llama política de pila, y se puede aprender más sobre ello en la documentación:

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"

Lección 3: utiliza UsePreviousValue al actualizar la pila con parámetros secretos

Cuando creas una entidad RDS MySQL, AWS te solicita que proporciones MasterUsername y MasterUserPassword. Dado que es mejor no almacenar secretos en el código fuente, y quería automatizar absolutamente todo, implementé un "mecanismo inteligente" en el que, antes del despliegue, las credenciales se obtendrán de S3, y si no se encuentran, se generarán nuevas credenciales y se almacenarán en S3.

Luego, estas credenciales se pasarán como parámetros al comando cloudformation create-change-set. Durante los experimentos con el script, ocurrió que se perdió la conexión con S3, y mi "mecanismo inteligente" lo interpretó como una señal para generar nuevas credenciales.

Si comenzara a usar este script en un entorno de trabajo y el problema de conexión volviera a ocurrir, actualizaría la pila con nuevas credenciales. En este caso particular, no pasaría nada malo. Sin embargo, opté por un enfoque diferente y comencé a proporcionar las credenciales solo una vez, al crear la pila. Y más tarde, cuando la pila necesite una actualización, en lugar de especificar el valor secreto del parámetro, simplemente usaría 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"

Lección 4: utiliza la configuración de rollback

Otro comando con el que trabajé usaba una función cloudformation, llamada rollback configuration. No me había encontrado con ella antes y rápidamente comprendí que haría que el despliegue de mis pilas fuera aún mejor. Ahora la uso cada vez que despliego mi código en Lambda o ECS utilizando CloudFormation.

Cómo funciona: especificas CloudWatch alarm arn en el parámetro —rollback-configuration, al crear un conjunto de cambios. Más tarde, cuando ejecutes el conjunto de cambios, AWS monitorea el alarm durante al menos un minuto. Revierte el despliegue si durante este tiempo el alarm cambia su estado a ALARM.

A continuación se muestra un ejemplo de fragmento de plantilla cloudformation, en el que creo cloudwatch alarm, que rastrea una métrica personalizada en la nube en forma de número de errores en los logs de la nube (la métrica se crea a través de MetricFilter):

Resources:
  # esta métrica rastrea el número de errores en los logs de cloudwatch. En este
  # caso particular se asume que los logs están en formato json y los logs de error son
  # identificados por el nivel "error". Véase 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 minuto
      EvaluationPeriods: 1
      Threshold: 0
      TreatMissingData: notBreaching
      ActionsEnabled: yes

Ahora alarm puede ser utilizado como rollback disparador al ejecutar el conjunto de herramientas:

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"

Lección 5: asegúrate de que estás desplegando la versión más reciente de la plantilla

Es fácil desplegar una versión de plantilla de CloudFormation que no sea la más reciente, pero esto puede causar grandes problemas. Una vez nos sucedió: un desarrollador no envió los últimos cambios de Git y, sin darse cuenta, desplegó una versión anterior de la pila. Esto provocó tiempo de inactividad en la aplicación que utilizaba esa pila.

Algo simple, como agregar una verificación para asegurarte de que la rama está actualizada antes de ejecutar el despliegue, sería beneficioso (asumiendo que git es tu herramienta de control de versiones):

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

if [[ "$HEADHASH" != "$UPSTREAMHASH" ]] ; then
   echo "La rama no está actualizada con origin. Abortando"
   exit 1
fi

Lección 6: no inventes la rueda

Puede parecer que desplegar con cloudformation es fácil. Solo necesitas un montón de scripts bash que ejecuten comandos de aws cli.

Hace 4 años comencé con scripts simples que llamaban al comando aws cloudformation create-stack. Pronto el script dejó de ser simple. Cada lección aprendida hacía el script cada vez más complejo. No solo era complicado, sino que estaba lleno de errores.

Ahora trabajo en un pequeño departamento de TI. La experiencia muestra que cada equipo tiene su propio enfoque para desplegar pilas de CloudFormation. Y eso es malo. Sería mejor si todos usaran un enfoque cohesivo. Afortunadamente, hay muchas herramientas que ayudan a desplegar y configurar pilas de CloudFormation.

Estas lecciones te ayudarán a evitar errores.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster