Manual/Referencia

Desplegar en AWS (Vercel para la app web)

Esta página es el manual de despliegue para una instalación de producción de Meratyn donde la aplicación de navegador se sirve en Vercel (o cualquier host estático) y AWS carga los servicios con estado y los proxies con mayor consumo de ancho de banda. El autoalojamiento completo con Docker Compose está cubierto en Autoalojar la consola de gestión; la infraestructura de capas alojadas GeoNode/GeoServer está cubierta en Desplegar capas alojadas.

Topología de un vistazo

La build web es completamente estática, así que no necesita AWS. Todo lo que sirve datos de proyecto, teselas o tráfico proxy sí.

Componente del repositorioContenedorHogar en AWS
App de navegador (apps/mappview)nginx de mappviewVercel (estático)
Consola de gestión + API de Share (apps/admin, services/admin)adminEC2 (Docker)
Proxy edge de teselas e IA (services/edge) + su Redisedge, edge-redisEC2 (Docker)
Sidecar de Python (services/server)incluido en la imagen del Dockerfile raíz tras nginx en /sidecar/EC2 (Docker)
Almacenamiento de objetos (rustfs)rustfsS3 — el contenedor solo imita a S3 localmente
Sitio de documentación, registro de plugins, build estática del visordocs, pluginsS3 + CloudFront, o proyectos adicionales de Vercel
Base de datos de GeoNodegeonode-db (PostGIS)RDS para PostgreSQL (PostGIS soportado)
GeoNode, GeoServer, celery, memcached, nginxcontenedores del perfil hosted-layersEC2 (Docker) — no existe reemplazo nativo de AWS
Monitoreo (gatus, grafana, prometheus, exportadores)status, grafana, prometheus, …Omitir al inicio; el monitoreo básico de CloudWatch es gratis. Volver a añadir en la caja si hace falta

Una instancia EC2 ejecutando el docker/compose/admin.yaml existente (menos los contenedores reemplazados arriba) es la forma deliberada: ECS/Fargate acomoda gestión y edge como tareas individuales, pero GeoNode + GeoServer necesitan volúmenes compartidos (EFS) y terminan costando más que una sola instancia burstable a esta escala.

Distribución de dominios

Los valores por defecto de Compose compilan un origen público por servicio en las builds del navegador (VITE_MAPPVIEW_*_URL). Mantén esa disposición en AWS:

