Despliegue en producci贸n
Gu铆a para desplegar Duckling en entornos de producci贸n.
Backend con Gunicorn
Instalaci贸n
Uso b谩sico
Configuraci贸n recomendada
gunicorn \
--workers 4 \
--threads 2 \
--timeout 300 \
--bind 0.0.0.0:5001 \
--access-logfile /var/log/docling/access.log \
--error-logfile /var/log/docling/error.log \
app:app
N煤mero de workers
Una buena regla pr谩ctica es (2 脳 n煤cleos de CPU) + 1 workers.
Compilaci贸n del frontend
El directorio dist/ contiene archivos est谩ticos listos para desplegar.
Configuraci贸n de Nginx
Configuraci贸n b谩sica
# /etc/nginx/sites-available/duckling
server {
listen 80;
server_name your-domain.com;
root /var/www/duckling/dist;
index index.html;
# Rutas del frontend
location / {
try_files $uri $uri/ /index.html;
}
# Proxy de la API
location /api/ {
proxy_pass http://localhost:5001/api/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Para subida de archivos
client_max_body_size 100M;
proxy_read_timeout 300s;
}
}
Configuraci贸n completa para producci贸n
upstream docling_backend {
server unix:/run/duckling.sock fail_timeout=0;
}
server {
listen 80;
server_name docling.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name docling.example.com;
ssl_certificate /etc/letsencrypt/live/docling.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/docling.example.com/privkey.pem;
# Cabeceras de seguridad
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Frontend
root /var/www/duckling/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
# Cach茅 de recursos est谩ticos
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# API
location /api/ {
proxy_pass http://docling_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Subida de archivos
client_max_body_size 200M;
proxy_read_timeout 300s;
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
}
}
Servicio systemd
Cree /etc/systemd/system/duckling.service:
[Unit]
Description=Duckling Backend
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/duckling/backend
Environment="PATH=/opt/duckling/backend/venv/bin"
ExecStart=/opt/duckling/backend/venv/bin/gunicorn \
--workers 4 \
--bind unix:/run/duckling.sock \
--timeout 300 \
app:app
Restart=always
[Install]
WantedBy=multi-user.target
Gestionar el servicio
# Habilitar e iniciar
sudo systemctl enable duckling
sudo systemctl start duckling
# Comprobar estado
sudo systemctl status duckling
# Ver registros
sudo journalctl -u duckling -f
Alternativa: Caddy
Para una configuraci贸n m谩s sencilla, use Caddy:
docling.example.com {
root * /var/www/duckling/dist
file_server
try_files {path} /index.html
handle /api/* {
reverse_proxy localhost:5001
}
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
}
}
Variables de entorno
Def铆nalas en producci贸n:
FLASK_ENV=production
SECRET_KEY=your-very-secure-random-key
DEBUG=False
FLASK_HOST=127.0.0.1
MAX_CONTENT_LENGTH=209715200 # 200 MB
Seguridad
No use nunca el SECRET_KEY por defecto en producci贸n. Genere una clave secreta aleatoria segura:
Comprobaciones de estado
Supervise el estado del servicio:
# Estado del backend
curl http://localhost:5001/api/health
# Respuesta
{"status": "healthy", "service": "duckling-backend"}
Registro
Registros de Gunicorn
# Registro de acceso
tail -f /var/log/docling/access.log
# Registro de errores
tail -f /var/log/docling/error.log
Registro estructurado
A帽ada en la aplicaci贸n Flask:
import logging
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
'logs/docling.log',
maxBytes=10000000,
backupCount=5
)
handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
app.logger.addHandler(handler)
Estrategia de copias de seguridad
Base de datos
Salidas
# Copia de seguridad de archivos convertidos
tar -czf backups/outputs_$(date +%Y%m%d).tar.gz outputs/
Copias de seguridad automatizadas
A帽ada a la crontab: