Version: 1.0
Fecha: 10 de Junio, 2026
Estado: NORMATIVO - Procedimiento de Referencia
Arquitecto: Carlos Alberto Torres Camargo
Audiencia: Desarrolladores Backend (Senior, Semi-Senior y Junior)
Este flujo describe como ejecutar un solo microservicio en tu maquina consumiendo la infraestructura de plataforma que ya esta corriendo en el servidor (Eureka, Config-Server, OAuth2 y Gateway), en lugar de levantar todo el stack localmente.
Es la alternativa ligera al Flujo de Arranque Local, que levanta toda la infraestructura y los servicios de plataforma con Docker en el propio PC.
| Enfoque | Que corre local | Cuando conviene |
|---|---|---|
| Arranque Local (todo local) | BD + Eureka + Config + Gateway + OAuth2 + servicio | Trabajo offline, sin VPN, o cambios en la plataforma misma |
| Prueba local con tunel SSH (este flujo) | Solo tu microservicio | Iteracion rapida sobre un servicio contra una plataforma real y poblada (DEV/QA) |
NOTA SOBRE RUTAS (importante). Las rutas que aparecen en este documento (
~/code/centrica/backend/,~/environment/<dominio>/.env,target/<servicio>.jar, etc.) reflejan la estructura de directorios del workspace de referencia del arquitecto, no una ruta obligatoria. Cada desarrollador debe ajustarlas a la raiz y el layout de su propia maquina. Lo que importa es la relacion entre las piezas (script de tunel +local-dev.env+ jar del servicio + espejo del.envdel dominio), no la ruta absoluta. Como toda la parametrizacion vive enlocal-dev.env(seccion 5), basta con poner ahi tus rutas reales (SERVICE_JAR,DOMAIN_ENV_FILE) y el script funciona igual.
El tunel SSH usa un unico proceso (ControlMaster) que reenvia a localhost los puertos de la infraestructura expuestos en el nodo. Tu servicio local "cree" que la plataforma corre en su propia maquina.
| Servicio de plataforma | Puerto | Para que lo usa tu servicio |
|---|---|---|
| Eureka | 8761 | Registro y descubrimiento de servicios |
| Config-Server | 8890 | Resolucion de configuracion en el bootstrap (config import) |
| OAuth2 | 8787 | Validacion de tokens / login |
| Gateway | 8090 | Consumo de otros modulos (nebula-*, simappe-admin, simappe-client) |
Regla: los servicios
nebula-*ysimappe-clientno exponen puerto host propio en el nodo (0/tcp): se consumen siempre via Gateway (localhost:8090), nunca por puerto directo.
Cuando tu servicio local X llama a otros servicios Y y Z del ecosistema (p.ej. nebula-masters, simappe-admin, nebula-shared clients), esas llamadas salen por el Gateway (localhost:8090 a traves del tunel), nunca a un puerto directo de Y/Z. El Gateway las enruta a las instancias reales de Y y Z que corren en el nodo.
Motivo: los nebula-* y simappe-* no exponen puerto host; el unico punto de entrada es el Gateway. Es la misma razon por la que simappe.admin.url debe ser una URL del Gateway (ver incidente OAuth2).
Para que las URLs de cliente de X resuelvan al Gateway tunelado, depende del perfil:
Perfil (APP_PROFILE) |
URLs de plataforma en el config | Que necesitas |
|---|---|---|
dev / qa (estandar, recomendado) |
usan hostnames Docker (simappe-gateway-server:8090, simappe-eureka-server:8761, ...) |
Obligatorio: registrar esos hostnames en /etc/hosts apuntando a 127.0.0.1 para que resuelvan por el tunel |
localhost (alternativa sin /etc/hosts) |
ya apuntan a localhost:8090 / 8761 / 8890 / 8787 |
Nada extra: el tunel las reenvia |
Como el flujo estandar usa perfil dev/qa, es obligatorio registrar estos hostnames en /etc/hosts (una sola vez) para que las URLs Docker resuelvan por el tunel.
1. Archivo a editar: /etc/hosts (requiere sudo). La linea a agregar es:
127.0.0.1 simappe-gateway-server simappe-eureka-server simappe-config-server simappe-oauth2-server simappe-admin
2. Registro automatico (idempotente) — pega este comando una sola vez; solo agrega la entrada si no existe:
LINE='127.0.0.1 simappe-gateway-server simappe-eureka-server simappe-config-server simappe-oauth2-server simappe-admin'
grep -qF 'simappe-gateway-server' /etc/hosts || echo "$LINE" | sudo tee -a /etc/hosts
3. Verificar:
getent hosts simappe-gateway-server # -> debe responder 127.0.0.1
Sin este registro, el servicio local no resuelve la plataforma y falla el bootstrap/llamadas.
Solo se reenvian los 4 puertos de plataforma. Si tu servicio consume ademas backing services directos que NO pasan por el Gateway (BD, Kafka, etc.), debes anadirlos a
TUNNEL_PORTSo mapear sus hostnames a donde sean alcanzables (a127.0.0.1via tunel, o a la IP del nodo10.1x0.0.2directo por VPN).
| # | Requisito | Verificacion |
|---|---|---|
| 1 | VPN Centrica conectada | Acceso a la red del nodo. Ver Diagnostico Conexion VPN |
| 2 | Acceso SSH al nodo (alias en ~/.ssh/config) |
ssh nodo-01 hostname responde |
| 3 | Servicio compilado (jar en target/) |
mvn -o clean package verde |
| 4 | Espejo local de los .env del dominio |
Existe ~/environment/<dominio>/.env. Ver Variables de Entorno Local |
| 5 | Hostnames Docker registrados en /etc/hosts (obligatorio con perfil dev/qa) |
simappe-gateway-server, simappe-eureka-server, simappe-config-server, simappe-oauth2-server, simappe-admin → 127.0.0.1. Ver seccion 2 |
~/.ssh/config recomendadoSe usan las IP internas de cada nodo (no el host publico). Asi el acceso solo funciona conectado a la VPN — se fuerza el uso de VPN siempre y se evita depender del NAT publico (que ademas no hace hairpin para el tunel).
# DEV — IP interna (solo accesible por VPN)
Host nodo-01
HostName 10.120.0.2
Port 2202
User sysadmin
IdentityFile ~/.ssh/id_ed25519
# QA — IP interna (solo accesible por VPN)
Host nodo-02
HostName 10.130.0.2
Port 2203
User arquitecto
IdentityFile ~/.ssh/id_ed25519
cd ~/code/centrica/backend
./nebula-tunnel.sh up
./nebula-tunnel.sh status # OK eureka/config/oauth2/gateway
Sin DB_ENCRYPTION_KEY todo request tenant-scoped revienta con 500 (ConnectionSchemaCipher: NO inicializado). Hay que sourcear el .env del dominio del servicio:
set -a; . ~/environment/nebula/business-domains/finance/.env; set +a
El
.envpuede traer unJAVA_TOOL_OPTIONSmulti-token que al sourcear en bash emite un warning inocuo. Detalle completo en Variables de Entorno Local.
APP_PROFILE=dev PORT=8081 java -jar target/<servicio>.jar
Con perfil dev (config real de DEV; qa para nodo-02), el servicio resuelve la plataforma por los hostnames Docker — que gracias al /etc/hosts (prerequisito #5) apuntan a 127.0.0.1 y viajan por el tunel: Config (:8890), Eureka (:8761), OAuth2 (:8787) y Gateway (:8090).
Perfil: el estandar es
APP_PROFILE=dev(oqa), que requiere el registro/etc/hostsde la seccion 2. Alternativa sin/etc/hosts:APP_PROFILE=localhost, cuyas URLs ya sonlocalhost.
Puerto del servicio local (
PORT): es el puerto donde escucha TU servicio (se inyecta por la env varPORT→server.port; en el ejemplo8081). Reglas:
- Debe ser un puerto libre y distinto de los del tunel (
8761 / 8890 / 8787 / 8090) y de cualquier otro servicio local que corras en paralelo (usa8081,8082, ... uno por servicio).- Si tu servicio se registra en Eureka, anuncia ese puerto y el host de tu maquina; tenlo presente por la consideracion de Eureka compartido (otros podrian intentar enrutar hacia tu instancia local).
curl -s localhost:8081/actuator/health # {"status":"UP"}
# y un request tenant-scoped real para confirmar DB_ENCRYPTION_KEY
./nebula-tunnel.sh down
Los scripts son archivos fijos y listos para usar (no plantillas que haya que editar). Toda la parametrizacion vive en un unico archivo .env de configuracion que el desarrollador crea con los valores de su servicio y nodo. Para cambiar de servicio o de ambiente solo se edita ese .env; los scripts nunca se tocan.
local-dev.env — unico archivo que crea/edita el desarrollador# ============================================================
# local-dev.env — configuracion de prueba local con tunel SSH.
# Crea este archivo con TUS valores. NO lo subas a Git.
# ============================================================
# --- Nodo destino (alias de ~/.ssh/config o IP) ---
SSH_HOST=nodo-01 # DEV: nodo-01 | QA: nodo-02
# --- Puertos de infra a reenviar (formato "nombre:puerto", separados por espacio) ---
TUNNEL_PORTS="eureka:8761 config:8890 oauth2:8787 gateway:8090"
# --- Servicio a ejecutar ---
SERVICE_JAR=target/nebula-treasury.jar
APP_PROFILE=dev # dev (nodo-01) | qa (nodo-02). Estandar; requiere /etc/hosts (seccion 2). Alternativa sin hosts: localhost
SERVICE_PORT=8081 # puerto local de TU servicio; libre y != a 8761/8890/8787/8090 ni a otro servicio local
# --- Archivo .env del dominio con los secretos (DB_ENCRYPTION_KEY, etc.) ---
DOMAIN_ENV_FILE=${HOME}/environment/nebula/business-domains/finance/.env
| Parametro | Que define |
|---|---|
SSH_HOST |
Alias SSH (o IP) del nodo destino. Cambia DEV↔QA sin tocar el script |
TUNNEL_PORTS |
Lista nombre:puerto de los servicios de plataforma a reenviar |
SERVICE_JAR |
Ruta al jar del microservicio a correr |
APP_PROFILE |
Perfil Spring del ambiente (dev/qa) |
SERVICE_PORT |
Puerto local donde escucha tu servicio |
DOMAIN_ENV_FILE |
.env del dominio que aporta DB_ENCRYPTION_KEY y demas secretos |
Versiona en el repo un
local-dev.env.examplecon estos campos vacios/de ejemplo; ellocal-dev.envreal va en.gitignore.
infra-tunnel.sh — archivo listo (se alimenta de local-dev.env)#!/usr/bin/env bash
# infra-tunnel.sh {up|down|status|restart}
# Lee la configuracion de ./local-dev.env (o la ruta en $TUNNEL_CONF). No se edita el script.
set -euo pipefail
CONF="${TUNNEL_CONF:-$(dirname "$0")/local-dev.env}"
[ -f "$CONF" ] || { echo "No existe la config: $CONF"; exit 1; }
set -a; . "$CONF"; set +a
: "${SSH_HOST:?Falta SSH_HOST en $CONF}"
: "${TUNNEL_PORTS:?Falta TUNNEL_PORTS en $CONF}"
SOCKET="${HOME}/.ssh/cm-infra-tunnel-${SSH_HOST}.sock"
read -ra PORTS <<< "$TUNNEL_PORTS"
is_up() { ssh -S "$SOCKET" -O check "$SSH_HOST" 2>/dev/null; }
up() {
is_up && { echo "Tunel ya activo hacia $SSH_HOST."; status; return 0; }
local fwd=()
for s in "${PORTS[@]}"; do fwd+=( -L "127.0.0.1:${s#*:}:127.0.0.1:${s#*:}" ); done
ssh -fN -M -S "$SOCKET" \
-o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes \
"${fwd[@]}" "$SSH_HOST"
echo "Tunel ARRIBA hacia $SSH_HOST. Modulos nebula/client -> gateway."
}
down() { is_up && { ssh -S "$SOCKET" -O exit "$SSH_HOST" 2>/dev/null || true; echo "Tunel CERRADO."; } || echo "Sin tunel activo."; }
status() { is_up || { echo "Tunel INACTIVO."; return; }; for s in "${PORTS[@]}"; do (exec 3<>"/dev/tcp/127.0.0.1/${s#*:}") 2>/dev/null && echo " OK ${s%%:*} (:${s#*:})" || echo " FALLA ${s%%:*} (:${s#*:})"; done; }
case "${1:-}" in
up) up ;; down) down ;; restart) down; sleep 1; up ;; status) status ;;
*) echo "Uso: $0 {up|down|restart|status}"; exit 1 ;;
esac
run-local.sh — archivo listo (tunel + secretos del dominio + arranque)#!/usr/bin/env bash
# run-local.sh — levanta el tunel, carga los secretos del dominio y corre el servicio.
# Toda la parametrizacion vive en ./local-dev.env. Sin argumentos.
set -euo pipefail
DIR="$(cd "$(dirname "$0")" && pwd)"
CONF="${TUNNEL_CONF:-$DIR/local-dev.env}"
[ -f "$CONF" ] || { echo "No existe la config: $CONF"; exit 1; }
set -a; . "$CONF"; set +a
: "${SERVICE_JAR:?Falta SERVICE_JAR en $CONF}"
: "${DOMAIN_ENV_FILE:?Falta DOMAIN_ENV_FILE en $CONF}"
TUNNEL_CONF="$CONF" "$DIR/infra-tunnel.sh" up
[ -f "$DOMAIN_ENV_FILE" ] || { echo "No existe DOMAIN_ENV_FILE: $DOMAIN_ENV_FILE"; exit 1; }
set -a; . "$DOMAIN_ENV_FILE"; set +a # exporta DB_ENCRYPTION_KEY y demas secretos
echo "Arrancando ${SERVICE_JAR} (perfil=${APP_PROFILE:-dev}, puerto=${SERVICE_PORT:-8081}, nodo=${SSH_HOST})"
APP_PROFILE="${APP_PROFILE:-dev}" PORT="${SERVICE_PORT:-8081}" java -jar "$SERVICE_JAR"
# 1) crear tu config una sola vez (no se versiona)
cp local-dev.env.example local-dev.env && $EDITOR local-dev.env
# 2) todo en uno: tunel + secretos + arranque
./run-local.sh
# o por separado, controlando el tunel
./infra-tunnel.sh up
./infra-tunnel.sh status
./infra-tunnel.sh down
Cambiar de DEV a QA o de servicio = editar
local-dev.env(SSH_HOST,APP_PROFILE,SERVICE_JAR,DOMAIN_ENV_FILE). El script no se toca. Patron verificado en DEV; QA usa el mismo flujo con sus valoresqa.
DB_ENCRYPTION_KEY es obligatoria. El TenantFilter inicializa la conexion multi-esquema cifrada en CADA request tenant-scoped; sin la clave devuelve 500. @IgnoreTenant no lo evita. Siempre sourcear el .env antes de arrancar.eureka.client.register-with-eureka=false si solo necesitas consumir (no ser consumido).localhost:8090, nunca por puerto directo (los nebula-* no exponen puerto host)..env no se versionan (estan en .gitignore) y viven solo en el espejo local ~/environment/. Nunca commitear claves.ServerAliveInterval=30; si la VPN cae, el tunel muere — re-levantar con up.| Sintoma | Causa probable | Solucion |
|---|---|---|
status muestra FALLA en un puerto |
Tunel no levanto ese reenvio (puerto local ocupado) | down + liberar el puerto local + up |
500 ConnectionSchemaCipher: NO inicializado |
No se sourceo el .env (falta DB_ENCRYPTION_KEY) |
set -a; . ~/environment/.../.env; set +a y re-arrancar |
| Bootstrap falla resolviendo config | Tunel abajo o config import no apunta a localhost:8890 |
./nebula-tunnel.sh status; revisar application.yml bootstrap |
| 401/403 en requests | OAuth2 no alcanzable o token invalido | Verificar puerto 8787 en status; re-login |
Connection refused a simappe-admin |
Config del oauth2/servicio apunta al host pelado en vez del Gateway | Ver patron simappe.admin.url = host:port del Gateway sin sufijo |
SSH a nodo-02 da timeout |
Se uso el host publico (NAT sin hairpin) | Usar la IP interna VPN 10.130.0.2:2203 en ~/.ssh/config |
Las rutas locales de esta tabla son del workspace de referencia del arquitecto; ajustalas a tu maquina (ver Nota sobre rutas en la seccion 1). Solo la ruta del servidor es fija.
| Elemento | Ruta (referencia — ajustar a tu workspace) |
|---|---|
| Script de tunel actual (DEV) | ~/code/centrica/backend/nebula-tunnel.sh |
local-dev.env + scripts genericos |
donde tengas tus utilidades locales (ej. junto al servicio o en ~/code/centrica/backend/) |
Espejo local de .env |
~/environment/<dominio>/.env (misma estructura que el servidor) |
.env en el servidor (fijo) |
/home/sysadmin/docker/compose/<dominio>/.env (directiva env_file) |
| Servicios aplicables | Cualquier microservicio nebula-* o simappe-* que resuelva config por Config-Server y se registre en Eureka |
.env y DB_ENCRYPTION_KEY| Version | Fecha | Autor | Descripcion |
|---|---|---|---|
| 1.2.1 | 2026-06-10 | Carlos Torres | Registro de /etc/hosts accionable: ruta del archivo + comando ejecutable idempotente + verificacion. |
| 1.2.0 | 2026-06-10 | Carlos Torres | Nota sobre rutas: las rutas del doc son del workspace de referencia del arquitecto; cada dev las ajusta en local-dev.env. Reforzado en seccion 8. |
| 1.1.0 | 2026-06-10 | Carlos Torres | Consumo de otros servicios (X→Y/Z) por el Gateway; perfil estandar dev/qa con registro obligatorio de hostnames en /etc/hosts (prereq #5); localhost como alternativa; IPs internas en ~/.ssh/config (fuerza VPN); regla del puerto del servicio local. |
| 1.0.0 | 2026-06-10 | Carlos Torres | Creacion del flujo de prueba local con tunel SSH (estandarizacion del proceso + implementacion generica parametrizable DEV/QA) |