Introducción: ¿Por qué Docker y Go son la pareja ideal?
Go (Golang) y Docker son dos herramientas que han cambiado el enfoque del desarrollo y la implementación de servicios backend. Go se compila en un único archivo binario estático, y Docker permite empaquetar ese binario en un entorno aislado. Juntos, ofrecen contenedores minimalistas, rápidos y seguros que se pueden ejecutar en cualquier lugar: desde el portátil de un desarrollador hasta un clúster de producción de Kubernetes.
En este artículo, recorreremos el camino desde cero hasta una aplicación Go lista para producción, empaquetada en Docker. Aprenderá a escribir un Dockerfile, optimizar el tamaño de la imagen, utilizar la compilación multi-etapa y ejecutar contenedores con un servicio Go.
1. Fundamentos: Aplicación Go y un Dockerfile simple
Comencemos con un servidor HTTP simple en Go. Crearemos un archivo main.go:
package main
import ( "fmt" "net/http")
func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "¡Hola desde Go dentro de Docker!") })
fmt.Println("El servidor se está ejecutando en :8080") http.ListenAndServe(":8080", nil)}Ahora escribamos un Dockerfile mínimo:
FROM golang:1.22-alpine AS builder
WORKDIR /appCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN go build -o server .
FROM alpine:3.19RUN apk --no-cache add ca-certificatesWORKDIR /root/COPY --from=builder /app/server .EXPOSE 8080CMD ["./server"]Construyamos la imagen y ejecutemos el contenedor:
docker build -t go-server .docker run -p 8080:8080 go-serverAhora abra su navegador y vaya a http://localhost:8080 — verá un saludo de la aplicación Go ejecutándose dentro del contenedor.
¿Por qué la compilación multi-etapa?
En la primera etapa, usamos la imagen oficial de Go con el conjunto completo de herramientas para la compilación. En la segunda etapa, usamos Alpine Linux mínimo, donde solo se copia el binario compilado. Esto permite obtener una imagen de aproximadamente 15-20 MB en lugar de más de 500 MB si mantuviéramos la imagen completa de Go.
2. Optimización de la imagen: minimización del tamaño y velocidad de compilación
Una imagen pequeña no solo ahorra espacio, sino que también permite una carga más rápida y una superficie de ataque menor. Veamos las técnicas clave para aplicaciones Go.
2.1. Usar .dockerignore
Cree un archivo .dockerignore en la raíz del proyecto:
.git*.mdDockerfile.dockerignorenode_modules/test/Esto evita copiar archivos innecesarios al contexto de compilación y acelera el proceso.
2.2. Almacenamiento en caché de dependencias
En nuestro Dockerfile, primero copiamos solo go.mod y go.sum, luego ejecutamos go mod download. Docker almacena en caché cada capa, y mientras estos archivos no cambien, las dependencias no se volverán a descargar con cada cambio de código.
2.3. CGO y compilación estática
Go usa CGO de forma predeterminada para algunas operaciones (por ejemplo, trabajar con la red a través de llamadas al sistema). Para obtener un binario completamente estático sin dependencias de libc, use las banderas de compilación:
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server .Esto garantiza que el binario funcione en cualquier distribución de Linux, incluidas imágenes mínimas como scratch.
2.4. Scratch: la imagen base más ligera
Si su aplicación no requiere shell, ping u otras utilidades, use la imagen vacía scratch:
FROM scratchCOPY --from=builder /app/server /EXPOSE 8080CMD ["/server"]El tamaño de dicha imagen será igual al tamaño del propio binario, a menudo menos de 10 MB.
3. Docker Compose: Aplicación Go multi-contenedor
Las aplicaciones reales rara vez consisten en u