Automatización de la gestión de AWS EC2 con Python y Boto3

A

Introducción

En este artículo, demostraré el uso de Python junto con el kit de desarrollo de software (SDK) Boto3 Amazon Web Services (AWS) que permite a las personas con conocimientos de programación en Python utilizar las intrincadas API REST de AWS para administrar sus recursos en la nube. Debido a la inmensidad de la API REST de AWS y los servicios en la nube asociados, me centraré solo en el servicio AWS Elastic Cloud Compute (EC2).

Estos son los temas que cubriré:

  • Iniciar una instancia EC2
  • Detener una instancia EC2
  • Terminar una instancia EC2
  • Hacer una copia de seguridad de una instancia EC2 creando una imagen
  • Crear una instancia EC2 a partir de una imagen
  • Programación de copias de seguridad y limpieza mediante cron en un servidor y AWS Lambda

Configuración de dependencias y entorno

Para comenzar, necesitaré crear un usuario en mi cuenta de AWS que tenga acceso programático a las API REST. Para simplificar, otorgaré derechos de administrador a este usuario, pero tenga en cuenta que es solo para simplificar la creación de este tutorial. Si está siguiendo, debe consultar las políticas de seguridad de TI de su organización antes de utilizar este usuario en un entorno de producción.

Paso 1: En mi consola de AWS, debo ir a la sección IAM en el menú de servicios, luego hacer clic en el enlace Usuarios y finalmente hacer clic en el botón Agregar usuario que me lleva a la pantalla que se muestra a continuación. En esta pantalla le doy al usuario el nombre “boto3-user” y marco la casilla de Acceso programático antes de hacer clic en el botón siguiente.

Paso 2: En la pantalla de permisos, hago clic en el mosaico Adjuntar políticas existentes directamente y luego selecciono la casilla de verificación Acceso del administrador antes de hacer clic en Siguiente, como se muestra a continuación.

Paso 3: haga clic en el siguiente, ya que no agregaré etiquetas opcionales.

Paso 4: Reviso el usuario que se va a crear y luego hago clic en Crear usuario.

Paso 5: Finalmente, descargo las credenciales como un archivo CSV y las guardo.

A continuación, necesito instalar las bibliotecas necesarias de Python 3 localmente dentro de un entorno virtual, así:

$ python -m venv venv
$ source venv/bin/activate
(venv)$ pip install boto3 pprint awscli

Por último, configuro las credenciales para la biblioteca boto3 usando la biblioteca awscli asegurándome de agregar las credenciales para la clave de acceso y la clave secreta que descargué en el paso 5 anterior.

$ aws configure
AWS Access Key ID [****************3XRQ]: **************
AWS Secret Access Key [****************UKjF]: ****************
Default region name [None]:
Default output format [None]:

Creación de una instancia EC2 para trabajar

En esta sección, voy a repasar cómo crear una sesión boto3 específica de la región de AWS y cómo crear una instancia de un cliente EC2 utilizando el objeto de sesión activa. Luego, utilizando ese cliente EC2 boto3, interactuaré con las instancias EC2 de esa región que administran el inicio, el apagado y la terminación.

Para crear una instancia EC2 para este artículo, realizo los siguientes pasos:

Paso 1: Hago clic en el enlace EC2 dentro del menú Servicios para abrir el Panel de EC2 y luego hago clic en el botón Iniciar instancia en el medio de la pantalla.

Paso 2: En la página Choose Amazon Machine Image (AMI), hago clic en el botón Seleccionar junto a la AMI de Amazon Linux.

Paso 3: Acepte el tipo de instancia t2.micro predeterminado y haga clic en el botón Revisar y lanzar.

Paso 4: En la página de revisión, amplío la sección Etiquetas y hago clic en Editar etiquetas para agregar etiquetas para el nombre y la copia de seguridad, luego hago clic en Iniciar revisión y volver a iniciar para volver a la página de revisión antes de finalmente hacer clic en el botón Iniciar para iniciar la instancia.

Ahora tengo una instancia EC2 en ejecución, como se muestra a continuación.

Sesión y cliente de Boto3

