Saltar a contenido

Despliegue del CDP en GCP

Revisión 1.2 (2026-09-11): despliegue real en devops-vm. Tras auditar la VM se cambiaron cuatro puntos respecto de lo descrito abajo; en caso de conflicto manda esta nota y unomi-eval/deploy/:

Punto Diseño original (v1.1) Decisión para devops-vm
VM e2-standard-4 dedicada devops-vm existente (c3-standard-4, 4 vCPU / 16 GB, Debian 12, Docker 28.5), compartida con 14 aplicaciones; ~7 GiB libres, sin swap
Dimensionado ES heap 4 GB, Unomi 2 GB ES heap 1 GB (límite 2,5 GiB), Unomi 1,5 GB (límite 2 GiB), Inoyu límite 512 MiB. Total ≈ 3,5 GiB. Límites de memoria por contenedor para no afectar a las demás apps
Túnel contenedor cloudflared propio, sin puertos en la VM cloudflared existente (token, Zero Trust), en devops-vm o en otro servidor de la VPC. La UI y la API se publican en la red interna (0.0.0.0:3131 y 0.0.0.0:8181; el firewall de GCP no los expone a internet) y los hostnames apuntan a http://10.158.0.22:<puerto>
Hostnames cdp-admin.<dominio>, cdp-api.<dominio> cdp.compulandia.com.py (UI) y cdp-api.compulandia.com.py (API)

La IP de origen que ve Unomi en los eventos protegidos es la del servidor que aloja cloudflared (subred de la VPC 10.158.0.0/20, añadida en EXTRA_TRUSTED_SUBNETS) o la puerta de enlace de la red cdp si corre en la misma VM. El código en /opt/cdp se despliega con git clone. Ajuste de kernel aplicado en la VM: vm.max_map_count=262144. Se eliminó el volumen cdp-unomi-data (/opt/apache-unomi/data): persistir la caché de Karaf sin etc/ impide que un contenedor recreado regenere etc/jetty.xml y el servidor HTTP no arranca.

Especificación técnica para desplegar Apache Unomi 3.0.0, Elasticsearch 9 e Inoyu OSS UI en contenedores Docker sobre una máquina virtual de Google Cloud, con acceso exclusivo a través de un túnel de Cloudflare para la interfaz de administración y para la recepción de eventos por API.

Versión 1.1 · 7 de septiembre de 2026 (sustituye a la 1.0: la ingesta pasa a ser solo por API, vía Zaraz, Jitsu y n8n)
Base Banco de pruebas unomi-eval (Unomi 3.0.0 + Elasticsearch 9.1.3 + Inoyu OSS UI e78c1bd con seis parches)
Archivos de referencia unomi-eval/deploy/ (compose, variables, reglas del túnel)

Resumen

Desplegar el stack completo en una única VM (e2-standard-4, 4 vCPU, 16 GB, disco de 100 GB) sin ningún puerto de entrada abierto. Publicar dos nombres de host por túnel de Cloudflare: la interfaz de administración protegida con inicio de sesión corporativo (Cloudflare Access) y la API de Unomi protegida con token de servicio, que es la única vía de entrada de eventos. No exponer ningún endpoint al navegador del cliente final: los eventos web los captura Cloudflare Zaraz y los entrega Jitsu; los eventos del ERP SAP los extrae n8n y también los entrega Jitsu. Mantener Elasticsearch solo en la red interna del stack. Copiar diariamente los índices a Cloud Storage y tomar instantáneas del disco.

