-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: código terraform de infraestructura y prototipo de función l…
…ambda.
- Loading branch information
1 parent
74ed2be
commit 7fe7c80
Showing
15 changed files
with
343 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
name: Deploy Lambda Docker Function | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
|
||
jobs: | ||
deploy: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Check out the code | ||
uses: actions/checkout@v2 | ||
|
||
- name: Set up Docker Buildx | ||
uses: docker/setup-buildx-action@v2 | ||
|
||
- name: Log in to Amazon ECR | ||
uses: aws-actions/amazon-ecr-login@v1 | ||
|
||
- name: Build and push Docker image | ||
env: | ||
ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPOSITORY }} # Define el repositorio de ECR en los secretos de GitHub | ||
AWS_REGION: us-east-1 | ||
LAMBDA_IMAGE_TAG: "latest" | ||
run: | | ||
docker build -t $ECR_REPOSITORY:$LAMBDA_IMAGE_TAG . | ||
docker tag $ECR_REPOSITORY:$LAMBDA_IMAGE_TAG $ECR_REPOSITORY:$LAMBDA_IMAGE_TAG | ||
docker push $ECR_REPOSITORY:$LAMBDA_IMAGE_TAG | ||
- name: Deploy to AWS Lambda | ||
env: | ||
LAMBDA_FUNCTION_NAME: "docker_lambda_function" # Nombre de tu función Lambda | ||
ECR_REPOSITORY_URI: "${{ secrets.AWS_ECR_REPOSITORY }}:${{ LAMBDA_IMAGE_TAG }}" | ||
run: | | ||
aws lambda update-function-code \ | ||
--function-name $LAMBDA_FUNCTION_NAME \ | ||
--image-uri $ECR_REPOSITORY_URI |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
node_modules | ||
dist | ||
.terraform | ||
.terraform.lock.hcl | ||
terraform.tfstate | ||
terraform.tfstate.backup | ||
.terraform.tfstate.lock.info |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,64 @@ | ||
# Parte 1: Infraestructura e IaC | ||
|
||
## Diagrama Infraestructura | ||
- Base de Datos: Se utilizará una base de datos relacional dado que es más compatible para procesar analítica avanzada y conectarla en un futuro con alguna solución de Data Analytics como Google BigQuery, Amazon Redshift o Snowflake, que es parte del requerimiento. Dado que en este caso estamos proponiendo una arquitectura en AWS, la tecnología escogida será Aurora PostgreSQL. | ||
- Tecnología Pub/Sub: Existen diversas tecnologías para lograr este propósito, en particular por experiencia personal he utilizado SNS/SQS, RabbitMQ y Redis, sintiendo que todas en general cumplen el propósito y la elección en particular tiene que ir por decisiones previas (si ya se cuenta con una tecnología para Pub/Sub utilizada es mejor mantenerla para evitar fragmentación de tecnologías) o features específicos de una cola de mensajería (como AMQP). Dado que estamos proponiendo una solución simple administrada por AWS, escogeremos SNS/SQS para este ejemplo. | ||
- Endpoint HTTP para servir datos almacenados: Soy muy fanático de armar arquitecturas de microservicios basadas en contenedores orquestadas por Kubernetes, utilizando las opciones administradas en la nube como Google Cloud GKE o Amazon EKS, si bien la curva de aprendizaje no es menor, permiten mucha flexibilidad y escalabilidad, y con un buen pipeline de CI/CD se pueden abstraer lo suficiente de los desarrolladores para que solo se centren en escribir código y no preocuparse por la infraestructura. En este caso sin embargo, por simplicidad escogeremos implementar funciones AWS Lambda para lograr el propósito, dado que por ejemplo, en un contexto de piloto, puede ser muy útil levantar una prueba de concepto de bajo costo antes de hacer un deploy más altamente disponible y enterprise grade. | ||
## Identificación Infraestructura | ||
- **Base de Datos**: Se utilizará una base de datos relacional dado que es más compatible para procesar analítica avanzada y conectarla en un futuro con alguna solución de Data Analytics como Google BigQuery, Amazon Redshift o Snowflake, que es parte del requerimiento. Dado que en este caso estamos proponiendo una arquitectura en AWS, la tecnología escogida será Aurora PostgreSQL. | ||
- **Tecnología Pub/Sub**: Existen diversas tecnologías para lograr este propósito, en particular por experiencia personal he utilizado SNS/SQS, RabbitMQ y Redis, sintiendo que todas en general cumplen el propósito y la elección en particular tiene que ir por decisiones previas (si ya se cuenta con una tecnología para Pub/Sub utilizada es mejor mantenerla para evitar fragmentación de tecnologías) o features específicos de una cola de mensajería (como AMQP). Dado que estamos proponiendo una solución simple administrada por AWS, escogeremos SNS/SQS para este ejemplo. | ||
- **Endpoint HTTP para servir datos almacenados**: En general soy más de la idea de utilizar de microservicios basadas en contenedores orquestadas por Kubernetes, utilizando las opciones administradas en la nube como Google Cloud GKE o Amazon EKS, si bien la curva de aprendizaje no es menor, permiten mucha flexibilidad y escalabilidad, y con un buen pipeline de CI/CD se pueden abstraer lo suficiente de los desarrolladores para que solo se centren en escribir código y no preocuparse por la infraestructura. En este caso sin embargo, por simplicidad escogeremos implementar funciones AWS Lambda para lograr el propósito, dado que por ejemplo, en un contexto de piloto, puede ser muy útil levantar una prueba de concepto de bajo costo antes de hacer un deploy más altamente disponible y enterprise grade. | ||
|
||
Finalmente, esto se ve representado en el siguiente diagrama de arquitectura de la solución: | ||
![Diagrama](diagrama-infra.png) | ||
|
||
## Código Terraform | ||
DISCLAIMER: Los códigos Terraform fueron generados mediante ChatGPT. | ||
## Despliegue de infraestructura mediante código Terraform | ||
### Disclaimers | ||
- Cómo correr los scripts terraform son parte del [quick start guide](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-build). | ||
- Los códigos Terraform fueron generados mediante ChatGPT, sin embargo, se probaron contra una cuenta AWS y editados para corregir errores o detalles durante la generación de las propuestas de scripts (ej: El script de creación de VPC utilizaba un argumento deprecado en la creación del NAT Gateway). | ||
- La contraseña de RDS está definida en variables.tf lo que es una mala práctica. Se realizó por simplicidad pero se recomendaría utilizar un almacenamiento de secretos como AWS Secrets Manager. | ||
|
||
- VPC: Este código despliega una VPC con dos subnets privadas y dos públicas, en las zonas 1A y 1B de Virginia del Norte (us-east-1). | ||
- SNS/SQS: Se utilizará SNS/SQS de AWS como tecnología para tener una arquitectura Pub/Sub. Existen otras tecnologías como Apache Kafka, RabbitMQ o Redis | ||
### Descripción de los scripts: | ||
- Configuración Base (main.tf / variables.tf): Contiene definiciones generales como la zona AWS del despliegue y variables a utilizar. | ||
- VPC (vpc.tf): Este código despliega una VPC con dos subnets privadas y dos públicas, en las zonas 1A y 1B de Virginia del Norte (us-east-1). | ||
- RDS (rds.tf): Se levantará un servidor de Aurora PostgreSQL Serverless para almacenamiento de datos. | ||
- SNS/SQS (pubsub.tf): Se utilizará SNS/SQS de AWS como tecnología para tener una arquitectura Pub/Sub. Existen otras tecnologías como Apache Kafka, RabbitMQ o Redis. | ||
- Lambda (lambda.tf): Levanta una función AWS Lambda a partir de una imagen Docker compatible. | ||
|
||
# Parte 2: Aplicaciones y flujo CI/CD | ||
|
||
## API HTTP | ||
- Se creó un handler simple de Lambda que expone dos endpoints. Un endpoint GET el que consultará la base de datos y devolverá las rows de una tabla en formato JSON | ||
|
||
## Deploy | ||
- Se creó workflow de Github Actions que hace deploy de la función Lambda a una cuenta AWS cuyos parámetros se definen en secrets de Github. | ||
|
||
## Ingesta a BBDD | ||
- Se creó método POST que ingesta directamente a la base de datos. | ||
|
||
## Diagrama de arquitectura. | ||
- El diagrama general fue explicado en la parte 1 de este README. | ||
|
||
# Parte 3: Pruebas de Integración | ||
|
||
## Implementar flujo de CI/CD que verifique que la API opera correctamente. | ||
|
||
## Proponer otras pruebas de integración. | ||
|
||
## Identificar puntos críticos del sistema | ||
|
||
## Cómo robustecer la solución | ||
|
||
# Parte 4: Métricas y Monitoreo | ||
|
||
## Proponer 3 métricas para entender la salud y rendimiento. | ||
|
||
## Proponer una herramienta de observabilidad | ||
|
||
## Describe como sería la implementación de esta heramienta en la nube | ||
|
||
## Escalabilidad de la solución propuesta | ||
|
||
## Dificultades o limitaciones de la observabilidad | ||
|
||
# Parte 5: Alertas y SRE | ||
|
||
## Reglas y/o umbrales | ||
|
||
## SLI y SLO. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Usa la imagen base de Lambda para Node.js | ||
FROM public.ecr.aws/lambda/nodejs:20 | ||
|
||
# Copia el archivo de código y dependencias | ||
COPY package*.json ./ | ||
RUN npm install | ||
|
||
COPY index.js ./ | ||
|
||
# Define el punto de entrada para la función Lambda | ||
CMD ["index.handler"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
const { Pool } = require('pg'); | ||
|
||
// Parámetros de configuración de la base de datos | ||
const dbConfig = { | ||
host: process.env.DB_HOST, | ||
user: process.env.DB_USER, | ||
password: process.env.DB_PASSWORD, | ||
database: process.env.DB_NAME, | ||
port: 5432 | ||
}; | ||
|
||
exports.handler = async (event) => { | ||
const client = new Pool(dbConfig); | ||
|
||
try { | ||
await client.connect(); | ||
|
||
if (event.httpMethod === "GET") { | ||
const res = await client.query('SELECT * FROM users;'); | ||
await client.end(); | ||
|
||
return { | ||
statusCode: 200, | ||
headers: { "Content-Type": "application/json" }, | ||
body: JSON.stringify(res.rows) | ||
}; | ||
} else if (event.httpMethod === "POST") { | ||
const body = JSON.parse(event.body); | ||
const { name, email } = body; | ||
|
||
if (!name || !email) { | ||
return { | ||
statusCode: 400, | ||
body: JSON.stringify({ message: "Los campos 'name' y 'email' son obligatorios" }) | ||
}; | ||
} | ||
|
||
const insertQuery = 'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *'; | ||
const res = await client.query(insertQuery, [name, email]); | ||
await client.end(); | ||
|
||
return { | ||
statusCode: 201, | ||
headers: { "Content-Type": "application/json" }, | ||
body: JSON.stringify({ message: "Usuario insertado correctamente", user: res.rows[0] }) | ||
}; | ||
} else { | ||
return { | ||
statusCode: 405, | ||
headers: { "Allow": "GET, POST" }, | ||
body: JSON.stringify({ message: "El método utilizado no está permitido." }) | ||
}; | ||
} | ||
} catch (error) { | ||
console.error("Ha ocurrido un error al procesar la request:", error); | ||
|
||
return { | ||
statusCode: 500, | ||
body: JSON.stringify({ message: "Ha ocurrido un error inesperado, por favor intentarlo nuevamente." }) | ||
}; | ||
} | ||
}; |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Crear el rol IAM para Lambda con permisos básicos | ||
resource "aws_iam_role" "lambda_execution_role" { | ||
name = "lambda_execution_role" | ||
|
||
assume_role_policy = jsonencode({ | ||
Version = "2012-10-17", | ||
Statement = [ | ||
{ | ||
Action = "sts:AssumeRole", | ||
Effect = "Allow", | ||
Principal = { | ||
Service = "lambda.amazonaws.com" | ||
} | ||
} | ||
] | ||
}) | ||
} | ||
|
||
# Adjuntar una política administrada de AWS que otorga permisos básicos de ejecución a Lambda | ||
resource "aws_iam_role_policy_attachment" "lambda_execution_policy" { | ||
role = aws_iam_role.lambda_execution_role.name | ||
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" | ||
} | ||
|
||
# Crear la función Lambda con la imagen Docker | ||
resource "aws_lambda_function" "docker_lambda" { | ||
function_name = "docker_lambda_function" | ||
role = aws_iam_role.lambda_execution_role.arn | ||
image_uri = var.docker_image | ||
package_type = "Image" | ||
environment { | ||
variables = { | ||
EXAMPLE_VAR = "example_value" | ||
} | ||
} | ||
vpc_config { | ||
subnet_ids = [aws_subnet.private_subnet_1.id, aws_subnet.private_subnet_2.id] | ||
security_group_ids = [aws_security_group.lambda_security_group.id] | ||
} | ||
tags = { | ||
Name = "docker-lambda" | ||
} | ||
} | ||
|
||
# Configuración opcional de permisos de invocación para otros servicios | ||
resource "aws_lambda_permission" "allow_invoke" { | ||
statement_id = "AllowExecutionFromAPIGateway" | ||
action = "lambda:InvokeFunction" | ||
function_name = aws_lambda_function.docker_lambda.function_name | ||
principal = "apigateway.amazonaws.com" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
provider "aws" { | ||
region = "us-east-1" | ||
} | ||
|
||
# Grupo de seguridad para la función Lambda que permitirá el acceso a la base de datos | ||
resource "aws_security_group" "lambda_security_group" { | ||
vpc_id = aws_vpc.main_vpc.id | ||
name = "lambda_security_group" | ||
|
||
egress { | ||
from_port = 0 | ||
to_port = 0 | ||
protocol = "-1" | ||
cidr_blocks = ["0.0.0.0/0"] | ||
} | ||
} | ||
|
||
# Crear un grupo de seguridad para la base de datos | ||
resource "aws_security_group" "db_security_group" { | ||
vpc_id = aws_vpc.main_vpc.id | ||
name = "db_security_group" | ||
description = "Permitir acceso a la base de datos desde la red permitida" | ||
|
||
# Regla de entrada | ||
ingress { | ||
from_port = 5432 | ||
to_port = 5432 | ||
protocol = "tcp" | ||
cidr_blocks = ["0.0.0.0/0"] | ||
} | ||
|
||
# Regla de salida | ||
egress { | ||
from_port = 0 | ||
to_port = 0 | ||
protocol = "-1" | ||
cidr_blocks = ["0.0.0.0/0"] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# Crear el tema SNS FIFO | ||
resource "aws_sns_topic" "devops_challenge_sns_fifo" { | ||
name = "devops-challenge-topic.fifo" | ||
fifo_topic = true | ||
content_based_deduplication = true | ||
|
||
tags = { | ||
Name = "devops-challenge-sns-topic" | ||
} | ||
} | ||
|
||
# Crear la cola SQS FIFO | ||
resource "aws_sqs_queue" "devops_challenge_sqs_fifo" { | ||
name = "devops-challenge-queue.fifo" | ||
fifo_queue = true | ||
content_based_deduplication = true | ||
message_retention_seconds = 86400 # Opcional: Ajusta el tiempo de retención según sea necesario (24 horas) | ||
|
||
tags = { | ||
Name = "devops-challenge-sqs-queue" | ||
} | ||
} | ||
|
||
# Suscripción de la cola SQS al tema SNS | ||
resource "aws_sns_topic_subscription" "sns_to_sqs_subscription" { | ||
topic_arn = aws_sns_topic.devops_challenge_sns_fifo.arn | ||
protocol = "sqs" | ||
endpoint = aws_sqs_queue.devops_challenge_sqs_fifo.arn | ||
|
||
# Configura los atributos para mensajes FIFO | ||
raw_message_delivery = true | ||
|
||
# Establece permisos para que SNS pueda enviar mensajes a SQS | ||
depends_on = [aws_sqs_queue_policy.allow_sns_to_sqs] | ||
} | ||
|
||
# Política de permisos para que SNS envíe mensajes a la cola SQS | ||
resource "aws_sqs_queue_policy" "allow_sns_to_sqs" { | ||
queue_url = aws_sqs_queue.devops_challenge_sqs_fifo.id | ||
|
||
policy = jsonencode({ | ||
Version = "2012-10-17", | ||
Statement = [ | ||
{ | ||
Effect = "Allow", | ||
Principal = { Service = "sns.amazonaws.com" }, | ||
Action = "sqs:SendMessage", | ||
Resource = aws_sqs_queue.devops_challenge_sqs_fifo.arn, | ||
Condition = { | ||
ArnEquals = { | ||
"aws:SourceArn" = aws_sns_topic.devops_challenge_sns_fifo.arn | ||
} | ||
} | ||
} | ||
] | ||
}) | ||
} |
Oops, something went wrong.