¡Por fin puedo empezar a escribir código! Comienzo creando un archivo vacío, un módulo de Python, llamado awsutils.py y en la parte superior importo la biblioteca y boto3luego defino una función que creará un objeto de sesión específico de la región .

# awsutils

import boto3

def get_session(region):
    return boto3.session.Session(region_name=region)

Si enciendo mi intérprete de Python e importo el módulo que acabo de crear, puedo usar la nueva get_sessionfunción para crear una sesión en la misma región que mi instancia EC2, luego crear una instancia de un objeto EC2.Client desde ella, así:

>>> import awsutils
>>> session = awsutils.get_session('us-east-1')
>>> client = session.client('ec2')

Luego puedo usar este objeto de cliente EC2 para obtener una descripción detallada de la instancia que se usa pprintpara hacer las cosas un poco más fáciles de ver el resultado de llamar describe_instancesal clientobjeto.

>>> import pprint
>>> pprint.pprint(client.describe_instances())
...

Estoy omitiendo el resultado porque es bastante detallado, pero sé que contiene un diccionario con una Reservationsentrada, que es una lista de datos que describen las instancias EC2 en esa región y ResponseMetadatasobre la solicitud que se acaba de realizar a la API REST de AWS.

Recuperar detalles de la instancia EC2

También puedo usar este mismo describe_instancesmétodo junto con un Filterparámetro para filtrar la selección por valores de etiqueta. Por ejemplo, si quiero obtener mi instancia creada recientemente con la etiqueta Name con un valor de ‘demo-instance’, se vería así:

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> pprint.pprint(demo)
...

Hay muchas formas de filtrar la salida de describe_instancesy los remito a los documentos oficiales para obtener más detalles.

Inicio y detención de una instancia EC2

Para detener la instancia de demostración, utilizo el stop_instancesmétodo del clientobjeto, que he instanciado previamente, proporcionándole el ID de la instancia como un parámetro de lista de entrada única para el InstanceIdsargumento como se muestra a continuación:

>>> instance_id = demo['Reservations'][0]['Instances'][0]['InstanceId']
>>> instance_id
'i-0c462c48bc396bdbb'
>>> pprint.pprint(client.stop_instances(InstanceIds=[instance_id]))
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 19:26:30 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': 'e04a4a64-74e4-442f-8293-261f2ca9433d',
                      'RetryAttempts': 0},
 'StoppingInstances': [{'CurrentState': {'Code': 64, 'Name': 'stopping'},
                        'InstanceId': 'i-0c462c48bc396bdbb',
                        'PreviousState': {'Code': 16, 'Name': 'running'}}]

La salida del último comando indica que la llamada al método está deteniendo la instancia. Si vuelvo a recuperar la instancia de demostración e imprimo el State, ahora veo que está detenido.

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> demo['Reservations'][0]['Instances'][0]['State']
{'Code': 80, 'Name': 'stopped'}

Para iniciar la copia de seguridad de la misma instancia, hay un método de complemento llamado start_instancesque funciona de manera similar al stop_instancesmétodo que demuestro a continuación.

>>> pprint.pprint(client.start_instances(InstanceIds=[instance_id]))
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 19:37:02 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': '21c65902-6665-4137-9023-43ac89f731d9',
                      'RetryAttempts': 0},
 'StartingInstances': [{'CurrentState': {'Code': 0, 'Name': 'pending'},
                        'InstanceId': 'i-0c462c48bc396bdbb',
                        'PreviousState': {'Code': 80, 'Name': 'stopped'}}]}

El resultado inmediato del comando es que está pendiente de inicio. Ahora, cuando vuelvo a buscar la instancia e imprimo su estado, muestra que se está ejecutando nuevamente.

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> demo['Reservations'][0]['Instances'][0]['State']
{'Code': 16, 'Name': 'running'}

Enfoque alternativo para recuperar, iniciar y detener

Además de la EC2.Clientclase con la que he estado trabajando hasta ahora, también hay una clase EC2.Instance que es útil en casos como este en el que solo necesito preocuparme por una instancia a la vez.

A continuación, utilizo el sessionobjeto generado previamente para obtener un objeto de recurso EC2, que luego puedo usar para recuperar y crear una instancia de un Instanceobjeto para mi instancia de demostración.

