Files
scripts/README.md
2026-07-18 03:40:01 +00:00

23 KiB

Laboratorio k3d estable para ZimaOS

Este paquete reemplaza el deploy-lab.sh original por una versión idempotente y no destructiva.

Qué corrige

  • No elimina lab-cluster en cada ejecución.
  • Crea el clúster solo cuando no existe.
  • Usa subnet: auto para mantener IP internas estables tras reinicios.
  • Conserva el datastore SQLite, certificados y token de K3s en /DATA/AppData/k3d-lab/k3s/server.
  • Conserva los datos de local-path-provisioner fuera de los contenedores.
  • Usa un token de clúster persistente.
  • Mantiene fijos los puertos 46421, 90 y 9443.
  • Valida que ZimaOS siga usando 192.168.68.61 antes de desplegar.
  • Instala Argo CD con versión fijada y server-side apply.
  • No crea el argocd-manager innecesario para desplegar en el mismo clúster.
  • Crea RBAC de Headlamp y RBAC de solo lectura para la validación postdeploy.
  • Regenera los archivos requeridos para los secrets de Gitea Actions.
  • Instala opcionalmente un watchdog systemd para recuperar el clúster después del boot.

Primera recuperación / creación

Ejecutar como el usuario devops, no con sudo:

chmod +x deploy-lab.sh

LAB_HOST_IP=192.168.68.61 \
./deploy-lab.sh bootstrap

Como Docker y k3d están actualmente vacíos, bootstrap creará un clúster nuevo. En ejecuciones posteriores iniciará y reconciliará el clúster existente.

Validación

./deploy-lab.sh status

kubectl get nodes -o wide
kubectl get pods -A
kubectl get ingress -A

Autostart después de reinicios

Ejecutar solamente después de que bootstrap termine correctamente:

sudo ./deploy-lab.sh install-autostart

Verificar:

systemctl status k3d-lab-ensure.timer --no-pager
journalctl -u k3d-lab-ensure.service -n 200 --no-pager

El timer se ejecuta 60 segundos después del arranque y posteriormente cada cinco minutos. El servicio intenta iniciar el clúster, regenera el kubeconfig y valida los tres nodos.

Recuperación manual

./deploy-lab.sh recover

recover no crea ni elimina el clúster. Solo intenta iniciar uno existente y reconcilia Argo CD.

Applications de Argo CD

Guarda los manifiestos Application, ApplicationSet o el app-of-apps en:

/DATA/AppData/k3d-lab/bootstrap/

Al ejecutar nuevamente bootstrap, el script aplicará automáticamente todos los .yaml y .yml de ese directorio.

Secrets para la validación postdeploy

Después de crear un clúster nuevo, se generan:

/DATA/AppData/k3d-lab/gitea-actions-secrets/K8S_SERVER.txt
/DATA/AppData/k3d-lab/gitea-actions-secrets/K8S_CA_B64.txt
/DATA/AppData/k3d-lab/gitea-actions-secrets/K8S_TOKEN.txt
/DATA/AppData/k3d-lab/gitea-actions-secrets/K8S_TLS_SERVER_NAME.txt

Copia sus valores a los secrets equivalentes del repositorio devops/apps-registry en Gitea. No publiques el contenido de K8S_TOKEN.txt.

Headlamp

Genera un token cuando sea necesario:

kubectl create token headlamp-admin -n headlamp

El script crea el RBAC, pero el Deployment/Ingress de Headlamp debe provenir del repositorio GitOps.

Reset limpio

Solo cuando realmente quieras iniciar desde cero:

CONFIRM_RESET=DELETE-lab-cluster ./deploy-lab.sh reset

Antes de limpiar el estado, el script crea un archivo en:

/DATA/AppData/k3d-lab/backups/

Variables principales

CLUSTER_NAME=lab-cluster
APP_DATA_PATH=/DATA/AppData/k3d-lab
KUBECONFIG_PATH=/DATA/.kube/config
LAB_HOST_IP=192.168.68.61
EXPECTED_HOST_IP=192.168.68.61
K3S_IMAGE=rancher/k3s:v1.35.5-k3s1
ARGOCD_VERSION=v3.2.0

