Manual de Construcción y Despliegue

Guía completa para desarrollar y desplegar proyectos FastAPI, Django con React/Next.js usando Docker y CI/CD

Introducción

Este manual cubre el proceso completo de desarrollo, desde la configuración inicial hasta el despliegue en producción de aplicaciones web modernas que utilizan:

Este manual asume que tienes conocimientos básicos de desarrollo web, línea de comandos y control de versiones con Git.

Estructura del Proyecto

Una buena estructura es fundamental para proyectos mantenibles y escalables. Aquí presentamos dos enfoques:

Opción 1: Monorepo (recomendado para proyectos pequeños/medianos)

project-root/
├── backend/              # Código del backend (FastAPI o Django)
│   ├── app/              # Aplicación principal
│   ├── requirements.txt  # Dependencias de Python
│   └── Dockerfile        # Configuración Docker para backend
├── frontend/             # Código del frontend (React o Next.js)
│   ├── public/           # Assets públicos
│   ├── src/              # Código fuente
│   └── Dockerfile        # Configuración Docker para frontend
├── docker-compose.yml    # Configuración para desarrollo local
├── .github/workflows/    # GitHub Actions para CI/CD
└── README.md             # Documentación del proyecto
            

Opción 2: Multirepo (para proyectos grandes o equipos distribuidos)

backend-repo/             # Repositorio separado para backend
│   ├── app/              # Aplicación principal
│   ├── requirements.txt  # Dependencias de Python
│   └── Dockerfile        # Configuración Docker

frontend-repo/            # Repositorio separado para frontend
│   ├── public/           # Assets públicos
│   ├── src/              # Código fuente
│   └── Dockerfile        # Configuración Docker

infra-repo/               # Repositorio para configuración de infraestructura
    ├── k8s/              # Configuración Kubernetes
    └── terraform/        # Configuración Terraform para cloud
            

La elección entre monorepo y multirepo depende del tamaño del proyecto, la estructura del equipo y los requisitos de despliegue independiente.

Configuración del Backend

FastAPI

Configuración básica para un proyecto FastAPI:

backend/Dockerfile
# Usamos una imagen oficial de Python
FROM python:3.9-slim

# Establecemos el directorio de trabajo
WORKDIR /app

# Copiamos primero requirements.txt para aprovechar la caché de Docker
COPY requirements.txt .

# Instalamos dependencias
RUN pip install --no-cache-dir -r requirements.txt

# Copiamos el resto del código
COPY . .

# Exponemos el puerto que usa FastAPI (normalmente 8000)
EXPOSE 8000

# Comando para ejecutar la aplicación
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
                
backend/requirements.txt
fastapi==0.68.0
uvicorn==0.15.0
python-dotenv==0.19.0
psycopg2-binary==2.9.1
                

Django

Configuración básica para un proyecto Django:

backend/Dockerfile
# Usamos una imagen oficial de Python
FROM python:3.9-slim

# Establecemos variables de entorno
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Creamos y establecemos el directorio de trabajo
WORKDIR /app

# Instalamos dependencias del sistema
RUN apt-get update && apt-get install -y \
    gcc \
    python3-dev \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# Copiamos e instalamos dependencias de Python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copiamos el proyecto
COPY . .

# Comando para ejecutar la aplicación
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "project.wsgi:application"]
                
backend/requirements.txt
Django==3.2.7
gunicorn==20.1.0
psycopg2-binary==2.9.1
python-dotenv==0.19.0
                

Para ambos frameworks, asegúrate de configurar adecuadamente las variables de entorno para desarrollo y producción, especialmente las credenciales de la base de datos.

Configuración del Frontend

React.js

Configuración básica para un proyecto React:

frontend/Dockerfile
# Fase de construcción
FROM node:16 as build

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

RUN npm run build

# Fase de producción
FROM nginx:alpine

COPY --from=build /app/build /usr/share/nginx/html

COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
                

Next.js

Configuración básica para un proyecto Next.js:

frontend/Dockerfile
# Fase de construcción
FROM node:16 as builder

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

RUN npm run build

# Fase de producción
FROM node:16-alpine

WORKDIR /app

COPY --from=builder /app/package*.json ./
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/node_modules ./node_modules

EXPOSE 3000

CMD ["npm", "start"]
                

Para Next.js, considera usar la imagen oficial de Vercel para despliegues optimizados: vercel/next.js

Configuración con Docker Compose

Docker Compose permite gestionar múltiples servicios (backend, frontend, base de datos) en desarrollo local.

docker-compose.yml
version: '3.8'

services:
  db:
    image: postgres:13
    environment:
      POSTGRES_USER: ${POSTGRES_USER:-postgres}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
      POSTGRES_DB: ${POSTGRES_DB:-appdb}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    networks:
      - app-network

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    command: bash -c "python manage.py migrate && gunicorn --bind 0.0.0.0:8000 project.wsgi:application"
    volumes:
      - ./backend:/app
    environment:
      - DATABASE_URL=postgres://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-appdb}
      - DEBUG=${DEBUG:-True}
    ports:
      - "8000:8000"
    depends_on:
      - db
    networks:
      - app-network

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    volumes:
      - ./frontend:/app
      - /app/node_modules
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - REACT_APP_API_URL=http://localhost:8000
    networks:
      - app-network
    depends_on:
      - backend

volumes:
  postgres_data:

networks:
  app-network:
    driver: bridge
                

Crea un archivo .env en la raíz del proyecto para gestionar las variables de entorno de forma segura.

Configuración de Bases de Datos

PostgreSQL con Django

backend/project/settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.getenv('POSTGRES_DB', 'appdb'),
        'USER': os.getenv('POSTGRES_USER', 'postgres'),
        'PASSWORD': os.getenv('POSTGRES_PASSWORD', 'postgres'),
        'HOST': os.getenv('DATABASE_HOST', 'db'),
        'PORT': os.getenv('DATABASE_PORT', '5432'),
    }
}
                

MongoDB con FastAPI

backend/app/db.py
from motor.motor_asyncio import AsyncIOMotorClient
from fastapi import Depends

MONGO_URI = os.getenv("MONGO_URI", "mongodb://mongo:27017")
MONGO_DB = os.getenv("MONGO_DB", "appdb")

client = AsyncIOMotorClient(MONGO_URI)
db = client[MONGO_DB]

async def get_db():
    return db
                

Configuración de CI/CD

GitHub Actions para FastAPI + React

.github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test-backend:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:13
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: testdb
        ports:
          - 5432:5432
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.9'
    
    - name: Install dependencies
      run: |
        cd backend
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    
    - name: Run tests
      run: |
        cd backend
        pytest
    
  build-and-deploy:
    needs: test-backend
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Login to Docker Hub
      uses: docker/login-action@v1
      with:
        username: ${{ secrets.DOCKER_HUB_USERNAME }}
        password: ${{ secrets.DOCKER_HUB_TOKEN }}
    
    - name: Build and push backend
      run: |
        cd backend
        docker build -t ${{ secrets.DOCKER_HUB_USERNAME }}/backend:latest .
        docker push ${{ secrets.DOCKER_HUB_USERNAME }}/backend:latest
    
    - name: Build and push frontend
      run: |
        cd frontend
        docker build -t ${{ secrets.DOCKER_HUB_USERNAME }}/frontend:latest .
        docker push ${{ secrets.DOCKER_HUB_USERNAME }}/frontend:latest
    
    - name: Deploy to production
      run: |
        ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "docker-compose pull && docker-compose up -d"
                

GitLab Runners para Django + Next.js

.gitlab-ci.yml
stages:
  - test
  - build
  - deploy

variables:
  DOCKER_DRIVER: overlay2
  POSTGRES_DB: testdb
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: postgres

test-backend:
  stage: test
  image: python:3.9
  services:
    - postgres:13
  before_script:
    - pip install -r backend/requirements.txt
  script:
    - cd backend
    - python manage.py test

build-backend:
  stage: build
  image: docker:19.03.12
  services:
    - docker:19.03.12-dind
  script:
    - docker build -t $CI_REGISTRY/backend:latest ./backend
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker push $CI_REGISTRY/backend:latest

build-frontend:
  stage: build
  image: docker:19.03.12
  services:
    - docker:19.03.12-dind
  script:
    - docker build -t $CI_REGISTRY/frontend:latest ./frontend
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker push $CI_REGISTRY/frontend:latest

deploy-production:
  stage: deploy
  image: alpine:3.12
  before_script:
    - apk add --no-cache openssh-client rsync
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
  script:
    - ssh -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST "docker-compose pull && docker-compose up -d"
  only:
    - main
                

Opciones de Despliegue

Render

Plataforma simple para desplegar aplicaciones con Docker. Ideal para proyectos pequeños/medianos.

Configuración: Conectar repositorio y configurar el servicio Docker.

Vercel

Óptimo para frontends Next.js. Ofrece despliegues instantáneos y previews para cada PR.

Configuración: Importar proyecto y configurar variables de entorno.

Fly.io

Permite desplegar aplicaciones Docker globalmente con baja latencia.

Configuración: Usar CLI para desplegar: flyctl deploy

VPS

Control total sobre la infraestructura. Requiere más configuración manual.

Configuración: Instalar Docker, configurar reverse proxy (Nginx) y certificados SSL.

AWS

Opciones escalables como ECS, EKS o servicios serverless (Lambda + API Gateway).

Configuración: Usar Terraform o AWS CDK para infraestructura como código.

Serverless

Para backends FastAPI con Mangum o Django con Zappa.

Configuración: Empaquetar aplicación como función Lambda.

Ejemplo: Despliegue en Render

  1. Crear un nuevo servicio en Render
  2. Seleccionar "Web Service" y conectar el repositorio
  3. Configurar las variables de entorno
  4. Especificar el comando de inicio y puerto
  5. Activar despliegue automático si se desea

Ejemplo: Despliegue en AWS ECS

infra/ecs.tf
resource "aws_ecs_cluster" "app_cluster" {
  name = "app-cluster"
}

resource "aws_ecs_task_definition" "app_task" {
  family                   = "app-task"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = 1024
  memory                   = 2048
  execution_role_arn       = aws_iam_role.ecs_task_execution_role.arn
  
  container_definitions = jsonencode([{
    name      = "backend"
    image     = "${aws_ecr_repository.backend.repository_url}:latest"
    cpu       = 512
    memory    = 1024
    essential = true
    portMappings = [{
      containerPort = 8000
      hostPort      = 8000
    }]
    environment = [
      { name = "DATABASE_URL", value = var.database_url }
    ]
  }])
}

resource "aws_ecs_service" "app_service" {
  name            = "app-service"
  cluster         = aws_ecs_cluster.app_cluster.id
  task_definition = aws_ecs_task_definition.app_task.arn
  desired_count   = 2
  launch_type     = "FARGATE"
  
  network_configuration {
    subnets          = aws_subnet.private.*.id
    security_groups  = [aws_security_group.ecs_tasks.id]
    assign_public_ip = false
  }
  
  load_balancer {
    target_group_arn = aws_lb_target_group.app.id
    container_name   = "backend"
    container_port   = 8000
  }
}
                

Mejores Prácticas

Seguridad

Rendimiento

Monitoreo

Conclusión

Este manual cubre el flujo completo para desarrollar y desplegar aplicaciones web modernas utilizando las mejores prácticas actuales. La combinación de Docker, CI/CD y plataformas de despliegue modernas permite crear procesos de desarrollo robustos y escalables.

Recuerda que cada proyecto es único y puede requerir ajustes en esta configuración base. Siempre adapta las herramientas y procesos a las necesidades específicas de tu aplicación y equipo.