1. Alcance y supuestos

  • Uso interno, un solo inquilino: los clientes de la compañía, operado internamente desde Inoyu OSS UI.
  • Ingesta exclusivamente por API servidor-a-servidor. No se utiliza el tracker JavaScript de Unomi ni el endpoint context.json desde el navegador. Consecuencias: Unomi no emite cookies al cliente final, no requiere que la tienda y el CDP compartan dominio, y no necesita la corrección del Content-Type del tracker.
  • Fuentes de eventos:
  • Web: navegador → Cloudflare Zaraz (captura en el edge) → Jitsu → Unomi.
  • ERP SAP: SAP → n8n (extracción y transformación) → Jitsu → Unomi. n8n puede llamar a Unomi directamente para operaciones administrativas (carga masiva de propiedades, consultas), pero los eventos siguen el mismo camino que la web para tener una sola capa de mapeo.
  • Jitsu existe previamente (autoalojado). Se contemplan dos ubicaciones: en el mismo proyecto de GCP (ruta privada) o fuera (ruta por cdp-api con token de servicio).
  • Versiones validadas: apache/unomi:3.0.0, elasticsearch:9.1.3, Inoyu OSS UI e78c1bd con los parches de unomi-eval/inoyu-ui/, cloudflared 2026.8.3.
  • Volumen objetivo: hasta 50 eventos por segundo sostenidos y 5 millones de eventos al año. El banco de pruebas midió 119 eventos por segundo en un host de 6 vCPU con el cliente de carga en la misma máquina.
  • Fuera de alcance: alta disponibilidad multi-nodo (requiere Unomi 3.1 y un clúster de Elasticsearch); entornos de prueba y producción separados (replicar el stack en otra VM con otro dominio); la configuración interna de Zaraz, Jitsu y n8n más allá de su contrato con Unomi.

2. Definiciones

