Stadium Wallet

Guía Oficial de Instalación, Pruebas y Arquitectura — Ecosistema completo de billetera digital para estadios sobre Red Hat OpenShift.

Owner: Maximiliano Pizarro, Specialist Solution Architect at Red Hat
Infra & Service Mesh: Francisco Raposo, Senior Specialist Solution Architect at Red Hat


1. Resumen Ejecutivo

High Level Architecture Arquitectura de alto nivel del ecosistema Stadium Wallet.

Este documento proporciona la guía definitiva para el despliegue, configuración y validación del ecosistema Stadium Wallet. La plataforma adopta un enfoque moderno basado en GitOps, seguridad Zero-Trust sin sidecars mediante OSSM3 (Ambient Mode), y una gestión integral del ciclo de vida de las APIs a través de Kuadrant y Red Hat Developer Hub.

El sistema se compone de un frontend interactivo (Vue.js) y tres microservicios core (.NET 8):

Microservicio Función
api-customers Gestión centralizada de identidad y perfiles de clientes
api-bills Lógica transaccional para el venue de Buffalo Bills
api-raiders Lógica transaccional para el venue de Las Vegas Raiders

Los microservicios interactúan con fuentes de datos externas (API de ESPN) de forma segura y auditable para obtener datos deportivos en tiempo real.

GitOps Declarativo

Sincronización continua con OpenShift GitOps (ArgoCD) — todo el estado se define en Git.

🔒

Zero-Trust sin Sidecars

OSSM3 Ambient Mode: mTLS automático, sin inyección de contenedores sidecar.

📈

Observabilidad Completa

Grafana, Prometheus, Kiali, TempoStack y OpenTelemetry para visibilidad total.

🌎

Multi-Cluster Federado

Topología Hub-and-Spoke con ACM, desplegado en clústeres East y West.

Recursos Relacionados

Recurso Descripción
Build a zero trust environment with Red Hat Connectivity Link Artículo en Red Hat Developer: arquitectura Zero Trust con OIDC/Keycloak y NeuralBank
Red Hat Connectivity Link — Documentación v1.3 Documentación oficial del producto
Red Hat Connectivity Link — Producto Página de producto con overview y casos de uso
Kuadrant — Documentación Documentación del proyecto upstream (AuthPolicy, RateLimitPolicy, DNSPolicy)
Kuadrant — Proyecto Sitio del proyecto open source
Getting Started with Connectivity Link on OpenShift Guía de inicio rápido en Red Hat Developer
OSSM3 Ambient Mode — Multi-Cluster Demo Repo de Francisco Raposo: Ansible playbooks para OSSM3, Bookinfo y observabilidad multi-cluster
Connectivity Link — Developer Hub Deployment Despliegue GitOps de RHDH en Red Hat Developer Sandbox con Kuadrant, Keycloak, MCP y RBAC
Stadium Wallet GitOps — Sitio de Documentación Guías operativas: arquitectura, getting started, despliegue ACM, gateway policies, scripts QA
RHBK NeuroFace Biometric Flow RHBK 26.0 con 2FA biométrico facial vía NeuroFace SPI — Helm chart, videos demo y arquitectura
NeuroFace — Servicio de Reconocimiento Facial Webapp de reconocimiento facial FastAPI + Angular 17 con OpenCV LBPH / dlib

2. Arquitectura y Flujos de Datos

2.1 Arquitectura de Tres Capas

La solución se estructura en un modelo moderno de tres capas:

Capa Componente Stack Tecnológico Función Escalabilidad
Frontend webapp (SPA) Vue 3, Vite, vue-router, Apache (UBI8 httpd-24) UI para login, consulta de saldos y generación de QR para pagos Stateless — HPA de OpenShift
Backend API 3 Microservicios independientes .NET 8.0 ASP.NET Core ApiCustomers (identidad), ApiWalletBuffaloBills (transacciones Bills), ApiWalletLasVegasRaiders (transacciones Raiders) Independientemente desplegables y escalables
Datos Almacenamiento persistente SQLite (customers.db, buffalobills.db, lasvegasraiders.db) Persistencia local por cada API Aislamiento estricto de datos

Nota de producción: Para despliegues productivos completos, las bases de datos SQLite deberían migrarse a soluciones de alta disponibilidad como PostgreSQL sobre OpenShift, potencialmente usando el operador Crunchy Data.

2.2 Diagrama de Arquitectura de Red y Service Mesh

graph TD
    subgraph Plano_de_Gestión["Plano de Gestión"]
        DevHub["Red Hat Developer Hub<br/>API Portal"]
        Argo["OpenShift GitOps<br/>Sincronización Continua"]
    end

    subgraph Cluster["OpenShift Cluster — Namespace: nfl-wallet"]
        GW["Gateway API / Kuadrant Ingress"]

        subgraph Mesh["OSSM3 Ambient Mesh — Zero-Trust"]
            Z["ztunnel<br/>L4 Secure Overlay / mTLS"]
            WP["Waypoint Proxy<br/>L7 Auth / Routing"]
            UI["webapp<br/>Vue.js :5173"]
            CAPI["api-customers<br/>.NET 8 :8080"]
            BAPI["api-bills<br/>.NET 8 :8080"]
            RAPI["api-raiders<br/>.NET 8 :8080"]
        end
    end

    subgraph Externos["Servicios Externos"]
        ESPN["API Pública de ESPN<br/>Scoreboards & Stats"]
    end

    User((Usuario Final)) --> GW
    Dev((Desarrollador)) --> DevHub
    Argo -- "Aplica Manifiestos" --> Cluster

    GW --> Z
    Z <--> UI
    UI -- "Llamadas API" --> Z
    Z <--> WP
    WP --> CAPI
    WP --> BAPI
    WP --> RAPI

    RAPI -- "Egress Traffic" --> ESPN
    BAPI -- "Egress Traffic" --> ESPN

2.3 Topología Multi-Cluster y Federación

El sistema utiliza un modelo Hub-and-Spoke, gobernado por las herramientas de plataforma y gestión de Red Hat:

graph TD
    subgraph Hub["Hub — OpenShift GitOps + ACM"]
        ACM_YAML["app-nfl-wallet-acm.yaml"]
        Placement["Placement<br/>nfl-wallet-gitops-placement"]
        GitOps["GitOpsCluster<br/>crea secrets east/west"]
        ACM_Decision["app-nfl-wallet-acm-cluster-decision.yaml"]
        AppSet["ApplicationSet — matrix<br/>clusterDecisionResource × list: dev, test, prod"]
        Apps["Applications:<br/>nfl-wallet-namespace-clusterName"]

        ACM_YAML --> Placement
        ACM_YAML --> GitOps
        ACM_Decision --> AppSet
        AppSet --> Apps
    end

    subgraph East["Cluster East"]
        E_Dev["nfl-wallet-dev"]
        E_Test["nfl-wallet-test"]
        E_Prod["nfl-wallet-prod"]
    end

    subgraph West["Cluster West"]
        W_Dev["nfl-wallet-dev"]
        W_Test["nfl-wallet-test"]
        W_Prod["nfl-wallet-prod"]
    end

    Apps --> East
    Apps --> West

2.4 Diagrama de Flujo de la Aplicación

sequenceDiagram
    participant U as User
    participant W as WebApp
    participant C as ApiCustomers
    participant B as ApiWalletBuffaloBills
    participant R as ApiWalletLasVegasRaiders

    U->>W: Open app (Home)
    W->>C: GET /api/Customers
    C-->>W: List of customers
    W-->>U: Show customer list

    U->>W: Click customer
    W->>C: GET /api/Customers/{id}
    W->>B: GET /api/Wallet/balance/{customerId}
    W->>B: GET /api/Wallet/transactions/{customerId}
    W->>R: GET /api/Wallet/balance/{customerId}
    W->>R: GET /api/Wallet/transactions/{customerId}

    C-->>W: Customer details
    B-->>W: Buffalo Bills balance + transactions
    R-->>W: Las Vegas Raiders balance + transactions

    W-->>U: Customer + Buffalo Bills + Las Vegas Raiders wallets

2.5 Flujo de Usuario

flowchart LR
    A[Landing: Customer list] --> B[Select customer]
    B --> C[Customer wallets page]
    C --> D[Buffalo Bills wallet]
    C --> E[Las Vegas Raiders wallet]
    D --> F[Balance & transactions]
    E --> F

2.6 Integración con API ESPN

Los microservicios api-bills y api-raiders requieren datos deportivos en tiempo real.


3. Stack Tecnológico

Componente Tecnología Propósito
Frontend Vue 3, Vite, vue-router SPA servida por Apache (UBI8)
Backend .NET 8.0 ASP.NET Core (x3) Microservicios: Customers, Bills, Raiders
Datos SQLite Una base de datos por API
Contenedores Podman / OpenShift Build e imágenes en Quay.io
Orquestación OpenShift 4.20+, Kubernetes Plataforma de contenedores
GitOps OpenShift GitOps (ArgoCD) Sincronización declarativa
Service Mesh OSSM 3.2 (Sail Operator, Ambient Mode) Zero-Trust, mTLS, L7 routing
Gateway Gateway API, Kuadrant Ingress, Rate Limiting, Auth
Observabilidad Prometheus, Grafana, Kiali, TempoStack, OpenTelemetry Métricas, trazas, topología
Multi-Cluster ACM (Advanced Cluster Management) Hub-and-Spoke, federación
Developer Portal Red Hat Developer Hub (RHDH) Catálogo de APIs, autoservicio

4. Prerrequisitos de Infraestructura

4.1 Requisitos del Clúster

Requisito Detalle Justificación
OpenShift Container Platform Versión 4.20 o superior, con privilegios cluster-admin Compatibilidad con OSSM 3.2 y las últimas políticas de Kuadrant
Topología Mínimo tres clústeres: Hub (ACM/GitOps), East (Workloads), West (Workloads) Validar la federación multi-cluster
SNO (Single Node OpenShift) Si se usa para PoC, aumentar maxPods (recomendado: 500 mínimo) Soportar la carga del Service Mesh y Kuadrant

4.2 Operadores Requeridos

Los siguientes operadores deben estar instalados y configurados por el administrador (Cluster Admin):

  1. OpenShift GitOps — Sincronización declarativa del repositorio
  2. OpenShift Service Mesh 3 (Sail Operator) — Plano de control de Istio Ambient Mode
  3. Gateway API Operator — Enrutamiento y exposición de servicios
  4. Kuadrant Operator — Rate Limiting y Auth Policies
  5. Red Hat Developer Hub (RHDH) — Portal de APIs con plugin de Kuadrant

4.3 Herramientas Locales

Herramienta Uso
oc CLI Login en los tres contextos de clúster
.NET 8.0 SDK + Node.js 20 Desarrollo local y validación pre-deploy
Podman Build, gestión y testing local de imágenes UBI8
Ansible Ejecución de playbooks de inicialización multi-cluster
Helm 3 Despliegue del chart nfl-wallet

5. Guía de Instalación GitOps

Por qué GitOps

Stadium Wallet adopta GitOps como modelo de despliegue porque resuelve problemas fundamentales de la operación de plataformas:

“Rather than manually configuring each component, you define the desired state in code, and GitOps ensures that state is achieved and maintained.”Build a zero trust environment with Red Hat Connectivity Link

La instalación se realiza de forma declarativa mediante OpenShift GitOps (ArgoCD), no mediante comandos imperativos.

5.1 Ejecución Local con Podman Compose

Para desarrollo local, el stack completo se ejecuta con Podman Compose:

# Desde la raíz del repositorio
podman-compose up -d --build

# Acceder a la aplicación
# http://localhost:5160

Servicios locales:

Podman Compose Ejecución del stack con Podman Compose: webapp y tres APIs en contenedores locales.

5.2 Desarrollo con Red Hat OpenShift Dev Spaces

El repositorio incluye un devfile.yaml para Red Hat OpenShift Dev Spaces, permitiendo desarrollar y testear en un IDE cloud sin instalar .NET ni Node.js localmente.

OpenShift Dev Spaces Workspace de OpenShift Dev Spaces con el proyecto Stadium Wallet.

Dev Spaces Build Build y ejecución en Dev Spaces: compilar e iniciar la webapp y las APIs desde el workspace.

Dev Spaces App Aplicación ejecutándose desde Dev Spaces: frontend y APIs servidos desde la nube.

5.3 Despliegue con Helm Chart

kubectl create namespace nfl-wallet

helm install nfl-wallet ./helm/nfl-wallet -n nfl-wallet

Valores del Chart (Referencia)

Clave Descripción Default
global.imageRegistry Registro de imágenes quay.io
imageNamespace Namespace del registro maximilianopizarro
apiCustomers.service.port Puerto del servicio 8080
apiBills.service.port Puerto del servicio 8081
apiRaiders.service.port Puerto del servicio 8082
webapp.service.port Puerto del servicio 5173
webapp.route.enabled Crear Route de OpenShift true
gateway.enabled Crear Gateway + HTTPRoutes false
gateway.className GatewayClass istio
apiKeys.enabled Crear Secret e inyectar API keys false
authorizationPolicy.enabled Istio AuthorizationPolicy para X-API-Key false
observability.rhobs.enabled Recursos RHOBS (ThanosQuerier, PodMonitor, UIPlugin) false
rhbk-neuroface.enabled Desplegar RHBK con autenticación biométrica NeuroFace 2FA false
webapp.keycloakUrl URL base de RHBK para Keycloak-js
webapp.keycloakRealm Nombre del realm de Keycloak neuroface
rhbk-neuroface.biometric.confidenceThreshold Confianza de reconocimiento facial % 65
rhbk-neuroface.biometric.maxEnrollmentImages Capturas de enrolamiento 5
rhbk-neuroface.biometric.cameraWidth Ancho de cámara px 640
rhbk-neuroface.biometric.cameraHeight Alto de cámara px 480
espn.apiKey Clave API de ESPN para gateway de Connectivity Link
espn.apiUrl Ruta proxy de ESPN /public/nfl
gateway.oidcPolicy Habilitar OIDC AuthPolicy por HTTPRoute (test) false

5.4 Aplicar la Application Root de ArgoCD

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: nfl-wallet-production
  namespace: openshift-gitops
spec:
  project: default
  source:
    repoURL: 'https://github.com/maximilianopizarro/nfl-wallet-gitops.git'
    targetRevision: HEAD
    path: helm/nfl-wallet
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: nfl-wallet
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

Una vez aplicado, ArgoCD desplegará los Deployments, Services, HTTPRoutes, y las políticas de Kuadrant de forma ordenada.

OpenShift Topology Vista de topología en OpenShift: webapp → api-customers, api-bills, api-raiders.


6. Service Mesh 3 (Ambient Mode)

OpenShift Service Mesh 3 (OSSM3) implementa un modelo de seguridad Zero Trust en la capa de red: cada conexión entre servicios se autentica y encripta automáticamente mediante mTLS, independientemente de su origen. El principio es “nunca confiar, siempre verificar” — ningún servicio puede comunicarse con otro sin presentar una identidad criptográfica válida emitida por la CA del mesh. Esto elimina la confianza implícita basada en la topología de red y proporciona defensa en profundidad contra movimiento lateral.

Lectura relacionada: El artículo Build a zero trust environment with Red Hat Connectivity Link profundiza en la integración de Service Mesh con Connectivity Link y Kuadrant para construir una arquitectura Zero Trust completa.