OrigenServicioServido por
mappview.…App webVercel
admin.mappview.…Consola de gestión + API de Share (puerto 8787), y /sidecar/* (puerto 8790)CloudFront → EC2
tiles.mappview.…Proxy edge (puerto 8788): proxies de teselas, OAM, Source Cooperative y Planetary Computer más el proxy de IACloudFront → EC2
plugins.mappview.…Registro de plugins (registry.json y zips)S3 + CloudFront; el documento de registro se obtiene en tiempo de ejecución, así que entradas nuevas no requieren recompilar el visor

Niveles

Comienza con el Nivel 1 y habilita el perfil hosted-layers solo cuando necesites capas gestionadas por la organización.

  • Nivel 1 — gestión, edge, imagen web+sidecar, S3. Proyectos Share, versiones, colecciones, tokens de API personales, miniaturas, fotos de campo, proxies de teselas y el proxy de IA.
  • Nivel 2 — añade el perfil hosted-layers (GeoNode, GeoServer, celery) más RDS. Añade carga/visualización de capas, la sincronización de catálogo y los endpoints de entrega WMS/WFS/WCS propiedad de gestión.

Paso 1: Bucket de S3 y usuario IAM

La gestión habla con el almacenamiento de objetos a través del SDK de AWS (services/admin/src/field-photos/rustfs.ts), así que el cambio desde el contenedor local rustfs es solo de entorno. Las fotos de campo se suben directamente desde el navegador mediante un POST prefirmado; el bucket se crea y su política se inicializa en el primer arranque, así que el usuario IAM necesita derechos de administración solo sobre ese bucket.

Crea una política acotada (mappview-field-photos es el bucket de ejemplo):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "MappViewFieldPhotos",
      "Effect": "Allow",
      "Action": [
        "s3:CreateBucket",
        "s3:PutBucketPolicy",
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": ["arn:aws:s3:::mappview-field-photos", "arn:aws:s3:::mappview-field-photos/*"]
    }
  ]
}

Crea un usuario IAM con esa política y guarda las llaves de acceso para el Paso 4. Las credenciales son explícitas en esta ruta de código, así que los roles de instancia IAM no se recogen. Habilita el versionado de S3 en el bucket como un deshacer barato para borrados.

Más adelante, crea una segunda distribución de CloudFront sobre el bucket (o un cache behavior en la distribución admin) y usa su URL HTTPS como RUSTFS_PUBLIC_URL — esa es la URL desde la que los navegadores descargan las fotos, así que debe ser HTTPS y legible por CORS desde el origen de la app web.

Paso 2: Instancia EC2

  1. Instancia. us-east-1 (o la región más cercana a tus usuarios; esta página cita precios de us-east-1). t3.medium (4 GB) para el Nivel 1, t3.xlarge (16 GB) con el perfil hosted-layers (GeoServer corre con un heap Java de 2 GB). t3.large cabe en el Nivel 2 solo sin el sidecar y con el heap de GeoServer reducido a 1.5 GB. Todo está conteinerizado — Bun, Python y Java no se necesitan en el host.

  2. AMI y user data. Amazon Linux 2023 con este user data:

    #!/bin/bash
    dnf install -y docker git docker-compose-plugin
    systemctl enable --now docker
    usermod -aG docker ec2-user
  3. Almacenamiento. 30 GB gp3 para el Nivel 1, 50 GB para el Nivel 2. El servicio de gestión guarda su base de datos SQLite y los objetos de proyecto/miniatura en el volumen management-data. Toma snapshots de ese volumen con programación — un respaldo que captura solo la base de datos o solo objects/ deja el inventario inconsistente.

  4. Security group. Permite entrada solo a los puertos 8787, 8788 y 8790 desde la managed prefix list de CloudFront orientada a orígenes (com.amazonaws.global.cloudfront.origin-facing), más SSH desde tu IP. TLS termina en CloudFront (ACM, gratis); los orígenes EC2 quedan en HTTP plano sobre esa ruta restringida.

  5. Red. Subred pública, sin NAT gateway necesaria, sin ALB — CloudFront → EC2 sobre HTTP con el security group haciendo el control de acceso. Un ALB añade ~$16/mes y cargos LCU por nada que CloudFront no haga ya.

  6. DNS. Hosted zone de Route 53, registros alias A/AAAA hacia las distribuciones. El certificado ACM que usa CloudFront debe vivir en us-east-1.

Paso 3: Override de Compose para AWS

El archivo Compose original vincula cada servicio a 127.0.0.1, lo correcto para un host local pero inalcanzable desde CloudFront. Añade un compose.override.yaml junto a él (Compose lo fusiona automáticamente y reemplaza las claves con valor de lista como ports):

services:
  admin:
    ports: ["8787:8787"]
  edge:
    ports: ["8788:8788"]

Mantén ADMIN_TRUST_PROXY=true (Paso 4) para que la deduplicación de vistas lea CF-Connecting-IP/X-Forwarded-For (CloudFront establece ambos) en lugar de la IP del edge de CloudFront.

Paso 4: Desplegar la pila del Nivel 1

Crea el archivo de entorno (Compose lee .env en la raíz del proyecto):

# --- gestión ---
APP_ORIGIN=https://admin.mappview.example.com
VIEWER_URL=https://mappview.example.com/
CORS_ORIGINS=https://mappview.example.com,https://admin.mappview.example.com,https://mappview.example.com/tauri,tauri://localhost,http://tauri.localhost
ADMIN_TRUST_PROXY=true
ADMIN_BIND_ADDRESS=0.0.0.0
ADMIN_LOCAL_TOKEN=
ADMIN_BOOTSTRAP_TOKEN=

CLERK_PUBLISHABLE_KEY=pk_live_...
CLERK_SECRET_KEY=sk_live_...
CLERK_AUTHORIZED_PARTIES=https://mappview.example.com,https://admin.mappview.example.com,https://mappview.example.com/tauri,tauri://localhost,http://tauri.localhost

# --- S3 en lugar de rustfs ---
RUSTFS_ENDPOINT=https://s3.us-east-1.amazonaws.com
RUSTFS_PUBLIC_URL=https://photos.mappview.example.com
RUSTFS_ACCESS_KEY=AKIA...
RUSTFS_SECRET_KEY=...
RUSTFS_BUCKET=mappview-field-photos
RUSTFS_REGION=us-east-1

# --- edge ---
EDGE_ALLOWED_ORIGINS=https://mappview.example.com,https://admin.mappview.example.com
AI_PROVIDERS=...
EDGE_ALLOWED_MODELS=...
EDGE_DEFAULT_MODEL=...
EDGE_RATE_LIMIT=10
MAPPVIEW_AI_PROXY_TOKEN=...

Notas sobre los valores:

  • RUSTFS_ENDPOINT debe seguir siendo una URL http(s); el cliente usa direccionamiento path-style, que AWS S3 aún acepta — reverificar tras actualizaciones del SDK.
  • CORS_ORIGINS debe incluir el origen de Vercel, el origen del visor y — para Share de escritorio — tauri://localhost y http://tauri.localhost.
  • Las llaves de Clerk se compilan en el bundle del navegador en tiempo de build, así que recompila las imágenes después de cambiarlas.

Luego despliega:

git clone https://github.com/your-org/mappview.git && cd mappview
docker compose -f docker/compose/admin.yaml up -d --build admin edge edge-redis mappview

# app web + sidecar en una imagen (Dockerfile raíz): usada solo para /sidecar/*
docker build -t mappview-web .
docker run -d --name mappview-web --restart unless-stopped \
  -p 8790:80 \
  -e MAPPVIEW_SIDECAR_TOKEN="$(openssl rand -hex 24)" \
  -v /data/conversion:/data \
  mappview-web
  • El nginx del contenedor mappview-web es el llamador confiable del sidecar: inyecta el header X-MappView-Token y reenvía un Host de loopback, que el TrustedHostMiddleware del sidecar exige. Pon CloudFront frente a este nginx (Paso 5), no frente al uvicorn desnudo.
  • -v /data/conversion:/data da a las herramientas de conversión su directorio de trabajo; la imagen confina lecturas/escrituras a MAPPVIEW_CONVERSION_ROOTS (por defecto /data) porque el sidecar es alcanzable same-origin. Si usas los endpoints PostGIS del sidecar, configura MAPPVIEW_POSTGIS_HOSTS con las entradas host:port permitidas — rechaza todo destino hasta entonces.
  • Guarda el MAPPVIEW_SIDECAR_TOKEN generado; el Paso 5 lo necesita.

La app web de Vercel alcanza el sidecar same-origin, exactamente como lo hace la build nginx del navegador. Añade un rewrite para que /sidecar/* en el origen de Vercel se proxee a la distribución admin:

{
  "rewrites": [
    {
      "source": "/sidecar/:path*",
      "destination": "https://admin.mappview.example.com/sidecar/:path*"
    }
  ]
}

Paso 5: Distribuciones de CloudFront

Dos distribuciones, una por origen público (CloudFront enruta los nombres de dominio alternativos por distribución, no por ruta). Crea el certificado ACM en us-east-1, cubriendo ambos nombres.

Distribución admin.mappview.…Distribución tiles.mappview.…
Origen 1EC2 : 8787 (HTTP)EC2 : 8788 (HTTP)
Comportamiento por defecto (*)Cache policy CachingDisabled + origin request policy AllViewerExceptHostHeader (Authorization, Cookie y headers de IP del cliente deben llegar a la API de Share)Cache policy CachingOptimized (las teselas son artefactos estáticos)
Comportamiento /sidecar/*Origen: EC2 : 8790 (HTTP), custom origin header X-MappView-Token = el valor del Paso 4, cache policy CachingDisabled—
Protocolo del visorRedirigir HTTP a HTTPSRedirigir HTTP a HTTPS
CompresiónActivadaActivada

Los tres orígenes EC2 usan solo HTTP — el security group restringe esos puertos a la prefix list de CloudFront, que es el control de acceso. Añade los registros alias de Route 53, luego verifica:

curl -s https://admin.mappview.example.com/api/health
curl -s https://tiles.mappview.example.com/healthz
curl -s https://mappview.example.com/sidecar/conversion/status

Finalmente, apunta el RUSTFS_PUBLIC_URL de la distribución de fotos (Paso 1) a su URL HTTPS de CloudFront.

Paso 6: Nivel 2 — capas alojadas

Sigue Desplegar capas alojadas para la topología completa y los requisitos de producción. En AWS dos cosas cambian:

  • Base de datos. Reemplaza el contenedor geonode-db con RDS para PostgreSQL (db.t4g.small basta para empezar; PostGIS está soportado en RDS). Crea un security group que permita 5432 solo desde la instancia EC2, y sobrescribe los valores DATABASE_HOST, DATABASE_URL y GEODATABASE_URL de los servicios geonode en el override de Compose — están fijados en docker/compose/admin.yaml, así que una entrada de override por servicio es la ruta limpia. Multi-AZ duplica el costo de la base de datos (~+$23/mes); single-AZ está bien para empezar.
  • Memoria. GeoServer (-Xmx2G), GeoNode/Django, celery, los servicios edge y la imagen del sidecar comparten la instancia — t3.xlarge es el mínimo; t3.large solo funciona si sueltas el contenedor del sidecar y reduces el heap de GeoServer a 1.5 GB.

Levanta el perfil con el mismo override:

docker compose -f docker/compose/admin.yaml --profile hosted-layers up -d --build

Estimación de costo (us-east-1, on-demand, single AZ)

ÍtemNivel 1Nivel 2
EC2 (t3.medium / t3.xlarge)~$30~$121
EBS gp3 (30 / 50 GB)~$2.4~$4
RDS db.t4g.small PostGIS + 20 GB—~$26
S3 + CloudFront + Route 53 + IPv4 pública~$5~$7
Total~$40/mes~$160/mes

La variable de escalado real es el egreso, no el cómputo. CloudFront incluye 1 TB de salida de datos por mes gratis (luego ~$0.085/GB); el egreso de EC2 puro es solo de 100 GB gratis y luego $0.09/GB, que es la razón por la que cada origen con mucha carga de ancho de banda pasa por CloudFront. El tráfico de teselas es de unos 10–50 MB por sesión de mapa, así que el 1 TB gratis cubre del orden de 20.000–100.000 sesiones al mes. Los ítems más grandes a vigilar son las descargas de capas alojadas (hasta 2 GB cada una, MAX_LAYER_BYTES) y las respuestas WMS/WMTS de GeoServer; el caché en disco GWC de GeoServer en EBS mantiene baratas las peticiones de teselas repetidas.

Operación

  • Actualizaciones. git pull, recompila las imágenes cambiadas y vuelve a ejecutar los mismos comandos docker compose up -d. Todos los valores VITE_* y la clave publicable de Clerk son de tiempo de build — cambiarlos exige recompilar las imágenes y el despliegue de Vercel juntos.
  • Respaldos. Snapshots de EBS del volumen management-data (base de datos + objects/ deben capturarse juntos) y versionado de S3 en el bucket de fotos. En el Nivel 2, los respaldos automatizados de RDS cubren los datos PostGIS.
  • Monitoreo. Las métricas básicas de instancia en CloudWatch son gratis. La pila de gatus/Grafana/Prometheus del repositorio corre bien como contenedores adicionales en la misma instancia si quieres de vuelta los chequeos de disponibilidad y los dashboards.
  • Secretos. Mueve los valores de .env a AWS Secrets Manager o SSM Parameter Store antes de que el despliegue crezca más allá de un solo operador; el archivo Compose usa valores de entorno planos a propósito solo para pruebas locales.

Capacidades a esperar

Siempre disponibles (Nivel 1)

  • Share — publicación de proyectos, historial de versiones inmutable, forks, colecciones, miniaturas, conteos de vistas y tokens personales mappview_… verificados como hashes SHA-256. El Share de escritorio funciona cuando los orígenes Tauri están en CORS_ORIGINS.
  • Embeds y “Abrir en Meratyn” — la URL del visor pasada en tiempo de build; las exportaciones HTML y los embeds <iframe> apuntan a la app de Vercel.
  • Proxies de teselas — fuentes de OpenAerialMap, Source Cooperative y Planetary Computer servidas desde tiles.mappview.… para que el navegador nunca necesite CORS ni tokens por proveedor.
  • Proxy de IA — el tráfico del asistente configurable por proveedor fluye por edge con modelos permitidos, un rate limit y las llaves upstream guardadas del lado del servidor.
  • Fotos de campo — subida directa de navegador a S3 vía POST prefirmado (25 MB por foto por defecto, FIELD_PHOTO_MAX_BYTES).
  • Procesamiento web con el sidecar desplegado — las herramientas de Conversión (GeoParquet, FlatGeobuf, PMTiles, COG), los flujos ráster con archivos nativos y las herramientas Whitebox fuera del catálogo del navegador pueden usar el sidecar; el rewrite de Vercel lo hace same-origin en /sidecar. Confínalo con MAPPVIEW_CONVERSION_ROOTS (por defecto /data) y MAPPVIEW_POSTGIS_HOSTS. La conversión a PMTiles y las rutas Whitebox del sidecar son solo amd64 — las instancias EC2 estándar son amd64, así que esto no es una restricción en AWS (sí lo es en ARM/Apple Silicon). Sin el sidecar, las operaciones de conversión, ráster y Whitebox compatibles con el navegador siguen disponibles; las rutas a archivos nativos y las operaciones fuera de esos motores necesitan el sidecar.
  • Cuotas de almacenamiento — 25 MB por documento de proyecto, 100 MB de cuota por defecto por usuario, ambas configurables por entorno.

Añadidas por el Nivel 2 (capas alojadas)

  • Publicación de capas — se suben archivos SIG a través de gestión; celery los publica en GeoNode/GeoServer de forma asíncrona.
  • Sincronización de catálogo — gestión reconcilia el catálogo de GeoNode cada GEONODE_CATALOGUE_SYNC_INTERVAL_MS (5 minutos por defecto).
  • Entrega OGC — gestión expone endpoints estables de capacidades y peticiones WMS/WFS/WCS para las capas publicadas, manteniendo privados los endpoints proveedores y emitiendo acceso firmado de corta vida (LAYER_ACCESS_TTL_SECONDS, 300 s por defecto). Estos fluyen por CloudFront como todo lo demás.