Despliegue y documentación del proyecto final

Lectura
20 min~10 min lectura

Despliegue y Documentación del Proyecto Final: Llevando el Sistema a Producción

Has culminado el desarrollo de un sistema de microservicios para e-commerce robusto, utilizando Go y gorilla/mux. Sin embargo, el viaje no termina con el código funcional en tu máquina local. Esta lección se centra en las etapas críticas finales: el despliegue y la documentación. Desplegar implica preparar tu aplicación para que sea accesible, escalable y confiable en un entorno de producción, manejando tráfico real. La documentación, por su parte, no es un mero trámite, sino un componente esencial que permite a otros desarrolladores (o a tu yo futuro) entender, consumir y mantener tus APIs de manera eficiente. Aquí transformaremos nuestro proyecto de un ejercicio de desarrollo a un sistema listo para el mundo real.

Abordaremos estrategias de despliegue modernas, centrándonos en la contenerización con Docker y la orquestación básica, que es el estándar de facto para microservicios. También cubriremos la creación de documentación interactiva de API usando OpenAPI (Swagger), una herramienta invaluable para equipos de frontend, móviles y de integración. Finalmente, discutiremos prácticas de monitorización básica y configuración de entorno, cerrando el ciclo de vida de desarrollo de software profesional con Go.

Concepto Clave: Contenerización y Documentación como Contrato

Imagina que tu microservicio es un motor de barco altamente especializado. La contenerización con Docker es el proceso de colocar ese motor dentro de un casco sellado, autónomo, que ya incluye todo lo que necesita para funcionar: el combustible correcto (dependencias), las herramientas de control (binarios del sistema) y un manual de instalación estandarizado (las capas de la imagen). Este "casco" (el contenedor) puede ser transportado e instalado en cualquier barco (servidor) que tenga un puerto de acoplamiento estándar (el motor de Docker), sin preocuparte por si el barco usa diésel o gasolina (las diferencias entre sistemas operativos o configuraciones del servidor). Esto garantiza que tu servicio se comporte exactamente igual en desarrollo, pruebas y producción.

Por otro lado, la documentación de API con OpenAPI actúa como el contrato legal y el manual de usuario del motor. No es solo una lista de funciones; es una especificación formal, legible tanto por humanos como por máquinas, que detalla punto por punto: qué endpoints existen (las palancas del motor), qué datos esperan recibir (el tipo de combustible), qué formatos usan, y qué prometen devolver (la potencia de salida). Este contrato evita malentendidos costosos entre el equipo que construye el motor (backend) y los equipos que construyen el barco, los sistemas de navegación y los controles (frontend, aplicaciones móviles). Es la base para la generación automática de clientes SDK y pruebas de integración.

Cómo Funciona en la Práctica: Flujo de Despliegue y Documentación

El proceso comienza con la Dockerización de cada microservicio. Para cada uno (por ejemplo, `servicio-productos` y `servicio-pedidos`), creamos un `Dockerfile`. Este archivo de instrucciones parte de una imagen base oficial de Go, copia el código fuente, descarga las dependencias, compila el binario optimizado para producción, y finalmente define cómo ejecutarlo. Luego, construimos la imagen con `docker build` y la etiquetamos. Para gestionar múltiples servicios, usamos `docker-compose`, que nos permite definir en un solo archivo (`docker-compose.yml`) todos nuestros servicios, sus variables de entorno, volúmenes de datos y la red que los conecta, permitiendo levantarlos todos con un solo comando: `docker-compose up`.

Paralelamente, integramos la generación de documentación OpenAPI en el propio código Go. Utilizamos anotaciones de comentarios especiales (siguiendo el estándar OpenAPI 3.0) directamente sobre nuestras funciones de handler de gorilla/mux. Una biblioteca como `swag` (de Swaggo) puede luego analizar este código y generar automáticamente un archivo `swagger.json` o `swagger.yaml`. Finalmente, servimos este archivo y una interfaz web interactiva (Swagger UI) desde un endpoint dedicado en nuestro propio servicio (por ejemplo, `/docs`). Así, al desplegar el servicio, la documentación vive y se actualiza junto con él, siempre sincronizada.

El despliegue final implica subir las imágenes Docker construidas a un registro de imágenes (como Docker Hub o Google Container Registry) y luego, en un servidor de producción (que puede ser una VM, un cluster de Kubernetes, o un servicio gestionado como Google Cloud Run), extraer esas imágenes y ejecutar los contenedores. La configuración sensible (claves de API, conexiones a bases de datos) se inyecta mediante variables de entorno o sistemas de gestión de secretos, nunca se codifica en la imagen.

Código en Acción: Dockerfile, docker-compose.yml y Anotaciones Swagger

A continuación, se presenta un ejemplo práctico y funcional de los archivos centrales para el despliegue y la documentación de nuestro microservicio de productos.

Dockerfile para un Microservicio Go

# Etapa 1: Construcción
FROM golang:1.21-alpine AS builder

# Instalar dependencias de compilación y herramientas necesarias
RUN apk add --no-cache git ca-certificates tzdata

# Establecer el directorio de trabajo dentro del contenedor
WORKDIR /app

# Copiar los archivos de módulos y descargar dependencias
COPY go.mod go.sum ./
RUN go mod download

# Copiar el código fuente
COPY . .

# Construir la aplicación. Los flags -ldflags reducen el tamaño del binario.
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo \
    -ldflags="-w -s" -o servicio-productos ./cmd/api/main.go

# Etapa 2: Imagen final mínima
FROM scratch

# Copiar certificados CA y zona horaria desde la etapa de builder
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

# Copiar el binario compilado desde la etapa de builder
COPY --from=builder /app/servicio-productos /servicio-productos

# Exponer el puerto en el que la aplicación escucha
EXPOSE 8080

# Comando para ejecutar la aplicación
ENTRYPOINT ["/servicio-productos"]

Archivo docker-compose.yml para Orquestar Servicios

version: '3.8'

services:
  # Base de datos PostgreSQL para el servicio de productos
  productos-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: usuario_prod
      POSTGRES_PASSWORD: ${DB_PASSWORD} # Se toma de variable de entorno del host
      POSTGRES_DB: productosdb
    volumes:
      - productos_db_data:/var/lib/postgresql/data
    networks:
      - ecommerce-net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U usuario_prod"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Nuestro microservicio de productos
  servicio-productos:
    build:
      context: ./servicio-productos # Ruta al directorio del servicio
      dockerfile: Dockerfile
    ports:
      - "8081:8080" # Mapea puerto host:contenedor
    environment:
      DB_HOST: productos-db
      DB_PORT: 5432
      DB_USER: usuario_prod
      DB_PASSWORD: ${DB_PASSWORD}
      DB_NAME: productosdb
      JWT_SECRET: ${JWT_SECRET}
    depends_on:
      productos-db:
        condition: service_healthy # Espera a que la BD esté lista
    networks:
      - ecommerce-net
    restart: unless-stopped

  # Podríamos añadir más servicios aquí (pedidos, usuarios, etc.)
  # servicio-pedidos:
  #   ...

# Volúmenes con nombre para persistencia de datos
volumes:
  productos_db_data:

# Red definida por el usuario para aislamiento y comunicación
networks:
  ecommerce-net:
    driver: bridge

Integración de Documentación OpenAPI (Swagger) en Go

Primero, instala las dependencias necesarias: `go get -u github.com/swaggo/swag/cmd/swag github.com/swaggo/http-swagger`. Luego, añade anotaciones a tu código.

// main.go
package main

import (
    "log"
    "net/http"
    "github.com/gorilla/mux"
    _ "github.com/tu-usuario/servicio-productos/docs" // Importa docs generados
    httpSwagger "github.com/swaggo/http-swagger"
)

// @title API de Productos - Sistema E-commerce
// @version 1.0
// @description Microservicio para la gestión del catálogo de productos.
// @host localhost:8081
// @BasePath /api/v1
func main() {
    r := mux.NewRouter()

    // Configurar rutas de la API
    api := r.PathPrefix("/api/v1").Subrouter()
    api.HandleFunc("/productos", GetProductos).Methods("GET")
    api.HandleFunc("/productos/{id}", GetProducto).Methods("GET")
    api.HandleFunc("/productos", CreateProducto).Methods("POST")

    // Ruta para la documentación Swagger UI
    r.PathPrefix("/docs").Handler(httpSwagger.WrapHandler)

    log.Println("Servidor de productos iniciado en :8080")
    log.Fatal(http.ListenAndServe(":8080", r))
}

// GetProductos obtiene la lista de productos
// @Summary Lista todos los productos
// @Description Obtiene una lista paginada de productos del catálogo
// @Tags productos
// @Accept json
// @Produce json
// @Param page query int false "Número de página" default(1)
// @Param limit query int false "Límite de items por página" default(10)
// @Success 200 {array} Producto
// @Failure 500 {object} map[string]string
// @Router /productos [get]
func GetProductos(w http.ResponseWriter, r *http.Request) {
    // Lógica del handler...
    w.Header().Set("Content-Type", "application/json")
    w.Write([]byte(`[{"id": 1, "nombre": "Laptop"}]`))
}

// Producto define el modelo para un producto.
type Producto struct {
    ID    int     `json:"id" example:"1"`
    Nombre string `json:"nombre" example:"Laptop Gamer"`
    Precio float64 `json:"precio" example:"1299.99"`
    Stock  int     `json:"stock" example:"15"`
}

Después de añadir las anotaciones, ejecuta `swag init` en el directorio de tu proyecto (que contiene `main.go`). Esto generará una carpeta `docs/`. Al acceder a `http://localhost:8081/docs/index.html`, verás la interfaz interactiva de Swagger.

Errores Comunes y Cómo Evitarlos

1. Imágenes Docker Gigantes: Usar la imagen completa de `golang` en la etapa final, en lugar de una mínima como `scratch` o `alpine`, resulta en imágenes de más de 1GB. Solución: Siempre utiliza una construcción multi-etapa (como en el ejemplo) para copiar solo el binario compilado y los certificados a una imagen mínima.

2. Credenciales Hardcodeadas en el Código o la Imagen: Incluir contraseñas de base de datos o claves API directamente en el `Dockerfile` o el código fuente. Solución: Usa siempre variables de entorno. Inyéctalas en tiempo de ejecución mediante `docker-compose.yml` o un orquestador. Considera usar un vault de secretos para producción.

3. Documentación Desincronizada: La documentación Swagger se escribe manualmente en un archivo YAML separado y se olvida actualizar cuando cambia la API. Solución: Integra las anotaciones en el código fuente (como se muestra). La documentación se genera automáticamente a partir de la fuente de verdad, el propio código.

4. No Gestionar el Estado de la Base de Datos: Usar el sistema de archivos del contenedor para datos de la base de datos, lo que causa pérdida de información al reiniciar. Solución: En Docker Compose, define volúmenes con nombre (como `productos_db_data`) para persistir los datos. En producción, usa servicios de base de datos gestionados o volúmenes persistentes en tu plataforma de orquestación.

5. Olvidar los Health Checks: No configurar comprobaciones de salud para los servicios y las bases de datos. Esto impide que el orquestador sepa si un servicio está realmente listo. Solución: Implementa un endpoint `/health` simple en tu API que verifique conexiones a BD, y declara `healthcheck` en `docker-compose.yml` y en tu configuración de producción (Kubernetes liveness/readiness probes).

Checklist de Dominio

Antes de considerar tu proyecto listo para producción, verifica que cumples con los siguientes puntos:

  • ✅ Cada microservicio tiene un Dockerfile optimizado que utiliza construcción multi-etapa y produce una imagen ligera.
  • ✅ Existe un archivo docker-compose.yml que define y conecta todos los servicios (APIs, bases de datos, brokers de mensajes) en una red aislada.
  • ✅ Ninguna credencial, clave API o configuración sensible está embebida en el código o las imágenes Docker. Se utilizan variables de entorno o un sistema de secretos.
  • ✅ La documentación de la API está generada automáticamente mediante anotaciones OpenAPI (Swagger) en el código y es servida desde un endpoint dedicado (e.g., `/docs`).
  • ✅ Cada servicio expone un endpoint de health check (e.g., `/health`) y está configurado en el orquestador para su monitorización.
  • ✅ Los datos de estado (como bases de datos) están configurados para usar volúmenes persistentes y no almacenamiento efímero del contenedor.
  • ✅ Se han definido políticas de restart (e.g., `restart: unless-stopped`) para recuperación automática ante fallos simples.
  • ✅ Se ha probado el despliegue completo localmente con `docker-compose up` y se ha verificado que todos los servicios se comunican correctamente y la documentación es accesible.
Tip Profesional: Ve un paso más allá en producción. Considera usar un CI/CD pipeline (con GitHub Actions, GitLab CI, etc.) que automáticamente construya tus imágenes Docker, ejecute las pruebas, escanee las imágenes en busca de vulnerabilidades (con Trivy o Docker Scout), y las despliegue en tu entorno de staging o producción. La automatización es la clave para despliegues consistentes y confiables.
De lección a portfolio

Convertí esta lección en una habilidad visible para entrevistas.

Guardá el curso, completá los ejercicios y conectá esta habilidad con una ruta de empleo, data, IA, programación o marketing.

Newsletter Cursalo

Recibí rutas y cursos nuevos

Sumate para recibir recursos orientados a empleo y portfolio.

  • Rutas de empleo
  • Cursos prácticos
  • Portfolio y entrevistas

Sin spam. También podés entrar con tu cuenta para guardar progreso. Iniciá sesión