Volúmenes de EBS: Ahorrar almacenamiento AWS

volúmenes de EBS

Ahorre su costo de almacenamiento en AWS con Terraform

Los volúmenes de EBS se recomiendan como almacenamiento para sistemas de archivos y aplicaciones de tipo base de datos. Por lo tanto, se pueden utilizar para almacenar una gran cantidad de datos.

Dado que con EBS, usted paga por la cantidad de GB que aprovisiona por mes hasta que libera el almacenamiento, uno debe asegurarse de que los volúmenes se liberen tan pronto como ya no se utilicen. De hecho, una de las mejores prácticas es crear instantáneas de los volúmenes de EBS no utilizados y eliminarlos para ahorrar costos.

Una solución es usar AWS Config, que tiene una regla y un plan de remediación para EBS no utilizado. Sabiendo que AWS Config tiene un costo, presentaremos en este artículo, una opción de bajo costo utilizando Infraestructura como código a través de Terraform. Este puede ser un laboratorio práctico para quienes preparan el examen de DevOps para profesionales certificados por AWS.


La solución

La solución es bastante simple.

volúmenes de EBS
Arquitectura de soluciones

Usamos una función Lambda que:

  1. Recupera la lista de los volúmenes de EBS disponibles
  2. Crea una instantánea para cada uno de ellos.
  3. Elimina los volúmenes de EBS

Una regla de EventBridge programada ejecutará la función según un programa definido.

La estructura del proyecto Terraform es la siguiente:

volúmenes de EBS

El archivo main.tf define los recursos de AWS necesarios.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.27"
    }
  }
  required_version = ">= 0.14.9"
}
# default profile
provider "aws" {
  profile = "default"
  region  = lookup(var.props, "region")
}
# loading policies json
data "local_file" "lambda_policy" {
  filename = "policy/policy.json"
}
# loading assumeRole json
data "local_file" "lambda_assumeRole_policy" {
  filename = "policy/assumeRole.json"
}

# lambda function
resource "aws_lambda_function" "function_ebs" {
  filename      = "code/index.zip"
  function_name = "automatic_ebs_snapshot"
  role          = aws_iam_role.lambda_assumeRole_policy.arn
  handler       = "index.lambda_handler"
  runtime       = "python3.9"
  environment {
    variables = {
        TAG_KEY = lookup(var.parameters,"tag_key"),
        TAG_VALUE = lookup(var.parameters,"tag_value")
    }
  }
  timeout = 600
  tags = var.tags
}

# iam role resource
resource "aws_iam_role" "lambda_assumeRole_policy" {
  name = "ebsSnapshotPolicy"
  assume_role_policy = data.local_file.lambda_assumeRole_policy.content
}

# iam policy resource
resource "aws_iam_role_policy" "pol" {
  name = "policy"
  role = aws_iam_role.lambda_assumeRole_policy.id
  policy = replace(data.local_file.lambda_policy.content, "ACCOUNT_ID", lookup(var.props,"account_id")) # substitue the account_id in policy.json for cloudwatch logs
}

# ----- EventBridge rule ----- 
resource "aws_cloudwatch_event_rule" "event_rule" {
  name        = "automatic_ebs_snapshot_rule"
  description = "Automatic rule for EBS snapshot creation"
  schedule_expression = lookup(var.schedule, "start")
  is_enabled = true
}
# ----- EventBridge target ----- 
resource "aws_cloudwatch_event_target" "target" {
  target_id = aws_lambda_function.function_ebs.id
  rule      = aws_cloudwatch_event_rule.event_rule.name
  arn       = aws_lambda_function.function_ebs.arn
}
# ----- Allow EventBridge to execute Lambda function ----- 
resource "aws_lambda_permission" "function_ebs_permission" {
  statement_id = "function_ebs_permission"
  action = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.function_ebs.function_name}"
  principal = "events.amazonaws.com"
  source_arn = "${aws_cloudwatch_event_rule.event_rule.arn}"
}

Los principales recursos son:

  • El rol y la política en la carpeta de políticas que definen los permisos para enumerar volúmenes de EBS, enumerar y actualizar etiquetas y crear instantáneas.
  • La función lambda definida en code/index.py asumirá la función de enumerar los volúmenes disponibles de EBS, crear las instantáneas y eliminar los volúmenes.
import boto3
import datetime
import os
import logging

TAG_KEY = os.environ.get('TAG_KEY')
TAG_VALUE = os.environ.get('TAG_VALUE')
logger = logging.getLogger()
logger.setLevel(logging.INFO)

ec2 = boto3.resource('ec2')

# create the tags 
def generate_tags(volumeId, tags):
    tag_final = []
    
    if tags:
        for tag in tags:
            
            if tag['Key']=='Name':
                tag['Key'] = 'Volume'
            tag_final.append(tag)
                
    tag_final.append({'Key': 'Source',  'Value': 'Automatically generated'})
    tag_final.append({'Key': 'Name', 'Value': f'snapshot-ebs-{volumeId}'})
    tag_final.append({'Key': TAG_KEY, 'Value': TAG_VALUE})
    tag_final.append({'Key': 'DateCreation', 'Value': f'{datetime.datetime.now()}'})
    
    return tag_final
    

def lambda_handler(event, context):
    
    volumes = ec2.volumes.filter(Filters=[{'Name': 'status', 'Values': ['available']}]) 
    
    for device in volumes:
                          
        snap = ec2.create_snapshot(
                VolumeId=device.id,
                TagSpecifications=[
                {
                    'ResourceType': 'snapshot',
                    'Tags': generate_tags(device.id, device.tags)
                },
            ])
        
        logger.info(f'Snapshot {snap.id} created for Volume {device.id}.')
        
        # delete volume
        device.delete()
        logger.info(f'Volume {device.id} has been deleted.')
    
    logger.info('EBS snapshot creation completed successfully!')
        
  • La regla EventBridge tiene la función lambda como objetivo y se ejecutará según un cronograma definido en la variable schedule.start en variable.tf a continuación:
# account information
variable "props" {
    type = map(string)
    default = {
        region = "us-east-1"
        account_id="123456789999"
    }
} 

# deployment tags; more tags could be added in the mapping
variable "tags" {
    type = map(string)
    default = {
        IaC = "Terraform"

    }
}

# environment variables used to tag the snapshot
variable "parameters" {
    type = map(string)
    default = {
        tag_key = "Achive"
        tag_value = "automatic"
    }
}

# schedule
variable "schedule" {
    type = map(string)
    default = {
        start = "cron(0 10 ? * SUN *)"
    }

}

Consulte el repositorio de GitHub para obtener más detalles.

Desplegar

Para poder implementar esta solución, estos son algunos requisitos previos:

  1. Terraform debe estar instalado
  2. AWS CLI debe estar instalado
  3. Se requiere un perfil de IAM con permisos para crear la regla EventBridge y la función Lambda.

El ejecutable build-deploy.sh contiene comandos para validar el código de Terraform e implementar los recursos en la nube de AWS. Una vez que se inicia Terraform, use el siguiente comando:

./build-deploy.sh

Resultado

Seis recursos serán creados por Terraform en AWS como se muestra a continuación:

volúmenes de EBS
Recursos creados

En la consola de AWS, se crean la función Lambda y su disparador EvenBridge.

volúmenes de EBS
Recursos creados en la consola de AWS

Conclusión

Mostramos cómo podemos implementar una solución ligera para reducir el costo de los volúmenes de EBS que no involucra a AWS Config. Este ejercicio es en sí mismo un buen laboratorio práctico para los colegas de DevOps/SysOps, ya que involucra Infraestructura como código con Terraform y automatización en la nube.

Podríamos mejorar aún más esta solución de muchas maneras: automatizar el ciclo de vida de las instantáneas, reemplazar la regla de programación de EventBridge por un patrón de eventos (cambio de estado de EBS), etc.

Referencias

  1. https://aws.amazon.com/ebs/pricing/
  2. https://aws.amazon.com/config/pricing/
  3. https://learn.hashicorp.com/collections/terraform/aws-get-started
  4. https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html

Recent Post