← maurobernal.com.ar

Etiqueta: arquitectura-software

  • Microservicios con Docker: lo que aprendí armando mi primera arquitectura

    Arrancé con un monolito .NET en un solo contenedor. Funcionaba bien, hasta que el equipo creció y todos tocábamos el mismo código. Desplegar un cambio en la pantalla de login requería redeployar toda la aplicación. Fue entonces cuando empecé a explorar microservicios con Docker.

    Monolito vs microservicios: cuándo tiene sentido el cambio

    AspectoMonolito en DockerMicroservicios en Docker
    Complejidad inicialBajaAlta
    Deploy independienteNo — todo o nadaSí — servicio por servicio
    Escalabilidad selectivaNoSí — escalar solo lo que lo necesita
    Fallo aisladoUn bug afecta todoUn servicio caído no baja todo
    Equipos independientesDifícilCada equipo dueño de su servicio
    Overhead operacionalBajoAlto — más servicios que monitorear

    Mi recomendación: empezá con el monolito. Cuando los puntos de dolor de la tabla de arriba se vuelvan reales en tu día a día, ahí es el momento de dividir.

    Mi primera arquitectura de microservicios: auth + api + frontend

    # docker-compose.yml — tres servicios independientes
    
    version: '3.8'
    
    services:
      # Servicio de autenticación (JWT, usuarios)
      auth-service:
        build: ./services/auth
        environment:
          - DB_CONNECTION=Host=postgres;Database=auth;Username=auth;Password=${AUTH_DB_PASS}
          - JWT_SECRET=${JWT_SECRET}
          - JWT_EXPIRY=1h
        depends_on:
          postgres:
            condition: service_healthy
        restart: unless-stopped
        # Sin puerto expuesto - solo accesible internamente
    
      # API principal de negocio
      api-service:
        build: ./services/api
        environment:
          - DB_CONNECTION=Host=postgres;Database=apidb;Username=api;Password=${API_DB_PASS}
          - AUTH_SERVICE_URL=http://auth-service:8080
          - CACHE_URL=redis:6379
        depends_on:
          - auth-service
          - redis
        restart: unless-stopped
    
      # Frontend React
      frontend:
        build: ./services/frontend
        environment:
          - REACT_APP_API_URL=http://api-service:8080
        restart: unless-stopped
    
      # Proxy - único punto de entrada externo
      nginx:
        image: nginx:1.25-alpine
        ports:
          - "80:80"
        volumes:
          - ./nginx/microservices.conf:/etc/nginx/nginx.conf:ro
        depends_on:
          - frontend
          - api-service
    
      # Infraestructura compartida
      postgres:
        image: postgres:16-alpine
        environment:
          POSTGRES_MULTIPLE_DATABASES: auth,apidb  # extensión para múltiples DBs
          POSTGRES_PASSWORD: ${POSTGRES_ROOT_PASS}
        volumes:
          - postgres-data:/var/lib/postgresql/data
        healthcheck:
          test: ["CMD-SHELL", "pg_isready"]
          interval: 10s
          retries: 5
    
      redis:
        image: redis:7-alpine
        volumes:
          - redis-data:/data
    
    volumes:
      postgres-data:
      redis-data:

    Comunicación entre servicios

    Dentro de la red Docker, los servicios se llaman por nombre. La API valida tokens llamando al servicio de auth en cada request:

    # En el código de api-service (.NET):
    // Validar token contra auth-service
    var authResponse = await _httpClient.GetAsync(
        $"{_authServiceUrl}/validate?token={token}"
    );
    
    // En el docker-compose, AUTH_SERVICE_URL = http://auth-service:8080
    // Docker resuelve "auth-service" al contenedor correcto automáticamente

    Deploy independiente: la ventaja real

    # Actualizar solo el servicio de auth sin tocar nada más
    docker compose up -d --build auth-service
    
    # Escalar solo la API (recibe más carga)
    docker compose up -d --scale api-service=3
    
    # Rollback solo del frontend
    docker compose stop frontend
    docker compose rm -f frontend
    TAG=anterior docker compose up -d frontend

    Lo que aprendí en el proceso

    La transición de monolito a microservicios no es solo técnica: es organizacional. Cada servicio necesita su propio repositorio (o al menos su propia carpeta), su propio pipeline de CI/CD y su propio dueño. La complejidad operacional sube. Por eso Docker Compose no es suficiente para microservicios en producción a escala — ese es el camino hacia Kubernetes, que vemos en el próximo artículo.


    Artículo anterior: Docker en CI/CD | Serie Docker Completo | Próximo: Seguridad en Docker →


    Artículo anterior: Docker en mi pipeline de CI/CD: builds reproducibles sin sorpresas | Serie Docker Completo | Próximo: Seguridad en Docker: errores que cometí y cómo los corregí →

Tags

tsql (27)mssql (26)sql (20)devops (20)dotnet (18)docker (15)performance (14)contenedores (11)dotnet10 (10)linux (9)csharp (8)microservicios (7)angular (7)angular21 (7)sql server (6)issabel (6)docker-compose (6)typescript (6)mysql (5).NET (5)