Desde hace algunas semanas he estado usando, como parte de mi trabajo diario, una de las opciones de Amazon Web Services que nos permite crear plantillas para automatizar la creación de recursos llamada CloudFormation.
En este artículo, tras revisar las las diferentes opciones que tenemos para crear recursos, describiremos algunas características de CloudFormation, y finalmente veremos mi propia experiencia al usar esta herramienta.
Creando recursos en AWS
Crear recursos en la nube, tales como máquinas, blobs para almacenamiento, colas para intercambio de mensajes, bases de datos, o incluso alarmas para medir el rendimiento, es algo que podemos hacer mediante tres maneras:
Mediante la interfaz de usuario
La interfaz nos ayuda a explorar la plataforma, conocer lo que nos ofrece, y suele tener además guías, recomendaciones y enlaces a documentación. Es un buen punto de entrada, pero propenso a error ya que no es sencillo de automatizar.
Usando las herramientas de línea de comandos
Prácticamente todas las acciones que se pueden hacer por la interfaz, se pueden hacer por la línea de comandos (Por ejemplo, aún no podemos crear dashboards de CloudWatch). Por una parte, tendremos que aprender la sintaxis, y el funcionamiento será menos intuitivo que usar el portal, pero por otra parte trabajar con la línea de comandos nos permite cierto grado de automatización.
A través del SDK
Esta opción viene a ser un complemento de la segunda, pero en vez de utilizar un lenguaje de consola de comandos como Bash para nuestra automatización, podemos usar lenguajes como C#, Python o Java para crear nuestros scripts.
Usando plantillas
Esta opción nos permite, mediante un fichero JSON, definir nuestros recursos, valores por defecto, relaciones entre ellos y parámetros que podremos rellenar cuando estemos aplicando la plantilla. Veamos un ejemplo:
{ "AWSTemplateFormatVersion" : "2010-09-09", "Resources" : { "myDynamoDBTable" : { "Type" : "AWS::DynamoDB::Table", "Properties" : { "AttributeDefinitions" : [ { "AttributeName" : "Album", "AttributeType" : "S" }, { "AttributeName" : "Artist", "AttributeType" : "S" } ], "KeySchema" : [ { "AttributeName" : "Album", "KeyType" : "HASH" }, { "AttributeName" : "Artist", "KeyType" : "RANGE" } ], "ProvisionedThroughput" : { "ReadCapacityUnits" : "5", "WriteCapacityUnits" : "5" }, "TableName" : "myTableName" } } } }
CloudFormation es el sistema que nos permite crear y utilizar estas plantillas, y es el que establece la sintaxis que acabamos de ver para definir nuestros recursos y los diferentes atributos, veamos algunas de sus características y ventajas.
Características
Parámetros
Los parámetros nos permiten personalizar nuestra plantilla en tiempo de creación y pueden ser muy útiles para aplica el mismo recurso en diferentes regiones, o tener la misma plantilla para varios grupos de recursos, utilizando los siguientes tipos de datos:
- Texto
- Número
- Números separados por comas
- Textos separados por comas
Podemos ver un ejemplo del uso de parámetros en el siguiente bloque, donde, además, podemos utilizar la propiedad AllowedValues para mostrar un desplegable con las diferentes opciones disponibles, o bien dejar un campo de texto libre:
"Parameters" : { "InstanceTypeParameter" : { "Type" : "String", "Default" : "t1.micro", "AllowedValues" : ["t1.micro", "m1.small", "m1.large"], "Description" : "Enter t1.micro, m1.small, or m1.large. Default is t1.micro." } }
Salida
Otra de las características que podemos utilizar de CloudFormation, es la definición de la salida, que nos permite obtener cualquier valor de los recursos que acabamos de crear, ya sean IDs de instancias de EC2, nombres DNS de recrusos, URLs de tablas en DynamoDB o ARN de colas SQS. Esto es especialmente útil si estamos aplicando nuestro script utilizando la línea de comandos de AWS.
"Outputs" : { "BackupLoadBalancerDNSName" : { "Description": "The DNSName of the backup load balancer", "Value" : { "Fn::GetAtt" : [ "BackupLoadBalancer", "DNSName" ]}, "Condition" : "CreateProdResources" }, "InstanceID" : { "Description": "The Instance ID", "Value" : { "Ref" : "EC2Instance" } } }
Referencias
Podemos enlazar recursos en tiempo de creación, es decir, podemos crear una cola SQS, y a su vez una segunda cola para que guarde los mensajes tras varios intentos (lo que se denomina Dead Letter Queue), o podemos crear una tabla y una alarma de CloudWatch asociada a la misma, utilizando referencias internas.
En este ejemplo podemos ver una dirección IP que asociamos a una instancia que hemos definido anteriormente:
"MyEIP" : { "Type" : "AWS::EC2::EIP", "Properties" : { "InstanceId" : { "Ref" : "MyEC2Instance" } } }
Ventajas
La primera ventaja es que es una manera limpia de crear y administrar recursos en AWS, los recursos creados como parte de una plantilla se pueden modificar directamente subiendo una nueva versión de la misma, o bien se pueden borrar, evitando tener que hacer estas acciones por separado.
En segundo lugar, nos facilita replicar nuestra infraestructura en diferentes regiones, ya que AWS mantiene una separación muy estricta entre las regiones (de hecho, no todas las regiones cuentan con todos los recursos, y los precios varían por región).
En tercer lugar, nos aporta control de cambios, ya que por una parte tenemos un fichero de texto que podemos agregar a nuestro sistema de control de versiones, y por otra parte, al aplicar las plantillas, mantenemos un histórico de cambios, donde se aprecian qué recursos se han creado, modificado y eliminado cuando actualizamos nuestras plantillas.
Finalmente, es gratis! AWS no hace ningún cargo por el uso de CloudFormation, solamente pagaremos por los recursos que creamos con el mismo, que sería lo mismo que si los creáramos por separado.
Mi experiencia personal
Personalmente he utilizado CloudFormation para crear plantillas a partir de recursos ya existentes entre diferentes zonas de AWS, creando ficheros (denominados «Stacks») para tablas DynamoDB, colas SQS y sistemas de notificaciones SNS, así como alarmas de CloudWatch.
Además de los parámetros que hemos visto antes, he necesitado personalizar los stacks que he creado, y para ello, utilizando la función Join para combinar valores por defecto y parámetros:
"Fn::Join" : [ ":", [ "a", "b", "c" ] ] //a:b:c
He experimentado bastante libertad a la hora de enlazar recursos y personalizar los valores, y el hecho de que muchos de los valores sean opcionales me ha permitido centrarme en las características específicas de mi Stack, y no tener que aprender todas las opciones antes de empezar a trabajar, lo cual es de agradecer.
Una cosa que sí hecho en falta de CloudFormation es la posibilidad de crear plantillas automáticamente especificando un recurso, esto nos permitiría convertir tablas, colas, o instancias EC2 en plantillas que podríamos luego replicar en otras zonas.
Resumen
CloudFormation nos permite, utilizando parámetros, crear una serie de ficheros JSON llamados templates, que podemos aplicar desde la UI o la línea de comandos para crear, modificar o eliminar recursos como colas SNS, tablas, máquinas EC2 o alarmas CloudWatch, sin tener que pagar un sobrecoste por esta característica.