Para aceptar conscientemente una IP distinta:

LAB_HOST_IP=192.168.68.X \
ALLOW_IP_CHANGE=true \
./deploy-lab.sh bootstrap

También deberás actualizar NPM y los secrets de Gitea Actions.

Arranque automático reforzado v3

La versión v3 agrega:

  • subnet: auto al crear el clúster para mantener IP estática por nodo.
  • restart=unless-stopped solamente a los contenedores de lab-cluster.
  • Dependencias systemd sobre el DockerRootDir real y APP_DATA_PATH.
  • Inicio 90 segundos después del boot.
  • Reintento automático del servicio cada 30 segundos si falla.
  • Watchdog cada 5 minutos.
  • Regeneración del kubeconfig.
  • Recuperación de agentes NotReady.
  • Re-registro automático de un agente persistentemente obsoleto.
  • Validación de Traefik, Argo CD y el puerto HTTP publicado.

Instalar

LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh bootstrap
sudo LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh install-autostart

Prueba de reinicio

Antes:

./deploy-lab.sh status
systemctl status k3d-lab-ensure.timer --no-pager
systemctl cat k3d-lab-ensure.service
systemctl cat docker.service

Después del reboot, espera hasta 10 minutos y valida:

systemctl status k3d-lab-ensure.service --no-pager
journalctl -b -u k3d-lab-ensure.service --no-pager
./deploy-lab.sh status
curl -I -H 'Host: argocd.cruzcloud.net' http://127.0.0.1:90
curl -I -H 'Host: shop.cruzcloud.net' http://127.0.0.1:90

Corrección v3.1 para ZimaOS

ZimaOS puede no incluir el comando openssl. El script ya no lo exige:

  1. Usa openssl cuando está disponible.
  2. Si no, usa /dev/urandom con od.
  3. Como último fallback, usa hexdump.

Para crear o recuperar el laboratorio, no uses sudo sh:

LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh bootstrap

Solo la instalación de systemd requiere sudo:

sudo LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh install-autostart

Corrección v3.2: rollout de Argo CD

  • argocd-server solo se reinicia cuando server.insecure cambia.
  • El timeout específico de Argo CD aumenta a 420 segundos.
  • Si el rollout falla, el script muestra Pods y eventos.
  • Si la réplica nueva ya está disponible y únicamente queda un Pod antiguo atascado en Terminating, elimina solo ese Pod antiguo y reintenta.
  • No fuerza la eliminación cuando todavía no existe una réplica nueva disponible.

Ejecución:

LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh bootstrap

Corrección v3.3: permisos de credenciales Gitea Actions

La generación de credenciales ya no ejecuta chmod sobre todos los .txt históricos. Cada archivo se genera temporalmente y se instala con:

  • propietario: usuario que ejecuta bootstrap;
  • grupo: grupo principal del usuario;
  • permisos: 0600.

Además se regenera KUBE_CONFIG_DATA.txt con un kubeconfig Base64 de privilegios mínimos para gitea-postdeploy-validator, manteniendo compatibilidad con el workflow anterior.

Para continuar:

LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh bootstrap

Corrección v3.4: propietario sin asumir grupo devops

ZimaOS tiene al usuario devops con grupo principal samba; no existe necesariamente un grupo llamado devops.

La v3.4:

  • usa chown devops y no chown devops:devops;
  • usa install -o devops sin forzar grupo para archivos privados;
  • mantiene permisos 0700 en el directorio de secretos;
  • mantiene permisos 0600 en credenciales y kubeconfigs;
  • conserva el grupo existente del filesystem cuando solo cambia el propietario.

El grupo de la unidad systemd sigue obteniéndose dinámicamente con id -gn devops, por lo que en ZimaOS será samba.

Corrección v3.5: kubeconfig sin current-context

La v3.5 valida y corrige el kubeconfig antes de instalarlo:

  • obtiene el kubeconfig directamente desde k3d;
  • confirma que contenga al menos un contexto;
  • activa explícitamente ese contexto;
  • confirma que exista un API Server;
  • instala /DATA/.kube/config con propietario devops y permisos 0600;
  • no depende de .profile;
  • systemd continúa usando la ruta explícita de kubeconfig.

Reparación manual equivalente:

TMP_KUBECONFIG="$(mktemp)"
k3d kubeconfig get lab-cluster > "$TMP_KUBECONFIG"
CTX="$(KUBECONFIG="$TMP_KUBECONFIG" kubectl config get-contexts -o name | head -1)"
KUBECONFIG="$TMP_KUBECONFIG" kubectl config use-context "$CTX"
sudo install -d -m 0700 -o devops /DATA/.kube
sudo install -m 0600 -o devops "$TMP_KUBECONFIG" /DATA/.kube/config
rm -f "$TMP_KUBECONFIG"

Corrección v3.6: systemd en ZimaOS sin /usr/local/sbin

ZimaOS no garantiza que /usr/local/sbin exista o sea escribible. La v3.6 instala la copia persistente del script en:

/DATA/AppData/k3d-lab/bin/deploy-k3d-lab

La unidad usa Bash explícitamente:

ExecStart=/bin/bash /DATA/AppData/k3d-lab/bin/deploy-k3d-lab ensure

Esto también funciona cuando /DATA tiene la opción de montaje noexec.

Instalación:

sudo LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh install-autostart

Corrección v3.7: watchdog systemd sin sudo

El servicio se ejecuta como devops y no dispone de una terminal para introducir la contraseña de sudo. La v3.7 elimina sudo del camino de ejecución del modo ensure.

Cambios:

  • comprueba escritura en /DATA/AppData/k3d-lab, no en la raíz /DATA;
  • regenera /DATA/.kube/config directamente como devops;
  • prepara propietario y permisos durante install-autostart;
  • no intenta iniciar Docker mediante sudo desde systemd;
  • muestra automáticamente systemctl status y journalctl si la prueba inicial del servicio falla.

Instalación:

sudo LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh install-autostart

Corrección v3.8: PATH de systemd y ubicación de k3d

La shell interactiva de ZimaOS puede encontrar k3d en una ruta bajo /DATA, mientras systemd utiliza un PATH mínimo. La v3.8:

  • localiza automáticamente k3d, kubectl, docker y curl;
  • agrega los directorios detectados al PATH de la unidad;
  • guarda las rutas en /etc/default/k3d-lab;
  • permite indicar rutas explícitas con K3D_BIN, KUBECTL_BIN, DOCKER_BIN y CURL_BIN;
  • muestra el PATH efectivo al terminar.

Instalación normal:

sudo LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh install-autostart

Fallback explícito:

K3D_BIN="$(command -v k3d)" \
KUBECTL_BIN="$(command -v kubectl)" \
DOCKER_BIN="$(command -v docker)" \
CURL_BIN="$(command -v curl)" \
sudo -E LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh install-autostart

Bootstrap GitOps completo v4.0

La versión v4.0 agrega dos piezas al laboratorio:

  1. App-of-Apps

    • Aplica root-apps-registry.
    • Fuente: https://gitea.cruzcloud.net/devops/apps-registry.git.
    • Revisión: main.
    • Ruta: apps.
    • Espera las ocho Applications existentes en el repositorio.
    • Informa cuáles siguen Progressing, OutOfSync o Degraded.
  2. Gitea Actions runner persistente

    • Contenedor: gitea-runner.
    • Registro persistente: /DATA/AppData/k3d-lab/gitea-runner/data/.runner.
    • Monta /var/run/docker.sock.
    • Política unless-stopped.
    • Etiqueta compatible con runs-on: ubuntu-latest.
    • El servicio systemd también valida y recupera el runner.

Primera adopción del runner existente

Primero confirma que no haya un workflow activo y ejecuta:

MIGRATE_EXISTING_RUNNER=true ./deploy-lab.sh runner

El script:

  • detecta la imagen actual;
  • copia /data desde el contenedor existente;
  • guarda .runner fuera del contenedor;
  • recrea gitea-runner con el volumen persistente;
  • no requiere un token nuevo si .runner pudo recuperarse.

Runner desde cero

Obtén un token desde Gitea:

apps-registry -> Settings -> Actions -> Runners

Guárdalo sin mostrarlo:

sudo install -d -m 0700 -o devops /DATA/AppData/k3d-lab/secrets
sudo sh -c 'umask 077; cat > /DATA/AppData/k3d-lab/secrets/gitea-runner-registration-token'

Pega únicamente el token y finaliza con Ctrl+D. Después:

sudo chown devops /DATA/AppData/k3d-lab/secrets/gitea-runner-registration-token
sudo chmod 0600 /DATA/AppData/k3d-lab/secrets/gitea-runner-registration-token
./deploy-lab.sh runner

El token solo se utiliza para generar /data/.runner. El contenedor permanente no conserva el token de registro como variable de entorno.

Repositorio privado de Argo CD

Cuando apps-registry sea privado, crea:

sudo sh -c 'umask 077; printf "%s\n" "devops" > /DATA/AppData/k3d-lab/secrets/argocd-repo-username'
sudo sh -c 'umask 077; cat > /DATA/AppData/k3d-lab/secrets/argocd-repo-password'

Pega un token de Gitea con lectura del repositorio y finaliza con Ctrl+D.

sudo chown devops /DATA/AppData/k3d-lab/secrets/argocd-repo-username
sudo chown devops /DATA/AppData/k3d-lab/secrets/argocd-repo-password
sudo chmod 0600 /DATA/AppData/k3d-lab/secrets/argocd-repo-*

Aplicar solo GitOps al clúster actual

./deploy-lab.sh gitops

Reconstrucción completa

LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh bootstrap

El orden es:

k3d -> Argo CD -> repositorio Gitea -> root-apps-registry
     -> Applications hijas -> RBAC postdeploy -> Gitea runner

Reinstalar systemd

sudo LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh install-autostart

Verificación

kubectl get applications.argoproj.io -n argocd
docker ps --filter name=gitea-runner
docker logs --tail 100 gitea-runner

Caché Git persistente v4.1

La versión v4.1 no recrea root-apps-registry como fuente principal. Antes de aplicar GitOps:

  1. clona apps-registry cuando no existe;
  2. ejecuta git fetch --prune cuando ya existe;
  3. alinea la copia local con origin/main;
  4. valida application.yaml y la carpeta apps/;
  5. aplica application.yaml directamente desde el clon;
  6. registra el commit utilizado;
  7. conserva el clon en /DATA para una recuperación cuando Gitea esté temporalmente fuera de servicio.

Ruta:

/DATA/AppData/k3d-lab/git/apps-registry

Commit utilizado:

/DATA/AppData/k3d-lab/git/apps-registry.commit

Actualizar únicamente el clon

./deploy-lab.sh repo-sync

Actualizar el clon y reconciliar Argo CD

./deploy-lab.sh gitops

Reconstrucción desde cero

LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh bootstrap

El bootstrap intenta obtener primero la última versión de main. Cuando Gitea no responde y ya existe un clon válido, utiliza el commit cacheado y permite que Argo CD vuelva a reconciliarse cuando Gitea regrese.

El watchdog no hace git fetch cada cinco minutos por defecto. Solo usa el clon para restaurar root-apps-registry si desaparece. Argo CD sigue siendo el encargado de observar Git y desplegar cambios durante la operación normal.

Para actualizar el clon también durante ensure:

APP_REGISTRY_REFRESH_ON_ENSURE=true \
sudo LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh install-autostart

No se recomienda en el valor predeterminado porque haría una consulta a Gitea en cada ejecución del watchdog.

Corrección v4.2: descubrimiento dinámico y monitoring

Applications esperadas

La lista ya no está codificada manualmente. El script descubre el metadata.name real de cada Application dentro de:

/DATA/AppData/k3d-lab/git/apps-registry/apps/

Esto evita falsos errores cuando el nombre real es, por ejemplo, monitoring-governance y no monitoring-governance-app.

