# 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`: ```bash 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 ```bash ./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: ```bash sudo ./deploy-lab.sh install-autostart ``` Verificar: ```bash 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 ```bash ./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: ```text /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: ```text /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: ```bash 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: ```bash CONFIRM_RESET=DELETE-lab-cluster ./deploy-lab.sh reset ``` Antes de limpiar el estado, el script crea un archivo en: ```text /DATA/AppData/k3d-lab/backups/ ``` ## Variables principales ```text 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: ```bash 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 ```bash 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: ```bash ./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: ```bash 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`: ```bash LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh bootstrap ``` Solo la instalación de systemd requiere sudo: ```bash 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: ```bash 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: ```bash 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: ```bash 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: ```text /DATA/AppData/k3d-lab/bin/deploy-k3d-lab ``` La unidad usa Bash explícitamente: ```ini 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: ```bash 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: ```bash 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: ```bash sudo LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh install-autostart ``` Fallback explícito: ```bash 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: ```bash 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: ```text apps-registry -> Settings -> Actions -> Runners ``` Guárdalo sin mostrarlo: ```bash 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: ```bash 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: ```bash 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`. ```bash 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 ```bash ./deploy-lab.sh gitops ``` ## Reconstrucción completa ```bash LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh bootstrap ``` El orden es: ```text k3d -> Argo CD -> repositorio Gitea -> root-apps-registry -> Applications hijas -> RBAC postdeploy -> Gitea runner ``` ## Reinstalar systemd ```bash sudo LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh install-autostart ``` ## Verificación ```bash 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: ```text /DATA/AppData/k3d-lab/git/apps-registry ``` Commit utilizado: ```text /DATA/AppData/k3d-lab/git/apps-registry.commit ``` ## Actualizar únicamente el clon ```bash ./deploy-lab.sh repo-sync ``` ## Actualizar el clon y reconciliar Argo CD ```bash ./deploy-lab.sh gitops ``` ## Reconstrucción desde cero ```bash 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`: ```bash 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: ```text /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: ```bash EXPECTED_GITOPS_APPS="ecommerce-app,monitoring-app" ./deploy-lab.sh gitops ``` ## Diagnóstico de kube-prometheus-stack ```bash ./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: ```yaml 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: ```text https://gitea.cruzcloud.net/devops/platform-infra.git ``` El repositorio contiene los overlays Kustomize utilizados por las Applications `*-governance`. ## Cachés persistentes ```text /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: ```bash kubectl kustomize ``` Los snapshots renderizados quedan en: ```text /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: ```text 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 ```bash ./deploy-lab.sh repo-sync ``` ## Reconciliar GitOps completo ```bash LAB_HOST_IP=192.168.68.61 ./deploy-lab.sh gitops ``` ## Reinstalar watchdog ```bash 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`. ```bash ./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: ```text 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: ```bash 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: ```text 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: ```text restart counter is at 645 sudo: a terminal is required Failed to allocate directory watch: Too many open files ``` Instalación: ```bash 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 ```