>>> ec2 = session.resource('ec2')
>>> instance = ec2.Instance(instance_id)

En mi opinión, un beneficio importante de usar la Instanceclase es que luego está trabajando con objetos reales en lugar de una representación de diccionario de un punto en el tiempo de la instancia, pero pierde el poder de poder realizar acciones en múltiples instancias a la vez que el EC2.Clientla clase proporciona.

Por ejemplo, para ver el estado de la instancia de demostración que acabo de crear anteriormente, es tan simple como esto:

>>> instance.state
{'Code': 16, 'Name': 'running'}

La Instanceclase tiene muchos métodos útiles, dos de los cuales son starty stopque usaré para iniciar y detener mis instancias, así:

>>> pprint.pprint(instance.stop())
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 19:58:25 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': 'a2f76028-cbd2-4727-be3e-ae832b12e1ff',
                      'RetryAttempts': 0},
 'StoppingInstances': [{'CurrentState': {'Code': 64, 'Name': 'stopping'},
                        'InstanceId': 'i-0c462c48bc396bdbb',
                        'PreviousState': {'Code': 16, 'Name': 'running'}}]}

Después de esperar aproximadamente un minuto para que se detenga por completo … luego verifico el estado nuevamente:

>>> instance.state
{'Code': 80, 'Name': 'stopped'}

Ahora puedo iniciarlo de nuevo.

>>> pprint.pprint(instance.start())
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 20:01:01 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': '3cfc6061-5d64-4e52-9961-5eb2fefab2d8',
                      'RetryAttempts': 0},
 'StartingInstances': [{'CurrentState': {'Code': 0, 'Name': 'pending'},
                        'InstanceId': 'i-0c462c48bc396bdbb',
                        'PreviousState': {'Code': 80, 'Name': 'stopped'}}]}

Luego revisando el estado nuevamente después de un rato …

>>> instance.state
{'Code': 16, 'Name': 'running'}

Creación de una imagen de respaldo de una instalación EC2.

Un tema importante en la administración de servidores es la creación de copias de seguridad a las que recurrir en caso de que un servidor se dañe. En esta sección, voy a demostrar cómo crear una copia de seguridad de la imagen de máquina de Amazon (AMI) de mi instancia de demostración, que luego AWS almacenará en su Simple Storage Service (S3). Esto se puede usar más tarde para recrear esa instancia EC2, al igual que usé la AMI inicial para crear la instancia de demostración.

Para comenzar, mostraré cómo usar la EC2.Clientclase y su create_imagemétodo para crear una imagen AMI de la instancia de demostración proporcionando el ID de la instancia y un nombre descriptivo para la instancia.