6.1 Modelo de Seguridad Zero-Sidecar

OSSM 3.2 en Ambient Mode separa las funciones de seguridad L4 y L7 en componentes especializados:

Componente Capa Función
ztunnel L4 Seguridad a nivel de nodo: mTLS para todo el tráfico East-West, telemetría L4, encriptación de transporte
Waypoint Proxy L7 Proxy Envoy dedicado por servicio: telemetría avanzada L7, routing HTTP complejo, control de acceso

Los Waypoints se despliegan estratégicamente para api-customers, api-bills y api-raiders sin inyectar sidecars en los pods.

Sidecar tradicional vs. Ambient Mode

En el modelo sidecar tradicional, cada pod recibe un contenedor istio-proxy inyectado automáticamente. Esto implica duplicar el consumo de memoria y CPU por cada workload, incrementar la latencia de startup, y complicar el debugging (cada pod tiene 2+ contenedores).

Ambient Mode elimina esta complejidad separando las responsabilidades:

Aspecto Sidecar Ambient
mTLS Proxy por pod ztunnel por nodo (DaemonSet)
Contenedores por pod 2+ (app + sidecar) 1 (solo app)
Overhead de memoria ~50-100 MB por sidecar Compartido por nodo
Políticas L7 Sidecar Envoy Waypoint Proxy (opcional, por servicio)
Complejidad operativa Alta (inyección, disruptions en rollouts) Baja (sin inyección, sin disruptions)

El resultado es la misma seguridad mTLS con menor overhead de recursos y menor complejidad operativa.

Impacto real en recursos

Los datos publicados por la comunidad Istio muestran el ahorro concreto que Ambient Mode aporta a escala:

“Ambient mode’s shared ztunnel uses about 1 GB of memory for 300 pods on 10 nodes. By contrast, sidecar mode deploys a proxy per pod, consuming approximately 21 GB of memory for the same 300 pods.”

Esto representa una reducción de ~95 % en consumo de memoria dedicada al mesh. Además, como ztunnel opera como DaemonSet (un proceso por nodo), el overhead no crece con el número de pods sino con el de nodos, haciendo Ambient Mode particularmente eficiente para plataformas con alta densidad de microservicios.

Métrica Sidecar (300 pods / 10 nodos) Ambient (300 pods / 10 nodos)
Memoria del mesh ~21 GB ~1 GB
Proxies desplegados 300 (uno por pod) 10 ztunnels + waypoints selectivos
Startup latency adicional Sí (inyección de sidecar) No

Fuente: Istio — Ambient Mode Overview · “Start with L4 security and selectively add L7 features only to services that need them.”

6.2 Enrolamiento en Ambient Mode

El namespace se inscribe en la malla mediante una etiqueta, aplicada automáticamente por ArgoCD:

apiVersion: v1
kind: Namespace
metadata:
  name: nfl-wallet
  labels:
    istio.io/dataplane-mode: ambient

Validación: Los pods de la aplicación NO tienen el contenedor istio-proxy, pero el tráfico se encripta mediante mTLS gestionado por el DaemonSet ztunnel.

Verificar que ztunnel está interceptando el tráfico del namespace:

# Confirmar que los pods NO tienen sidecar (1/1 containers)
oc get pods -n nfl-wallet -o custom-columns=NAME:.metadata.name,CONTAINERS:.spec.containers[*].name,READY:.status.containerStatuses[*].ready

# Verificar que ztunnel está activo y procesando tráfico
oc logs -n ztunnel -l app=ztunnel --tail=20 | grep "nfl-wallet"

# Confirmar identidad SPIFFE asignada a los workloads
oc exec -n ztunnel $(oc get pod -n ztunnel -l app=ztunnel -o name | head -1) -- curl -s localhost:15000/config_dump | grep "nfl-wallet"

6.3 Waypoint Proxy

El Waypoint Proxy se despliega solo cuando se requieren políticas L7 (HTTP routing, AuthPolicy, telemetría avanzada). Si un servicio solo necesita mTLS (L4), ztunnel es suficiente y no se requiere Waypoint — esto reduce el consumo de recursos.

Cuándo usar cada componente:

Necesidad Componente Ejemplo en Stadium Wallet
mTLS + telemetría básica ztunnel (L4) Comunicación webapp ↔ apis
AuthPolicy / RateLimitPolicy Waypoint (L7) Validación de API Key en api-customers
Routing HTTP avanzado Waypoint (L7) URL rewrite en HTTPRoutes
Trazas distribuidas (spans L7) Waypoint (L7) Spans en Jaeger/Tempo

El Waypoint se integra nativamente con las políticas de Kuadrant: cuando una AuthPolicy o RateLimitPolicy referencia un HTTPRoute, el Waypoint es el componente que ejecuta la validación L7 en coordinación con Authorino y Limitador.

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: nfl-wallet-waypoint
  namespace: nfl-wallet
  labels:
    istio.io/waypoint-for: service
spec:
  gatewayClassName: istio-waypoint
  listeners:
  - name: mesh
    port: 15008
    protocol: HBONE

6.4 Federación y Trust

La federación multi-cluster establece un dominio de confianza unificado entre los clústeres East y West. El proceso se basa en tres pilares:

Referencia: El repo ossm3-ambient-mode contiene los scripts de Ansible para automatizar la generación de la CA compartida, el intercambio de remote secrets y la configuración de meshNetworks entre clústeres.


7. Connectivity Link y Gateway API

Red Hat Connectivity Link es un framework Kubernetes-native que unifica Gateway API, gestión de políticas (autenticación, rate limiting) y DNS en una experiencia declarativa. Basado en el proyecto upstream Kuadrant, Connectivity Link permite definir políticas de conectividad como CRDs que se aplican automáticamente al Gateway, eliminando la necesidad de configurar proxies, rate limiters y auth servers de forma manual.

En el contexto de Stadium Wallet, Connectivity Link orquesta:

Documentación oficial:

Por qué Gateway API y no Ingress tradicional

La migración de Ingress a Gateway API no es una decisión estética — es una necesidad operativa con un horizonte concreto:

“Kuadrant extends Gateway API to add a connectivity management API that makes it easy for platform engineers and application developers to collaborate on connectivity concerns.”kuadrant.io

La ventaja fundamental de Gateway API es la separación de responsabilidades mediante CRDs formales: el equipo de plataforma controla el Gateway, los equipos de desarrollo controlan sus HTTPRoute, y las políticas de seguridad se aplican como attachments independientes. Esto elimina la necesidad de coordinar annotations en un único recurso Ingress compartido.

Fuentes: Kubernetes Gateway API · Introducing ingress2gateway · Red Hat Connectivity Link — Now GA

7.1 Ingress con HTTPRoute

La Kubernetes Gateway API es el estándar que reemplaza al recurso Ingress tradicional. Su principal ventaja es la separación de responsabilidades: el equipo de infraestructura define el recurso Gateway (listeners, protocolos, certificados), mientras que los equipos de desarrollo definen sus propios HTTPRoute (paths, backends, rewrites). Esta separación se formaliza mediante los CRDs:

CRD Responsable Función
GatewayClass Proveedor (Istio/Envoy) Define el controlador que implementa el Gateway
Gateway Platform Engineer Listeners (puertos, protocolos, TLS), políticas globales
HTTPRoute Developer Routing por path/header, backends, URL rewrite
ReferenceGrant Platform Engineer Autoriza referencias cross-namespace
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: nfl-wallet-frontend
spec:
  parentRefs:
  - name: nfl-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: webapp
      port: 5173

Se crean cuatro HTTPRoutes: webapp (/), api-customers (/api-customers), api-bills (/api-bills), api-raiders (/api-raiders), con URL rewrite al backend.

7.2 Habilitar Gateway

El Gateway define los listeners que aceptan tráfico externo. En Stadium Wallet, el Helm chart crea un Gateway con listener HTTP que es gestionado por el controlador Istio/Envoy de Connectivity Link:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: nfl-wallet-gateway
  namespace: nfl-wallet
spec:
  gatewayClassName: openshift-gateway
  listeners:
  - name: http
    port: 8080
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: Same

Despliegue via Helm:

helm install nfl-wallet ./helm/nfl-wallet -n nfl-wallet \
  --set gateway.enabled=true \
  --set gateway.className=openshift-gateway

7.3 Rate Limiting con Kuadrant

Kuadrant implementa rate limiting mediante dos componentes: la RateLimitPolicy (CRD declarativo que define las reglas) y Limitador (servicio que mantiene los contadores en memoria y evalúa las cuotas). El flujo de enforcement es:

  1. Un request llega al Gateway (Envoy)
  2. Envoy consulta a Limitador con los descriptores definidos en la RateLimitPolicy
  3. Limitador evalúa los contadores (ventana de tiempo + límite) y responde allow/deny
  4. Si el límite se excede, el Gateway retorna 429 Too Many Requests antes de que el request llegue al backend
apiVersion: kuadrant.io/v1beta2
kind: RateLimitPolicy
metadata:
  name: api-customers-limit
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: api-customers-route
  limits:
    "customer-api-standard":
      rates:
      - limit: 100
        duration: 1
        unit: minute

Tiers de Rate Limiting

Stadium Wallet define tres tiers de acceso que se aplican mediante PlanPolicy en combinación con el plugin de Kuadrant en RHDH:

Tier Límite Caso de Uso
Bronze 100 req/día Evaluación y desarrollo
Silver 500 req/día Aplicaciones en testing
Gold 1000 req/día Producción

Habilitar Rate Limiting + Auth:

helm upgrade nfl-wallet ./helm/nfl-wallet -n nfl-wallet \
  --set gateway.enabled=true \
  --set gateway.rateLimitPolicy.enabled=true \
  --set gateway.authPolicy.enabled=true \
  --set gateway.authPolicy.bills.enabled=true

Connectivity Link Connectivity Link: Gateway API y HTTPRoutes exponiendo webapp y APIs.

Connectivity Link con Auth Connectivity Link con Kuadrant AuthPolicy (X-API-Key) y RateLimitPolicy en /api-bills.


8. Seguridad: API Keys y Políticas

La seguridad en Stadium Wallet se implementa en múltiples capas, siguiendo el principio de defensa en profundidad. El flujo end-to-end de un request autenticado es:

  1. El request llega al Gateway (Envoy gestionado por Connectivity Link)
  2. Authorino intercepta el request y busca credenciales: extrae el header X-Api-Key y lo compara contra los Secrets de Kubernetes que tienen el label api: nfl-wallet-prod
  3. Si la credencial es válida, OPA evalúa las reglas Rego definidas en la AuthPolicy
  4. Limitador verifica que el consumidor no haya excedido su cuota (según su Tier: bronze/silver/gold)
  5. Si todas las validaciones pasan, el request se forwarded al backend con mTLS (ztunnel/Waypoint)
  6. Si falla autenticación → 403 Forbidden; si falla cuota → 429 Too Many Requests

Modelos de autenticación soportados por Connectivity Link: Stadium Wallet utiliza API Keys como mecanismo de autenticación. Connectivity Link también soporta OIDC/OAuth2 con proveedores como Red Hat build of Keycloak, como se demuestra en el artículo Build a zero trust environment with Red Hat Connectivity Link con la aplicación NeuralBank. Ambos modelos son complementarios y pueden coexistir en el mismo clúster.

8.1 Dos Modelos de Seguridad

Modelo Ubicación CRD Caso de Uso
Istio AuthorizationPolicy Service Mesh (workload) security.istio.io/v1 Validación directa en el pod
AuthPolicy con Authorino Gateway (Kuadrant) kuadrant.io/v1 Validación en el gateway con 403 personalizado

8.2 AuthorizationPolicy de Istio

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: api-raiders-require-apikey
spec:
  selector:
    matchLabels:
      app: api-raiders
  action: ALLOW
  rules:
    - when:
        - key: request.headers[x-api-key]
          values: ["*"]

8.3 AuthPolicy de Kuadrant (Gateway)

La AuthPolicy es el CRD de Kuadrant que define reglas de autenticación y autorización a nivel de Gateway o HTTPRoute. Internamente, Kuadrant delega la ejecución a Authorino, que actúa como servidor de autorización externo (ext-authz) integrado con Envoy.

Cómo Authorino descubre credenciales: Authorino busca Secrets de Kubernetes que contengan labels específicos (por ejemplo api: nfl-wallet-prod). Cuando un request incluye el header X-Api-Key, Authorino compara el valor contra todos los Secrets que matchean el label selector. Este mecanismo permite aprovisionar y revocar API Keys de forma dinámica sin reiniciar ningún componente — basta con crear o eliminar un Secret con el label correspondiente.

apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: nfl-wallet-api-bills-auth
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: nfl-wallet-api-bills
  rules:
    authorization:
      require-apikey:
        opa:
          rego: |
            allow = true {
              input.context.request.http.headers["x-api-key"] != ""
            }
    response:
      unauthorized:
        body:
          value: '{"error":"Forbidden","message":"Missing or invalid X-API-Key header."}'
        headers:
          content-type:
            value: application/json

8.4 Seguridad por Ambiente

Stadium Wallet implementa una estrategia de seguridad progresiva donde cada ambiente incrementa el nivel de protección. Esto permite iteración rápida en desarrollo, validación de integración en test, y Zero Trust completo en producción:

Ambiente API Key AuthPolicy Login Biométrico Modelo de Mesh
Dev No requerido Sin autenticación RHBK + NeuroFace (chart 0.1.3) Sidecar mode (istio-injection: enabled)
Test nfl-wallet-customers-key AuthPolicy + API keys + OIDC policy RHBK + NeuroFace (chart 0.1.3) Ambient mode (istio.io/dataplane-mode: ambient)
Prod nfl-wallet-customers-key AuthPolicy + API keys + canary route — (chart 0.1.1) Ambient mode + Waypoint proxies

Autenticación Biométrica (RHBK + NeuroFace)

La versión 0.1.3 del chart incluye Red Hat build of Keycloak (RHBK) con autenticación biométrica facial NeuroFace como dependencia opcional de Helm. Los usuarios se autentican con usuario/contraseña seguidos de reconocimiento facial como segundo factor (2FA).

El chart precarga el realm con 7 cuentas de cliente de prueba (alineadas con los datos semilla de la API Customers). En el primer inicio de sesión cada usuario debe completar el enrolamiento biométrico (captura facial). Los inicios posteriores usan verificación facial como segundo factor.

Arquitectura del Componente

El sistema biométrico consiste en dos contenedores desplegados en el mismo namespace, conectados mediante un SPI de Keycloak personalizado:

┌─────────────────────────────────┐     ┌──────────────────────────────────┐
│  RHBK (Keycloak 26 — UBI9)     │     │  NeuroFace Backend (FastAPI)     │
│                                 │     │                                  │
│  ┌───────────────────────────┐  │     │  POST /api/images   ← enrollment│
│  │ Biometric SPI (JAR)       │  │     │  POST /api/train    ← training  │
│  │                           │──┼─────┼─►POST /api/recognize ← verify   │
│  │ • BiometricAuthenticator  │  │     │  GET  /api/health   ← health    │
│  │   (2FA facial login)      │  │     │  GET  /api/labels   ← labels    │
│  │                           │  │     │                                  │
│  │ • BiometricEnrollment     │  │     └──────────────────────────────────┘
│  │   (delegated registration)│  │
│  └───────────────────────────┘  │     ┌──────────────────────────────────┐
│                                 │     │  NeuroFace Frontend (Angular 17) │
│  Realm: neuroface               │     │  ← Protected by OIDC client     │
│  Client: neuroface-app          │     │     "neuroface-app"              │
│  Flow: biometric browser        │     └──────────────────────────────────┘
│  Flow: biometric registration   │
└─────────────────────────────────┘

