Desplegando Go en AWS Lambda con GitHub Actions y Terraform
Introducci贸n
El despliegue de aplicaciones serverless se ha vuelto cada vez m谩s popular debido a su escalabilidad, eficiencia en costos y facilidad de gesti贸n. Amazon Web Services (AWS) Lambda proporciona una poderosa plataforma de computaci贸n sin servidor, mientras que Go (Golang) ofrece un lenguaje de programaci贸n de alto rendimiento y tipado est谩tico.
En este art铆culo, exploraremos c贸mo desplegar aplicaciones basadas en Go en AWS Lambda integrando GitHub Actions y Terraform. Al combinar estas herramientas, los desarrolladores pueden automatizar el proceso de despliegue, simplificar el control de versiones y asegurar pr谩cticas consistentes de infraestructura como c贸digo para sus proyectos sin servidor.
Prerrequisitos
Antes de comenzar, necesitar谩s lo siguiente:
- Una cuenta de AWS
- Una cuenta de GitHub
- AWS CLI instalado y configurado con tus credenciales de AWS (consulta la documentaci贸n de AWS para obtener instrucciones)
- Go instalado (consulta la documentaci贸n de Go para obtener instrucciones)
- Terraform instalado (consulta la documentaci贸n de Terraform para obtener instrucciones)
Paso 1: Crear un repositorio GitHub
En primer lugar, crea un repositorio GitHub para tu aplicaci贸n en Go.
Ve a GitHub y haz clic en “New repository”. Ingresa un nombre para tu repositorio y haz clic en “Create repository”.
Paso 2: Crear una aplicaci贸n Go
En un directorio vac铆o, crea una aplicaci贸n Go:
go mod init go_lambda
Esto crear谩 un archivo go.mod
que se usar谩 para gestionar las dependencias de tu aplicaci贸n en Go.
Luego, inicializa el repositorio Git:
git init
Despu茅s, crea un archivo .gitignore
:
# .gitignore
**/.terraform/*
*.tfstate
bootstrap
lambda-handler.zip
Por 煤ltimo, establece el origen remoto:
git remote add origin <TU_URL_DEL_REPOSITORIO>
Paso 3: Crear un archivo main.go
Crea el archivo main.go:
// main.go
package main
import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
func hello() (events.APIGatewayProxyResponse, error) {
return events.APIGatewayProxyResponse{
Body: "Hello World!",
StatusCode: 200,
}, nil
}
func main() {
// Hacer que el controlador est茅 disponible para llamadas a procedimientos remotos por AWS Lambda
lambda.Start(hello)
}
Este archivo contiene el c贸digo m铆nimo necesario para crear una aplicaci贸n Go que se pueda desplegar en AWS Lambda. Importa los paquetes events
y lambda
de la biblioteca aws-lambda-go
y define una funci贸n hello
que devuelve una respuesta que nuestra API Gateway retornar谩 al cliente. La funci贸n main
es el punto de entrada para nuestra aplicaci贸n y llama a la funci贸n hello
. La funci贸n lambda.Start
inicia el controlador de AWS Lambda1.
Paso 4: Instalar dependencias
Luego, instala las dependencias para tu aplicaci贸n Go:
go get github.com/aws/aws-lambda-go/events
go get github.com/aws/aws-lambda-go/lambda
Esto instalar谩 aws-lambda-go/events, que contiene la estructura APIGatewayProxyResponse
, y aws-lambda-go/lambda, que contiene la funci贸n lambda.Start
.
Paso 5: Crear el backend remoto
Usaremos S3 como nuestro backend remoto para Terraform. Esto es necesario porque estaremos usando GitHub Actions para desplegar nuestro c贸digo y necesitamos almacenar el estado de Terraform en una ubicaci贸n remota.
NOTA No estaremos creando una tabla DynamoDB para el bloqueo de estado para mantener las cosas simples, pero deber铆as considerar hacer esto si est谩s desplegando a producci贸n. Puedes leer m谩s sobre esto en la documentaci贸n de Terraform.
Para crear el bucket S3 para nuestro backend remoto, usaremos el siguiente comando:
aws s3api create-bucket --bucket <TU_NOMBRE_DE_BUCKET> --region us-east-1
Alternativamente, puedes crear el bucket utilizando la Consola de AWS siguiendo las instrucciones en la documentaci贸n de AWS.
Paso 6: Crear la configuraci贸n de Terraform
Ahora que hemos creado nuestra aplicaci贸n Go y hemos instalado las dependencias, podemos crear la configuraci贸n de Terraform. Usaremos la siguiente configuraci贸n de Terraform para desplegar nuestra aplicaci贸n Go en AWS Lambda:
# providers.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.7.0"
}
}
}
# backend.tf
terraform {
backend "s3" {
# 隆Reemplaza esto con el nombre de tu bucket!
bucket = "<TU_NOMBRE_DE_BUCKET>"
key = "go-lambda-test.tfstate"
region = "us-east-1"
}
}
# main.tf
resource "aws_lambda_function" "go_function" {
filename = "lambda-handler.zip"
function_name = "go-lambda-test"
handler = "bootstrap"
role = aws_iam_role.iam_for_lambda.arn
source_code_hash = filebase64sha256("lambda-handler.zip")
runtime = "go1.x"
}
data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}
resource "aws_iam_role" "iam_for_lambda" {
name = "iam_for_lambda"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
# API Gateway
resource "aws_api_gateway_rest_api" "go_api" {
name = "go_api"
description = "Esta es mi API para fines de demostraci贸n"
}
resource "aws_api_gateway_resource" "go_api_resource" {
rest_api_id = aws_api_gateway_rest_api.go_api.id
parent_id = aws_api_gateway_rest_api.go_api.root_resource_id
path_part = "test"
}
resource "aws_api_gateway_method" "go_api_method" {
rest_api_id = aws_api_gateway_rest_api.go_api.id
resource_id = aws_api_gateway_resource.go_api_resource.id
http_method = "GET"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "go_api_integration" {
rest_api_id = aws_api_gateway_rest_api.go_api.id
resource_id = aws_api_gateway_resource.go_api_resource.id
http_method = aws_api_gateway_method.go_api_method.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.go_function.invoke_arn
}
resource "aws_api_gateway_method" "proxy_root" {
rest_api_id = aws_api_gateway_rest_api.go_api.id
resource_id = aws_api_gateway_rest_api.go_api.root_resource_id
http_method = "ANY"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "proxy_root_integration" {
rest_api_id = aws_api_gateway_rest_api.go_api.id
resource_id = aws_api_gateway_rest_api.go_api.root_resource_id
http_method = aws_api_gateway_method.proxy_root.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.go_function.invoke_arn
}
resource "aws_api_gateway_deployment" "go_api_deployment" {
depends_on = [
aws_api_gateway_integration.go_api_integration,
aws_api_gateway_integration.proxy_root_integration,
]
rest_api_id = aws_api_gateway_rest_api.go_api.id
stage_name = "test"
}
resource "aws_lambda_permission" "apigw" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.go_function.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.go_api.execution_arn}/*/*"
}
Paso 7: Configurar credenciales de AWS para GitHub Actions
Para configurar credenciales de AWS para GitHub Actions, usaremos federaci贸n OIDC. Esta es una forma segura de autenticarse con AWS sin tener que almacenar tus credenciales de AWS en tu repositorio de GitHub.
Necesitaremos seguir los siguientes pasos para configurar credenciales de AWS para GitHub Actions:
- Abre la Consola de AWS y navega al servicio IAM.
- Haz clic en “Identity providers” en la barra de navegaci贸n izquierda.
- Haz clic en “Add provider”.
- Selecciona “OpenID Connect” como el tipo de proveedor.
- Para “Provider URL”, ingresa
https://token.actions.githubusercontent.com
2. - Haz clic en “Get thumbprint”.
- Para “Audience”, ingresa
sts.amazonaws.com
2. - Haz clic en “Add provider”.
Luego, necesitaremos crear un rol IAM para GitHub Actions. Necesitaremos seguir los siguientes pasos para crear un rol IAM para GitHub Actions:
- Haz clic en el proveedor que acabas de crear. Deber铆a llamarse “token.actions.githubusercontent.com”.
- Haz clic en “Assign role”.
- Selecciona “Create a new role” y haz clic en “Next”.
- Para “Identity provider”, selecciona “token.actions.githubusercontent.com”.
- Para Audience, ingresa
sts.amazonaws.com
. - Haz clic en “Next: Permissions”.
- Selecciona “AmazonS3FullAccess”, “AWSLambdaFullAccess”, “IAMFullAccess”, y “AmazonAPIGatewayAdministrator” y haz clic en “Next: Tags”.
- Haz clic en “Next: Review”.
- Para “Role name”, ingresa
github-actions
y haz clic en “Create role”. - Busca el rol que acabas de crear y haz clic en 茅l.
- Copia el “ARN” (Deber铆a parecer algo como
arn:aws:iam::000000000000:role/github-actions
). Lo necesitar谩s en el siguiente paso.
NOTA: Considera usar una pol铆tica IAM m谩s restrictiva para tu rol IAM de GitHub Actions.
Paso 8: Crear el flujo de trabajo de GitHub Actions
Luego, crearemos el flujo de trabajo de GitHub Actions. Usaremos el siguiente flujo de trabajo para desplegar nuestra aplicaci贸n Go en AWS Lambda:
No olvides reemplazar <ROL_IAM>
con el ARN del rol IAM que has creado en el paso anterior.
# .github/workflows/deploy.yml
name: 'Deploy'
on:
push:
branches: [ "main" ]
pull_request:
workflow_dispatch:
permissions:
id-token: write
contents: read
jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
environment: producci贸n
defaults:
run:
shell: bash
steps:
# Checkout de repositorio al runner de GitHub Actions
- name: Checkout
uses: actions/checkout@v3
# Configurar credenciales de AWS
# Necesitar谩s reemplazar <ROL_IAM> con el ARN del rol IAM que creaste en el paso anterior
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: <ROL_IAM>
aws-region: us-east-1
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
- name: Configurar entorno Go
uses: actions/setup-go@v4.0.1
# Este paso construye la aplicaci贸n Go y crea un archivo zip que contiene el binario
# Es importante notar que el binario debe llamarse "bootstrap"
- name: Construir aplicaci贸n Go
run: |
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bootstrap main.go
zip lambda-handler.zip bootstrap
# Inicializar un directorio de trabajo de Terraform nuevo o existente creando archivos iniciales, cargando cualquier estado remoto, descargando m贸dulos, etc.
- name: Terraform Init
run: terraform init
- name: Terraform Format
run: terraform fmt -check
- name: Terraform Plan
run: terraform plan -input=false
- name: Output ref y event_name
run: |
echo ${{github.ref}}
echo ${{github.event_name}}
# Al hacer push a "main", construir o cambiar la infraestructura seg煤n los archivos de configuraci贸n de Terraform
- name: Terraform Apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply -auto-approve -input=false
- name: Output URL de invocaci贸n de API Gateway
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: |
terraform output api_endpoint
Paso 9: Desplegar la aplicaci贸n Go en AWS Lambda
Ahora que hemos creado el flujo de trabajo de GitHub Actions, podemos subir nuestro c贸digo a GitHub y dejar que GitHub Actions despliegue nuestra aplicaci贸n Go en AWS.
git add .
git commit -m "Desplegar aplicaci贸n Go en AWS Lambda"
git push --set-upstream origin main
Puedes ver el flujo de trabajo de GitHub Actions yendo a la pesta帽a “Actions” en tu repositorio de GitHub.
Paso 10: Probar la API
Ahora que hemos desplegado nuestra aplicaci贸n Go en AWS Lambda, podemos obtener la URL de invocaci贸n de API Gateway yendo a la pesta帽a “Actions” en tu repositorio de GitHub y haciendo clic en la 煤ltima ejecuci贸n del flujo de trabajo. Luego, viendo la salida del paso “Output API Gateway invocation URL”.
Si hacemos una solicitud a la URL de invocaci贸n de API Gateway, deber铆amos ver la siguiente respuesta:
Conclusi贸n
En este art铆culo, exploramos c贸mo desplegar aplicaciones basadas en Go en AWS Lambda usando GitHub Actions y Terraform. Al combinar estas herramientas, los desarrolladores pueden automatizar el proceso de despliegue, simplificar el control de versiones y asegurar pr谩cticas consistentes de infraestructura como c贸digo para sus proyectos sin servidor.
Puedes encontrar el c贸digo fuente para este art铆culo en el repositorio de GitHub.