>>> import datetime
>>> date = datetime.datetime.utcnow().strftime('%Y%m%d')
>>> date
'20181221'
>>> name = f"InstanceID_{instance_id}_Image_Backup_{date}"
>>> name
'InstanceID_i-0c462c48bc396bdbb_Image_Backup_20181221'
>>> name = f"InstanceID_{instance_id}_Backup_Image_{date}"
>>> name
'InstanceID_i-0c462c48bc396bdbb_Backup_Image_20181221'
>>> pprint.pprint(client.create_image(InstanceId=instance_id, Name=name))
{'ImageId': 'ami-00d7c04e2b3b28e2d',
 'ResponseMetadata': {'HTTPHeaders': {'content-length': '242',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 20:13:55 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': '7ccccb1e-91ff-4753-8fc4-b27cf43bb8cf',
                      'RetryAttempts': 0}}

De manera similar, puedo usar el método de la Instanceclase create_imagepara realizar la misma tarea, que devuelve una instancia de una EC2.Imageclase que es similar a la EC2.Instanceclase.

>>> image = instance.create_image(Name=name + '_2')

Etiquetado de imágenes e instancias EC2

Una característica muy poderosa, pero extremadamente simple, de las instancias EC2 y las imágenes AMI es la capacidad de agregar etiquetas personalizadas. Puede agregar etiquetas tanto a través de la consola de administración de AWS, como mostré al crear la instancia de demostración con las etiquetas Name y BackUp, como también mediante programación con boto3 y la API REST de AWS.

Como EC2.Instancetodavía tengo un objeto flotando en la memoria de mi intérprete de Python, lo usaré para mostrar las etiquetas de la instancia de demostración.

>>> instance.tags
[{'Key': 'BackUp', 'Value': ''}, {'Key': 'Name', 'Value': 'demo-instance'}]

Tanto el EC2.Instancey las EC2.Imageclases tienen un conjunto idéntico funcionamiento de create_tagsmétodos para agregar etiquetas a sus recursos representados. A continuación, demuestro cómo agregar una etiqueta RemoveOn a la imagen creada anteriormente, que se combina con una fecha en la que debe eliminarse. El formato de fecha utilizado es “AAAAMMDD”.

>>> image.create_tags(Tags=[{'Key': 'RemoveOn', 'Value': remove_on}])
[ec2.Tag(resource_id='ami-081c72fa60c8e2d58', key='RemoveOn', value="20181222")]

Nuevamente, se puede lograr lo mismo con la EC2.Clientclase al proporcionar una lista de ID de recursos, pero con el cliente puede etiquetar tanto las imágenes como las instancias EC2 al mismo tiempo si lo desea especificando sus ID en el parámetro Resource de la create_tagsfunción, así :

>>> pprint.pprint(client.create_tags(Resources=['ami-00d7c04e2b3b28e2d'], Tags=[{'Key': 'RemoveOn', 'Value': remove_on}]))
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '221',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 20:52:39 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': '645b733a-138c-42a1-9966-5c2eb0ca3ba3',
                      'RetryAttempts': 0}}

Creación de una instancia EC2 a partir de una imagen de respaldo

Me gustaría comenzar esta sección dándoles algo en qué pensar. Póngase en la mentalidad incómoda de un administrador de sistemas, o peor aún, un desarrollador que finge ser un administrador de sistemas porque el producto en el que están trabajando no tiene uno (advertencia … ese soy yo), y uno de sus servidores EC2 tiene se corrompe.

¡Eeek! Es hora de revolver … ahora necesita averiguar qué tipo de sistema operativo, tamaño y servicios se estaban ejecutando en el servidor inactivo … buscar a tientas la configuración e instalación del servidor base, más cualquier aplicación que pertenezca a él, y rezar todo aparece correctamente.

¡Uf! Tome un respiro y relájese porque estoy a punto de mostrarle cómo volver a ponerse en marcha rápidamente, además … alerta de spoiler … Voy a extraer estos comandos únicos del intérprete de Python en un conjunto de scripts que funcione en el final para que lo modifiques más y lo uses.

Bien, con ese ejercicio mental fuera del camino, déjame volver al trabajo. Para crear una instancia EC2 a partir de una ID de imagen, utilizo el método de la EC2.Clientclase run_instancesy especifico la cantidad de instancias que se iniciarán y el tipo de instancia que se ejecutará.

>>> pprint.pprint(client.run_instances(ImageId='ami-081c72fa60c8e2d58', MinCount=1, MaxCount=1, InstanceType="t2.micro"))
...

Estoy omitiendo la salida nuevamente debido a su verbosidad. Eche un vistazo a los documentos oficiales del método run_instances , ya que hay muchos parámetros entre los que elegir para personalizar exactamente cómo ejecutar la instancia.

Eliminar imágenes de respaldo

Idealmente, estaría haciendo copias de seguridad en un intervalo bastante frecuente (es decir, diariamente como mínimo) y junto con todas estas copias de seguridad vienen tres cosas, una de las cuales es bastante buena y las otras dos son algo problemáticas. En el lado bueno de las cosas, estoy haciendo instantáneas de los estados conocidos de mi servidor EC2, lo que me da un punto en el tiempo al que recurrir si las cosas van mal. Sin embargo, en el lado malo, estoy creando desorden en mis depósitos S3 y acumulando cargos con cada copia de seguridad adicional que guardo en el almacenamiento.