También se puede seguir forzando una lista explícita:

EXPECTED_GITOPS_APPS="ecommerce-app,monitoring-app" ./deploy-lab.sh gitops

Diagnóstico de kube-prometheus-stack

./deploy-lab.sh monitoring-diagnose

El comando muestra:

  • versión del chart;
  • estado Sync/Health;
  • operación de Argo CD;
  • mensaje del hook;
  • Jobs y Pods admission-create/admission-patch;
  • eventos recientes;
  • condiciones de la Application.

Para chart 58.2.1 o 58.2.2, usa como mínimo 58.3.3 y agrega:

helm:
  parameters:
    - name: prometheusOperator.admissionWebhooks.patch.ttlSecondsAfterFinished
      value: "60"

Multi-repo y governance v4.3

La versión v4.3 incorpora el segundo repositorio GitOps:

https://gitea.cruzcloud.net/devops/platform-infra.git

El repositorio contiene los overlays Kustomize utilizados por las Applications *-governance.

Cachés persistentes

/DATA/AppData/k3d-lab/git/apps-registry
/DATA/AppData/k3d-lab/git/platform-infra

Validación Kustomize

El script descubre en apps-registry/apps/*.yaml todas las Applications cuyo repoURL apunta a platform-infra, valida que sus rutas existan y ejecuta:

kubectl kustomize <ruta>

Los snapshots renderizados quedan en:

/DATA/AppData/k3d-lab/rendered/platform-infra/

Durante un bootstrap se aplican antes de crear las Applications de workload. Esto garantiza que Namespace, ResourceQuota, LimitRange y NetworkPolicy estén presentes antes de instalar Helm charts.

Contrato obligatorio de monitoring

monitoring-governance debe contener un LimitRange, porque su ResourceQuota exige requests y limits de CPU y memoria.

Se incluyen archivos de referencia en:

repo-patches/platform-infra/monitoring-governance/
├── limitrange.yaml
└── kustomization.yaml

Copia limitrange.yaml al repositorio platform-infra y agrega el recurso a su kustomization.yaml.

Sincronizar ambos repositorios

./deploy-lab.sh repo-sync

Reconciliar GitOps completo

LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh gitops

Reinstalar watchdog

sudo LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh install-autostart

Recuperación de operación obsoleta de Grafana

Grafana puede funcionar mientras Argo CD mantiene una operación antigua, por ejemplo target 58.3.3 con operación activa 58.2.1.

./deploy-lab.sh monitoring-diagnose
./deploy-lab.sh monitoring-recover

La recuperación valida el LimitRange, termina la operación anterior, elimina únicamente los Jobs admission temporales, ejecuta hard refresh, sincroniza la revisión actual y espera Synced/Healthy.

Cuando argocd no está instalado en ZimaOS se usa temporalmente la imagen oficial de Argo CD con --core y /DATA/.kube/config.

El manifiesto funcional está en:

repo-patches/apps-registry/apps/monitoring-app.yaml

Corrección v4.4.1: compatibilidad CLI Argo CD 3.2

argocd app terminate-op y argocd app sync no aceptan --app-namespace en la versión usada por el laboratorio.

La función argocd_core_cli ahora ejecuta:

argocd app terminate-op monitoring-app --core
argocd app sync monitoring-app --core --async --prune --assumeYes

Las Applications del laboratorio residen en argocd, el namespace de control de Argo CD.

Corrección v4.4.2

Se corrigen dos comportamientos:

  1. Cuando monitoring-app ya tiene:

    • revisión de operación igual al target;
    • fase Succeeded;
    • estado Synced/Healthy;

    el script termina correctamente y no inicia otra sincronización.

  2. En modo argocd --core, el kubeconfig temporal fija el namespace actual en argocd. Esto evita:

configmap "argocd-cm" not found

El flujo intenta primero hard refresh y auto-sync. Solo llama argocd app sync --core si Argo CD no inicia la reconciliación.

Corrección v4.4.3: Gitea runner y restart storm

El watchdog se ejecuta como devops; no puede pedir una contraseña de sudo. La v4.4.3:

  • crea las rutas persistentes del Gitea runner durante install-autostart;
  • elimina sudo del flujo ensure -> ensure_gitea_runner;
  • cambia el servicio oneshot a Restart=no;
  • deja los reintentos exclusivamente al timer de cinco minutos;
  • limita fallos con StartLimitIntervalSec=15min y StartLimitBurst=3;
  • agrega Persistent=true al timer.

Esto evita ciclos como:

restart counter is at 645
sudo: a terminal is required
Failed to allocate directory watch: Too many open files

Instalación:

sudo systemctl stop k3d-lab-ensure.timer
sudo systemctl stop k3d-lab-ensure.service || true
sudo systemctl reset-failed k3d-lab-ensure.service

sudo LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh install-autostart

Corrección v4.4.4: credenciales TLS para Gitea Actions

El error:

x509: certificate is valid for ... 192.168.68.61 ... not <otro valor>

indica que el secret K8S_TLS_SERVER_NAME no coincide con el host usado en K8S_SERVER.

La versión agrega:

./deploy-lab.sh postdeploy-secrets

Este modo:

  • recrea el ServiceAccount y RBAC de lectura;
  • regenera token, CA, server y nombre TLS;
  • guarda un kubeconfig persistente de validación;
  • comprueba acceso real a Deployments en ecommerce.

Valores esperados para este laboratorio:

K8S_SERVER=https://192.168.68.61:46421
K8S_TLS_SERVER_NAME=192.168.68.61

También incluye un build.yaml robustecido en:

repo-patches/apps-registry/.gitea/workflows/build.yaml

El workflow normaliza el secret TLS y detiene la ejecución con un mensaje claro cuando no coincide con el host de K8S_SERVER.

Corrección v4.4.5: kubeconfig atómico para Gitea Actions

El workflow usa primero el secret único:

KUBE_CONFIG_DATA

Este secret contiene conjuntamente servidor, CA, tls-server-name y token. Así se evita mezclar una CA de un clúster anterior con el servidor o token actuales. Los cuatro secrets K8S_* se conservan únicamente como fallback.

Regeneración:

LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh postdeploy-secrets

Luego actualiza en Gitea KUBE_CONFIG_DATA con el contenido de:

/DATA/AppData/k3d-lab/gitea-actions-secrets/KUBE_CONFIG_DATA.txt

El workflow muestra únicamente la huella SHA256 pública de la CA, nunca el certificado, token ni contenido del secret.

Corrección v4.4.6: token persistente para Gitea Actions

El modo postdeploy-secrets ya no utiliza kubectl create token, porque ese mecanismo entrega tokens con duración limitada y el API Server puede acortar la duración solicitada.

La versión crea y administra:

ServiceAccount/ecommerce/gitea-postdeploy-validator
Secret/ecommerce/gitea-postdeploy-validator-token

El Secret es de tipo:

kubernetes.io/service-account-token

El controlador de Kubernetes genera dentro del Secret:

  • token;
  • ca.crt;
  • namespace.

El RBAC continúa siendo de solo lectura sobre Deployments, ReplicaSets y Pods del namespace ecommerce.

Regeneración:

LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh postdeploy-secrets

Después actualiza solamente el secret de Gitea:

KUBE_CONFIG_DATA

usando:

/DATA/AppData/k3d-lab/gitea-actions-secrets/KUBE_CONFIG_DATA.txt

El workflow valida que el kubeconfig contenga token y muestra únicamente metadatos seguros: ServiceAccount y expiración, sin imprimir el token.

Corrección v4.4.6.1: indentación de build.yaml

El bloque Python usado para leer metadatos del token había quedado en la columna 1 del YAML. Eso cerraba prematuramente el bloque:

run: |

La corrección mantiene todo el código Python con la indentación YAML del script. Al procesarse run: |, Bash recibe nuevamente el código Python desde la columna 1.

Archivo corregido:

repo-patches/apps-registry/.gitea/workflows/build.yaml

También se incluye una copia en la raíz del paquete:

build.yaml