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.