A fines de 2020, AWS anunció la compatibilidad con imágenes de contenedores para Lambda. Esta característica le permite empaquetar e implementar funciones de Lambda como imágenes de contenedor de hasta 10 GB de tamaño. Este artículo cubrirá cómo puede usar Terraform para implementar funciones de Python Lambda respaldadas por la imagen del contenedor. En este artículo, cubrimos cómo puede usar Terraform para implementar funciones Python Lambda respaldadas por la imagen del contenedor.
Una de las tareas comunes en el mundo de la nube es replicar repositorios de código fuente desde las instalaciones a la nube o entre entornos de nube. Entonces, para ilustrar el enfoque, decidimos agregar compatibilidad con Git y GitPython a la función Lambda.
Estructura del proyecto
Aquí hay una estructura de proyecto que usaremos durante esta demostración:
$ tree lambda_container
lambda_container
├── README.md
├── lambdas
│ └── git_client
│ ├── Dockerfile
│ └── index.py
└── main.tf
2 directories, 4 files
lambdas
– la carpeta donde ponemos el código fuente de las funciones de Lambdamain.tf
– Código de demostración de Terraform, que construirá el contenedor Docker para la función git_client Lambda y luego implementará la función
Dockerfile
Describamos un contenedor Docker que alojará todas las dependencias para nuestras funciones lambda. Aquí está el Dockerfile
contenido:
FROM public.ecr.aws/lambda/python:3.8
RUN yum update -y && \
yum install -y git && \
rm -Rf /var/cache/yum && \
pip install git-remote-codecommit boto3 GitPython awscli
COPY index.py ${LAMBDA_TASK_ROOT}
CMD [ "index.handler" ]
Estamos tomando la imagen Docker pública de Python 3.8 de Amazon como base. Luego, instalamos Git, limpiamos los cachés de yum para hacer que el contenedor sea más pequeño e instalamos las dependencias requeridas, lo que nos permite usar Git con CodeCommit mediante IAM para la autenticación.
A continuación, estamos copiando el index.py
archivo a la carpeta donde debe residir el código de la función Lambda. Consulte Uso de variables de entorno de AWS Lambda para obtener información adicional.
Finalmente, estamos especificando ejecutar el método del controlador desde el archivo index.py en el lanzamiento del contenedor.
Código Lambda
Tan pronto como finalice la declaración del contenedor Lambda, podemos escribir una función Lambda, que la usará. Aquí hay un ejemplo de código, que mostrará cómo clonar el repositorio de Git. Estoy seguro de que podrá ajustar este ejemplo a sus necesidades personales:
import logging
import os
import git
TMP_DIR = "/tmp"
REPO_DIR = 'aws-config-rules'
REPO_URL = f'https://github.com/andreivmaksimov/{REPO_DIR}'
CLONE_PATH = os.path.join(TMP_DIR, REPO_DIR)
LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.INFO)
def clone(branch='master'):
repo = git.Repo.clone_from(REPO_URL, CLONE_PATH, branch=branch)
with repo.config_writer() as git_config:
git_config.set_value('user', 'email', '[email protected]')
git_config.set_value('user', 'name', 'Git Lambda')
def handler(event, context):
LOGGER.info('Event: %s', event)
LOGGER.info('Cloning repo: %s', REPO_URL)
clone()
En este código, declaramos las bibliotecas de Python necesarias, algunas constantes, la configuración del registrador y un par de funciones:
def clone(branch='master')
– esta función muestra cómo clonar el repositorio de Gitdef handler(event, context)
– esta función es el punto de entrada principal a la función Lambda, registra el evento entrante y llama aclone
función
Código de Terraform
Tan pronto como tengamos el código Lambda y declaremos su contenedor, podemos escribir código Terraform para automatizar la implementación. Aquí está:
variable region {
default = "us-east-1"
}
provider aws {
region = var.region
}
data aws_caller_identity current {}
locals {
prefix = "git"
account_id = data.aws_caller_identity.current.account_id
ecr_repository_name = "${local.prefix}-demo-lambda-container"
ecr_image_tag = "latest"
}
resource aws_ecr_repository repo {
name = local.ecr_repository_name
}
resource null_resource ecr_image {
triggers = {
python_file = md5(file("${path.module}/lambdas/git_client/index.py"))
docker_file = md5(file("${path.module}/lambdas/git_client/Dockerfile"))
}
provisioner "local-exec" {
command = <<EOF
aws ecr get-login-password --region ${var.region} | docker login --username AWS --password-stdin ${local.account_id}.dkr.ecr.${var.region}.amazonaws.com
cd ${path.module}/lambdas/git_client
docker build -t ${aws_ecr_repository.repo.repository_url}:${local.ecr_image_tag} .
docker push ${aws_ecr_repository.repo.repository_url}:${local.ecr_image_tag}
EOF
}
}
data aws_ecr_image lambda_image {
depends_on = [
null_resource.ecr_image
]
repository_name = local.ecr_repository_name
image_tag = local.ecr_image_tag
}
resource aws_iam_role lambda {
name = "${local.prefix}-lambda-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow"
}
]
}
EOF
}
data aws_iam_policy_document lambda {
statement {
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
effect = "Allow"
resources = [ "*" ]
sid = "CreateCloudWatchLogs"
}
statement {
actions = [
"codecommit:GitPull",
"codecommit:GitPush",
"codecommit:GitBranch",
"codecommit:ListBranches",
"codecommit:CreateCommit",
"codecommit:GetCommit",
"codecommit:GetCommitHistory",
"codecommit:GetDifferences",
"codecommit:GetReferences",
"codecommit:BatchGetCommits",
"codecommit:GetTree",
"codecommit:GetObjectIdentifier",
"codecommit:GetMergeCommit"
]
effect = "Allow"
resources = [ "*" ]
sid = "CodeCommit"
}
}
resource aws_iam_policy lambda {
name = "${local.prefix}-lambda-policy"
path = "/"
policy = data.aws_iam_policy_document.lambda.json
}
resource aws_lambda_function git {
depends_on = [
null_resource.ecr_image
]
function_name = "${local.prefix}-lambda"
role = aws_iam_role.lambda.arn
timeout = 300
image_uri = "${aws_ecr_repository.repo.repository_url}@${data.aws_ecr_image.lambda_image.id}"
package_type = "Image"
}
output "lambda_name" {
value = aws_lambda_function.git.id
}
Este código de Terraform se probó con la versión 0.14.8 de Terraform.
En este ejemplo, estamos utilizando los siguientes recursos de terraformación:
aws_ecr_repository
– crea un registro ECR donde Terraform guardará la imagen del contenedor Docker, que luego será utilizada por la función Lambdanull_resource
– se utiliza para crear el contenedor Docker y enviarlo al registro ECR, activa los cambios en el código de la función Lambda y Dockerfile y permite que Terraform comprenda cuándo reconstruir la imagen y actualizar la función Lambdaaws_ecr_image
– nos permite consultar información sobre la imagen de Docker publicadaaws_iam_role
,aws_iam_policy_document
yaws_iam_policy
– declara permisos (enviar registros a CloudWatch, acceso CodeCommit) para la función Lambdaaws_lambda_function
– Declaración de la función Lambda en sí
Implementación
Para probar la solución, primero debe implementar el código de Terraform:
terraform init
terraform apply -auto-approve
Luego debe ejecutar la función Lambda:
aws lambda invoke --function-name git-lambda out --log-type Tail --query 'LogResult' --output text | base64 -d
Aquí hay un resultado esperado:
START RequestId: b8b742d6-5bd6-4098-90e3-5e30f5c6e816 Version: $LATEST
[INFO] 2021-03-16T02:10:28.064Z b8b742d6-5bd6-4098-90e3-5e30f5c6e816 Event: {}
[INFO] 2021-03-16T02:10:28.064Z b8b742d6-5bd6-4098-90e3-5e30f5c6e816 Cloning repo: https://github.com/andreivmaksimov/aws-config-rules
END RequestId: b8b742d6-5bd6-4098-90e3-5e30f5c6e816
REPORT RequestId: b8b742d6-5bd6-4098-90e3-5e30f5c6e816 Duration: 4069.15 ms Billed Duration: 6131 ms Memory Size: 128 MB Max Memory Used: 83 MB Init Duration: 2061.73 ms
Limpieza
Para limpiar todo, ejecute el siguiente comando:
terraform destroy
Resumen
Este artículo creó un contenedor Docker para la función AWS Lambda e implementó la solución completa mediante Terraform. Esperamos que este artículo le haya resultado útil. Si es así, por favor, ayúdanos a difundirlo por el mundo. Si tiene alguna pregunta, no dude en hacerla en la sección de chat a continuación.