Skip to content

Cloud Architecture

Anderson Fabian Garcia Nieto edited this page Jul 9, 2026 · 3 revisions

Cloud Architecture

Linker1 se ejecuta en Oracle Cloud Infrastructure (OCI), región sa-bogota-1. La arquitectura sigue un modelo de VM única con acceso controlado mediante Bastion, sin IP pública expuesta directamente.


Diagrama de Arquitectura

┌─────────────────────────────────────────────────────────────────────────────┐
│                              INTERNET                                       │
│                                                                             │
│   Usuarios ──────────────────────────────────────────────────┐              │
│                                                              │              │
│   Grafana Cloud (Observabilidad)         LaunchDarkly        │              │
│   ┌─────────────────────────┐            ┌──────────┐        │              │
│   │  Loki   (Logs)          │◀───OTLP────│ Feature  │        │              │
│   │  Mimir  (Métricas)      │  http/     │  Flags   │        │              │
│   │  Tempo  (Traces)        │  protobuf  └──────────┘        │              │
│   │  Synthetic Monitoring   │                                │              │
│   └─────────────────────────┘                                │              │
│                                                              │              │
└──────────────────────────────────────────────────────────────┼──────────────┘
                                                               │
┌──────────────────────────────────────────────────────────────┼──────────────┐
│                     OCI — sa-bogota-1                        │              │
│                                                              │              │
│   ┌───────────────┐         ┌───────────────────────────────┼────────────┐ │
│   │  OCI Bastion  │         │       VCN / Subnet (privada)  │            │ │
│   │               │─SSH───▶ │                               │            │ │
│   │  (Managed SSH │         │   ┌───────────────────────────▼──────────┐ │ │
│   │   Sessions)   │         │   │         VM (vm-linker1-app)          │ │ │
│   └───────┬───────┘         │   │         Ubuntu + Java 21             │ │ │
│           │                 │   │                                      │ │ │
│           │                 │   │   ┌──────────┐      ┌─────────────┐ │ │ │
│           │                 │   │   │  Nginx   │:80──▶│  Javalin    │ │ │ │
│           │                 │   │   │  (proxy) │      │  :8080      │ │ │ │
│           │                 │   │   └──────────┘      │             │ │ │ │
│           │                 │   │                      │  ┌────────┐│ │ │ │
│   ┌───────▼───────┐        │   │                      │  │ SQLite ││ │ │ │
│   │ GitHub Actions│        │   │                      │  │ (.db)  ││ │ │ │
│   │ (deploy via   │        │   │                      │  └────────┘│ │ │ │
│   │  bastion)     │        │   │                      │            │ │ │ │
│   └───────────────┘        │   │   ┌──────────────┐   │  systemd   │ │ │ │
│                            │   │   │    MySQL      │   │  service   │ │ │ │
│                            │   │   │ (healthcheck) │◀──│            │ │ │ │
│                            │   │   └──────────────┘   └────────────┘ │ │ │
│                            │   │                                      │ │ │
│                            │   └──────────────────────────────────────┘ │ │
│                            │                                            │ │
│                            └────────────────────────────────────────────┘ │
│                                                                          │
│   ┌─────────────────────────┐                                            │
│   │      OCI Vault          │                                            │
│   │  (SSH keys, passwords)  │                                            │
│   └─────────────────────────┘                                            │
│                                                                          │
└──────────────────────────────────────────────────────────────────────────┘

Componentes en Detalle

1. GitHub (Código y CI/CD)

Componente Función
Repositorio co-eiv-devsecops/linker1 — código fuente, IaC, workflows, documentación
GitHub Actions Tres workflows: CI (ci.yml), CD (pipeline.yml), Release (release.yml)
GitHub Packages Registro Maven para publicar artefactos JAR con versión CI
GitHub Releases Artefactos binarios versionados con SemVer para rollback
Branch Protection main y DEV protegidas: PR + approval + CI verde obligatorio

2. OCI Bastion

Aspecto Detalle
Propósito Acceso SSH a la VM sin exponer IP pública
Tipo de sesión Managed SSH (bastion session create-managed-ssh)
Usado por pipeline.yml (deploy + rollback), manual vía Cloud Shell
TTL 1800 segundos (30 minutos) por sesión
Autenticación Par de claves SSH (DEPLOYMENT_PRIVATE_KEY / DEPLOYMENT_PUBLIC_KEY)

El Bastion es el único punto de entrada a la VM. No existe ninguna otra forma de acceder: ni IP pública, ni VPN, ni port forwarding directo.

3. VM de Producción (vm-linker1-app)

Aspecto Detalle
Shape VM.Standard.E5.Flex (1 OCPU, 4 GB RAM)
OS Ubuntu (cloud-init provisioning)
IP Pública assign_public_ip = false — sin IP pública directa
Acceso público A través del dominio 1.n-la-c.app (DNS configurado por el instructor)
Provisioning cloud-init.yaml instala Java 21, Maven, Nginx; clona el repo; build inicial