Componentes SPI

La capacidad biométrica se implementa como un SPI de Keycloak (Service Provider Interface) empaquetado como JAR desplegado en la imagen RHBK:

Proveedor Tipo ID Descripción
BiometricAuthenticator Authenticator biometric-authenticator 2FA vía NeuroFace /api/recognize
BiometricEnrollment Required Action biometric-enrollment Enrolamiento facial multi-imagen en primer login
NeuroFaceClient Interno Cliente HTTP para la API REST de NeuroFace

Configuración del Realm

Componente Detalles
Clientes neuroface-app (público, PKCE S256), neuroface-backend (bearer-only)
Flujo Browser biometric browser — cookie O (contraseña + 2FA facial)
Flujo Registro biometric registration — creación delegada
Acción Requerida biometric-enrollment — enrolamiento facial en primer login
Roles biometric-user, biometric-admin
Grupo biometric-enrolled — asignado automáticamente tras enrolamiento

Endpoints API de NeuroFace

NeuroFace es un servicio de reconocimiento facial construido con FastAPI y Angular 17, containerizado con imágenes certificadas Red Hat UBI9. El SPI se comunica con estos endpoints:

Endpoint Método Uso
/api/health GET Health check antes de operaciones biométricas
/api/images POST Subir imágenes faciales durante enrolamiento (multipart)
/api/train POST Entrenar el modelo de reconocimiento tras enrolamiento
/api/recognize POST Verificar identidad facial durante login 2FA
/api/labels GET Listar etiquetas biométricas registradas

Flujos de Autenticación

1. Creación Delegada con Enrolamiento Biométrico:

sequenceDiagram
    participant Admin as KC Admin
    participant User as Usuario
    participant RHBK as RHBK (Keycloak)
    participant SPI as Biometric SPI
    participant NF as NeuroFace API

    Admin->>RHBK: Crear usuario + asignar Required Action<br/>"biometric-enrollment"
    User->>RHBK: Primer login con credenciales temporales
    RHBK->>SPI: Disparar enrolamiento biométrico
    SPI->>User: Webcam: capturar 3–5 imágenes<br/>desde diferentes ángulos
    User-->>SPI: Imágenes faciales (base64)
    SPI->>NF: POST /api/images (label=username)
    SPI->>NF: POST /api/train
    NF-->>SPI: Modelo entrenado
    SPI->>RHBK: biometric_enrolled = true<br/>Usuario se une al grupo "biometric-enrolled"

2. Login con Segundo Factor Biométrico (2FA):

sequenceDiagram
    participant User as Usuario
    participant RHBK as RHBK (Keycloak)
    participant SPI as Biometric SPI
    participant NF as NeuroFace API

    User->>RHBK: Usuario + Contraseña
    RHBK->>SPI: Disparar verificación 2FA
    SPI->>User: Webcam: capturar imagen facial
    User-->>SPI: Imagen facial (base64)
    SPI->>NF: POST /api/recognize { image: base64 }
    NF-->>SPI: { label, confidence }
    alt label == username AND confidence >= threshold
        SPI->>RHBK: Verificación OK
        RHBK-->>User: Acceso concedido
    else Sin coincidencia o confianza baja
        SPI->>RHBK: Verificación fallida
        RHBK-->>User: Acceso denegado
    end

Despliegue

helm install nfl-wallet ./helm/nfl-wallet -n nfl-wallet \
  --set "rhbk-neuroface.enabled=true" \
  --set "webapp.keycloakUrl=https://<release>-rhbk-neuroface-<namespace>.apps.<cluster>"

Nota: keycloakUrl debe ser la URL base de RHBK sin /realms/<nombre> — keycloak-js añade automáticamente la ruta del realm a partir de webapp.keycloakRealm (por defecto neuroface).

Valores del Helm Chart

RHBK (Keycloak):

Parámetro Por defecto Descripción
rhbk.image.repository registry.redhat.io/rhbk/keycloak-rhel9 Imagen RHBK
rhbk.image.tag 26.0 Tag de imagen
rhbk.replicas 1 Réplicas
rhbk.resources.limits.cpu 1 Límite CPU
rhbk.resources.limits.memory 1Gi Límite memoria
admin.username / admin.password admin / admin Credenciales admin iniciales

Configuración Biométrica:

Parámetro Descripción Por defecto
biometric.confidenceThreshold Confianza mínima de coincidencia facial (0–100) 65
biometric.maxEnrollmentImages Número de capturas de enrolamiento 5
biometric.webcamWidth × webcamHeight Resolución de cámara (px) 640 × 480

Presets de resolución de cámara: QVGA 320×240, VGA 640×480 (por defecto), HD 720p 1280×720, Full HD 1920×1080. Mayor resolución mejora la precisión pero aumenta el tiempo de procesamiento. El ApplicationSet habilita el login biométrico en dev y test con 1920 × 1080 (FHD).

Subchart NeuroFace:

Parámetro Por defecto Descripción
neuroface.enabled true Desplegar subchart NeuroFace
neuroface.backend.aiModel lbph Modelo IA (lbph / dlib)
neuroface.backend.replicas 1 Réplicas del backend
neuroface.frontend.replicas 1 Réplicas del frontend
neuroface.chat.enabled true Habilitar chat IA (Granite LLM)
neuroface.persistence.enabled true Habilitar almacenamiento persistente
neuroface.persistence.size 1Gi Tamaño del PVC
neuroface.route.enabled true Crear Route de OpenShift para NeuroFace

Fuente: RHBK NeuroFace Biometric Flow — Documentación completa, videos demo, capturas y referencia del Helm chart. Charts disponibles en Artifact Hub (rhbk-neuroface) y Artifact Hub (neuroface).

Política OIDC (Ambiente Test)

En test, el chart activa gateway.oidcPolicy. Esto crea objetos AuthPolicy de Kuadrant (uno por HTTPRoute de API) que validan tokens JWT OIDC emitidos por el realm de RHBK. Las políticas OIDC apuntan a HTTPRoutes individuales (api-customers, api-bills, api-raiders) y conviven con la AuthPolicy de API key existente en el Gateway.

La URL del emisor OIDC sigue el patrón:

https://nfl-wallet-rhbk-neuroface-nfl-wallet-test.apps.<cluster-domain>/realms/neuroface

8.5 Restricción de Acceso entre Namespaces (Test / Prod)

En un entorno multi-ambiente sobre el mismo clúster, es crítico que los servicios de test no puedan acceder a los de prod y viceversa. OSSM3 en Ambient Mode proporciona este aislamiento a través de AuthorizationPolicy de Istio a nivel de namespace.

Principio de aislamiento

Cada namespace (nfl-wallet-test, nfl-wallet-prod) aplica una AuthorizationPolicy que solo permite tráfico originado desde el mismo namespace y desde el sistema de mesh (gateways, waypoints):

# Restricción de acceso: solo tráfico del mismo namespace en PROD
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: restrict-cross-namespace
  namespace: nfl-wallet-prod
spec:
  action: ALLOW
  rules:
  # Permitir tráfico desde el mismo namespace
  - from:
    - source:
        namespaces: ["nfl-wallet-prod"]
  # Permitir tráfico desde el gateway/waypoint de Istio
  - from:
    - source:
        namespaces: ["istio-system"]
  # Permitir tráfico desde el ztunnel (ambient mode)
  - from:
    - source:
        namespaces: ["ztunnel"]
# Restricción equivalente para TEST
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: restrict-cross-namespace
  namespace: nfl-wallet-test
spec:
  action: ALLOW
  rules:
  - from:
    - source:
        namespaces: ["nfl-wallet-test"]
  - from:
    - source:
        namespaces: ["istio-system"]
  - from:
    - source:
        namespaces: ["ztunnel"]

Resultado del aislamiento

Origen → Destino Permitido Mecanismo
nfl-wallet-test → nfl-wallet-test Si Same-namespace rule
nfl-wallet-prod → nfl-wallet-prod Si Same-namespace rule
nfl-wallet-test → nfl-wallet-prod No Bloqueado por AuthorizationPolicy
nfl-wallet-prod → nfl-wallet-test No Bloqueado por AuthorizationPolicy
nfl-wallet-dev → nfl-wallet-prod No Bloqueado por AuthorizationPolicy
istio-system → nfl-wallet-prod Si Gateway/Waypoint ingress
External (via Gateway) → nfl-wallet-prod Si Tráfico entra por istio-system

Aplicación via Kustomize

Las AuthorizationPolicy se incluyen en cada overlay de Kustomize:

nfl-wallet/overlays/test/restrict-cross-namespace.yaml
nfl-wallet/overlays/prod/restrict-cross-namespace.yaml
nfl-wallet/overlays/test-east/restrict-cross-namespace.yaml
nfl-wallet/overlays/prod-west/restrict-cross-namespace.yaml

ArgoCD sincroniza estas políticas automáticamente al desplegar cada ambiente.

Dev sin restricción: El ambiente dev (nfl-wallet-dev) intencionalmente no aplica esta restricción para facilitar el desarrollo y debugging cross-service.

8.5.1 Políticas entre Namespaces con Gateway API e Istio

Además de las AuthorizationPolicy a nivel de workload, la Gateway API e Istio ofrecen mecanismos adicionales para controlar el tráfico entre namespaces. Estas opciones operan a diferentes niveles (L4/L7) y ofrecen granularidad distinta.

Opción 1: ReferenceGrant (Gateway API nativo)

El recurso ReferenceGrant (antes ReferencePolicy) de la Gateway API controla qué namespaces pueden referenciar recursos de otro namespace. Esto es útil para restringir qué HTTPRoutes pueden apuntar a Services en otro namespace:

# Permitir que SOLO las HTTPRoutes de nfl-wallet-prod
# referencien el gateway en istio-system
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-prod-to-gateway
  namespace: istio-system
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: nfl-wallet-prod
  to:
  - group: ""
    kind: Service

Sin un ReferenceGrant correspondiente, las HTTPRoutes de nfl-wallet-test no pueden referenciar el Gateway de otro namespace, lo que impide la exposición accidental de rutas entre ambientes.

Opción 2: Istio PeerAuthentication (mTLS estricto por namespace)

PeerAuthentication fuerza mTLS estricto en un namespace, lo cual garantiza que solo pods con identidad SPIFFE válida del mismo trust domain puedan comunicarse:

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: strict-mtls
  namespace: nfl-wallet-prod
spec:
  mtls:
    mode: STRICT

Combinado con AuthorizationPolicy, esto asegura que incluso si un pod malicioso intenta enviar tráfico, el ztunnel rechazará la conexión si no tiene un certificado mTLS válido del namespace permitido.

Opción 3: Sidecar Resource (control de egress por namespace)

El recurso Sidecar de Istio (también funcional en Ambient Mode a través de Waypoints) limita los hosts a los que un namespace puede enviar tráfico saliente:

apiVersion: networking.istio.io/v1
kind: Sidecar
metadata:
  name: restrict-egress
  namespace: nfl-wallet-test
spec:
  egress:
  - hosts:
    # Solo puede comunicarse con servicios en su propio namespace
    - "./nfl-wallet-test/*"
    # Y con el sistema Istio
    - "istio-system/*"

Esto previene que los servicios de test descubran o intenten conectarse a servicios de producción, ya que no serán visibles en su service registry.

Opción 4: Gateway Listeners con allowedRoutes (namespace scoping)

Los Listeners del Gateway pueden restringir qué namespaces pueden crear HTTPRoutes que lo referencien:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: nfl-wallet-gateway
  namespace: nfl-wallet-prod
spec:
  gatewayClassName: istio
  listeners:
  - name: prod-listener
    port: 443
    protocol: HTTPS
    tls:
      mode: Terminate
      certificateRefs:
      - name: prod-tls-cert
    allowedRoutes:
      namespaces:
        from: Same                   # Solo HTTPRoutes del mismo namespace
# Gateway compartido que acepta rutas de namespaces específicos
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shared-gateway
  namespace: istio-system
spec:
  gatewayClassName: istio
  listeners:
  - name: prod-only
    port: 443
    protocol: HTTPS
    hostname: "*.prod.nfl-wallet.com"
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            environment: production  # Solo namespaces con este label
  - name: test-only
    port: 443
    protocol: HTTPS
    hostname: "*.test.nfl-wallet.com"
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            environment: test

Opción 5: Kuadrant RateLimitPolicy por namespace

Kuadrant permite aplicar RateLimitPolicy directamente al Gateway, con límites diferenciados por namespace de origen. Esto evita que un ambiente monopolice los recursos compartidos:

apiVersion: kuadrant.io/v1beta2
kind: RateLimitPolicy
metadata:
  name: per-namespace-limits
  namespace: istio-system
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: shared-gateway
  limits:
    "test-namespace-limit":
      rates:
      - limit: 50
        duration: 1
        unit: minute
      when:
      - selector: metadata.filter_metadata.istio_authn.source.namespace
        operator: eq
        value: nfl-wallet-test
    "prod-namespace-limit":
      rates:
      - limit: 500
        duration: 1
        unit: minute
      when:
      - selector: metadata.filter_metadata.istio_authn.source.namespace
        operator: eq
        value: nfl-wallet-prod

Comparación de Opciones

Mecanismo Capa Qué Controla Cuándo Usar
AuthorizationPolicy L4/L7 Quién puede enviar tráfico a un workload Aislamiento básico entre namespaces
ReferenceGrant API Qué namespaces pueden crear rutas hacia un Gateway/Service Controlar qué ambientes usan qué gateways
PeerAuthentication L4 Requiere mTLS estricto para todo tráfico Garantizar identidad criptográfica
Sidecar (egress) L7 A qué hosts puede enviar tráfico un namespace Limitar el descubrimiento de servicios
allowedRoutes API Qué namespaces pueden crear HTTPRoutes en un listener Scoping de gateways compartidos
RateLimitPolicy L7 Cuántas requests por namespace Prevenir que un ambiente abuse del gateway

Recomendación: Para Stadium Wallet, se combinan AuthorizationPolicy (aislamiento de workloads), PeerAuthentication STRICT (mTLS obligatorio), y allowedRoutes en el Gateway (scoping de rutas por namespace). Esta combinación provee defensa en profundidad.

8.6 Failover Multi-Cluster con DNSPolicy y Route 53

Para lograr alta disponibilidad geográfica y failover automático entre los clústeres East y West, Kuadrant integra DNSPolicy con Amazon Route 53 (u otros proveedores DNS compatibles). Esto permite que, si un clúster falla, el tráfico se redirija automáticamente al clúster sano.

Arquitectura del Failover DNS