Una forma de mitigar las desventajas del desorden y el aumento de los costos de almacenamiento es eliminar las imágenes de respaldo después de que haya transcurrido un tiempo predeterminado y ahí es donde las etiquetas que creé anteriormente me salvarán. Puedo consultar mis imágenes de respaldo de EC2 y ubicar las que tienen una etiqueta RemoveOn particular y luego eliminarlas.

Puedo comenzar usando el describe_imagesmétodo en la EC2.Clientinstancia de la clase junto con un filtro para la etiqueta ‘RemoveOn’ para obtener todas las imágenes que etiqueté para eliminar en una fecha determinada.

>>> remove_on = '201812022'
>>> images = client.describe_images(Filters=[{'Name': 'tag:RemoveOn', 'Values': [remove_on]}])

A continuación, repito todas las imágenes y llamo al método del cliente deregister_imagepasándole el ID de la imagen iterada y listo, no más imagen.

>>> remove_on = '201812022'
>>> for img in images['Images']:
...     client.deregister_image(ImageId=img['ImageId'])

Terminación de una instancia EC2

Bueno, habiendo cubierto cómo iniciar, detener, crear y eliminar imágenes de respaldo y lanzar una instancia EC2 desde una imagen de respaldo, me estoy acercando al final de este tutorial. Ahora todo lo que queda por hacer es limpiar mis instancias de demostración llamando a la EC2.Clientclase terminate_instancesy pasando los ID de instancia para terminar. Nuevamente, usaré describe_instancesun filtro para el nombre de la instancia de demostración para obtener los detalles y obtener su ID de instancia. Entonces puedo usarlo terminate_instancespara deshacerme de él para siempre.

Nota : Sí, esto es algo para siempre, así que tenga mucho cuidado con este método.

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> pprint.pprint(client.terminate_instances(InstanceIds=[instance_id]))
{'ResponseMetadata': {'HTTPHeaders': {'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 22:14:20 GMT',
                                      'server': 'AmazonEC2',
                                      'transfer-encoding': 'chunked',
                                      'vary': 'Accept-Encoding'},
                      'HTTPStatusCode': 200,
                      'RequestId': '78881a08-0240-47df-b502-61a706bfb3ab',
                      'RetryAttempts': 0},
 'TerminatingInstances': [{'CurrentState': {'Code': 32,
                                            'Name': 'shutting-down'},
                           'InstanceId': 'i-0c462c48bc396bdbb',
                           'PreviousState': {'Code': 16, 'Name': 'running'}}]}

Uniendo las cosas para un script de automatización

Ahora que he analizado estas funcionalidades emitiendo comandos uno por uno usando el intérprete de shell de Python (que recomiendo encarecidamente a los lectores que hagan al menos una vez por su cuenta para experimentar con las cosas), reuniré todo en dos scripts separados llamados ec2backup. .py y amicleanup.py.

El script ec2backup.py simplemente consultará todas las instancias EC2 disponibles que tienen la etiqueta BackUp y luego creará una imagen AMI de respaldo para cada una mientras las etiqueta con una etiqueta RemoveOn con un valor de 3 días en el futuro.

# ec2backup.py

from datetime import datetime, timedelta
import awsutils

def backup(region_id='us-east-1'):
    '''This method searches for all EC2 instances with a tag of BackUp
       and creates a backup images of them then tags the images with a
       RemoveOn tag of a YYYYMMDD value of three UTC days from now
    '''
    created_on = datetime.utcnow().strftime('%Y%m%d')
    remove_on = (datetime.utcnow() + timedelta(days=3)).strftime('%Y%m%d')
    session = awsutils.get_session(region_id)
    client = session.client('ec2')
    resource = session.resource('ec2')
    reservations = client.describe_instances(Filters=[{'Name': 'tag-key', 'Values': ['BackUp']}])
    for reservation in reservations['Reservations']:
        for instance_description in reservation['Instances']:
            instance_id = instance_description['InstanceId']
            name = f"InstanceId({instance_id})_CreatedOn({created_on})_RemoveOn({remove_on})"
            print(f"Creating Backup: {name}")
            image_description = client.create_image(InstanceId=instance_id, Name=name)
            images.append(image_description['ImageId'])
            image = resource.Image(image_description['ImageId'])
            image.create_tags(Tags=[{'Key': 'RemoveOn', 'Value': remove_on}, {'Key': 'Name', 'Value': name}])

if __name__ == '__main__':
    backup()

El siguiente es el script amicleanup.py que consulta todas las imágenes AMI que tienen una etiqueta RemoveOn igual a la fecha del día en que se ejecutó en el formato “AAAAMMDD” y las elimina.

# amicleanup.py

from datetime import datetime
import awsutils

def cleanup(region_id='us-east-1'):
    '''This method searches for all AMI images with a tag of RemoveOn
       and a value of YYYYMMDD of the day its ran on then removes it
    '''
    today = datetime.utcnow().strftime('%Y%m%d')
    session = awsutils.get_session(region_id)
    client = session.client('ec2')
    resource = session.resource('ec2')
    images = client.describe_images(Filters=[{'Name': 'tag:RemoveOn', 'Values': [today]}])
    for image_data in images['Images']:
        image = resource.Image(image_data['ImageId'])
        name_tag = [tag['Value'] for tag in image.tags if tag['Key'] == 'Name']
        if name_tag:
            print(f"Deregistering {name_tag[0]}")
        image.deregister()

if __name__ == '__main__':
    cleanup()

Implementación Cron

Una forma relativamente sencilla de implementar la funcionalidad de estos dos scripts sería programar dos tareas cron en un servidor Linux para ejecutarlas. En un ejemplo a continuación, he configurado una tarea cron para que se ejecute todos los días a las 11 p.m. para ejecutar el script ec2backup.py y luego otra a las 11:30 p.m. para ejecutar el script amicleanup.py.

0 23 * * * /path/to/venv/bin/python /path/to/ec2backup.py
30 23 * * * /path/to/venv/bin/python /path/to/amicleanup.py

Implementación de AWS Lambda

Una solución más elegante es utilizar AWS Lambda para ejecutar los dos como un conjunto de funciones. Hay muchos beneficios de usar AWS Lambda para ejecutar código, pero para este caso de uso de ejecutar un par de funciones de Python para crear y eliminar imágenes de respaldo, las más pertinentes son la alta disponibilidad y evitar pagar por recursos inactivos. Ambos beneficios se obtienen mejor cuando se compara el uso de Lambda con la ejecución de los dos trabajos cron descritos en la última sección.

Si tuviera que configurar mis dos trabajos cron para que se ejecuten en un servidor existente, ¿qué sucede si ese servidor deja de funcionar? No solo tengo el dolor de cabeza de tener que volver a activar ese servidor, sino que también corro la posibilidad de perder una ejecución programada de los trabajos cron que controlan el proceso de copia de seguridad y limpieza del servidor EC2. Esto no es un problema con AWS Lambda, ya que está diseñado con redundancia para garantizar una disponibilidad extremadamente alta.

El otro beneficio principal de no tener que pagar por recursos inactivos se comprende mejor en un ejemplo en el que puedo haber creado una instancia solo para administrar estos dos scripts que se ejecutan una vez al día. Este método no solo cae dentro de la falla de disponibilidad potencial del último elemento, sino que ahora se ha aprovisionado una máquina virtual completa para ejecutar dos scripts una vez al día, lo que constituye una cantidad muy pequeña de tiempo de cómputo y muchos recursos desperdiciados inactivos. Este es un caso excelente para utilizar AWS Lambda para mejorar la eficiencia operativa.

Otra eficiencia operativa resultante del uso de Lambda es no tener que perder tiempo manteniendo un servidor dedicado.

Para crear una función de AWS Lambda para las copias de seguridad de la imagen de la instancia EC2, siga estos pasos:

Paso 1. En el menú Servicio, haga clic en Lambda dentro de la sección Computar.

Paso 2. Haga clic en el botón Crear función.

Paso 3. Seleccione la opción Autor desde cero, escriba “ec2backup” como nombre de función, seleccione Python 3.6 de las opciones de tiempo de ejecución, luego agregue el usuario boto3 para el rol y haga clic en Crear función como se muestra a continuación:

Paso 4. En el diseñador, seleccione Eventos de CloudWatch y agregue un trabajo cron cron(0 11 * ? * *)que hará que la función se ejecute todos los días a las 11 p.m.

