Skip to content
English
On this page

Ejercicio: Despliegue de Aplicación Go en AWS Elastic Beanstalk

Objetivo: Implementar una aplicación Go utilizando Elastic Beanstalk con configuración de autoescalado, balanceo de carga y monitoreo.

  1. Estructura de la aplicación Go:
go-app/
├── .ebignore
├── .elasticbeanstalk/
│   └── config.yml
├── application.go
├── go.mod
├── templates/
│   ├── index.html
│   ├── health.html
│   └── metrics.html
└── Procfile
  1. Aplicación Go (application.go):
go
package main

import (
    "fmt"
    "html/template"
    "log"
    "net/http"
    "os"
    "runtime"
    "time"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/cloudwatch"
)

type PageData struct {
    InstanceID string
    Region     string
    Time       string
    Memory     float64
    CPUUsage   float64
}

func main() {
    port := os.Getenv("PORT")
    if port == "" {
        port = "5000"
    }

    // Cargar templates
    templates := template.Must(template.ParseGlob("templates/*.html"))

    // Rutas
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        data := getPageData()
        templates.ExecuteTemplate(w, "index.html", data)
    })

    http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        data := getPageData()
        templates.ExecuteTemplate(w, "health.html", data)
    })

    http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
        data := getPageData()
        templates.ExecuteTemplate(w, "metrics.html", data)
    })

    // Iniciar servidor
    fmt.Printf("Server running on port %s...\n", port)
    log.Fatal(http.ListenAndServe(":"+port, nil))
}

func getPageData() PageData {
    var m runtime.MemStats
    runtime.ReadMemStats(&m)

    // Obtener ID de instancia de metadata de EC2
    instanceID := getEC2Metadata("instance-id")
    region := getEC2Metadata("placement/region")

    return PageData{
        InstanceID: instanceID,
        Region:     region,
        Time:       time.Now().Format(time.RFC1123),
        Memory:     float64(m.Alloc) / 1024 / 1024, // MB
        CPUUsage:   getCPUUsage(),
    }
}

func getEC2Metadata(path string) string {
    resp, err := http.Get("http://169.254.169.254/latest/meta-data/" + path)
    if err != nil {
        return "unknown"
    }
    defer resp.Body.Close()

    data := make([]byte, 100)
    n, _ := resp.Body.Read(data)
    return string(data[:n])
}

func getCPUUsage() float64 {
    sess := session.Must(session.NewSession())
    svc := cloudwatch.New(sess)

    input := &cloudwatch.GetMetricStatisticsInput{
        Namespace:  aws.String("AWS/EC2"),
        MetricName: aws.String("CPUUtilization"),
        StartTime:  aws.Time(time.Now().Add(-5 * time.Minute)),
        EndTime:    aws.Time(time.Now()),
        Period:     aws.Int64(300),
        Statistics: []*string{aws.String("Average")},
    }

    result, err := svc.GetMetricStatistics(input)
    if err != nil {
        return 0.0
    }

    if len(result.Datapoints) > 0 {
        return *result.Datapoints[0].Average
    }
    return 0.0
}
  1. Templates HTML:

index.html:

html
<!DOCTYPE html>
<html>
<head>
    <title>Go App - Home</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100">
    <nav class="bg-blue-600 p-4">
        <div class="container mx-auto">
            <div class="flex items-center justify-between">
                <div class="text-white font-bold">Go App Demo</div>
                <div>
                    <a href="/" class="text-white px-3 py-2">Home</a>
                    <a href="/health" class="text-white px-3 py-2">Health</a>
                    <a href="/metrics" class="text-white px-3 py-2">Metrics</a>
                </div>
            </div>
        </div>
    </nav>

    <div class="container mx-auto mt-8 p-4">
        <div class="bg-white rounded-lg shadow-lg p-6">
            <h1 class="text-2xl font-bold mb-4">Instance Information</h1>
            <div class="grid grid-cols-2 gap-4">
                <div>
                    <p class="font-semibold">Instance ID:</p>
                    <p>{{.InstanceID}}</p>
                </div>
                <div>
                    <p class="font-semibold">Region:</p>
                    <p>{{.Region}}</p>
                </div>
                <div>
                    <p class="font-semibold">Current Time:</p>
                    <p>{{.Time}}</p>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

health.html:

html
<!DOCTYPE html>
<html>
<head>
    <title>Go App - Health</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100">
    <nav class="bg-blue-600 p-4">
        <div class="container mx-auto">
            <div class="flex items-center justify-between">
                <div class="text-white font-bold">Go App Demo</div>
                <div>
                    <a href="/" class="text-white px-3 py-2">Home</a>
                    <a href="/health" class="text-white px-3 py-2">Health</a>
                    <a href="/metrics" class="text-white px-3 py-2">Metrics</a>
                </div>
            </div>
        </div>
    </nav>

    <div class="container mx-auto mt-8 p-4">
        <div class="bg-white rounded-lg shadow-lg p-6">
            <h1 class="text-2xl font-bold mb-4">Health Status</h1>
            <div class="grid grid-cols-2 gap-4">
                <div class="col-span-2">
                    <div class="bg-green-100 border-l-4 border-green-500 p-4">
                        <p class="font-bold">Status: Healthy</p>
                        <p>All systems operational</p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

metrics.html:

html
<!DOCTYPE html>
<html>
<head>
    <title>Go App - Metrics</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100">
    <nav class="bg-blue-600 p-4">
        <div class="container mx-auto">
            <div class="flex items-center justify-between">
                <div class="text-white font-bold">Go App Demo</div>
                <div>
                    <a href="/" class="text-white px-3 py-2">Home</a>
                    <a href="/health" class="text-white px-3 py-2">Health</a>
                    <a href="/metrics" class="text-white px-3 py-2">Metrics</a>
                </div>
            </div>
        </div>
    </nav>

    <div class="container mx-auto mt-8 p-4">
        <div class="bg-white rounded-lg shadow-lg p-6">
            <h1 class="text-2xl font-bold mb-4">Application Metrics</h1>
            <div class="grid grid-cols-2 gap-4">
                <div>
                    <p class="font-semibold">Memory Usage:</p>
                    <p>{{printf "%.2f" .Memory}} MB</p>
                </div>
                <div>
                    <p class="font-semibold">CPU Usage:</p>
                    <p>{{printf "%.2f" .CPUUsage}}%</p>
                </div>
            </div>
        </div>
    </div>
</body>
</html>
  1. Archivo go.mod:
go
module go-app

go 1.19

require github.com/aws/aws-sdk-go v1.44.82
  1. Procfile:
web: go-app
  1. .ebignore:
.git
.elasticbeanstalk/*
.gitignore
Procfile
  1. Configuración de Elastic Beanstalk (.elasticbeanstalk/config.yml):
yaml
branch-defaults:
  main:
    environment: go-app-prod
environment-defaults:
  go-app-prod:
    branch: null
    repository: null
global:
  application_name: go-app
  default_ec2_keyname: null
  default_platform: Go 1.19
  default_region: us-east-1
  include_git_submodules: true
  instance_profile: null
  platform_name: null
  platform_version: null
  sc: null
  workspace_type: Application
  1. Script de despliegue (deploy.sh):
bash
#!/bin/bash

# Inicializar Elastic Beanstalk
eb init -p go-1.19 go-app --region us-east-1

# Crear ambiente de producción
eb create go-app-prod \
    --elb-type application \
    --instance-types t2.micro \
    --min-instances 2 \
    --max-instances 4 \
    --scaling-metric-name CPUUtilization \
    --scaling-metric-unit Percent \
    --scaling-metric-value 70 \
    --vpc.id vpc-xxxxx \
    --vpc.ec2subnets subnet-xxxxx,subnet-yyyyy \
    --vpc.elbsubnets subnet-xxxxx,subnet-yyyyy \
    --vpc.securitygroups sg-xxxxx \
    --enable-spot

# Configurar variables de entorno
eb setenv \
    AWS_REGION=us-east-1 \
    ENVIRONMENT=production

# Desplegar la aplicación
eb deploy
  1. Pasos para el despliegue:
bash
# 1. Instalar EB CLI
pip install awsebcli

# 2. Inicializar el proyecto
eb init

# 3. Crear el ambiente y desplegar
./deploy.sh

# 4. Verificar el estado del despliegue
eb status

# 5. Abrir la aplicación en el navegador
eb open
  1. Comandos útiles para monitoreo:
bash
# Ver logs
eb logs

# Ver estado de salud
eb health

# Monitorear eventos
eb events

# Escalar manualmente
eb scale 3

# Terminar ambiente
eb terminate go-app-prod

Este ejercicio incluye:

  • Aplicación Go con múltiples endpoints
  • Plantillas HTML con Tailwind CSS
  • Monitoreo de recursos (CPU, memoria)
  • Integración con CloudWatch
  • Configuración de autoescalado
  • Load Balancer tipo Application
  • Instancias Spot para optimización de costos
  • Configuración de VPC y subredes
  • Múltiples zonas de disponibilidad

La aplicación desplegada será:

  • Altamente disponible (múltiples AZs)
  • Escalable automáticamente
  • Monitoreable
  • Económicamente optimizada (uso de instancias Spot)
  • Segura (configuración de VPC y grupos de seguridad)