graph TD
    DNS["Route 53 — DNS<br/>stadium-wallet.example.com<br/>Routing Policy: Failover / Weighted"]

    DNS -- "Health Check East" --> East
    DNS -- "Health Check West" --> West

    subgraph East["Cluster East — Primary"]
        E_GW["nfl-wallet-gateway-istio"]
        E_Web["webapp"]
        E_Cust["api-customers"]
        E_Bills["api-bills"]
        E_Raiders["api-raiders"]
        E_GW --> E_Web
        E_GW --> E_Cust
        E_GW --> E_Bills
        E_GW --> E_Raiders
    end

    subgraph West["Cluster West — Secondary"]
        W_GW["nfl-wallet-gateway-istio"]
        W_Web["webapp"]
        W_Cust["api-customers"]
        W_Bills["api-bills"]
        W_Raiders["api-raiders"]
        W_GW --> W_Web
        W_GW --> W_Cust
        W_GW --> W_Bills
        W_GW --> W_Raiders
    end

Definición de DNSPolicy con Kuadrant

Kuadrant proporciona el CRD DNSPolicy que se vincula al Gateway y gestiona automáticamente los registros DNS:

apiVersion: kuadrant.io/v1
kind: DNSPolicy
metadata:
  name: nfl-wallet-dns-failover
  namespace: nfl-wallet-prod
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: nfl-wallet-gateway
  providerRefs:
  - name: aws-route53-credentials    # Secret con credenciales de Route 53
  routingStrategy: loadbalanced       # Estrategia: loadbalanced o simple
  loadBalancing:
    geo: us-east-1                    # Región geográfica de este clúster
    defaultGeo: true                  # Este es el default si la geo no matchea
    weight: 120                       # Peso relativo para weighted routing

Configuración del Provider (Route 53)

El Secret con credenciales de AWS para Route 53:

apiVersion: v1
kind: Secret
metadata:
  name: aws-route53-credentials
  namespace: nfl-wallet-prod
type: Opaque
data:
  AWS_ACCESS_KEY_ID: <base64>
  AWS_SECRET_ACCESS_KEY: <base64>
  AWS_REGION: <base64>               # us-east-1

Estrategias de Routing DNS

Estrategia Comportamiento Caso de Uso
simple Un solo registro A/CNAME Un solo clúster, sin failover
loadbalanced Múltiples registros con health checks Multi-cluster con failover automático

Failover con Health Checks

Cuando se usa routingStrategy: loadbalanced, Kuadrant configura automáticamente:

  1. Health Checks de Route 53: Verifican que el endpoint del Gateway responda en cada clúster
  2. Registros DNS ponderados: Distribuyen tráfico entre East y West según los pesos configurados
  3. Failover automático: Si el health check de East falla, Route 53 deja de resolver hacia East y envía todo el tráfico a West
# DNSPolicy para Cluster East (Primary)
apiVersion: kuadrant.io/v1
kind: DNSPolicy
metadata:
  name: nfl-wallet-dns-east
  namespace: nfl-wallet-prod
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: nfl-wallet-gateway
  providerRefs:
  - name: aws-route53-credentials
  routingStrategy: loadbalanced
  loadBalancing:
    geo: us-east-1
    defaultGeo: true
    weight: 120
# DNSPolicy para Cluster West (Secondary)
apiVersion: kuadrant.io/v1
kind: DNSPolicy
metadata:
  name: nfl-wallet-dns-west
  namespace: nfl-wallet-prod
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: nfl-wallet-gateway
  providerRefs:
  - name: aws-route53-credentials
  routingStrategy: loadbalanced
  loadBalancing:
    geo: us-west-2
    defaultGeo: false
    weight: 80

Resultado: Resolución DNS

Escenario East Health West Health DNS Resolución
Normal Healthy Healthy 60% East / 40% West (por pesos 120:80)
East Down Unhealthy Healthy 100% West (failover automático)
West Down Healthy Unhealthy 100% East
Ambos Down Unhealthy Unhealthy Sin resolución (alerta)

Nota: DNSPolicy requiere que el operador de Kuadrant tenga acceso a la API de Route 53 (o el proveedor DNS configurado). Las credenciales deben manejarse con External Secrets Operator o Sealed Secrets en producción.


9. GitOps Multi-Cluster con ACM

Por qué Red Hat Advanced Cluster Management

En un entorno de producción real, una sola instancia de OpenShift no es suficiente. Los requisitos de alta disponibilidad, localidad de datos y cumplimiento regulatorio exigen distribuir workloads en múltiples clústeres. Sin embargo, gestionar N clústeres de forma independiente multiplica la complejidad operativa: N conjuntos de políticas, N configuraciones de red, N despliegues manuales.

Red Hat Advanced Cluster Management (ACM) resuelve esto con un modelo Hub-and-Spoke:

En Stadium Wallet, ACM genera automáticamente 6 Applications de ArgoCD (dev/test/prod × east/west) a partir de un único ApplicationSet con clusterDecisionResource, garantizando que cualquier cambio en Git se propague de forma idéntica a todos los clústeres.

Fuente: Red Hat Advanced Cluster Management for Kubernetes

9.1 Modos de Despliegue

Con ACM (Hub + Managed Clusters East/West)

# 1. Placements + GitOpsCluster (crea secrets east/west en ArgoCD)
kubectl apply -f app-nfl-wallet-acm.yaml -n openshift-gitops

# 2. ApplicationSet (genera 6 Applications)
kubectl apply -f app-nfl-wallet-acm-cluster-decision.yaml -n openshift-gitops

Sin ACM (East/West independientes)

kubectl apply -f app-nfl-wallet-east.yaml -n openshift-gitops
kubectl apply -f app-nfl-wallet-west.yaml -n openshift-gitops

9.2 Ambientes, Versiones del Chart y Namespaces

Cada Application despliega dos fuentes: (1) overlays de Kustomize (namespace, Route, AuthPolicy, Secrets) y (2) el Helm chart Stadium Wallet del HelmChartRepository.

Ambiente Namespace Versión del Chart Características
Dev nfl-wallet-dev 0.1.3 Gateway route + login biométrico RHBK (NeuroFace)
Test nfl-wallet-test 0.1.3 Gateway + AuthPolicy + API keys + ruta ESPN + login biométrico RHBK + OIDC policy
Prod nfl-wallet-prod 0.1.1 Gateway + canary + AuthPolicy + API keys (sin login biométrico)

9.3 Estructura de Overlays Kustomize

Path Uso
nfl-wallet/overlays/dev Dev single-cluster
nfl-wallet/overlays/test Test single-cluster
nfl-wallet/overlays/prod Prod single-cluster
nfl-wallet/overlays/dev-east ACM: dev en east
nfl-wallet/overlays/dev-west ACM: dev en west
nfl-wallet/overlays/test-east ACM: test en east
nfl-wallet/overlays/test-west ACM: test en west
nfl-wallet/overlays/prod-east ACM: prod en east
nfl-wallet/overlays/prod-west ACM: prod en west

9.4 Estructura del Repositorio GitOps

.
├── app-nfl-wallet-acm.yaml                   # Placements + GitOpsCluster (ACM)
├── app-nfl-wallet-acm-cluster-decision.yaml  # ApplicationSet (list generator)
├── app-nfl-wallet-east.yaml                  # ApplicationSet east (sin ACM)
├── app-nfl-wallet-west.yaml                  # ApplicationSet west (sin ACM)
├── kuadrant.yaml                             # Kuadrant CR
├── nfl-wallet/                               # Kustomize (routes, AuthPolicy, API keys)
│   ├── base/                                 # gateway route
│   ├── base-canary/                          # canary route (prod)
│   └── overlays/                             # dev, test, prod + east/west
├── kuadrant-system/                           # Patches de recursos para Authorino/Limitador
├── nfl-wallet-observability/                 # Grafana + ServiceMonitors
├── observability/                            # Grafana Operator base
├── developer-hub/catalog/nfl-wallet/         # Catálogo Backstage (Domain, System, Components, APIs)
├── docs/                                     # Documentación
└── scripts/                                  # force-sync-apps, test-apis, etc.

9.5 Requisitos de Recursos de Kuadrant

Los recursos por defecto del operador (100m CPU / 32Mi RAM) provocan latencia superior a 20 s en la llamada ext-authz del gateway a Authorino, especialmente en sandboxes con mTLS habilitado. El ApplicationSet kuadrant-resources despliega parches de recursos en ambos clústeres mediante ServerSideApply:

kubectl apply -f app-kuadrant-resources.yaml -n openshift-gitops
Componente CPU Request Memoria Request CPU Limit Memoria Limit
Authorino 500m 256Mi 2 1Gi
Limitador 250m 128Mi 1 256Mi

Los recursos están en kuadrant-system/ (Kustomize). ArgoCD usa selfHeal: true para reaplicarlos si los operadores los restablecen.

Proxy del Gateway: El Deployment nfl-wallet-gateway-istio lo rastrea la Application de ArgoCD de nfl-wallet (Istio lo crea a partir del Gateway del Helm chart). Incluirlo en kuadrant-resources provoca SharedResourceWarning. Si los recursos del proxy del gateway son insuficientes, aplicar manualmente: kubectl apply -f kuadrant-system/gateway-resources.yaml.

9.6 Pruebas Canary 0.1.3 en Prod

Para probar el chart 0.1.3 con login biométrico en un contexto de producción:

  1. Cambiar chartVersion de "0.1.1" a "0.1.3" para la entrada prod en el ApplicationSet
  2. Añadir los valores Helm de RHBK (copiar del bloque condicional dev/test)
  3. Hacer push y dejar que ArgoCD sincronice. Acceder a la URL canary para verificar el login biométrico
  4. Si se aprueba, mantener 0.1.3. Si no, revertir chartVersion a "0.1.1" y volver a hacer push

ArgoCD como motor de reconciliación: Las siguientes capturas muestran cómo ArgoCD gestiona las Applications generadas por el ApplicationSet. Cada Application corresponde a una combinación ambiente/clúster y se sincroniza de forma independiente, permitiendo rollbacks selectivos por ambiente sin afectar el resto.

OpenShift GitOps OpenShift GitOps (ArgoCD) — Applications y estado de sincronización.

GitOps Applications ArgoCD — Detalle de las Applications generadas por el ApplicationSet.

ACM como plano de control multi-cluster: ACM proporciona la vista unificada de todos los clústeres managed. El hub distribuye las políticas de red, seguridad y compliance a East y West de forma consistente, mientras que la Placement API decide dinámicamente dónde se despliega cada workload.

ACM Topology ACM — Topología con hub y managed clusters (East, West).

ACM Applications ACM — ApplicationSet y las 6 Applications generadas (dev/test/prod × east/west).

ACM Apps Overview ACM — Vista general de las aplicaciones desplegadas en los managed clusters.

9.7 Sitio de Documentación del Repositorio GitOps

El sitio de documentación de Stadium Wallet GitOps proporciona guías detalladas paso a paso para cada aspecto del despliegue GitOps. Complementa este documento con runbooks operativos e instrucciones específicas por ambiente.

Guías Disponibles

Guía Descripción
Architecture Placement, ApplicationSet matrix, topología multi-cluster (ACM e independiente)
Getting Started Prerequisitos, clonar, verificar Kustomize, desplegar east/west o ACM — guía de 10 pasos
ARGO-ACM-DEPLOY Lógica ACM: ManagedClusterSetBinding, Placement, GitOpsCluster, orden de aplicaciones
Gateway Policies AuthPolicy (API key), RateLimitPolicy, política OIDC, login biométrico RHBK, ruta canary
Observability Grafana Operator, ServiceMonitors, scripts de tráfico run-tests.sh
QA Test Plan Script automatizado qa-test-plan.sh — 10 pruebas end-to-end (sync GitOps, mesh, auth, rate limiting, cross-cluster)
QA Diagrams Diagramas Mermaid visuales para cada caso de prueba QA con referencias a recursos YAML
ApplicationSet Referencia YAML del ApplicationSet y detalles del matrix generator
Troubleshooting Problemas comunes: RBAC de ApplicationSet, OutOfSync, errores 503, aprobación de CSR

Modos de Despliegue

El sitio documenta dos modos de despliegue, cada uno con su propia ruta de inicio:

Con ACM (Hub + Managed Clusters):

# 1. Placements + GitOpsCluster (crea secrets east/west en ArgoCD)
kubectl apply -f app-nfl-wallet-acm.yaml -n openshift-gitops

# 2. ApplicationSet (genera 6 Applications: dev/test/prod × east/west)
kubectl apply -f app-nfl-wallet-acm-cluster-decision.yaml -n openshift-gitops

# 3. Patches de recursos Kuadrant (escalado Authorino, Limitador)
kubectl apply -f app-kuadrant-resources.yaml -n openshift-gitops

Sin ACM (East/West Independientes):

kubectl apply -f app-nfl-wallet-east.yaml -n openshift-gitops
kubectl apply -f app-nfl-wallet-west.yaml -n openshift-gitops

Políticas de Gateway por Ambiente

El sitio detalla cómo los overlays de Kustomize aplican diferentes políticas de seguridad por ambiente:

Ambiente Contenido del Overlay Chart
Dev Ruta de Gateway, namespace-mesh (istio-injection), login biométrico RHBK 0.1.3
Test Ruta de Gateway, AuthPolicy, API keys, ruta ESPN, PlanPolicy, login biométrico RHBK, política OIDC 0.1.3
Prod Ruta de Gateway, ruta canary, AuthPolicy, API keys, PlanPolicy (sin biométrico) 0.1.1

La política OIDC en test crea objetos AuthPolicy de Kuadrant por HTTPRoute de API que validan tokens JWT del realm RHBK, coexistiendo con la AuthPolicy de API key existente en el Gateway.

Script QA Automatizado

El script qa-test-plan.sh ejecuta la suite completa de pruebas desde el cluster hub contra east y west:

export CLUSTER_DOMAIN_EAST="cluster-east.sandbox.opentlc.com"
export CLUSTER_DOMAIN_WEST="cluster-west.sandbox.opentlc.com"
export API_KEY_TEST="nfl-wallet-customers-key"
export API_KEY_PROD="nfl-wallet-customers-key"

./scripts/qa-test-plan.sh

El script valida sync GitOps, enrolamiento en ambient mesh, egress ESPN, rate limiting, enforcement de AuthPolicy, accesibilidad cross-cluster, salud del stack de observabilidad, Swagger UI, endpoints RHBK NeuroFace y escalado de recursos — produciendo un reporte PASS/FAIL para cada caso de prueba.

Fuente: Stadium Wallet GitOps — Sitio de Documentación — Guías operativas completas, diagramas de arquitectura y scripts QA automatizados.


10. Red Hat Developer Hub

Red Hat Developer Hub (RHDH), basado en el proyecto upstream Backstage, proporciona una experiencia de autoservicio para desarrolladores donde pueden descubrir APIs, solicitar acceso y obtener credenciales sin necesidad de tickets, intervención manual de operaciones, ni conocimiento de la infraestructura subyacente. Este enfoque inner-loop reduce la fricción entre equipos de desarrollo y plataforma.

La gobernanza de las APIs se centraliza mediante Kuadrant en el backend y RHDH en el frontend. El Plugin de Kuadrant para RHDH conecta ambos mundos: las APIs se registran automáticamente en el catálogo de Backstage mediante annotations en los manifiestos GitOps, y las políticas de acceso (Tiers, AuthPolicy, RateLimitPolicy) se descubren y gestionan desde el portal.

Mapeo de Tiers a CRDs

Los Tiers de acceso definidos en RHDH se materializan como CRDs de Kuadrant en el clúster:

Tier en RHDH CRD Kuadrant Límite Secret Label
Bronze PlanPolicy + RateLimitPolicy 100 req/día tier: bronze
Silver PlanPolicy + RateLimitPolicy 500 req/día tier: silver
Gold PlanPolicy + RateLimitPolicy 1000 req/día tier: gold

Cuando un administrador define un Tier via PlanPolicy, Kuadrant crea automáticamente la RateLimitPolicy correspondiente. Cuando un desarrollador solicita acceso desde RHDH, Kuadrant provisiona el Secret con el API Key y los labels que Authorino utiliza para validación.

10.1 Flujo de Autoservicio

  1. Descubrimiento: En el catálogo de RHDH, localizar nfl-wallet-api-customers (Tipo: API - OpenAPI, Lifecycle: production)
  2. Solicitud de Acceso: Clic en + Request API Access
  3. Configuración del Tier: Seleccionar silver (500 per daily)
  4. Use Case (opcional): Justificación técnica o de negocio
  5. Aprobación y Aprovisionamiento: Kuadrant orquesta la creación de la credencial (API Key o Token OIDC)
  6. Enforcement: El Gateway API intercepta, valida la credencial y aplica el límite de 500 peticiones/día

RHDH Kuadrant Policies Red Hat Developer Hub — Plugin Kuadrant: vista de Policies para nfl-wallet-api-customers. PlanPolicy y AuthPolicy descubiertas. Tiers efectivos: gold (1000/día), silver (500/día), bronze (100/día).

RHDH API Definition Red Hat Developer Hub — Definición de API: Stadium Wallet - Customers API v1 (OAS 3.0). Endpoints GET /Customers y GET /Customers/{id} con botón Authorize para autenticación.

RHDH Request Access Red Hat Developer Hub — Flujo de solicitud de acceso: modal “Request API Access” con selección de Tier (silver - 500 per daily) y campo de Use Case. Owner: Maximiliano Pizarro, Lifecycle: production.

RHDH API Keys Red Hat Developer Hub — API Keys aprovisionadas: Tier silver aprobado (2/3/2026), API Key generada con ejemplos de uso en cURL, Node.js, Python y Go.

10.2 Uso de la API Key desde Developer Hub

Una vez que el acceso es aprobado en RHDH, el portal genera una API Key vinculada al Tier solicitado. Esta key se almacena como un Secret de Kubernetes con el label api: <namespace> (por ejemplo api: nfl-wallet-prod), que es el mecanismo que Authorino (Kuadrant) utiliza para descubrir y validar credenciales.

Flujo completo: del portal al request

  1. RHDH genera el Secret con la API Key y le asigna el label api: nfl-wallet-prod:
apiVersion: v1
kind: Secret
metadata:
  name: consumer-api-key-silver-<hash>
  namespace: nfl-wallet-prod
  labels:
    api: nfl-wallet-prod
    authorino.kuadrant.io/managed-by: authorino
    tier: silver
type: Opaque
data:
  api_key: <base64-encoded-key>
  1. AuthPolicy referencia el label api: nfl-wallet-prod como selector de credenciales. Cuando llega un request, Authorino busca todos los Secrets con ese label y valida que el header X-Api-Key coincida con alguno de ellos.

  2. El consumidor usa la key obtenida del portal RHDH en sus requests:

# Ejemplo con cURL (como se muestra en el portal RHDH)
curl -X GET https://nfl-wallet-prod.apps.cluster.example.com/api-customers/Customers \
  -H "X-Api-Key: <your-api-key>"
# Ejemplo con Python (como se muestra en el portal RHDH)
import requests

headers = {"X-Api-Key": "<your-api-key>"}
response = requests.get(
    "https://nfl-wallet-prod.apps.cluster.example.com/api-customers/Customers",
    headers=headers
)
  1. El Gateway valida el request: si la key coincide con un Secret que tiene el label correcto y el Tier no ha excedido su cuota (por ejemplo, 500 req/día para silver), el request llega al backend. Si no, retorna 403 Forbidden o 429 Too Many Requests.

Relación Label → AuthPolicy → Secret

sequenceDiagram
    participant Portal as RHDH Portal
    participant K8s as Kubernetes Cluster<br/>nfl-wallet-prod
    participant Auth as Authorino
    participant Backend as Backend API

    Portal->>K8s: Request API Access (Tier: silver)
    K8s->>K8s: Approve → Create Secret<br/>label: api=nfl-wallet-prod, tier=silver

    Note over Portal,K8s: Consumer obtiene API Key del portal

    rect rgb(240, 248, 255)
        Portal->>Auth: Request con X-Api-Key header
        Auth->>K8s: Buscar Secrets con label<br/>api=nfl-wallet-prod
        K8s-->>Auth: Secret encontrado
        alt Key válida y dentro de cuota
            Auth->>Backend: 200 OK → Forward request
        else Key inválida
            Auth-->>Portal: 403 Forbidden
        else Cuota excedida
            Auth-->>Portal: 429 Too Many Requests
        end
    end

Importante: Los Secrets con API Keys deben existir en el mismo namespace que la AuthPolicy. Para producción, se recomienda usar Sealed Secrets o External Secrets Operator en lugar de commitear keys directamente en Git.

El despliegue de Developer Hub de Stadium Wallet también está validado en Red Hat Developer Sandbox a través del repositorio GitOps connectivity-link. Este repositorio provee un ApplicationSet consolidado que despliega el stack completo de Connectivity Link — incluyendo RHDH, Keycloak, Service Mesh, Kuadrant y la aplicación demo NeuralBank — en un único cluster OpenShift con aprovisionamiento automatizado por Ansible.

Arquitectura

El repositorio Connectivity Link utiliza un ApplicationSet con ordenamiento sync_wave para desplegar todos los componentes de infraestructura en la secuencia correcta:

Sync Wave Componentes
Wave 0 Operador OpenShift GitOps
Wave 1 Namespaces (developer-hub, rhbk, neuralbank, etc.)
Wave 2 Operadores (RHBK Operator, configuraciones RBAC)
Wave 3 Infraestructura (Service Mesh, RHCL Operator, Developer Hub)
Wave 4–7 Aplicaciones (NeuralBank Stack, LiteMaaS, catálogo NFL-Wallet)

Custom Resource de RHDH

Developer Hub se despliega usando el CRD Backstage rhdh.redhat.com/v1alpha3 con configuración declarativa:

apiVersion: rhdh.redhat.com/v1alpha3
kind: Backstage
metadata:
  name: developer-hub
spec:
  application:
    appConfig:
      configMaps:
      - name: app-config-rhdh
    dynamicPluginsConfigMapName: dynamic-plugins-rhdh
    extraEnvs:
      secrets:
      - name: secrets-rhdh
      - name: developer-hub-k8s-sa-token
        key: token
    extraFiles:
      configMaps:
      - name: rhdh-rbac-policy
    replicas: 1
    route:
      enabled: true
  database:
    enableLocalDb: true
  deployment:
    patch:
      spec:
        template:
          spec:
            automountServiceAccountToken: true

Plugins Dinámicos

El despliegue incluye plugins dinámicos preconfigurados que extienden la funcionalidad de RHDH:

Plugin Propósito Estado
Kuadrant (frontend + backend) Gestión de API Products, API Keys, PlanPolicy, RateLimitPolicy, AuthPolicy Habilitado
Keycloak Org Sincronización de usuarios/grupos desde realms de Keycloak Habilitado
GitLab Scaffolder Software Templates para crear nuevos componentes desde GitLab Habilitado
Kubernetes Backend Visibilidad de recursos in-cluster (pods, deployments, services) Habilitado
Tekton Visualización de pipelines CI/CD (PipelineRuns, TaskRuns) Habilitado
MCP Actions (backend) Servidor Model Context Protocol para interacciones asistidas por IA Habilitado
Quickstart Experiencia guiada de onboarding Habilitado
RBAC Interfaz de control de acceso basado en roles y gestión de políticas Habilitado
Lightspeed Asistente de chat con IA (backend llama-stack) Deshabilitado (conflictos de versión)

Modelo de Permisos RBAC

El despliegue implementa un modelo RBAC jerárquico con tres niveles de permisos, sincronizados desde grupos de Keycloak:

platform-team (acceso completo)
  ├── infrastructure
  ├── platformengineers
  └── rhdh

application-team (acceso limitado)
  ├── developers
  └── devteam1

authenticated-users (solo lectura)
  └── todos los usuarios logueados
Rol Catálogo Scaffolder Plugins Políticas RBAC
Platform Team CRUD completo Acceso total Instalar/Desinstalar CRUD completo
Application Team Solo lectura Ejecutar templates
Usuarios Autenticados Solo lectura Ejecutar templates

El app-config.yaml configura descubrimiento automático de catálogo desde GitLab, registrando las APIs de Stadium Wallet junto con otros componentes de Connectivity Link:

Proveedor de Catálogo Repositorio Contenido
nfl-wallet maximilianoPizarro/NFL-Wallet Definiciones API (OpenAPI), entidades Component
nfl-wallet-catalog connectivity-link/developer-hub/catalog/ Entidades Component dev/test/prod, API Products
neuralbank-stack connectivity-link/neuralbank-stack/ Demo NeuralBank (app de referencia OIDC)
rhbk connectivity-link/rhbk/ Despliegue de Keycloak
operators connectivity-link/operators/ Suscripciones de operadores
groups connectivity-link/groups/ Dominios, Sistemas, Usuarios, Grupos

Inicio Rápido

# 1. Forkear el repositorio
git clone https://gitlab.com/maximilianoPizarro/connectivity-link.git

# 2. Login en OpenShift (cluster-admin requerido)
oc login --token=<token> --server=https://api.<cluster>:6443

# 3. Ejecutar el instalador automatizado (actualiza dominio, instala operadores, despliega todo)
./install.sh

# 4. Acceder a Developer Hub
open https://developer-hub.apps.<cluster-domain>

El script instalador (install.sh) automatiza el despliegue completo: detecta el dominio del cluster, actualiza todas las referencias en los manifiestos, instala el operador OpenShift GitOps y aplica el applicationset-instance.yaml consolidado que despliega todos los componentes vía ArgoCD.

Fuente: connectivity-link/developer-hub — Manifiestos de despliegue completos, configuración de plugins dinámicos, políticas RBAC y playbooks de Ansible.


11. Observabilidad

Por qué este stack de observabilidad

En una arquitectura de microservicios con Service Mesh multi-cluster, la observabilidad no es un “nice to have” — es un requisito operativo. Sin visibilidad sobre lo que ocurre en la malla, diagnosticar un error 5xx o una degradación de latencia requiere recorrer manualmente logs de múltiples pods en múltiples clústeres.

El stack elegido cubre las cuatro dimensiones de la observabilidad cloud-native:

Dimensión Herramienta Qué responde
Métricas Prometheus + promxy ¿Cuántas requests por segundo? ¿Cuál es el error rate? ¿Cómo evoluciona la latencia p99?
Dashboards Grafana ¿Cómo se comparan los ambientes? ¿Hay anomalías en un clúster específico?
Topología del mesh Kiali ¿Qué servicios se comunican entre sí? ¿Dónde se concentra el tráfico? ¿Hay circuitos rotos?
Trazas distribuidas TempoStack + OpenTelemetry ¿Cuánto tarda cada hop en una request? ¿Dónde está el cuello de botella?

Cada componente se integra nativamente con Istio/OSSM3: ztunnel y los Waypoint Proxies emiten métricas y spans automáticamente mediante el protocolo OTLP, sin necesidad de instrumentar el código de la aplicación. Esto significa que al enrolar un namespace en Ambient Mode, la observabilidad se habilita “gratis” para todo el tráfico L4 y L7.

11.1 Stack de Observabilidad

Componente Función
Prometheus + promxy Fan-out proxy para métricas desde East y West
Grafana Dashboards: request rate, response codes, duration, error rate
Kiali Topología en tiempo real del Service Mesh federado
TempoStack Backend de trazas distribuidas (Jaeger-compatible)
OpenTelemetry Instrumentación con OTLP/HTTP — spans L7 desde Waypoint proxies

11.2 Habilitar Observabilidad con Helm

helm upgrade nfl-wallet ./helm/nfl-wallet -n nfl-wallet --install \
  --set gateway.enabled=true \
  --set observability.rhobs.enabled=true \
  --set observability.rhobs.thanosQuerier.enabled=true \
  --set observability.rhobs.podMonitorGateway.enabled=true \
  --set observability.rhobs.uiPlugin.enabled=true

11.3 Dashboard de Grafana

El dashboard “Stadium Wallet – All environments” incluye:

11.4 Queries de Prometheus (Referencia)

Métrica Query de ejemplo
Total Requests (rate) sum(rate(istio_requests_total[5m]))
Successful Requests (2xx) sum(rate(istio_requests_total{response_code=~"2.."}[5m]))
Error Rate sum(rate(istio_requests_total{response_code=~"5.."}[5m])) / sum(rate(istio_requests_total[5m]))

11.5 Script de Pruebas de Tráfico

export CLUSTER_DOMAIN="cluster-thmg4.thmg4.sandbox4076.opentlc.com"
export API_KEY_TEST="nfl-wallet-customers-key"
export API_KEY_PROD="nfl-wallet-customers-key"
./observability/run-tests.sh all
Comando Descripción
./observability/run-tests.sh all Ejecuta dev, test y prod
./observability/run-tests.sh dev Solo dev (sin API key)
./observability/run-tests.sh test Solo test (con API_KEY_TEST)
./observability/run-tests.sh prod Solo prod (con API_KEY_PROD)
./observability/run-tests.sh loop Loop continuo: dev + test + prod

Métricas agregadas (Grafana): El dashboard “Stadium Wallet – All environments” permite comparar el comportamiento de los tres ambientes (dev/test/prod) en un solo panel. Al ejecutar el script con loop, se genera tráfico continuo que alimenta las métricas de request rate, response codes y duración.

Grafana Dashboard Dashboard de Grafana “Stadium Wallet – All environments” con métricas: request rate, response codes, duration, error rate.

Topología del mesh (Kiali): Kiali visualiza en tiempo real las relaciones entre servicios dentro del mesh. Los nodos representan workloads y los bordes representan tráfico observado. Los colores indican el estado de salud: verde (saludable), amarillo (degradado), rojo (errores). Esto permite identificar rápidamente qué servicio está generando errores o recibiendo tráfico inesperado.

Kiali Topology Kiali — Topología del Service Mesh federado mostrando flujo de tráfico entre namespaces (dev/test/prod).

Tráfico multi-cluster (Kiali): En la configuración con ACM, Kiali muestra el grafo de servicios federado entre los clústeres East y West, incluyendo los gateways Istio y los waypoints. Esto permite verificar que el tráfico cross-cluster fluye correctamente a través del túnel HBONE.

Kiali Service Graph Kiali — Grafo de servicios con tráfico multi-cluster (East/West), gateways y waypoints.


12. Canary / Blue-Green Deployments

El overlay de producción incluye una Route canary adicional (nfl-wallet-canary.apps.<cluster-domain>) que apunta al mismo gateway Service (nfl-wallet-gateway-istio), permitiendo tráfico blue/green cuando el chart crea el HTTPRoute correspondiente.

Métricas de Canary por Ambiente

Las siguientes capturas de Grafana muestran el comportamiento del tráfico durante un despliegue canary, donde se observa la distribución de requests entre los ambientes dev, test y prod:

Canary Blue-Green - Total Requests Total de requests (última hora) por ambiente durante un despliegue canary — nfl-wallet-dev (verde), nfl-wallet-prod (amarillo), nfl-wallet-test (azul). Se observa el incremento gradual de tráfico hacia producción.

Canary Blue-Green - Request Rate Request rate por ambiente y servicio durante canary — Se visualiza cómo api-customers (dev), gateway-istio (prod/test) y webapp distribuyen el tráfico entre versiones.

Definición del Canary con HTTPRoutes

El despliegue canary se implementa mediante dos HTTPRoutes que apuntan al mismo Gateway pero con hostnames distintos. Esto permite dividir el tráfico entre la versión estable (producción) y la versión canary:

# HTTPRoute principal (producción estable)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: nfl-wallet-webapp
  namespace: nfl-wallet-prod
spec:
  parentRefs:
  - name: nfl-wallet-gateway
  hostnames:
  - "nfl-wallet-prod.apps.cluster-east.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: webapp
      port: 5173
      weight: 100      # 100% del tráfico estable
# HTTPRoute canary (nueva versión)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: nfl-wallet-webapp-canary
  namespace: nfl-wallet-prod
spec:
  parentRefs:
  - name: nfl-wallet-gateway
  hostnames:
  - "nfl-wallet-canary.apps.cluster-east.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: webapp-canary        # Service de la versión canary
      port: 5173
      weight: 100

Para un weighted canary (porcentaje de tráfico en el mismo hostname), se usa un único HTTPRoute con múltiples backendRefs:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: nfl-wallet-webapp-weighted
  namespace: nfl-wallet-prod
spec:
  parentRefs:
  - name: nfl-wallet-gateway
  hostnames:
  - "nfl-wallet-prod.apps.cluster-east.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: webapp               # Versión estable
      port: 5173
      weight: 90                 # 90% del tráfico
    - name: webapp-canary        # Versión canary
      port: 5173
      weight: 10                 # 10% del tráfico

Configuración en Kustomize

La ruta canary se define en los overlays de Kustomize para producción:

El overlay de producción incluye el Route de OpenShift para el canary host:

# nfl-wallet/overlays/prod/canary-route.yaml
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: nfl-wallet-canary
  namespace: nfl-wallet-prod
spec:
  host: nfl-wallet-canary.apps.cluster-east.example.com
  to:
    kind: Service
    name: nfl-wallet-gateway-istio
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect

Para cambiar el dominio, editar el patch en cada overlay correspondiente.


13. Plan de Pruebas y Validación (QA)

Una vez finalizada la sincronización de ArgoCD, QA/Operaciones debe ejecutar el QA Test Plan automatizado más reciente del repositorio GitOps. Este plan se ejecuta desde el clúster hub y valida east y west en una sola corrida.

Resumen      
10 Pruebas Automatizadas (qa-test-plan.sh) 13 Escenarios Diagramados (qa-diagrams) 3 Ambientes (dev, test, prod) 2 Clústeres (East, West)

13.1 QA Test Plan Automatizado (Última Versión)

Script fuente: scripts/qa-test-plan.sh (del repo nfl-wallet-gitops).

# Ejecutar todas las pruebas automatizadas
./scripts/qa-test-plan.sh

# Ejecutar solo pruebas específicas
./scripts/qa-test-plan.sh QA-05 QA-06

# Omitir verificación TLS
./scripts/qa-test-plan.sh --insecure

Prerrequisitos

Requisito Detalle
oc CLI Autenticado contra el hub (oc whoami = hub). Requerido para QA-01 y QA-02
curl Requerido para pruebas HTTP (QA-03 a QA-10)
Acceso HTTPS saliente Acceso a rutas east/west/hub *.apps.<cluster-domain>
API keys Los valores por defecto pueden sobrescribirse con variables de entorno

Cobertura Automatizada (QA-01 a QA-10)

Prueba Valida Criterio de Éxito
QA-01 GitOps Sync Estado de sync/health en ArgoCD Apps requeridas en Synced y Healthy
QA-02 Ambient Mesh Ausencia de sidecar istio-proxy Pods sin sidecar
QA-03 Egress ESPN Alcance de ruta ESPN en test 200 en ruta pública, o respuesta auth-path que confirme ruta existente
QA-04 RHDH Portal Catálogo API + plugin Kuadrant Verificación manual (SKIP en script)
QA-05 Rate Limiting Enforcement de RateLimitPolicy Aparece 429 tras cuota, o endpoint sigue alcanzable
QA-06 AuthPolicy Enforcement de API key en test/prod 401/403 sin key, 200 con key
QA-07 Cross-Cluster Disponibilidad API/webapp en east/west HTTP 200 en ambos clústeres
QA-08 Observability Grafana + Promxy + query de métricas Rutas disponibles y datos de métricas
QA-09 Swagger UI Endpoints Swagger de 3 APIs HTTP 200/301
QA-10 Load Test Comportamiento del gateway bajo concurrencia Tasa de éxito >= 30% y 429 opcional

Variables de Entorno Estándar

export EAST_DOMAIN="cluster-64k4b.64k4b.sandbox5146.opentlc.com"
export WEST_DOMAIN="cluster-7rt9h.7rt9h.sandbox1900.opentlc.com"
export HUB_DOMAIN="cluster-72nh2.dynamic.redhatworkshops.io"
export API_KEY_CUSTOMERS="nfl-wallet-customers-key"
export API_KEY_BILLS="nfl-wallet-bills-key"
export API_KEY_RAIDERS="nfl-wallet-raiders-key"

Versiones de Chart Validadas por QA

Ambiente Chart Login Biométrico Política OIDC
dev 0.1.3 Habilitado (FHD 1920x1080) Deshabilitada
test 0.1.3 Habilitado (FHD 1920x1080) Habilitada
prod 0.1.1 Deshabilitado Deshabilitada

13.2 Validación Extendida con Diagramas (QA-01 a QA-13)

QA-01 — GitOps Sync

ArgoCD Applications Healthy & Synced

flowchart TD
  A["oc get applications.argoproj.io\n-n openshift-gitops"] --> B{"Apps\nfound?"}
  B -- No --> F1["FAIL: Cannot list apps"]
  B -- Yes --> C["Parse each app:\nname / sync / health"]
  C --> D{"kuadrant-resources-*\nOutOfSync?"}
  D -- Yes --> W["WARNING:\nSharedResourceWarning\napply gateway-resources.yaml"]
  D -- No --> E{"Synced &\nHealthy?"}
  E -- Yes --> P["App OK"]
  E -- No --> F2["App unhealthy"]
  W --> G{"All nfl-wallet\napps OK?"}
  P --> G
  F2 --> G
  G -- Yes --> PASS["PASS"]
  G -- No --> FAIL["FAIL"]
app-nfl-wallet-acm-cluster-decision.yaml ApplicationSet
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: nfl-wallet
  namespace: openshift-gitops
spec:
  generators:
  - matrix:
      generators:
      - clusterDecisionResource:
          configMapRef: acm-placement
          labelSelector:
            matchLabels:
              cluster.open-cluster-management.io/placement: nfl-wallet-gitops-placement
          requeueAfterSeconds: 180
      - list:
          elements:
          - env: dev
            chartVersion: "0.1.3"
          - env: test
            chartVersion: "0.1.3"
          - env: prod
            chartVersion: "0.1.1"
  template:
    metadata:
      name: 'nfl-wallet-{{env}}-{{name}}'
    spec:
      project: default
      sources:
      - repoURL: 'https://github.com/maximilianoPizarro/nfl-wallet-gitops.git'
        targetRevision: HEAD
        path: 'nfl-wallet/overlays/{{env}}-{{name}}'
      - repoURL: 'https://maximilianopizarro.github.io/NFL-Wallet'
        chart: nfl-wallet
        targetRevision: '{{chartVersion}}'
      destination:
        server: '{{server}}'
        namespace: 'nfl-wallet-{{env}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
        - CreateNamespace=true
        - ServerSideApply=true
app-kuadrant-resources.yaml ApplicationSet
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: kuadrant-resources
  namespace: openshift-gitops
spec:
  generators:
  - list:
      elements:
      - cluster: east
        server: 'https://api.cluster-east:6443'
      - cluster: west
        server: 'https://api.cluster-west:6443'
  template:
    metadata:
      name: 'kuadrant-resources-{{cluster}}'
    spec:
      project: default
      source:
        repoURL: 'https://github.com/maximilianoPizarro/nfl-wallet-gitops.git'
        targetRevision: HEAD
        path: kuadrant-system
      destination:
        server: '{{server}}'
        namespace: kuadrant-system
      syncPolicy:
        automated:
          selfHeal: true
        syncOptions:
        - ServerSideApply=true
app-nfl-wallet-acm.yaml Placement
apiVersion: cluster.open-cluster-management.io/v1beta1
kind: Placement
metadata:
  name: nfl-wallet-gitops-placement
  namespace: openshift-gitops
spec:
  predicates:
  - requiredClusterSelector:
      labelSelector:
        matchLabels:
          nfl-wallet: "true"
---
apiVersion: apps.open-cluster-management.io/v1beta1
kind: GitOpsCluster
metadata:
  name: nfl-wallet-gitops
  namespace: openshift-gitops
spec:
  argoServer:
    cluster: local-cluster
    argoNamespace: openshift-gitops
  placementRef:
    kind: Placement
    name: nfl-wallet-gitops-placement
kuadrant-system/gateway-resources.yaml Manual Patch
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfl-wallet-gateway-istio
  namespace: nfl-wallet-prod
spec:
  template:
    spec:
      containers:
      - name: istio-proxy
        resources:
          requests:
            cpu: 500m
            memory: 256Mi
          limits:
            cpu: "2"
            memory: 1Gi

QA-02 — Ambient Mesh

Pods con 1 contenedor (sin sidecar)

flowchart TD
  A["oc get pods\n-n nfl-wallet-dev/test/prod"] --> B{"Pods\nfound?"}
  B -- No --> S["SKIP: Run from\nmanaged cluster"]
  B -- Yes --> C["Check each pod:\ncount containers"]
  C --> D{"Has\nistio-proxy?"}
  D -- Yes --> F["FAIL: Sidecar detected"]
  D -- No --> E{"Only 1\ncontainer?"}
  E -- Yes --> P["No sidecar — Ambient"]
  E -- No --> W["WARNING: Multiple containers"]
  P --> R{"All pods OK?"}
  W --> R
  F --> R
  R -- Yes --> PASS["PASS"]
  R -- No --> FAIL["FAIL"]
overlays/dev/namespace-mesh.yaml Namespace
apiVersion: v1
kind: Namespace
metadata:
  name: nfl-wallet-dev
  labels:
    istio-injection: enabled
overlays/test/namespace-mesh.yaml Namespace
apiVersion: v1
kind: Namespace
metadata:
  name: nfl-wallet-test
  labels:
    istio.io/dataplane-mode: ambient
overlays/prod/namespace-mesh.yaml Namespace
apiVersion: v1
kind: Namespace
metadata:
  name: nfl-wallet-prod
  labels:
    istio.io/dataplane-mode: ambient

QA-03 — Egress ESPN

Ruta ESPN accesible (solo ambiente test)

flowchart TD
  A["curl ESPN route\n/auth/nfl + X-Api-Key"] --> B{"HTTP\ncode?"}
  B -- 200 --> P1["PASS: ESPN OK"]
  B -- 301/302 --> P2["PASS: Redirect active"]
  B -- 401/403 --> C["Route exists\nbut auth failed"]
  C --> D["Try /public/nfl\nno auth required"]
  D --> E{"HTTP\n200?"}
  E -- Yes --> P3["PASS: Public path"]
  E -- No --> P4["PASS: Route exists"]
  B -- other --> F["Fallback:\ntest api-bills on dev"]
  F --> G{"HTTP\n200?"}
  G -- Yes --> P5["PASS: api-bills OK"]
  G -- No --> FAIL["FAIL"]
overlays/test/nfl-wallet-espn-route.yaml HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: nfl-wallet-espn
  namespace: nfl-wallet-test
spec:
  parentRefs:
  - name: nfl-wallet-gateway
  hostnames:
  - "nfl-wallet-test.apps.cluster-east.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /public/nfl
    backendRefs:
    - name: api-bills
      port: 8081
overlays/test/auth-policy-patch.yaml AuthPolicy
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: nfl-wallet-gateway-auth
  namespace: nfl-wallet-test
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: nfl-wallet-gateway
  rules:
    authentication:
      api-key-authn:
        apiKey:
          selector:
            matchLabels:
              api: nfl-wallet-test
        credentials:
          customHeader:
            name: X-Api-Key
    response:
      unauthorized:
        headers:
          content-type:
            value: application/json
        body:
          value: '{"error":"Forbidden","message":"Invalid or missing API Key"}'
overlays/test/api-keys-secret.yaml Secret
apiVersion: v1
kind: Secret
metadata:
  name: nfl-wallet-customers-key
  namespace: nfl-wallet-test
  labels:
    api: nfl-wallet-test
    authorino.kuadrant.io/managed-by: authorino
stringData:
  api_key: changeme-test-key
type: Opaque

QA-04 — Portal RHDH

El catálogo de Developer Hub muestra las APIs

flowchart TD
  A["Manual Verification"] --> B["1. Navigate to\nRed Hat Developer Hub"]
  B --> C["2. Search for\nnfl-wallet-api-customers"]
  C --> D["3. Verify OpenAPI\nspec renders correctly"]
  D --> E["4. Check Kuadrant Plugin:\nPlanPolicy & AuthPolicy"]
  E --> SKIP["SKIP: Manual"]
developer-hub/catalog-info.yaml Backstage Catalog
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: nfl-wallet-api-customers
  description: Stadium Wallet - Customers API
  annotations:
    backstage.io/techdocs-ref: dir:.
    kuadrant.io/api-name: nfl-wallet-api-customers
spec:
  type: service
  lifecycle: production
  owner: maximiliano-pizarro
  providesApis:
  - nfl-wallet-customers-api

QA-05 — Rate Limiting

429 tras exceder la cuota (505 peticiones)

flowchart TD
  A["Send 505 sequential\ncurl requests with X-Api-Key"] --> B["Count responses:\n200 / 429 / errors"]
  B --> C{"Got any\n429?"}
  C -- Yes --> P1["PASS: Rate limit active\nat request #N"]
  C -- No --> D{"Any\n200s?"}
  D -- Yes --> E{"Any\nerrors?"}
  E -- Yes --> P2["PASS: Reachable,\nno 429 seen"]
  E -- No --> P3["PASS: All 200,\nno RateLimitPolicy"]
  D -- No --> FAIL["FAIL: Unreachable"]
overlays/test/plan-policy.yaml RateLimitPolicy
apiVersion: kuadrant.io/v1beta2
kind: RateLimitPolicy
metadata:
  name: nfl-wallet-rate-limit
  namespace: nfl-wallet-test
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: nfl-wallet-api-customers
  limits:
    silver:
      rates:
      - limit: 500
        window: 1d
      when:
      - selector: auth.identity.metadata.labels.tier
        operator: eq
        value: silver
    bronze:
      rates:
      - limit: 100
        window: 1d
overlays/test/api-keys-secret.yaml Secret
apiVersion: v1
kind: Secret
metadata:
  name: nfl-wallet-customers-key
  namespace: nfl-wallet-test
  labels:
    api: nfl-wallet-test
    authorino.kuadrant.io/managed-by: authorino
stringData:
  api_key: changeme-test-key
type: Opaque
kuadrant-system/resource-requirements.yaml Authorino + Limitador
apiVersion: operator.authorino.kuadrant.io/v1beta2
kind: Authorino
metadata:
  name: authorino
  namespace: kuadrant-system
spec:
  replicas: 1
  resources:
    requests:
      cpu: 500m
      memory: 256Mi
    limits:
      cpu: "2"
      memory: 1Gi
---
apiVersion: limitador.kuadrant.io/v1alpha1
kind: Limitador
metadata:
  name: limitador
  namespace: kuadrant-system
spec:
  replicas: 1
  resources:
    requests:
      cpu: 250m
      memory: 128Mi
    limits:
      cpu: "1"
      memory: 256Mi

QA-06 — AuthPolicy

401/403 sin clave, 200 con clave, JWT OIDC

flowchart TD
  A["curl WITHOUT\nX-Api-Key"] --> B{"HTTP\n401/403?"}
  B -- Yes --> C["Auth enforced"]
  B -- No --> F1["Not enforced"]
  C --> D["curl WITH X-Api-Key\nup to 5 retries"]
  D --> E{"HTTP\n200?"}
  E -- Yes --> G["API key works"]
  E -- No --> F2["Key rejected"]
  G --> H["OIDC: curl\n.well-known endpoint"]
  H --> I{"HTTP\n200?"}
  I -- Yes --> J["POST token_endpoint\ngrant_type=password\nuser=john.doe"]
  I -- No --> W1["WARNING: RHBK not ready"]
  J --> K{"Got\naccess_token?"}
  K -- Yes --> L["curl with\nBearer token"]
  K -- No --> W2["WARNING: Password reset needed"]
  L --> M{"HTTP\n200?"}
  M -- Yes --> P["JWT accepted"]
  M -- No --> W3["WARNING: Token OK, API non-200"]
overlays/test/auth-policy-patch.yaml AuthPolicy
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: nfl-wallet-gateway-auth
  namespace: nfl-wallet-test
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: nfl-wallet-gateway
  rules:
    authentication:
      api-key-authn:
        apiKey:
          selector:
            matchLabels:
              api: nfl-wallet-test
        credentials:
          customHeader:
            name: X-Api-Key
    response:
      unauthorized:
        headers:
          content-type:
            value: application/json
        body:
          value: '{"error":"Forbidden","message":"Invalid or missing API Key"}'
overlays/test/api-keys-secret.yaml Secret
apiVersion: v1
kind: Secret
metadata:
  name: nfl-wallet-customers-key
  namespace: nfl-wallet-test
  labels:
    api: nfl-wallet-test
    authorino.kuadrant.io/managed-by: authorino
stringData:
  api_key: changeme-test-key
type: Opaque
overlays/test/oidc-policy-customers.yaml OIDC
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: oidc-api-customers
  namespace: nfl-wallet-test
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: nfl-wallet-api-customers
  rules:
    authentication:
      oidc-rhbk:
        jwt:
          issuerUrl: https://nfl-wallet-rhbk-neuroface-nfl-wallet-test.apps.cluster-east.example.com/realms/neuroface
overlays/test/oidc-policy-bills.yaml OIDC
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: oidc-api-bills
  namespace: nfl-wallet-test
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: nfl-wallet-api-bills
  rules:
    authentication:
      oidc-rhbk:
        jwt:
          issuerUrl: https://nfl-wallet-rhbk-neuroface-nfl-wallet-test.apps.cluster-east.example.com/realms/neuroface
overlays/test/oidc-policy-raiders.yaml OIDC
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: oidc-api-raiders
  namespace: nfl-wallet-test
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: nfl-wallet-api-raiders
  rules:
    authentication:
      oidc-rhbk:
        jwt:
          issuerUrl: https://nfl-wallet-rhbk-neuroface-nfl-wallet-test.apps.cluster-east.example.com/realms/neuroface
overlays/prod/auth-policy-patch.yaml AuthPolicy
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: nfl-wallet-gateway-auth
  namespace: nfl-wallet-prod
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: nfl-wallet-gateway
  rules:
    authentication:
      api-key-authn:
        apiKey:
          selector:
            matchLabels:
              api: nfl-wallet-prod
        credentials:
          customHeader:
            name: X-Api-Key
    response:
      unauthorized:
        headers:
          content-type:
            value: application/json
        body:
          value: '{"error":"Forbidden","message":"Invalid or missing API Key"}'
overlays/prod/api-keys-secret.yaml Secret
apiVersion: v1
kind: Secret
metadata:
  name: nfl-wallet-customers-key
  namespace: nfl-wallet-prod
  labels:
    api: nfl-wallet-prod
    authorino.kuadrant.io/managed-by: authorino
stringData:
  api_key: changeme-prod-key
type: Opaque

QA-07 — Cross-Cluster

East y West sirven workloads independientes

flowchart TD
  A["curl dev APIs\non EAST cluster"] --> B["api-customers\napi-bills\napi-raiders"]
  C["curl dev APIs\non WEST cluster"] --> D["api-customers\napi-bills\napi-raiders"]
  B --> E{"All\n200?"}
  D --> F{"All\n200?"}
  E -- Yes --> G["East OK"]
  E -- No --> H["WARNING: East timeout"]
  F -- Yes --> I["West OK"]
  F -- No --> J["WARNING: West timeout"]
  G --> K["Test webapp /\non both clusters"]
  H --> K
  I --> K
  J --> K
  K --> L{"Both clusters\nrespond?"}
  L -- Both --> P1["PASS: Both OK"]
  L -- One --> P2["PASS: One OK\nother = sandbox latency"]
  L -- Neither --> FAIL["FAIL"]
overlays/dev-east/kustomization.yaml Kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: nfl-wallet-dev
resources:
- ../dev
patches:
- path: route-patch.yaml
overlays/dev-west/kustomization.yaml Kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: nfl-wallet-dev
resources:
- ../dev
patches:
- path: route-patch.yaml
base/gateway-route.yaml Route
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: nfl-wallet
spec:
  to:
    kind: Service
    name: nfl-wallet-gateway-istio
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect

QA-08 — Observabilidad

Métricas Prometheus y Grafana accesibles

flowchart TD
  A["curl Grafana route\non Hub cluster"] --> B{"HTTP\n200/302?"}
  B -- Yes --> C["Grafana OK"]
  B -- No --> F1["Grafana down"]
  D["curl Promxy route\non Hub cluster"] --> E{"HTTP\n200/302?"}
  E -- Yes --> G["Promxy OK"]
  E -- No --> F2["Promxy down"]
  C --> H{"oc CLI\navailable?"}
  G --> H
  H -- Yes --> I["Query Prometheus:\nistio_requests_total"]
  H -- No --> J["PASS if Grafana OK"]
  I --> K{"result in\nresponse?"}
  K -- Yes --> PASS["PASS: Metrics OK"]
  K -- No --> L{"Grafana\nOK?"}
  L -- Yes --> P2["PASS: Grafana reachable"]
  L -- No --> FAIL["FAIL"]
app-observability-east-west.yaml ApplicationSet
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: observability-east-west
  namespace: openshift-gitops
spec:
  generators:
  - list:
      elements:
      - cluster: east
        server: 'https://api.cluster-east:6443'
      - cluster: west
        server: 'https://api.cluster-west:6443'
  template:
    metadata:
      name: 'observability-{{cluster}}'
    spec:
      project: default
      source:
        repoURL: 'https://github.com/maximilianoPizarro/nfl-wallet-gitops.git'
        targetRevision: HEAD
        path: nfl-wallet-observability
      destination:
        server: '{{server}}'
        namespace: nfl-wallet-observability
      syncPolicy:
        automated:
          selfHeal: true
        syncOptions:
        - CreateNamespace=true
nfl-wallet-observability/prometheus-route.yaml Route
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: promxy
  namespace: nfl-wallet-observability
spec:
  to:
    kind: Service
    name: promxy
  tls:
    termination: edge

QA-09 — Swagger UI

Cada API sirve /api-service/swagger

flowchart TD
  A["For each API:\ncustomers / bills / raiders"] --> B["curl\n/api-SERVICE/swagger"]
  B --> C{"HTTP\n200 or 301?"}
  C -- Yes --> P["Swagger OK"]
  C -- No --> D["Try alt path:\n/SERVICE/swagger"]
  D --> E{"HTTP\n200 or 301?"}
  E -- Yes --> P2["Alt path OK"]
  E -- No --> F["Not accessible"]
  P --> R{"All APIs\naccessible?"}
  P2 --> R
  F --> R
  R -- Yes --> PASS["PASS"]
  R -- No --> FAIL["FAIL"]
base/gateway-route.yaml Route
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: nfl-wallet
spec:
  to:
    kind: Service
    name: nfl-wallet-gateway-istio
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
app-nfl-wallet-acm-cluster-decision.yaml Helm chart
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: nfl-wallet
  namespace: openshift-gitops
spec:
  generators:
  - matrix:
      generators:
      - clusterDecisionResource:
          configMapRef: acm-placement
          labelSelector:
            matchLabels:
              cluster.open-cluster-management.io/placement: nfl-wallet-gitops-placement
          requeueAfterSeconds: 180
      - list:
          elements:
          - env: dev
            chartVersion: "0.1.3"
          - env: test
            chartVersion: "0.1.3"
          - env: prod
            chartVersion: "0.1.1"
  template:
    metadata:
      name: 'nfl-wallet-{{env}}-{{name}}'
    spec:
      project: default
      sources:
      - repoURL: 'https://github.com/maximilianoPizarro/nfl-wallet-gitops.git'
        targetRevision: HEAD
        path: 'nfl-wallet/overlays/{{env}}-{{name}}'
      - repoURL: 'https://maximilianopizarro.github.io/NFL-Wallet'
        chart: nfl-wallet
        targetRevision: '{{chartVersion}}'
      destination:
        server: '{{server}}'
        namespace: 'nfl-wallet-{{env}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
        - CreateNamespace=true
        - ServerSideApply=true

QA-10 — Load Test

10 workers × 20 peticiones concurrentes

flowchart TD
  A["Spawn 10 parallel\nworkers"] --> B["Each worker sends\n20 curl + X-Api-Key"]
  B --> C["Collect results:\n200 / 429 / errors"]
  C --> D{"Any\n429?"}
  D -- Yes --> P1["PASS: RateLimit enforced"]
  D -- No --> E{"All\n200?"}
  E -- Yes --> P2["PASS: No rate limit hit"]
  E -- No --> F{"Success\nrate >= 30%?"}
  F -- Yes --> P3["PASS: Intermittent errors"]
  F -- No --> FAIL["FAIL: Too many errors"]
overlays/test/plan-policy.yaml RateLimitPolicy
apiVersion: kuadrant.io/v1beta2
kind: RateLimitPolicy
metadata:
  name: nfl-wallet-rate-limit
  namespace: nfl-wallet-test
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: nfl-wallet-api-customers
  limits:
    silver:
      rates:
      - limit: 500
        window: 1d
      when:
      - selector: auth.identity.metadata.labels.tier
        operator: eq
        value: silver
    bronze:
      rates:
      - limit: 100
        window: 1d
kuadrant-system/resource-requirements.yaml Authorino + Limitador
apiVersion: operator.authorino.kuadrant.io/v1beta2
kind: Authorino
metadata:
  name: authorino
  namespace: kuadrant-system
spec:
  replicas: 1
  resources:
    requests:
      cpu: 500m
      memory: 256Mi
    limits:
      cpu: "2"
      memory: 1Gi
---
apiVersion: limitador.kuadrant.io/v1alpha1
kind: Limitador
metadata:
  name: limitador
  namespace: kuadrant-system
spec:
  replicas: 1
  resources:
    requests:
      cpu: 250m
      memory: 128Mi
    limits:
      cpu: "1"
      memory: 256Mi
kuadrant-system/gateway-resources.yaml Gateway Proxy
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfl-wallet-gateway-istio
  namespace: nfl-wallet-prod
spec:
  template:
    spec:
      containers:
      - name: istio-proxy
        resources:
          requests:
            cpu: 500m
            memory: 256Mi
          limits:
            cpu: "2"
            memory: 1Gi

QA-11 — RHBK NeuroFace

Endpoints de login biométrico (dev/test)

flowchart TD
  A["For each env:\ndev / test"] --> B["For each cluster:\neast / west"]
  B --> C["curl RHBK\n/realms/neuroface"]
  C --> D{"HTTP\n200?"}
  D -- Yes --> E["RHBK healthy"]
  D -- No --> F["RHBK down"]
  E --> G["curl .well-known\n/openid-configuration"]
  G --> H{"HTTP\n200?"}
  H -- Yes --> I["OIDC OK"]
  H -- No --> J["OIDC down"]
  I --> K{"All realm +\nOIDC OK?"}
  F --> K
  J --> K
  K -- Yes --> PASS["PASS"]
  K -- No --> FAIL["FAIL"]
app-nfl-wallet-acm-cluster-decision.yaml RHBK Helm values
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: nfl-wallet
  namespace: openshift-gitops
spec:
  generators:
  - matrix:
      generators:
      - clusterDecisionResource:
          configMapRef: acm-placement
          labelSelector:
            matchLabels:
              cluster.open-cluster-management.io/placement: nfl-wallet-gitops-placement
          requeueAfterSeconds: 180
      - list:
          elements:
          - env: dev
            chartVersion: "0.1.3"
          - env: test
            chartVersion: "0.1.3"
          - env: prod
            chartVersion: "0.1.1"
  template:
    metadata:
      name: 'nfl-wallet-{{env}}-{{name}}'
    spec:
      project: default
      sources:
      - repoURL: 'https://github.com/maximilianoPizarro/nfl-wallet-gitops.git'
        targetRevision: HEAD
        path: 'nfl-wallet/overlays/{{env}}-{{name}}'
      - repoURL: 'https://maximilianopizarro.github.io/NFL-Wallet'
        chart: nfl-wallet
        targetRevision: '{{chartVersion}}'
      destination:
        server: '{{server}}'
        namespace: 'nfl-wallet-{{env}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
        - CreateNamespace=true
        - ServerSideApply=true
overlays/test/oidc-policy-customers.yaml OIDC
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: oidc-api-customers
  namespace: nfl-wallet-test
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: nfl-wallet-api-customers
  rules:
    authentication:
      oidc-rhbk:
        jwt:
          issuerUrl: https://nfl-wallet-rhbk-neuroface-nfl-wallet-test.apps.cluster-east.example.com/realms/neuroface
overlays/test/oidc-policy-bills.yaml OIDC
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: oidc-api-bills
  namespace: nfl-wallet-test
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: nfl-wallet-api-bills
  rules:
    authentication:
      oidc-rhbk:
        jwt:
          issuerUrl: https://nfl-wallet-rhbk-neuroface-nfl-wallet-test.apps.cluster-east.example.com/realms/neuroface
