Contenedorización de aplicaciones Go: Docker desde cero hasta producción

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué Go y Docker son el dúo ideal?

\n\n

Go (o Golang) es un lenguaje que desde su nacimiento fue creado para arquitecturas cloud modernas. Incluso antes de que Docker se convirtiera en el estándar de facto, Go ya podía compilarse en un binario estático que no requiere ninguna dependencia. Y Docker es una tecnología que permite empaquetar una aplicación con todo su entorno en un contenedor ligero. Cuando combinas estas dos tecnologías, obtienes una solución prácticamente ideal: tamaño de imagen mínimo, inicio ultrarrápido y ausencia total de conflictos de dependencias.

\n\n

En este artículo no daremos lecciones sobre teoría de contenedorización. En su lugar, escribiremos una aplicación Go simple, la compilaremos en una imagen Docker, la optimizaremos con una compilación multi-etapa y nos aseguraremos de que nuestro contenedor esté listo para producción. Todo esto, con ejemplos de código reales que puedes ejecutar ahora mismo.

\n\n

Paso 1: Escribimos una aplicación Go simple

\n\n

Para empezar, crearemos un servidor HTTP simple en Go. Este será nuestro programa de "prueba". Cree un archivo main.go con el siguiente contenido:

\n\n
package main\n\nimport (\n    "fmt"\n    "log"\n    "net/http"\n    "os"\n)\n\nfunc main() {\n    port := os.Getenv("PORT")\n    if port == "" {\n        port = "8080"\n    }\n\n    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {\n        fmt.Fprintf(w, "¡Hola desde el contenedor Go! 🐹")\n    })\n\n    log.Printf("Servidor iniciado en el puerto %s", port)\n    log.Fatal(http.ListenAndServe(":"+port, nil))\n}\n
\n\n

Este código es un clásico: escucha en un puerto (que se puede configurar mediante la variable de entorno PORT) y responde a cualquier solicitud con un simple saludo. Ahora queremos que esta aplicación funcione en Docker, independientemente del sistema operativo en el que se ejecute el contenedor.

\n\n

Paso 2: Primer Dockerfile — enfoque ingenuo

\n\n

La forma más sencilla de "contenedorizar" una aplicación Go es tomar una imagen completa de Go, copiar el código en ella y ejecutarlo. Cree un archivo Dockerfile:

\n\n
# Usamos la imagen oficial de Go\nFROM golang:1.22\n\n# Establecemos el directorio de trabajo\nWORKDIR /app\n\n# Copiamos los archivos del proyecto\nCOPY go.mod go.sum ./\nRUN go mod download\nCOPY . .\n\n# Compilamos la aplicación\nRUN go build -o main .\n\n# Ejecutamos\nCMD ["./main"]\n
\n\n

Compilamos la imagen y la ejecutamos:

\n\n
docker build -t go-app:v1 .\ndocker run -p 8080:8080 go-app:v1\n
\n\n

Abra el navegador y vaya a http://localhost:8080. ¿Funciona? Excelente. Pero hay un problema: el tamaño de la imagen. Vamos a comprobarlo:

\n\n
docker images go-app:v1\n
\n\n

Verá algo como 1.2 GB. ¡Esto es un desastre! La imagen contiene un compilador Go completo, un gestor de paquetes, utilidades del sistema, todo lo que no es necesario para ejecutar el binario compilado. En producción, una imagen así tardaría una eternidad en cargarse y ocuparía mucho espacio.

\n\n

Paso 3: Compilación multi-etapa — el secreto de las imágenes ligeras

\n\n

Docker admite la compilación multi-etapa. Esto es cuando se utilizan varias instrucciones FROM en un solo Dockerfile, y solo la última etapa va a la imagen final. La primera etapa, la de "construcción", contiene el compilador y todas las herramientas. La segunda etapa es mínima, por ejemplo, scratch (imagen vacía) o alpine.

\n\n

Reescribamos nuestro Dockerfile:

\n\n
# Etapa 1: Compilación\nFROM golang:1.22 AS builder\n\nWORKDIR /app\n\n# Cacheamos las dependencias\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copiamos el código fuente y compilamos\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -o main .\n\n# Etapa 2: Imagen final\nFROM alpine:latest\n\nRUN apk --no-cache add ca-certificates\n\nWORKDIR /root/\n\n# Copiamos solo el binario de la primera etapa\nCOPY --from=builder /app/main .\n\nEXPOSE 8080\n\nCMD ["./main"]\n
\n\n

¿Qué ha pasado aquí? Hemos creado dos etapas:

Blogs

Book Recommendations