fc489c2115
Reflect the move from per-client Jenkinsfiles/labels to one parameterized job (NODO + CLIENTE), the mc-based install.sh, and flag create-node.sh / create-client.sh as obsolete until they're rewritten for this scheme. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
174 lines
9.0 KiB
Markdown
174 lines
9.0 KiB
Markdown
# Jenkins SCO — Deploy Server
|
|
|
|
Jenkins centralizado para el despliegue de software en terminales SCO (Self-Checkout) en producción.
|
|
|
|
## Estructura del repositorio
|
|
|
|
```
|
|
.
|
|
├── docker-compose.yml
|
|
└── data/
|
|
└── jenkins/
|
|
├── Dockerfile # Imagen Jenkins con plugins
|
|
├── jenkins.yaml # Configuración JCasC (nodos, seguridad, jobs)
|
|
├── deploy/
|
|
│ └── install.sh # Script desplegado en cada SCO
|
|
└── pipelines/
|
|
└── Jenkinsfile.sco # Pipeline único de deploy (NODO + CLIENTE)
|
|
```
|
|
|
|
## Arquitectura
|
|
|
|
- **Jenkins master**: corre en Docker en un servidor (`172.19.13.204`). No expone puertos al host; solo es alcanzable dentro de la red interna Docker `proxy`.
|
|
- **Nginx Proxy Manager (NPM)**: reverse proxy interno que resuelve el hostname `jenkins.laoficina1782.com` y hace forward hacia `jenkins-server:8080`. Corre en el mismo servidor, red `proxy`.
|
|
- **Cloudflare Tunnel (`cloudflared`)**: es el único punto de entrada desde internet. El contenedor `cloudflared` abre una conexión saliente hacia el edge de Cloudflare (autenticada con `TUNNEL_TOKEN`), por lo que **no hace falta abrir puertos entrantes en el firewall/router** del servidor. El tunnel está configurado del lado de Cloudflare para enrutar el hostname público hacia `http://npm:80` dentro de la red `proxy`.
|
|
- **Agentes (SCOs)**: cada terminal SCO conecta al master vía WebSocket (no JNLP), outbound hacia `wss://jenkins.laoficina1782.com/`. El proceso del agente corre como `root`.
|
|
- **Storage**: Minio almacena el software de cada cliente en buckets separados. El nombre del bucket es el mismo valor que se selecciona en el parámetro `CLIENTE` del deploy (ej: bucket `mbs-ralphs`).
|
|
|
|
### Flujo de tráfico
|
|
|
|
```
|
|
Internet
|
|
│
|
|
▼
|
|
Cloudflare Edge (DNS + proxy)
|
|
│ túnel saliente autenticado (TUNNEL_TOKEN)
|
|
▼
|
|
cloudflared (contenedor, red "proxy", sin puertos publicados)
|
|
│ http://npm:80
|
|
▼
|
|
NPM (nginx-proxy-manager) — vhost jenkins.laoficina1782.com
|
|
│ http://jenkins-server:8080
|
|
▼
|
|
jenkins-server (sin puertos publicados al host)
|
|
```
|
|
|
|
Jenkins **no** expone puertos al host: NPM y `cloudflared` se comunican con él por nombre de contenedor dentro de la red Docker `proxy`. Como no hay puertos entrantes abiertos en el servidor, toda la superficie pública depende de Cloudflare (WAF, DNS, TLS terminado en el edge).
|
|
|
|
> ⚠️ NPM publica además `80`, `443` y `81` directamente al host (ver `docker-compose.yml`). Si el firewall del servidor permite esas conexiones, Jenkins/NPM también serían alcanzables sin pasar por Cloudflare. Para que el tunnel sea realmente el único punto de entrada, esos puertos deberían quedar bloqueados a nivel de firewall/red salvo acceso local.
|
|
|
|
### Variables de entorno
|
|
|
|
El tunnel necesita el token generado en el dashboard de Cloudflare (Zero Trust → Networks → Tunnels):
|
|
|
|
```sh
|
|
cp .env.example .env
|
|
# editar .env y setear CLOUDFLARE_TUNNEL_TOKEN=<token>
|
|
```
|
|
|
|
## Levantar el servidor
|
|
|
|
```sh
|
|
docker compose up -d --build
|
|
```
|
|
|
|
Jenkins queda disponible en `https://jenkins.laoficina1782.com` una vez configurados el proxy y el tunnel.
|
|
|
|
### Nginx Proxy Manager
|
|
|
|
Panel de administración: `http://172.19.13.204:81` (o localmente en el servidor).
|
|
|
|
Configuración del proxy host para Jenkins:
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| Domain Names | `jenkins.laoficina1782.com` |
|
|
| Scheme | `http` |
|
|
| Forward Hostname | `jenkins-server` |
|
|
| Forward Port | `8080` |
|
|
| Websockets Support | ✅ activado |
|
|
|
|
### Cloudflare Tunnel
|
|
|
|
1. En el dashboard de Cloudflare (Zero Trust → Networks → Tunnels), crear un tunnel y copiar su token.
|
|
2. Configurar la ruta pública (Public Hostname) del tunnel: `jenkins.laoficina1782.com` → `http://npm:80`.
|
|
3. Setear el token en `.env` como `CLOUDFLARE_TUNNEL_TOKEN` (ver arriba).
|
|
4. Levantar el stack: el contenedor `cloudflared` se conecta solo, sin necesidad de abrir puertos en el servidor.
|
|
|
|
## Conectar un agente SCO
|
|
|
|
1. Descargar el agente desde Jenkins:
|
|
```sh
|
|
curl -sO http://<server-ip>:8080/jnlpJars/agent.jar
|
|
mv agent.jar /opt/jenkins-agent/agent.jar
|
|
```
|
|
|
|
2. Obtener el secret del nodo: en Jenkins → *Manage Jenkins → Nodes → (nombre del nodo)*.
|
|
|
|
3. Crear el servicio systemd en el SCO:
|
|
```ini
|
|
# /etc/systemd/system/jenkins-agent.service
|
|
[Unit]
|
|
Description=Jenkins Agent
|
|
After=network.target
|
|
|
|
[Service]
|
|
User=root
|
|
ExecStart=/usr/bin/java -jar /opt/jenkins-agent/agent.jar \
|
|
-url https://jenkins.laoficina1782.com/ \
|
|
-webSocket \
|
|
-secret <SECRET> \
|
|
-name "<nombre-del-nodo>" \
|
|
-workDir "/opt/jenkins-agent"
|
|
Restart=always
|
|
RestartSec=10
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
4. Habilitar y arrancar:
|
|
```sh
|
|
systemctl daemon-reload
|
|
systemctl enable --now jenkins-agent
|
|
```
|
|
|
|
## Ejecutar un deploy
|
|
|
|
Hay un único job, `deploy-sco`, que despliega a **un nodo puntual** (no a una tienda entera).
|
|
|
|
1. En Jenkins, abrir el job `deploy-sco`.
|
|
2. Click en **Build with Parameters**.
|
|
3. Completar **NODO** con el nombre exacto del agente a deployar (ej. `ralph-yabucoa-sco05`).
|
|
4. Seleccionar **CLIENTE** en el dropdown — es el nombre del bucket de Minio que se sincroniza en ese nodo.
|
|
|
|
Ambos parámetros son obligatorios: si `NODO` queda vacío o `CLIENTE` queda en `SELECCIONAR_CLIENTE` (el placeholder inicial), el build falla explícitamente en vez de deployar por accidente. Para desplegar a varios nodos (ej. toda una tienda) hay que correr el job una vez por nodo.
|
|
|
|
> Nota: el **primer build** de este job (o de cualquier job parametrizado nuevo) corre antes de que Jenkins conozca los parámetros declarados en el Jenkinsfile — es una limitación de cómo Jenkins descubre los parámetros la primera vez. El pipeline lo detecta (`params.CLIENTE == null`) y termina como `NOT_BUILT` sin deployar nada; alcanza con volver a correr el job con **Build with Parameters** normalmente.
|
|
|
|
También se puede disparar desde consola con el Jenkins CLI o `curl`, ver [Ejecutar un job desde consola](#ejecutar-un-job-desde-consola).
|
|
|
|
### Ejecutar un job desde consola
|
|
|
|
```sh
|
|
curl -O https://jenkins.laoficina1782.com/jnlpJars/jenkins-cli.jar
|
|
|
|
java -jar jenkins-cli.jar -s https://jenkins.laoficina1782.com/ \
|
|
-auth TU_USUARIO:TU_API_TOKEN \
|
|
build deploy-sco -p NODO=ralph-yabucoa-sco05 -p CLIENTE=mbs-ralphs -s -v
|
|
```
|
|
|
|
- `-s` espera a que termine el build; `-v` muestra el log en consola.
|
|
- El API Token se genera en el usuario de Jenkins → Configure → API Token.
|
|
- Alternativa sin el CLI: `curl -X POST https://jenkins.laoficina1782.com/job/deploy-sco/buildWithParameters --user TU_USUARIO:TU_API_TOKEN --data-urlencode "NODO=ralph-yabucoa-sco05" --data-urlencode "CLIENTE=mbs-ralphs"`.
|
|
- Si se dispara por CLI/`curl` **omitiendo** `-p CLIENTE=...`, Jenkins no manda un valor vacío: rellena el parámetro con su default, que es `SELECCIONAR_CLIENTE` (el placeholder es a propósito el primer valor de la lista, para que un `CLIENTE` faltante falle en vez de deployar silenciosamente al primer cliente real de la lista).
|
|
|
|
## Qué hace `install.sh`
|
|
|
|
Recibe el nombre del cliente como argumento (`$1`), que es el mismo valor que se selecciona en el parámetro `CLIENTE` del deploy:
|
|
|
|
1. Da de alta (o reutiliza) un alias `mc` apuntando a Minio a través del hostname público `https://minio-api.laoficina1782.com`.
|
|
2. Mirrorea el bucket `$CUSTOMER` desde Minio hacia `/opt/sco/` en el SCO (excluyendo datos persistentes del backend), con `--remove --overwrite` para dejar el destino idéntico al bucket.
|
|
|
|
Los pasos de instalación local (permisos, servicios systemd, autostart, crontabs, paquetes Python, imágenes Docker, inicialización de bases de datos del cpi-server, `engine.properties` de SymmetricDS) ya no están en este script — quedaron fuera del alcance actual de `install.sh`, que hoy solo se ocupa de traer el software del cliente a `/opt/sco/`.
|
|
|
|
## Agregar un nodo o un cliente nuevo
|
|
|
|
Desde que el deploy pasó a un job único (`deploy-sco`, con `NODO` + `CLIENTE` como parámetros en vez de un Jenkinsfile/label por cliente), dar de alta algo nuevo es mucho más simple y **ya no requiere tocar `docker-compose.yml` ni crear Jenkinsfiles**:
|
|
|
|
- **Agregar un nodo** (a un cliente existente o nuevo): agregar una entrada `permanent` en `.jenkins.nodes` de `data/jenkins/jenkins.yaml` con el nombre del agente (no hace falta `labelString`, el deploy targetea por nombre exacto de nodo). Después conectar el agente físico siguiendo [Conectar un agente SCO](#conectar-un-agente-sco).
|
|
- **Agregar un cliente**: agregar el nombre del bucket a la lista `choices` del parámetro `CLIENTE` en `data/jenkins/pipelines/Jenkinsfile.sco`.
|
|
- En ambos casos, aplicar el cambio con `docker compose up -d --build` (el contenedor de Jenkins re-lee `jenkins.yaml` vía JCasC).
|
|
|
|
> ⚠️ `data/jenkins/scripts/create-node.sh` y `create-client.sh` quedaron **obsoletos**: automatizan el esquema viejo (un Jenkinsfile y un label `sco <cliente> <tienda>` por cliente), que ya no existe. No usarlos hasta reescribirlos para el esquema de `deploy-sco`.
|