Término Definición
Unomi Servidor de perfiles (CDP) de Apache. Recibe eventos, resuelve identidad, ejecuta reglas, mantiene segmentos y puntuaciones. Expone la API de eventos (/cxs/eventcollector) y la API administrativa (/cxs/*) con autenticación Basic del usuario karaf.
Elasticsearch Almacén de todos los datos de Unomi: perfiles, sesiones, eventos y definiciones. Índices de eventos y sesiones con rotación por tamaño.
Inoyu OSS UI Interfaz web de administración (Next.js). El navegador nunca habla con Unomi: la UI reenvía cada llamada por su servidor con las credenciales de karaf.
Zaraz Gestor de etiquetas de Cloudflare que ejecuta la captura de eventos en el edge, sin JavaScript de terceros en la página. Envía los eventos a Jitsu.
Jitsu Plataforma de ingesta de eventos. Recibe de Zaraz y de n8n, normaliza y entrega a Unomi mediante una función de destino HTTP. Única capa de mapeo hacia el modelo de Unomi.
n8n Orquestador de flujos. Extrae de SAP (pedidos, clientes, facturación), transforma y envía a Jitsu.
Scope Identificador del sitio o sistema origen de los eventos. Todo evento debe pertenecer a un scope registrado en Unomi.
Evento protegido Tipo de evento (login, updateProperties) aceptado solo con la cabecera X-Unomi-Peer correcta y desde una IP autorizada.
Cloudflare Tunnel Conector (cloudflared) que abre conexiones salientes desde la VM hacia Cloudflare y recibe por ellas el tráfico de los nombres de host publicados. Sin IP pública ni puertos de entrada.
Cloudflare Access Autenticación Zero Trust aplicada a un nombre de host antes del túnel: inicio de sesión corporativo para personas, tokens de servicio para sistemas.

3. Arquitectura

flowchart LR
  subgraph fuentes[Fuentes]
    web["Navegador del cliente<br/>tienda.ejemplo.com.py"]
    sap["ERP SAP"]
  end
  subgraph edge[Cloudflare]
    zaraz["Zaraz<br/>captura de eventos web"]
    access["Zero Trust Access<br/>SSO / token de servicio"]
    tun["Tunnel"]
  end
  subgraph ingesta[Ingesta]
    n8n["n8n<br/>extracción SAP"]
    jitsu["Jitsu<br/>normalización · función destino Unomi"]
  end
  subgraph vm["GCP · VM e2-standard-4 · Docker · red interna 172.30.0.0/24"]
    cfd["cloudflared"]
    ui["inoyu-ui :3131"]
    unomi["unomi :8181"]
    es[("elasticsearch :9200")]
  end
  mkt["Navegador de marketing"]
  gcs[("Cloud Storage<br/>snapshots")]
  web --> zaraz --> jitsu
  sap --> n8n --> jitsu
  jitsu -->|"HTTPS cdp-api<br/>eventcollector"| access
  n8n -.->|"HTTPS cdp-api<br/>operaciones administrativas"| access
  mkt -->|"HTTPS cdp-admin"| access
  access --> tun
  tun -.->|"QUIC 7844 saliente"| cfd
  cfd -->|"cdp-admin"| ui
  cfd -->|"cdp-api"| unomi
  ui -->|"Basic karaf"| unomi
  unomi --> es
  es -.->|"snapshot diario"| gcs
Componente Imagen Función Exposición
cloudflared cloudflare/cloudflared:2026.8.3 Conector del túnel. Recibe el tráfico de cdp-admin y cdp-api y lo reenvía dentro de la red Docker. Solo salida (UDP/TCP 7844, respaldo TCP 443)
inoyu-ui cdp/inoyu-oss-ui:e78c1bd (construida localmente) Interfaz de administración. Interna (3131)
unomi apache/unomi:3.0.0 Motor CDP: API de eventos y administrativa, reglas, segmentos, scoring, purga. Interna (8181 HTTP, 9443 HTTPS)
elasticsearch elasticsearch:9.1.3 Persistencia. Un solo nodo, sin réplicas. Interna (9200)
Zaraz, Jitsu, n8n Externos al stack Captura, normalización y extracción. Según su propio despliegue

Si Jitsu (y n8n) se despliegan en el mismo proyecto de GCP, conectar por la VPC y omitir el túnel para ese tramo: publicar el puerto 8181 de Unomi solo en la IP interna de la VM y autorizar la subred de Jitsu en el firewall y en la lista de IPs de eventos protegidos. El resto del documento asume la ruta por cdp-api, válida en ambos casos.

4. Modelo de ingesta e identidad

4.1 Contrato de entrada a Unomi

Un único endpoint, POST /cxs/eventcollector, con este cuerpo:

{
  "sessionId": "<identificador de sesión del origen>",
  "profileId":  "<identificador estable del cliente en el origen>",
  "events": [
    {
      "eventType": "productView",
      "scope": "tienda-web",
      "source": { "itemId": "tienda-web", "itemType": "site", "scope": "tienda-web" },
      "target": { "itemId": "sku-123", "itemType": "product", "scope": "tienda-web", "properties": {} },
      "properties": { "productId": "sku-123", "categoria": "hogar", "precio": 890000 }
    }
  ]
}

Comportamientos verificados en el banco de pruebas que condicionan el diseño:

  • Un profileId desconocido crea el perfil en el mismo envío; no hace falta crearlo antes.
  • Un sessionId ya ligado a otro perfil manda sobre el profileId enviado. Cada sesión del origen debe llevar un sessionId propio, nunca reutilizado entre clientes.
  • Los eventos protegidos (login, updateProperties) requieren la cabecera X-Unomi-Peer y una IP de origen autorizada; la IP que ve Unomi es la de la conexión real (el contenedor cloudflared cuando llega por el túnel).
  • Cada evento se valida contra el esquema JSON de su tipo. Un evento rechazado responde igualmente HTTP 200 con updated: 0; el único rastro es un WARN SchemaServiceImpl en el log de Unomi. Jitsu no puede detectar el rechazo por el código HTTP: validar en Jitsu antes de enviar y vigilar el log.
  • El campo timeStamp del evento se ignora: Unomi registra la hora de recepción. Los eventos históricos (por ejemplo pedidos antiguos de SAP) no pueden cargarse con su fecha original como eventos; cargar el histórico como propiedades del perfil (total acumulado, fecha de última compra) y enviar como eventos solo lo que ocurre a partir de la puesta en marcha.
  • incrementPropertyAction con propertyTarget suma únicamente valores enteros de 32 bits tomados de target.properties; enviar importes en guaraníes enteros (o en miles si se acercan a 2.147 millones acumulados).

4.2 Identificadores

Origen profileId sessionId Identificación
Web (Zaraz → Jitsu) Identificador anónimo estable del visitante que gestiona Zaraz/Jitsu (cookie de primera parte del sitio), con prefijo web- Identificador de sesión de Jitsu (nuevo tras 30 minutos de inactividad) Al iniciar sesión en la tienda, evento login con el correo del cliente en target.properties.email
SAP (n8n → Jitsu) sap-<código de cliente> sap-<código de cliente>-<fecha> Evento login (una vez por cliente, o en cada carga) con el correo en target.properties.email y sapCustomerId

La unificación se hace en Unomi con la regla de fusión ya validada (mergeProfilesOnPropertyAction sobre el identificador evalMergeIdentifier = correo, con copyPropertiesAction): el primer perfil que declara un correo lo conserva; cualquier otro perfil que declare el mismo correo se fusiona en él y su id queda como alias. Se verificó que un perfil creado por SAP con el mismo correo que un perfil web se fusiona en el perfil web y que los eventos posteriores enviados con el id de SAP se atribuyen al perfil unificado.

Reglas para los productores:

  • Enviar siempre el mismo profileId para el mismo visitante o cliente; nunca generar uno por evento.
  • Enviar el login una sola vez por sesión web, desde Jitsu con X-Unomi-Peer (nunca desde el navegador).
  • Enviar el correo normalizado (minúsculas, sin espacios) en todos los orígenes: es la clave de fusión.
  • No enviar datos personales en properties de eventos que no lo necesiten; el perfil los recibe por login.

4.3 Mapeo de eventos

Definir en Jitsu una función de destino que transforme cada evento de origen al tipo de Unomi y lo envíe a https://cdp-api.<dominio>/cxs/eventcollector con las cabeceras de Access (CF-Access-Client-Id, CF-Access-Client-Secret) y, para los protegidos, X-Unomi-Peer. Tabla mínima para la tienda:

Evento de origen Tipo en Unomi Scope properties target
Zaraz pageview view (esquema de fábrica) tienda-web — página
Zaraz product_view productView tienda-web productId, categoria, precio producto
Zaraz add_to_cart addToCart tienda-web productId, cantidad producto
Zaraz purchase / SAP pedido confirmado purchase tienda-web / sap orderId, total, items[] pedido con properties.total
Inicio de sesión en la tienda / alta de cliente en SAP login (protegido) según origen — usuario con email, firstName, lastName, sapCustomerId
Cambio de datos maestros en SAP updateProperties (protegido) sap propiedades a fijar —

Registrar en Unomi un scope por origen (tienda-web, sap) y un esquema JSON por tipo de evento propio, con additionalProperties: false en properties y con source y target declarados. Versionar scopes, esquemas, reglas, segmentos y scoring como archivos JSON en el repositorio y aplicarlos por API (como hace unomi-eval/tests/08-demo-ecommerce.sh), de modo que un entorno nuevo se reconstruya desde el repositorio.

4.4 Flujo de un evento web

  1. Zaraz captura la acción en el edge (sin script de Unomi en la página) y la envía a Jitsu con el identificador anónimo y el de sesión.
  2. La función de destino de Jitsu construye el cuerpo del apartado 4.1 y lo envía a cdp-api. Cloudflare verifica el token de servicio y aplica el límite de tasa; el túnel entrega la petición a Unomi.
  3. Unomi valida el evento contra su esquema, ejecuta las reglas (contadores, interés, importes), recalcula segmentos y puntuaciones y persiste en Elasticsearch.
  4. Al iniciar sesión el cliente, Jitsu envía el login protegido; Unomi copia los datos al perfil y fusiona con cualquier perfil previo que tenga el mismo correo (web o SAP).

5. Infraestructura en Google Cloud

5.1 Máquina virtual

Elemento Valor Justificación
Tipo e2-standard-4 (4 vCPU, 16 GB) Consumo medido en reposo: Elasticsearch 1,6 a 2 GiB con heap de 1 GB, Unomi 0,9 GiB, Inoyu 0,1 GiB. Con heap de 4 GB para Elasticsearch y 2 GB para Unomi quedan más de 6 GB de caché de sistema de archivos. Mínimo para un piloto: e2-standard-2 con heaps de 2 GB y 1 GB.
Disco de arranque pd-balanced, 100 GB, ext4 Un evento ocupa alrededor de 1 KB indexado; 5 millones de eventos al año con sesiones y perfiles ocupan entre 10 y 15 GB.
Sistema operativo Debian 12 o Ubuntu 24.04 LTS Docker Engine y el plugin Compose desde el repositorio oficial de Docker.
Cuenta de servicio Dedicada: roles/storage.objectAdmin limitado al bucket de snapshots y roles/logging.logWriter Sin claves descargadas: Elasticsearch usa la identidad de la VM para escribir en Cloud Storage.
Acceso administrativo OS Login + IAP para SSH Sin puerto 22 público.
Instantáneas de disco Diarias, retención 14 días Recuperación completa de la VM. Complementa los snapshots de Elasticsearch.
Bucket gs://cdp-snapshots-<proyecto>, Nearline, retención 90 días, región de la VM Destino de los snapshots de Elasticsearch.

5.2 Red y firewall

  • VPC con una subred privada. Sin IP pública en la VM; Cloud NAT para la salida (túnel, imágenes, paquetes).
  • Entrada: ninguna regla, salvo la de IAP (35.235.240.0/20, TCP 22). Si Jitsu está en la misma VPC, una regla que permita TCP 8181 desde su subred hacia la VM.
  • Salida: 443 y 7844 hacia Internet; el resto denegado.

5.3 Preparación del sistema

echo 'vm.max_map_count=262144' > /etc/sysctl.d/99-elasticsearch.conf && sysctl --system   # requisito de Elasticsearch
# Docker Engine + plugin Compose (repositorio oficial); usuario de despliegue en el grupo docker
# chrony activo: los cortes de sesión y las ventanas de "últimos N días" dependen del reloj
# Ops Agent de Google para métricas y logs del host y de los contenedores

6. Stack Docker

Utilizar unomi-eval/deploy/docker-compose.yml: cuatro servicios en una red bridge con subred fija 172.30.0.0/24, tres volúmenes con nombre y ningún puerto publicado en la VM. Reutiliza el Dockerfile y los parches de Inoyu del banco de pruebas.

Servicio Depende de Volúmenes Comprobación de salud Recursos
elasticsearch — cdp-es-data, cdp-es-snapshots _cluster/health amarillo o verde heap 4 GB, memlock ilimitado, nofile 65536
unomi elasticsearch sano cdp-unomi-data (logs, auditoría, estado de Karaf) GET /cxs/cluster con Basic; gracia 120 s heap 1 a 2 GB; arranque en 45 a 90 s
inoyu-ui unomi sano — GET /api/health ~100 MB
cloudflared unomi sano, inoyu-ui iniciado — cloudflared tunnel ready ~50 MB

La subred fija importa: Unomi valida los eventos protegidos contra la IP remota real de la conexión, que a través del túnel es la del contenedor cloudflared; la lista de IPs autorizadas debe contener esa subred.

7. Configuración

7.1 Unomi

Todas las propiedades se inyectan por variables de entorno; la imagen las resuelve en etc/custom.system.properties.

Variable Valor Efecto
UNOMI_ELASTICSEARCH_ADDRESSES elasticsearch:9200 Conexión al almacén por la red interna.
UNOMI_ELASTICSEARCH_DEFAULTINDEX_SHARDS / _REPLICAS 1 / 0 Un solo nodo: evitar 5 shards por índice y réplicas sin asignar (clúster en amarillo permanente).
UNOMI_ELASTICSEARCH_ROLLOVER_SHARDS / _REPLICAS / _MAXSIZE 1 / 0 / 10gb Rotación de índices de eventos y sesiones por tamaño.
UNOMI_ROOT_PASSWORD secreto Contraseña del usuario karaf (API administrativa y consola). El usuario no es configurable.
UNOMI_HEALTHCHECK_PASSWORD secreto Usuario health de /health/check.
UNOMI_THIRDPARTY_PROVIDER1_KEY secreto Valor esperado en X-Unomi-Peer para eventos protegidos. Compartir solo con Jitsu y n8n.
UNOMI_THIRDPARTY_PROVIDER1_IPADDRESSES 172.30.0.0/24,127.0.0.1,::1 (+ subred de Jitsu si va por VPC) Orígenes admitidos para eventos protegidos.
UNOMI_THIRDPARTY_PROVIDER1_ALLOWEDEVENTS login,updateProperties Tipos de evento que requieren clave.
UNOMI_CLUSTER_PUBLIC_ADDRESS https://cdp-api.<dominio> Dirección que Unomi comunica en sus respuestas.
UNOMI_CLUSTER_INTERNAL_ADDRESS https://unomi:9443 Dirección interna del nodo.
UNOMI_PROFILE_PURGE_INACTIVETIME 365 Días sin actividad tras los cuales se purga un perfil.
UNOMI_EVENT_PURGE_EXISTTIME / UNOMI_SESSION_PURGE_EXISTTIME 400 Días de retención de eventos y sesiones; ajustar a la política de datos.
UNOMI_MONTHLY_INDEX_PURGE_EXISTTIME 13 Meses de retención de índices rotados.
UNOMI_GRAPHQL_FEATURE_ACTIVATED false GraphQL desactivado en producción.
EXTRA_JAVA_OPTS -Xms1g -Xmx2g Heap de la JVM de Unomi.

Las variables de cookie de perfil (UNOMI_PROFILE_COOKIE_*) no aplican: ningún navegador habla con Unomi.

7.2 Elasticsearch

Ajuste Valor Efecto
discovery.type single-node Sin descubrimiento de clúster.
xpack.security.enabled false Sin TLS ni contraseñas en 9200. Aceptable solo porque el puerto no se publica y la red Docker es privada. Si Elasticsearch sale de la VM, activar seguridad y usar UNOMI_ELASTICSEARCH_USERNAME, _PASSWORD, _SSL_ENABLE.
ES_JAVA_OPTS -Xms4g -Xmx4g Heap fijo: no más de la mitad de la memoria disponible y nunca superior a 31 GB.
bootstrap.memory_lock true + memlock ilimitado Evita el intercambio a disco del heap.
path.repo /snapshots Repositorio local de snapshots, además del de Cloud Storage.

7.3 Inoyu OSS UI

Tres variables se incrustan en el bundle al compilar y llegan como argumentos de construcción: UNOMI_URL y NEXT_PUBLIC_UNOMI_URL (http://unomi:8181) y UNOMI_VERSION (3.0). En tiempo de ejecución: UNOMI_PASSWORD (la de karaf), JWT_SECRET, ADMIN_EMAIL, ADMIN_PASSWORD, DEPLOYMENT_TYPE=on-premise, COOKIE_SECURE=true, NEXT_PUBLIC_BASE_URL=https://cdp-admin.<dominio>. La UI tiene un único usuario administrador; la identidad de cada persona la aporta Cloudflare Access delante.

7.4 Cloudflare

Crear el túnel en Zero Trust → Networks → Tunnels (gestionado por token) y cargar las reglas de unomi-eval/deploy/cloudflared-ingress.yml como nombres de host públicos, en ese orden. El conector recibe el token por CLOUDFLARE_TUNNEL_TOKEN.

Nombre de host Destino Rutas Protección
cdp-admin.<dominio> http://inoyu-ui:3131 Todas Access, política "Allow" para los correos del dominio corporativo (Google Workspace o código por correo); sesión de 24 h.
cdp-api.<dominio> http://unomi:8181 Todas Access, política "Service Auth" con un token de servicio por sistema (Jitsu, n8n), más la autenticación Basic de Unomi en la API administrativa. Límite de tasa: 3.000 peticiones/min por token.

Ajustes adicionales: modo SSL/TLS del dominio en "Full" como mínimo; caché en "Bypass" para ambos nombres; registros de Access para cdp-admin. No hay nombre de host público: Zaraz opera en el edge del dominio de la tienda y solo habla con Jitsu.

Con Zaraz: crear la herramienta de envío hacia el endpoint de ingesta de Jitsu (petición HTTP o componente gestionado), disparada por los eventos de la tienda (pageview, product_view, add_to_cart, purchase), con el identificador anónimo y el de sesión como propiedades.

8. Comunicación

Origen Destino Protocolo / puerto Autenticación Contenido
Navegador del cliente final Zaraz (edge del dominio de la tienda) HTTPS 443 — Eventos de navegación y compra capturados por Zaraz.
Zaraz Jitsu HTTPS 443 Clave de escritura de Jitsu Eventos web crudos.
n8n Jitsu HTTPS 443 Clave de escritura de Jitsu Eventos de SAP normalizados (pedidos, clientes).
Jitsu (función de destino) cdp-api → unomi HTTPS 443 → HTTP 8181 Token de servicio de Access; X-Unomi-Peer en login y updateProperties POST /cxs/eventcollector.
n8n cdp-api → unomi HTTPS 443 → HTTP 8181 Token de servicio de Access + Basic karaf Operaciones administrativas: carga de propiedades, consultas de segmentos, exportaciones.
Navegador de marketing cdp-admin → inoyu-ui HTTPS 443 → HTTP 3131 Access (SSO) + sesión de la UI Administración.
inoyu-ui unomi HTTP 8181 (interna) Basic karaf API administrativa por el proxy /api/cxs/*.
unomi elasticsearch HTTP 9200 (interna) — (red privada) Persistencia.
cloudflared Edge de Cloudflare QUIC UDP 7844, respaldo TCP 443, saliente Token del túnel Transporte del tráfico entrante.
elasticsearch Cloud Storage HTTPS 443, saliente Identidad de la VM Snapshots diarios.
Operador VM SSH 22 vía IAP OS Login Mantenimiento.

9. Seguridad

  • Sin superficie de entrada en la VM: ningún puerto publicado; comprobar con ss -ltn.
  • Secretos en .env con permisos 600 y copia en Secret Manager: contraseña de karaf, clave de eventos protegidos, secreto JWT, credenciales de la UI, token del túnel, tokens de servicio de Access. Generar con openssl rand -hex 32. Rotar la contraseña de karaf implica reiniciar Unomi e Inoyu y actualizar n8n.
  • Un token de servicio de Access por sistema (Jitsu, n8n), revocable por separado.
  • La API administrativa nunca es pública ni alcanzable sin token de servicio. Ningún endpoint queda abierto al navegador del cliente final.
  • Validación por esquema JSON con additionalProperties: false para cada tipo de evento; scopes registrados únicamente para los orígenes conocidos.
  • Datos personales: retención por las variables de purga; procedimiento de borrado por solicitud (DELETE /cxs/privacy/profiles/{id}?withData=true&purgeAll=true) y de exportación (POST /cxs/profiles/export). Coordinar el borrado con Jitsu y con el origen para que el cliente no se recree con el mismo identificador.
  • Auditoría: Unomi escribe data/security/audit.log en el volumen; Access registra cada acceso a cdp-admin.
  • Actualizaciones: imágenes fijadas por versión; probar primero en la VM de pruebas. Unomi 3.0.1 (julio de 2026) no tiene imagen oficial; construirla desde el tarball si se adopta. Unomi 3.1 en estabilización.

10. Operación

10.1 Despliegue inicial

git clone <repositorio> && cd unomi-eval/deploy
cp .env.example .env && chmod 600 .env          # completar dominios y secretos
docker compose build inoyu-ui                    # aplica los seis parches y compila
docker compose up -d
docker compose ps                                # esperar "healthy" en elasticsearch, unomi, cloudflared
# Aplicar scopes, esquemas, reglas, segmentos y scoring desde el repositorio (apartado 4.3)
# Configurar la función de destino en Jitsu y el flujo de n8n; enviar un evento de prueba por cada origen

10.2 Copias de seguridad

# Repositorio de snapshots en Cloud Storage (una vez; usa la identidad de la VM)
docker compose exec unomi curl -X PUT http://elasticsearch:9200/_snapshot/gcs -H 'Content-Type: application/json' \
  -d '{"type":"gcs","settings":{"bucket":"cdp-snapshots-<proyecto>","base_path":"unomi"}}'
# Política diaria, retención de 7 a 30 copias
docker compose exec unomi curl -X PUT http://elasticsearch:9200/_slm/policy/diaria -H 'Content-Type: application/json' \
  -d '{"schedule":"0 30 3 * * ?","name":"<unomi-{now/d}>","repository":"gcs","config":{"indices":["context-*"]},"retention":{"expire_after":"30d","min_count":7,"max_count":30}}'

Restauración: POST /_snapshot/gcs/<snapshot>/_restore con Unomi detenido; luego arrancar Unomi. Complementar con las instantáneas programadas del disco. Probar una restauración antes de la puesta en marcha.

10.3 Monitoreo y alertas

Señal Fuente Umbral sugerido
Salud de Unomi GET /health/check con usuario health (por cdp-api o red interna) Proveedor distinto de LIVE durante 2 minutos
Salud de Elasticsearch _cluster/health Estado rojo; disco > 80 % (marca de agua al 85 %)
Túnel Zero Trust → Tunnels; métricas locales en :2000/metrics Conector inactivo > 1 minuto
Eventos rechazados Log de Unomi, líneas SchemaServiceImpl ... Schema validation found Más de 1 % de los eventos en 10 minutos
Entregas fallidas Registro de la función de destino de Jitsu (errores HTTP, tiempos de espera) Cualquier fallo sostenido 5 minutos
Latencia de ingesta Analytics de Cloudflare para cdp-api p95 > 500 ms
Host Ops Agent: CPU, memoria, disco, reinicios de contenedores Memoria > 90 %; cualquier reinicio

10.4 Mantenimiento

  • Actualizar imágenes: cambiar la versión en .env, docker compose pull && docker compose up -d. Unomi migra índices al arrancar una versión mayor; leer sus notas de migración antes.
  • Reinicio completo: docker compose restart; Unomi operativo en 45 a 90 s con reglas, segmentos y esquemas intactos (verificado). Jitsu reintenta las entregas fallidas según su configuración; confirmar que la cola de reintentos cubre ese tiempo.
  • Espacio: rotación de índices y purga automáticas; vigilar docker system df y limpiar imágenes antiguas.
  • Escalado vertical: cambiar el tipo de máquina y los heaps. Horizontal: requiere Unomi 3.1 y un clúster de Elasticsearch de tres nodos; no previsto.

11. Lista de verificación

  • [ ] VM creada con tipo, disco, cuenta de servicio, OS Login e instantáneas; sin reglas de entrada.
  • [ ] Sistema preparado: vm.max_map_count, Docker y Compose, chrony, Ops Agent.
  • [ ] Túnel creado; cdp-admin y cdp-api con sus reglas de ingress; catch-all 404.
  • [ ] Políticas de Access: personas en cdp-admin; un token de servicio por sistema en cdp-api; límite de tasa.
  • [ ] .env completo con secretos generados y copia en Secret Manager.
  • [ ] Stack arrancado; cuatro servicios sanos; ss -ltn sin puertos del stack en la VM.
  • [ ] Scopes, esquemas y configuración de negocio aplicados desde el repositorio.
  • [ ] Función de destino de Jitsu configurada con token de servicio y X-Unomi-Peer; flujo de n8n para SAP.
  • [ ] Prueba de extremo a extremo por cada origen: evento aceptado (sin WARN de esquema), perfil visible en Inoyu, login fusiona web y SAP por correo.
  • [ ] Repositorio de snapshots y política diaria activos; una restauración probada.
  • [ ] Alertas configuradas y procedimiento de borrado por solicitud documentado.

12. Costo estimado mensual

Concepto Estimación (USD) Nota
VM e2-standard-4 ≈ 100 us-central1, uso continuo, sin descuento por compromiso
Disco 100 GB pd-balanced + instantáneas ≈ 12
Cloud Storage Nearline, 50 GB ≈ 1 Snapshots de Elasticsearch
Cloudflare Tunnel, Access y Zaraz 0 Zero Trust gratuito hasta 50 usuarios; Zaraz incluido en el plan del dominio
Licencias 0 Apache 2.0 en el stack; Elasticsearch bajo SSPL/Elastic 2.0, uso propio permitido

Precios de lista aproximados a la fecha; verificar en la calculadora de Google Cloud para la región elegida. Jitsu y n8n no se incluyen: forman parte de la infraestructura existente.