4. Nginx (Reverse Proxy)

server {
    listen 80 default_server;
    server_name _;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
  • Escucha en puerto 80 (HTTP público)
  • Reenvía todo el tráfico a Javalin en puerto 8080
  • Propaga headers de IP real y protocolo para logging

5. Linker1 (Aplicación Java)

Aspecto Detalle
Ubicación del JAR /opt/linker1/linker1.jar
Base de datos /var/lib/linker1/linker1.db (SQLite)
Frontend Servido desde recursos empaquetados en el JAR
Puerto 8080 (configurable vía LINKER_PORT)
Proceso Gestionado por systemd (linker1.service)

6. systemd (linker1.service)

[Unit]
Description=Linker1 URL Shortener
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/linker1
ExecStart=/usr/bin/java -jar /opt/linker1/linker1.jar
Restart=always
RestartSec=5
User=ubuntu
Environment="LINKER_PORT=8080"
Environment="LINKER_DB_PATH=/var/lib/linker1/linker1.db"
Environment="LD_SDK_KEY=..."
Environment="OTEL_SERVICE_NAME=linker1"
Environment="OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf"
# ... más variables opcionales

[Install]
WantedBy=multi-user.target
  • Restart=always: si el proceso muere, se reinicia automáticamente.
  • RestartSec=5: espera 5 segundos antes del restart (evita restart loops).
  • Variables de entorno inyectan toda la configuración en runtime.

7. Grafana Cloud (Observabilidad)

Servicio Función Protocolo
Loki Almacenamiento y consulta de logs OTLP → http/protobuf
Mimir Almacenamiento y consulta de métricas OTLP → http/protobuf
Tempo Almacenamiento y consulta de traces distribuidos OTLP → http/protobuf
Synthetic Monitoring Polling cada 5 min a /healthz HTTP

El endpoint OTLP se configura como https://otlp-gateway-<region>.grafana.net/otlp, autenticado con headers Authorization=Basic <token>.

8. LaunchDarkly (Feature Flags)

  • SDK server-side (Java) conectado con LD_SDK_KEY
  • Evaluación de flags en tiempo real sin restart de la aplicación
  • Modo offline disponible para testing (LD_OFFLINE=true)
  • Feature flag actual: new-ui (toggle entre interfaz V1 y V2)

9. MySQL (Backing Service)

  • Usado exclusivamente para el healthcheck (GET /healthzSELECT 1)
  • No almacena datos de la aplicación (eso lo hace SQLite)
  • Si no está configurado, /healthz retorna 503 pero la app sigue funcional

Infraestructura como Código (IaC)

La infraestructura está definida en Terraform (infra/):

Archivo Contenido
provider.tf Configuración del provider OCI
variables.tf 15 variables (región, shape, secrets, etc.)
main.tf Instancia OCI (oci_core_instance) + cloud-init
cloud-init.yaml Provisioning: Java 21, Maven, Nginx, clone + build
outputs.tf Output del OCID de la instancia
terraform.tfvars.example Template de variables

Seguridad en Terraform

Los secrets que contienen caracteres especiales (%, ") son escapados antes de ser inyectados en la unidad systemd:

systemd_escape = { for k, v in {
    ld_sdk_key = var.ld_sdk_key
    // ...
  } : k => replace(replace(v, "%", "%%"), "\"", "\\\"")
}

Esta misma lógica de escape existe en deploy.sh y pipeline.yml, asegurando consistencia entre los tres mecanismos de despliegue.


Flujo de una Request

Usuario                    DNS/CDN             VM
  │                          │                  │
  │──GET 1.n-la-c.app/abc──▶│                  │
  │                          │──:80──▶ Nginx    │
  │                          │         │        │
  │                          │    proxy_pass     │
  │                          │         │        │
  │                          │    :8080 ▼        │
  │                          │     Javalin       │
  │                          │         │        │
  │                          │   LinkRoutes      │
  │                          │   ├─ traceResolve │  ← span: link.resolve
  │                          │   ├─ service.get  │  ← span: link.resolve.lookup
  │                          │   ├─ SQLite query │
  │                          │   └─ redirect 301 │
  │                          │         │        │
  │◀────301 Location: url────│◀────────┘        │
  │                                             │
  │                          Grafana Cloud       │
  │                          ├─ trace exportado  │
  │                          ├─ métrica contada  │
  │                          └─ log registrado   │

Resiliencia de la Arquitectura

Mecanismo Qué protege
systemd Restart=always Auto-recuperación si el proceso Java muere
Rollback automático Si el deploy falla, se restaura la versión anterior
Health check /healthz Monitoreo activo de backing services
Synthetic Monitoring Detección proactiva de caídas cada 5 minutos
Bastion (sin IP pública) Superficie de ataque reducida
Graceful degradation OTLP/MySQL ausentes = features degradados, no crash
Branch protection Previene que código roto llegue a producción

Clone this wiki locally