overlays/test/oidc-policy-raiders.yaml OIDC
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: oidc-api-raiders
  namespace: nfl-wallet-test
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: nfl-wallet-api-raiders
  rules:
    authentication:
      oidc-rhbk:
        jwt:
          issuerUrl: https://nfl-wallet-rhbk-neuroface-nfl-wallet-test.apps.cluster-east.example.com/realms/neuroface

QA-12 — Canary Deployment

Prod = v0.1.1, Canary = v0.1.3 vía hostname separado

flowchart TD
  A["For each cluster:\neast / west"] --> B["curl prod URL\nnfl-wallet-prod.*"]
  A --> C["curl canary URL\nnfl-wallet-canary.*"]
  B --> D{"HTTP\n200?"}
  D -- Yes --> E["Check body:\nno login/keycloak"]
  D -- 000 --> F["WARNING: Timeout\nsandbox latency"]
  E --> G{"No login\nfound?"}
  G -- Yes --> H["Prod = v0.1.1"]
  G -- No --> I["Wrong version"]
  C --> J{"HTTP\n200?"}
  J -- Yes --> K["Canary reachable"]
  J -- 000 --> L["WARNING: No Route\nor timeout"]
  H --> M{"Prod verified\nAND canary\nreachable?"}
  K --> M
  M -- Both --> PASS["PASS"]
  M -- One --> P2["PASS: partial"]
  M -- Error --> FAIL["FAIL"]
overlays/prod/canary-httproute.yaml HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: nfl-wallet-webapp-canary
  namespace: nfl-wallet-prod
spec:
  parentRefs:
  - name: nfl-wallet-gateway
  hostnames:
  - "nfl-wallet-canary.apps.cluster-east.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: webapp
      port: 5173
base-canary/canary-route.yaml Route
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: nfl-wallet-canary
spec:
  host: nfl-wallet-canary.apps.cluster-east.example.com
  to:
    kind: Service
    name: nfl-wallet-gateway-istio
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect

QA-13 — Resource Scaling

RHBK, NeuroFace y Gateway con recursos escalados

flowchart TD
  A["For each ns:\nnfl-wallet-dev / test"] --> B["oc get deploy\nnfl-wallet-rhbk-neuroface"]
  B --> C{"CPU = 1\nor 1000m?"}
  C -- Yes --> D["RHBK scaled"]
  C -- 500m --> E["WARNING: Chart default\nre-apply ApplicationSet"]
  A --> F["oc get deploy\nneuroface-backend"]
  F --> G{"CPU = 1\nor 1000m?"}
  G -- Yes --> H["NeuroFace scaled"]
  G -- 100m --> I["WARNING: Chart default\nre-apply ApplicationSet"]
  A --> J["oc get deploy\nnfl-wallet-gateway-istio"]
  J --> K{"CPU = 1\nor 1000m?"}
  K -- Yes --> L["Gateway scaled"]
  K -- N/A --> M["WARNING: Apply\ngateway-resources.yaml"]
  D --> R{"All\nscaled?"}
  H --> R
  L --> R
  R -- Yes --> PASS["PASS"]
  R -- No --> FAIL["FAIL"]
app-nfl-wallet-acm-cluster-decision.yaml rhbk.resources
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: nfl-wallet
  namespace: openshift-gitops
spec:
  generators:
  - matrix:
      generators:
      - clusterDecisionResource:
          configMapRef: acm-placement
          labelSelector:
            matchLabels:
              cluster.open-cluster-management.io/placement: nfl-wallet-gitops-placement
          requeueAfterSeconds: 180
      - list:
          elements:
          - env: dev
            chartVersion: "0.1.3"
          - env: test
            chartVersion: "0.1.3"
          - env: prod
            chartVersion: "0.1.1"
  template:
    metadata:
      name: 'nfl-wallet-{{env}}-{{name}}'
    spec:
      project: default
      sources:
      - repoURL: 'https://github.com/maximilianoPizarro/nfl-wallet-gitops.git'
        targetRevision: HEAD
        path: 'nfl-wallet/overlays/{{env}}-{{name}}'
      - repoURL: 'https://maximilianopizarro.github.io/NFL-Wallet'
        chart: nfl-wallet
        targetRevision: '{{chartVersion}}'
      destination:
        server: '{{server}}'
        namespace: 'nfl-wallet-{{env}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
        - CreateNamespace=true
        - ServerSideApply=true
kuadrant-system/resource-requirements.yaml Authorino + Limitador
apiVersion: operator.authorino.kuadrant.io/v1beta2
kind: Authorino
metadata:
  name: authorino
  namespace: kuadrant-system
spec:
  replicas: 1
  resources:
    requests:
      cpu: 500m
      memory: 256Mi
    limits:
      cpu: "2"
      memory: 1Gi
---
apiVersion: limitador.kuadrant.io/v1alpha1
kind: Limitador
metadata:
  name: limitador
  namespace: kuadrant-system
spec:
  replicas: 1
  resources:
    requests:
      cpu: 250m
      memory: 128Mi
    limits:
      cpu: "1"
      memory: 256Mi
kuadrant-system/gateway-resources.yaml Gateway Proxy
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfl-wallet-gateway-istio
  namespace: nfl-wallet-prod
spec:
  template:
    spec:
      containers:
      - name: istio-proxy
        resources:
          requests:
            cpu: 500m
            memory: 256Mi
          limits:
            cpu: "2"
            memory: 1Gi

14. Referencia de API

Servicio Puerto (Service) Puerto (Pod) Path API Documentación
api-customers 8080 8080 /api /api/swagger
api-bills 8081 8080 /api /api/swagger
api-raiders 8082 8080 /api /api/swagger
webapp 5173 8080 / N/A
Kiali Dashboard 443 N/A / Hub Centralizado
Grafana 443 N/A / Hub Centralizado

URLs por Ambiente

Ambiente Patrón de Host Ejemplo
Dev nfl-wallet-dev.apps.<clusterDomain> nfl-wallet-dev.apps.cluster-thmg4...opentlc.com
Test nfl-wallet-test.apps.<clusterDomain> nfl-wallet-test.apps.cluster-thmg4...opentlc.com
Prod nfl-wallet-prod.apps.<clusterDomain> nfl-wallet-prod.apps.cluster-thmg4...opentlc.com

API Keys por Ambiente

Ambiente Key (customers) Header
Dev No requerida
Test nfl-wallet-customers-key X-Api-Key
Prod nfl-wallet-customers-key X-Api-Key

15. Troubleshooting

Pods no se comunican (Error 503)

Causa: Componentes del dataplane de Ambient Mode inestables.

# Reiniciar CNI pods
oc -n istio-cni delete pod -l k8s-app=istio-cni-node

# Reiniciar ztunnel
oc -n ztunnel delete pod -l app=ztunnel

ArgoCD indica “Out of Sync”

Causa: Alguien modificó un recurso directamente en el clúster.

Solución: Forzar sincronización en ArgoCD → Sync → Replace.

HTTP 403 Forbidden

Causa: AuthPolicy activa pero no se envía la API Key, o el acceso está pendiente de aprobación en RHDH.

Solución: Verificar el header X-API-Key en las peticiones. Revisar estado de aprobación en Developer Hub.

HTTP 500 en /api-bills con AuthPolicy

Causa: AuthConfig en istio-system no está correctamente vinculado al host del gateway.

# Verificar AuthConfig
kubectl get authconfig -n istio-system

# Parchear el host si es necesario
kubectl patch authconfig <HASH> -n istio-system \
  --type=json -p='[{"op":"replace","path":"/spec/hosts","value":["<gateway-host>"]}]'

SNO CSR Approval Failure

oc get csr | grep Pending | awk '{print $1}' | xargs oc adm certificate approve

CORS Failure (Frontend/Backend)

Solución: Asegurar que CORS__AllowedOrigins en los deployments de API coincide con la URL pública de la webapp.

HTTP 503 “Application is not available”

Sin datos en Grafana

  1. Generar tráfico: ./observability/run-tests.sh loop
  2. Verificar Prometheus targets (Status → Targets)
  3. Verificar labels de Service: kubectl get svc -n nfl-wallet-prod -l gateway.networking.k8s.io/gateway-name
  4. En Grafana Explore, ejecutar istio_requests_total

16. Publicar en Artifact Hub

# 1. Empaquetar el chart
helm package helm/nfl-wallet --destination docs/

# 2. Actualizar el índice del repo Helm
cd docs
helm repo index . --url https://maximilianopizarro.github.io/NFL-Wallet --merge index.yaml
cd ..

# 3. Commit y push

Los usuarios pueden instalar:

helm repo add nfl-wallet https://maximilianopizarro.github.io/NFL-Wallet
helm repo update
helm install nfl-wallet nfl-wallet/nfl-wallet -n nfl-wallet

Anexo — Capturas de Pantalla

A.1 Aplicación Wallet

Wallet Landing Wallet Landing Page — Punto de entrada de la aplicación web Stadium Wallet.

Customer List Lista de Clientes — Seleccionar un cliente para ver sus wallets por equipo.

Wallet Balances Balances de Wallets — Buffalo Bills y Las Vegas Raiders: saldos y transacciones.

QR Payment Flujo de Pago QR — Pago desde una wallet de equipo.

Load Balance Carga de Saldo — Agregar fondos a una wallet de equipo.

A.2 Plataforma y Observabilidad

Dashboards de métricas: Grafana agrega las métricas emitidas por los Waypoint Proxies y ztunnel, permitiendo monitorear request rate, response codes, duración y error rate para cada ambiente. El dashboard utiliza la variable namespace para filtrar entre dev, test y prod.

Grafana Dashboard Grafana — Dashboard “Stadium Wallet – All environments”: request rate, response codes, duration, error rate por ambiente.

Topología y tráfico del mesh: Kiali proporciona visualización en tiempo real del grafo de servicios dentro del mesh. Los nodos representan workloads y los bordes muestran tráfico HTTP observado con tasas de éxito/error. Esto permite diagnosticar problemas de conectividad sin necesidad de inspeccionar logs individuales.

Service Mesh Grafana Kiali — Grafo de servicios con tráfico multi-namespace (dev/test/prod) y métricas HTTP.

Kiali Topology Kiali — Topología detallada del Service Mesh con leyenda de nodos, workloads y servicios.

Kiali Multi-Cluster Kiali — Service Graph multi-cluster mostrando tráfico entre East y West con gateways Istio.

Administración del mesh desde OpenShift Console: La vista integrada de Service Mesh en OpenShift Console muestra los control planes, gateways y waypoints activos, proporcionando un overview operativo sin salir de la consola de administración.

Service Mesh Overview OpenShift Console — Vista del Service Mesh: control planes, gateways, waypoints y componentes.

APIs expuestas: Las APIs de Stadium Wallet se documentan automáticamente via OpenAPI (Swagger). Cada microservicio expone su especificación, que luego RHDH descubre y registra en el catálogo de Backstage.

API Customers API Customers — Swagger UI del servicio de clientes.

API Bills API Bills — Swagger UI del servicio de Buffalo Bills wallet.

A.3 Red Hat Developer Hub — Plugin Kuadrant

Portal de autoservicio para desarrolladores: Las siguientes capturas muestran el flujo completo dentro de RHDH: desde el descubrimiento de la API y sus políticas, hasta la solicitud de acceso y la obtención de credenciales. Este flujo reemplaza el proceso manual de crear tickets y esperar aprovisionamiento — el desarrollador obtiene su API Key en minutos, con el Tier de rate limiting ya configurado.

RHDH Policies RHDH Kuadrant Plugin — Pestaña Policies: PlanPolicy y AuthPolicy descubiertas para nfl-wallet-api-customers. Tiers efectivos: gold (1000/día), silver (500/día), bronze (100/día).

RHDH API Definition RHDH Kuadrant Plugin — Pestaña Definition: Stadium Wallet - Customers API v1 (OAS 3.0) con endpoints documentados y selector de servidor por ambiente.

RHDH Request Access RHDH Kuadrant Plugin — Modal de solicitud de acceso: selección de Tier silver (500 per daily), campo de Use Case y botón Submit Request.

RHDH API Keys RHDH Kuadrant Plugin — API Keys aprovisionadas con Tier silver aprobado, clave generada y ejemplos de código en cURL, Node.js, Python y Go.

Observabilidad multi-cluster con ACM: ACM no solo gestiona el despliegue de workloads sino también la infraestructura de observabilidad. El ApplicationSet observability-east-west despliega Grafana, dashboards, datasources y routes de forma idéntica en ambos clústeres, garantizando que la experiencia de monitoreo sea consistente independientemente de dónde se ejecuten los servicios.

ACM Observability ACM — ApplicationSet observability-east-west: topología con Configmap, Grafana, GrafanaDashboard, GrafanaDataSource, Namespace y Route para observabilidad centralizada.

Grafana Multi-Cluster Grafana Multi-Cluster — Dashboard “Stadium Wallet - All environments” con filtro por cluster (East/West): request rate, response codes, request duration (p50/p99), total requests, error rate y request rate por servicio.

GitOps y gestión de clústeres: ArgoCD reconcilia el estado declarado en Git con el estado real de cada clúster. ACM complementa esto proporcionando la vista de topología del hub y los managed clusters, y el estado de cada Application distribuida.

GitOps ArgoCD OpenShift GitOps (ArgoCD) — Applications y estado de sincronización.

ACM Topology ACM — Topología con hub y managed clusters (East, West).

ACM Applications ACM — ApplicationSet y las 6 Applications generadas.

ACM Overview ACM — Vista general del Advanced Cluster Management.

ACM Detail ACM — Detalle de clústeres managed y su estado.

Métricas y trazas detalladas: El stack de observabilidad proporciona múltiples niveles de detalle: desde métricas agregadas del gateway (request rate, error rate) hasta trazas distribuidas individuales que muestran el recorrido completo de una request a través de los servicios. Esto permite investigar problemas desde lo general (¿hay un aumento de errores?) hasta lo específico (¿qué request falló y en qué servicio?).

Observability Observabilidad — Consola OpenShift con métricas del monitoring stack.

Observability Metrics Métricas del gateway (request rate, success y error rates) disponibles tras configurar PodMonitor/ServiceMonitor.

Observability Detail Vista detallada de observabilidad con métricas Istio/Envoy del gateway Stadium Wallet.

Análisis de tráfico y trazas distribuidas: Las trazas distribuidas (via TempoStack/Jaeger) muestran el tiempo que cada hop toma dentro de una request, permitiendo identificar cuellos de botella. El análisis de tráfico complementa las trazas con una vista de flujo de requests, latencia y distribución de códigos de respuesta.

Traffic Analysis Análisis de tráfico — Flujo de requests, latencia y códigos de respuesta.

Jaeger Traces Jaeger — Trazas distribuidas de los servicios del Stadium Wallet.


Stadium Wallet v2.0 — Documentación generada para GitHub Pages
Stack: OpenShift 4.20+ · GitOps (ArgoCD) · OSSM 3.2 (Ambient Mode) · Kuadrant · Gateway API · RHDH · Vue.js · .NET 8
Owner: Maximiliano Pizarro, Specialist Solution Architect at Red Hat · Infra & Service Mesh: Francisco Raposo, Senior Specialist Solution Architect at Red Hat