Post

Guía Definitiva: CI/CD con Jenkins y Kubernetes (K3s) en Proxmox desde cero

Guía Definitiva: CI/CD con Jenkins y Kubernetes (K3s) en Proxmox desde cero

🚀 CI/CD con Jenkins y Kubernetes (K3s) en Proxmox desde cero

Montar un entorno de Integración y Despliegue Continuo (CI/CD) puede parecer intimidante al principio, especialmente cuando lidias con múltiples capas de virtualización. En este post documento paso a paso cómo construí mi propio laboratorio DevOps usando Proxmox, Kubernetes (K3s) y Jenkins.

Objetivo final: crear una pipeline automatizada que despliegue una página web personalizada en un clúster de Kubernetes cada vez que hagamos una build en Jenkins.


📐 Arquitectura del Laboratorio

Una arquitectura estilo Matrioshka (capas dentro de capas):

1
2
3
4
5
Proxmox VE (Hipervisor)
└── VM Ubuntu (Servidor K3s)
    └── Contenedor LXC Ubuntu
        └── Contenedor Docker
            └── Jenkins
CapaTecnologíaRol
HipervisorProxmox VEBase física
VMUbuntuClúster K3s
LXCUbuntuHost Docker
ContenedorJenkinsMotor CI/CD

Fase 1 — Preparando Kubernetes (K3s)

Elegí K3s de Rancher porque es increíblemente ligero e ideal para laboratorios de un solo nodo.

1.1 Instalación

Accede por SSH a la VM de Ubuntu y ejecuta el instalador oficial:

1
curl -sfL https://get.k3s.io | sh -

1.2 Extraer el kubeconfig

Este archivo es el “pasaporte” que necesitará Jenkins para comunicarse con el clúster. Guárdalo.

1
cat /etc/rancher/k3s/k3s.yaml

⚠️ Importante: Reemplaza la IP 127.0.0.1 por la IP real del servidor K3s en tu red local.


Fase 2 — Instalando Jenkins (LXC + Docker en Proxmox)

Levantamos un contenedor LXC en Proxmox, instalamos Docker dentro y arrancamos Jenkins.

2.1 Obtener la contraseña inicial de Jenkins

Al acceder por primera vez en el puerto 8080, Jenkins pedirá una contraseña. Ejecútalo en la consola del LXC:

1
docker exec jenkins-master cat /var/jenkins_home/secrets/initialAdminPassword

2.2 Configuración inicial

En el asistente de Jenkins, selecciona “Instalar plugins sugeridos” y espera a que el entorno se prepare.


Fase 3 — El Reto de las Comunicaciones (Kubectl dentro de Docker)

Este es el paso más crítico. Jenkins está atrapado dentro de un contenedor Docker, dentro de un LXC. Necesitamos darle la herramienta kubectl y el kubeconfig para que pueda hablar con K3s.

Ejecuta estos comandos desde la consola del LXC (fuera de Docker):

1
2
3
4
5
6
7
8
9
10
11
12
# 1. Copiar el binario de kubectl al contenedor y darle permisos
docker cp /usr/local/bin/kubectl jenkins-master:/usr/local/bin/kubectl
docker exec -u root jenkins-master chmod +x /usr/local/bin/kubectl

# 2. Crear el directorio .kube para el usuario jenkins
docker exec -u root jenkins-master mkdir -p /var/jenkins_home/.kube

# 3. Copiar el kubeconfig (k3s.yaml) que obtuvimos en la Fase 1
docker cp /root/.kube/config jenkins-master:/var/jenkins_home/.kube/config

# 4. Asignar los permisos correctos
docker exec -u root jenkins-master chown -R jenkins:jenkins /var/jenkins_home/.kube

Fase 4 — Creando la Primera Pipeline (Infraestructura como Código)

4.1 Crear el proyecto

En el panel de Jenkins: Nueva tarea → Pipeline.

4.2 El Jenkinsfile

El siguiente script declara todo el despliegue. Usa un ConfigMap para inyectar HTML personalizado en Nginx y escala a 4 réplicas:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
pipeline {
    agent any
    stages {
        stage('Desplegar Mi Propio Código') {
            steps {
                sh '''
                cat <<EOF | kubectl apply --kubeconfig=/var/jenkins_home/.kube/config -f -
---
# 1. ConfigMap con el HTML personalizado
apiVersion: v1
kind: ConfigMap
metadata:
  name: mi-web-html
data:
  index.html: |
    <!DOCTYPE html>
    <html>
    <body style="background-color: #2b303b; color: #42b983; text-align: center; padding: 100px; font-family: sans-serif;">
      <h1>🚀 ¡Hola Mundo desde mi miniPC con Proxmox!</h1>
      <h2>Acabo de dominar Kubernetes y Jenkins</h2>
    </body>
    </html>
---
# 2. Deployment con 4 réplicas de Nginx
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mi-web-pro
spec:
  replicas: 4
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: mi-volumen-html
          mountPath: /usr/share/nginx/html/index.html
          subPath: index.html
      volumes:
      - name: mi-volumen-html
        configMap:
          name: mi-web-html
---
# 3. Service de tipo NodePort para exponer la web
apiVersion: v1
kind: Service
metadata:
  name: mi-web-pro-svc
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80
      nodePort: 32000
EOF
                '''
            }
        }
    }
}

Fase 5 — Verificando el Despliegue

Haz clic en “Construir ahora” en Jenkins. Una vez que la build termine en verde, verifica el estado del clúster desde la VM de K3s:

1
sudo kubectl get pods

Deberías ver 4 pods de Nginx en estado Running. La web estará disponible en:

1
http://<IP-DEL-SERVIDOR-K3S>:32000

📋 Resumen del Flujo Completo

1
2
3
4
5
6
7
8
9
Jenkins (Docker/LXC)
    │
    │  kubectl apply (via kubeconfig)
    ▼
K3s API Server (VM)
    │
    ├── ConfigMap (HTML)
    ├── Deployment (4x Nginx)
    └── Service NodePort :32000

🔗 Referencias

This post is licensed under CC BY 4.0 by the author.