Propósito: Documento técnico que sirve como guía exhaustiva para configurar los ambientes QA y PROD del pipeline CI/CD, basado en el trabajo realizado para el ambiente DEV el 19/02/2026.
Este documento está diseñado para ser usado como guía para el equipo de infraestructura.
[Developer] ──push──► [GitLab nodo-00:8082]
│ webhook HTTP 200
▼
[Jenkins nodo-00:8080]
│
┌────────────┼────────────┐
▼ ▼ ▼
Checkout Build Docker
(GitLab) (Maven) Build/Push
│
[Registry 10.110.0.2:5000]
│
▼
SSH ──► [nodo-01 DEV]
docker compose up
| Nodo | Rol | IP Interna | Acceso SSH |
|---|---|---|---|
nodo-00 |
Jenkins + GitLab + Nexus + Registry | 10.110.0.2 |
sysadmin@200.118.43.29 -p 2201 |
nodo-01 |
Docker DEV (servicios activos) | 10.120.0.2 |
sysadmin@200.118.43.29 -p 2202 |
nodo-QA |
Docker QA (por configurar) | TBD | TBD |
nodo-PROD |
Docker PROD (por configurar) | TBD | TBD |
docker ps --format "table {{.Names}}\t{{.Ports}}"
| Contenedor | Función | Red Docker |
|---|---|---|
jenkins |
CI/CD Server | tools-network |
gitlab |
Repositorio Git | tools-network |
nexus |
Registry Maven | tools-network |
docker-bridge |
Expone Docker socket como TCP | tools-network |
| Contenedor | IP interna |
|---|---|
| nexus | 172.19.0.6 |
| gitlab | 172.19.0.8 |
| jenkins | 172.19.0.9 |
| docker-bridge | asignada dinámicamente |
Los contenedores se comunican por nombre DNS dentro de
tools-network(ej:http://nexus:8081,http://jenkins:8080,http://gitlab).
⚠️ Estas acciones NO se hacen automáticamente. Requieren intervención humana o autorización explícita.
# Dentro del contenedor Jenkins:
docker exec --user jenkins jenkins ssh-keygen -t ed25519 -f /var/jenkins_home/.ssh/id_ed25519 -N ""
docker exec --user jenkins jenkins cat /var/jenkins_home/.ssh/id_ed25519.pub
# Pegar la clave pública en:
# nodo-01: ~/.ssh/authorized_keys (usuario sysadmin)
# Verificar:
docker exec --user jenkins jenkins ssh -p 2202 -o StrictHostKeyChecking=no sysadmin@10.120.0.2 "echo SSH_JENKINS_OK"
El contenedor Jenkins LTS incluye JDK 21. Java 25 no se descarga automáticamente.
# Descargar Temurin 25 dentro del contenedor:
docker exec jenkins bash -c 'curl -fL -o /tmp/jdk25.tar.gz \
https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.2%2B10/OpenJDK25U-jdk_x64_linux_hotspot_25.0.2_10.tar.gz && \
mkdir -p /var/jenkins_home/tools/hudson.model.JDK/JDK-25 && \
tar -xzf /tmp/jdk25.tar.gz -C /var/jenkins_home/tools/hudson.model.JDK/JDK-25 --strip-components=1'
# Verificar:
docker exec jenkins /var/jenkins_home/tools/hudson.model.JDK/JDK-25/bin/java -version
Ruta usada en Jenkinsfile: /var/jenkins_home/tools/hudson.model.JDK/JDK-25
El contenedor Jenkins no tiene Docker CLI. Se instala el binario estático:
curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-27.5.1.tgz -o /tmp/docker.tgz
tar -xzf /tmp/docker.tgz -C /tmp/
docker cp /tmp/docker/docker jenkins:/usr/local/bin/docker
docker exec --user root jenkins chmod +x /usr/local/bin/docker
Jenkins no tiene el Docker socket montado. Se crea un contenedor puente:
docker run -d --name docker-bridge \
--network tools-network \
-v /var/run/docker.sock:/var/run/docker.sock \
--restart unless-stopped \
alpine/socat TCP-LISTEN:2375,fork,reuseaddr UNIX-CONNECT:/var/run/docker.sock
Jenkins usa DOCKER_HOST=tcp://docker-bridge:2375 en el Jenkinsfile.
⚠️ Este contenedor desaparece si se hace
docker rm docker-bridge. Debe recrearse si se reinicia el nodo.
El registry 10.110.0.2:5000 es HTTP. Docker bloquea HTTP por defecto.
Requiere sudo en nodo-00:
sudo tee /etc/docker/daemon.json << 'EOF'
{
"dns": ["8.8.8.8", "1.1.1.1"],
"insecure-registries": ["10.110.0.2:5000"]
}
EOF
sudo kill -HUP $(pidof dockerd)
# Los contenedores NO se detienen con kill -HUP (solo reconfigura el daemon)
Jenkins no hereda el settings.xml local. Se crea dentro del contenedor:
echo '<settings>...(ver sección 6)...</settings>' | \
docker exec -i jenkins tee /var/jenkins_home/.m2/settings.xml
Regla crítica: el <server id=""> debe coincidir con el ID del mirror que hace la conexión, no con el ID del repositorio original.
Las librerías nebula-models y similares no se publican automáticamente. Deben subirse manualmente:
# Desde nodo-00 (dentro del contenedor Jenkins que tiene acceso a Nexus):
docker cp nebula-models-X.X.X.jar jenkins:/tmp/
docker cp pom.xml jenkins:/tmp/nebula-models-pom.xml
docker exec jenkins curl -u 'arquitecto.centrica:PASSWORD' -X POST \
'http://nexus:8081/service/rest/v1/components?repository=centrica' \
-F 'maven2.groupId=com.centrica.nebula' \
-F 'maven2.artifactId=nebula-models' \
-F 'maven2.version=X.X.X-SNAPSHOT' \
-F 'maven2.asset1=@/tmp/nebula-models-X.X.X.jar' \
-F 'maven2.asset1.extension=jar' \
-F 'maven2.asset2=@/tmp/nebula-models-pom.xml' \
-F 'maven2.asset2.extension=pom'
| Plugin | Para qué |
|---|---|
| Git Plugin | Checkout desde GitLab |
| GitLab Plugin | Integración GitLab |
| Pipeline | Jenkinsfile declarativo |
| SSH Agent Plugin | Deploy con SSH keys |
| Maven Integration | Herramienta Maven |
| Docker Pipeline | Operaciones Docker |
| Multibranch Scan Webhook Trigger | Webhook automático por push |
Instalar en: Manage Jenkins → Plugins → Available plugins
Manage Jenkins → Tools
| Tool | Name | Cómo instalar |
|---|---|---|
| JDK | JDK-25 |
Manual (ver sección 3.2) |
| Maven | Maven-3.9 |
Auto-install 3.9.9 |
Manage Jenkins → Credentials → System → Global
| ID | Tipo | Valor |
|---|---|---|
nodo01-ssh-key |
SSH Username with private key | Key del jenkins container |
gitlab-token |
Username with password | arquitecto.centrica / token GitLab |
Para nodo-QA y nodo-PROD, agregar credenciales adicionales:
nodo-qa-ssh-keynodo-prod-ssh-keyModelo A — ver
GITFLOW_DETALLADO.md§1 yFLUJO_HT_A_PROD.mdpara el flujo end-to-end desde la HT hasta producción.
main ◄── qa ◄── develop
│
└── bugfix/HT-CON-XXX-fix-qa (fixes detectados en QA)
| Rama | Trigger | Deploy destino | Aprobación | Disparador del MR |
|---|---|---|---|---|
develop |
Automático (webhook) | nodo-01 DEV | ❌ Sin gate | Backend Sr/Jr (MR de feature) |
qa |
Automático (webhook) | nodo-02 QA | ✅ Gate manual en Jenkins | Gatekeeper (MR develop→qa, manual al cierre de sprint) |
main |
Automático (webhook) | nodo-PROD | ✅ Aprobación bloqueante | Arquitecto (MR qa→main + tag) |
Flujo de promoción (camino feliz):
develop → (MR Gatekeeper) → qa → (QA aprueba en Jenkins UI) → main → (Arquitecto aprueba) → tag vX.Y.Z → deploy PROD
Cuando QA reporta un defecto sobre el build en qa, el fix se aplica primero a qa, no a develop. Razón: durante el hardening del lote, develop puede contener HT del siguiente sprint ya mergeadas; un merge develop → qa arrastraría esas HT al ambiente QA y rompería el aislamiento del lote bajo prueba.
qa ──► bugfix/HT-CON-019-fix-qa ──► qa (redeploy nodo-02)
│
└─► merge qa → develop (sync)
Procedimiento detallado: ver GITFLOW_DETALLADO.md §3.2.
Reglas:
bugfix/HT-CON-XXX-fix-qa deriva siempre de qa, nunca de develop.qa, es obligatorio sincronizar qa → develop antes de cerrar el ciclo. Si se omite, el fix se pierde en el siguiente cut develop→qa.fix-qa para fast-track del Gatekeeper.<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
<servers>
<server>
<id>nexus-centrica</id>
<username>arquitecto.centrica</username>
<password><!-- CONTRASEÑA NEXUS --></password>
</server>
<!-- CRÍTICO: los IDs de server deben coincidir con los IDs de los mirrors -->
<server>
<id>nexus-internal</id>
<username>arquitecto.centrica</username>
<password><!-- CONTRASEÑA NEXUS --></password>
</server>
<server>
<id>unblock-http</id>
<username>arquitecto.centrica</username>
<password><!-- CONTRASEÑA NEXUS --></password>
</server>
</servers>
<mirrors>
<mirror>
<id>nexus-internal</id>
<mirrorOf>nexus-centrica</mirrorOf>
<url>http://nexus:8081/repository/centrica/</url>
<blocked>false</blocked>
</mirror>
<mirror>
<id>unblock-http</id>
<mirrorOf>external:http:*</mirrorOf>
<url>http://nexus:8081/repository/centrica/</url>
<blocked>false</blocked>
</mirror>
</mirrors>
<profiles>
<profile>
<id>nexus-centrica</id>
<activation><activeByDefault>true</activeByDefault></activation>
<repositories>
<repository>
<id>nexus-centrica</id>
<url>http://nexus:8081/repository/centrica/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>nexus-centrica</id>
<url>http://nexus:8081/repository/centrica/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</settings>
pipeline {
agent any
tools {
maven 'Maven-3.9'
// JDK manejado con JAVA_HOME explícito (no tools jdk)
}
environment {
REGISTRY = '10.110.0.2:5000'
IMAGE = 'nebula/business-domains/finance/nebula-accounting-core'
SERVICE = 'nebula-accounting-core'
NODO01_USER = 'sysadmin'
NODO01_HOST = '10.120.0.2' // IP interna nodo-01
NODO01_PORT = '2202'
COMPOSE_DIR = '~/docker/compose/nebula/business-domains/finance'
JAVA_HOME = '/var/jenkins_home/tools/hudson.model.JDK/JDK-25'
DOCKER_HOST = 'tcp://docker-bridge:2375'
DOCKER_CLI = '/usr/local/bin/docker'
}
stages {
stage('Checkout') { ... }
stage('Build') {
// OBLIGATORIO: export JAVA_HOME explícito en el shell
// ${env.PATH} en el bloque environment NO evalúa en runtime
sh '''
export JAVA_HOME=/var/jenkins_home/tools/hudson.model.JDK/JDK-25
export PATH=$JAVA_HOME/bin:$PATH
java -version
mvn clean package -DskipTests -U
'''
}
stage('Docker Build & Push') {
// Usar DOCKER_CLI explícito, no 'docker' directamente
sh "${DOCKER_CLI} build ..."
}
stage('Deploy DEV') { when { branch 'develop' } ... }
stage('Deploy QA') { when { branch 'qa' } input(...) ... }
stage('Deploy PROD') { when { branch 'main' } input(...) ... }
}
}
GitLab Admin Area → Settings → Network → Outbound requests
☑ Allow requests to the local network from webhooks and integrations
GitLab → Proyecto → Settings → Webhooks → Add new webhook
| Campo | Valor |
|---|---|
| URL | http://172.19.0.9:8080/multibranch-webhook-trigger/invoke?token=nebula-accounting-core-webhook |
| Trigger | ☑ Push events |
| SSL verification | ☐ Deshabilitado |
La IP
172.19.0.9es la IP de Jenkins entools-network. Verificar con:docker inspect jenkins | grep tools-network -A5
Jenkins → Job → Configure → Scan Multibranch Pipeline Triggers
☑ Scan by webhook → Token: nebula-accounting-core-webhook
| Falla | Causa | Solución |
|---|---|---|
Could not connect to gitlab.centricasoluciones.com:443 |
Jenkins no resuelve DNS público desde contenedor | Usar URL interna: http://gitlab/repo.git |
Blocked mirror for repositories [nexus-centrica (http://...)] |
Maven 3.8+ bloquea HTTP por defecto | Agregar <blocked>false</blocked> en mirror del settings.xml |
status code: 401 en Nexus |
Server ID en settings.xml no coincide con mirror ID | Los <server id=""> deben coincidir con los IDs de <mirror> |
error: release version 25 not supported |
JDK del sistema en Jenkins es 21, no 25 | Instalar Temurin 25 manualmente y exportar JAVA_HOME en el shell |
${env.PATH} vacío en Jenkinsfile |
No evalúa en bloque environment de Declarative Pipeline |
Usar export PATH=$JAVA_HOME/bin:$PATH directamente en el sh |
docker: not found |
Docker CLI no incluido en Jenkins LTS | Instalar binario estático de Docker 27.5.1 |
http: server gave HTTP response to HTTPS client |
Registry HTTP no registrado como insecure | Agregar a /etc/docker/daemon.json y kill -HUP $(pidof dockerd) |
nebula-models no resuelve en Nexus |
Librería interna nunca publicada a Nexus | Subir JAR+POM via REST API de Nexus desde contenedor Jenkins |
GitLab webhook: Invalid URL given |
GitLab bloquea URLs de red local | Habilitar en Admin → Network → Outbound requests |
nodo-qa-ssh-key)NODO_QA_HOST, COMPOSE_DIR_QA)qa o main)dev-network (o qa-network)docker-compose.yml al path configurado~/.ssh/authorized_keysdocker-bridge si el contenedor fue eliminadoMultibranch Scan Webhook Trigger si no está# 1. Verificar SSH desde Jenkins al nuevo nodo
docker exec --user jenkins jenkins ssh -p PORT -o StrictHostKeyChecking=no sysadmin@IP_NUEVO_NODO "echo OK"
# 2. Verificar que Docker funciona en Jenkins
docker exec jenkins /usr/local/bin/docker version
# 3. Verificar Nexus accesible
docker exec jenkins curl -u 'user:pass' -s -o /dev/null -w '%{http_code}' http://nexus:8081/repository/centrica/
# 4. Lanzar build manual y verificar stages
# Jenkins → Job → Build Now → ver log completo
<server> siempre debe coincidir con el ID del <mirror> que lo usa, no con el ID del repositorio original.export JAVA_HOME=... && export PATH=... dentro del bloque sh, nunca en el bloque environment (no evalúa ${env.PATH} en runtime).${DOCKER_CLI} o /usr/local/bin/docker, no asumir binario en PATH.insecure-registries del daemon.json. Recargar con kill -HUP $(pidof dockerd) (sin reiniciar contenedores).--restart unless-stopped para que sobreviva reinicios del daemon.nebula-models y similares deben publicarse manualmente a Nexus antes del primer build en CI. No se publican automáticamente.develop → qa → main. Nunca hacer merge inverso. Los MR de promoción deben ser revisados.sysadmin requiere contraseña para sudo. Cualquier operación sudo debe hacerse de forma manual en una sesión interactiva o con autorización explícita del usuario.Documento generado: 19/02/2026 — Pipeline CI/CD DEV operativo en build #10
Autor: Carlos Torres (arquitecto.centrica)
| Version | Fecha | Autor | Descripcion |
|---|---|---|---|
| 1.2.0 | 2026-05-07 | Carlos Torres | Seccion 5 ampliada con mitigacion de fixes QA (bugfix/*-fix-qa con sync qa->develop). Cross-references a GITFLOW_DETALLADO.md y FLUJO_HT_A_PROD.md. |
| 1.1.0 | 2026-03-04 | Carlos Torres | Revision, sanitizacion y publicacion en Wiki Arquitectura Centrica. |
| 1.0.0 | 2026-02-19 | Carlos Torres | Creacion del documento. |