Paso 5. En el editor de código, agregue el siguiente código:

import boto3
import os
from datetime import datetime, timedelta

def get_session(region, access_id, secret_key):
    return boto3.session.Session(region_name=region,
                                aws_access_key_id=access_id,
                                aws_secret_access_key=secret_key)

def lambda_handler(event, context):
    '''This method searches for all EC2 instances with a tag of BackUp
       and creates a backup images of them then tags the images with a
       RemoveOn tag of a YYYYMMDD value of three UTC days from now
    '''
    created_on = datetime.utcnow().strftime('%Y%m%d')
    remove_on = (datetime.utcnow() + timedelta(days=3)).strftime('%Y%m%d')
    session = get_session(os.getenv('REGION'),
                          os.getenv('ACCESS_KEY_ID'),
                          os.getenv('SECRET_KEY'))
    client = session.client('ec2')
    resource = session.resource('ec2')
    reservations = client.describe_instances(Filters=[{'Name': 'tag-key', 'Values': ['BackUp']}])
    for reservation in reservations['Reservations']:
        for instance_description in reservation['Instances']:
            instance_id = instance_description['InstanceId']
            name = f"InstanceId({instance_id})_CreatedOn({created_on})_RemoveOn({remove_on})"
            print(f"Creating Backup: {name}")
            image_description = client.create_image(InstanceId=instance_id, Name=name)
            image = resource.Image(image_description['ImageId'])
            image.create_tags(Tags=[{'Key': 'RemoveOn', 'Value': remove_on}, {'Key': 'Name', 'Value': name}])

Paso 6. En la sección debajo del editor de código, agregue algunas variables de entorno.

  • REGION con un valor de la región de las instancias EC2 para respaldar que es us-east-1 en este ejemplo
  • ACCESS_KEY_ID con el valor de la clave de acceso de la sección donde se configuró el usuario boto3
  • SECRET_KEY con el valor de la clave secreta de la sección donde se configuró boto3-user

Paso 7. Haga clic en el botón Guardar en la parte superior de la página.

Para la funcionalidad de limpieza de imágenes, siga los mismos pasos con los siguientes cambios.

Paso 3. Le doy un nombre de “amicleanup”

Paso 4. Utilizo una configuración de hora ligeramente diferente cron(30 11 * ? * *)para ejecutar a las 11:30 p.m.

Paso 5. Utilice la siguiente función de limpieza:

import boto3
from datetime import datetime
import os

def get_session(region, access_id, secret_key):
    return boto3.session.Session(region_name=region,
                                aws_access_key_id=access_id,
                                aws_secret_access_key=secret_key)

def lambda_handler(event, context):
    '''This method searches for all AMI images with a tag of RemoveOn
       and a value of YYYYMMDD of the day its ran on then removes it
    '''
    today = datetime.utcnow().strftime('%Y%m%d')
    session = get_session(os.getenv('REGION'),
                          os.getenv('ACCESS_KEY_ID'),
                          os.getenv('SECRET_KEY'))
    client = session.client('ec2')
    resource = session.resource('ec2')
    images = client.describe_images(Filters=[{'Name': 'tag:RemoveOn', 'Values': [today]}])
    for image_data in images['Images']:
        image = resource.Image(image_data['ImageId'])
        name_tag = [tag['Value'] for tag in image.tags if tag['Key'] == 'Name']
        if name_tag:
            print(f"Deregistering {name_tag[0]}")
        image.deregister()

Conclusión

En este artículo, he explicado cómo utilizar la biblioteca Boto3 del SDK de AWS Python para interactuar con los recursos de EC2. Demuestro cómo automatizar las tareas de administración operativa para la creación de copias de seguridad de imágenes AMI para instancias EC2 y la limpieza posterior de esas imágenes de copia de seguridad mediante trabajos cron programados en un servidor dedicado o con AWS Lambda.

Si está interesado en aprender a usar Boto y AWS Simple Storage Service (S3), consulte el artículo de Scott Robinson aquí en Pharos.sh.

Como siempre, gracias por leer y no dude en comentar o criticar a continuación.

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad