Guía completa para desarrollar y desplegar proyectos FastAPI, Django con React/Next.js usando Docker y CI/CD
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.
Una buena estructura es fundamental para proyectos mantenibles y escalables. Aquí presentamos dos enfoques:
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
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 básica para un proyecto FastAPI:
# 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"]
fastapi==0.68.0 uvicorn==0.15.0 python-dotenv==0.19.0 psycopg2-binary==2.9.1
Configuración básica para un proyecto Django:
# 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"]
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 básica para un proyecto React:
# 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;"]
Configuración básica para un proyecto Next.js:
# 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
Docker Compose permite gestionar múltiples servicios (backend, frontend, base de datos) en desarrollo local.
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.
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'), } }
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
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"
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
Plataforma simple para desplegar aplicaciones con Docker. Ideal para proyectos pequeños/medianos.
Configuración: Conectar repositorio y configurar el servicio Docker.
Óptimo para frontends Next.js. Ofrece despliegues instantáneos y previews para cada PR.
Configuración: Importar proyecto y configurar variables de entorno.
Permite desplegar aplicaciones Docker globalmente con baja latencia.
Configuración: Usar CLI para desplegar: flyctl deploy
Control total sobre la infraestructura. Requiere más configuración manual.
Configuración: Instalar Docker, configurar reverse proxy (Nginx) y certificados SSL.
Opciones escalables como ECS, EKS o servicios serverless (Lambda + API Gateway).
Configuración: Usar Terraform o AWS CDK para infraestructura como código.
Para backends FastAPI con Mangum o Django con Zappa.
Configuración: Empaquetar aplicación como función Lambda.
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 } }
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.