diff --git a/monitoring/argocd-app-kube-prometheus-stack.yaml b/monitoring/argocd-app-kube-prometheus-stack.yaml new file mode 100644 index 0000000..074aacd --- /dev/null +++ b/monitoring/argocd-app-kube-prometheus-stack.yaml @@ -0,0 +1,207 @@ +# Fase 2 de meter monitoring/ bajo ArgoCD (2026-07-25): adoptar el chart. +# +# Hasta hoy el kube-prometheus-stack era un release Helm gestionado A MANO +# (helm upgrade desde este directorio) y sus ~40 manifiestos renderizados +# estaban volcados en git sin que nadie los aplicara: decoracion. Ahora la +# fuente de verdad es esta Application y git manda. +# +# EL NOMBRE IMPORTA: ArgoCD usa el nombre de la Application como Release.Name +# al renderizar. Tiene que ser "kube-prometheus-stack" o TODOS los objetos +# cambiarian de nombre (kube-prometheus-stack-grafana -> otro-grafana) y +# ArgoCD crearia un stack paralelo en vez de adoptar el que ya corre. +# +# skipCrds: true A PROPOSITO. ArgoCD pasa --include-crds por defecto, lo que +# le daria los 10 CRD de monitoring.coreos.com. Esos CRD NO los gestiona nadie +# hoy (no llevan label managed-by ni anotacion de release: Helm los instalo +# desde crds/ y no los rastrea). Dejarlos fuera evita que ArgoCD se pelee por +# ellos y evita el limite de 262144 bytes de last-applied en CRDs enormes. +# Contrapartida: al subir de version de chart, los CRD hay que actualizarlos +# a mano (kubectl apply -f del directorio crds/ del chart nuevo). +# +# La version va CLAVADA a 83.2.0, que es lo que corre. Esto es una adopcion, +# NO una actualizacion (el repo ya va por 87.x). Subir de version es otro dia +# y otro commit, para que si algo se rompe se sepa que lo rompio. +# +# Los valores van INLINE, no en un fichero aparte con multi-source $values: +# ese montaje se auto-podo la Application de infisical el 2026-06-18. Y tener +# los valores en dos sitios es justo la deriva que veniamos a matar. +# +# Verificado antes de adoptar: helm template con estos valores == lo desplegado +# campo a campo (las unicas diferencias eran nulls y ceros que el API server +# omite), y CERO objetos vivos con instance=kube-prometheus-stack que el chart +# no renderice, asi que prune no tiene nada que llevarse por delante. +# +# Para diffear a mano lo que ArgoCD va a aplicar: +# helm template kube-prometheus-stack prometheus-community/kube-prometheus-stack \ +# --version 83.2.0 -n monitoring --skip-crds -f <(yq '.spec.source.helm.valuesObject' este-fichero) +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: kube-prometheus-stack + namespace: argocd + annotations: + notifications.argoproj.io/subscribe.on-sync-failed.telegram: "5138407666" + notifications.argoproj.io/subscribe.on-health-degraded.telegram: "5138407666" + # Diff en el servidor, NO el merge a 3 bandas del cliente. Sin esto la + # adopcion NO era gratis: el render del subchart de grafana no declara + # managed-by: Helm ni el restartedAt del podTemplate, y el merge clasico + # los habria BORRADO -> Grafana se reinicia y Helm pierde la propiedad del + # Deployment (helm upgrade/uninstall empezarian a fallar por ownership). + # Con ServerSideDiff, ArgoCD solo mira los campos que declara y deja en paz + # los que puso otro. Medido: 96/97 Synced con el diff clasico, 97/97 con + # este. Va de la mano de ServerSideApply de abajo (uno diffea como el otro + # aplica; mezclarlos deja la app OutOfSync para siempre). + argocd.argoproj.io/compare-options: ServerSideDiff=true +spec: + project: default + source: + repoURL: https://prometheus-community.github.io/helm-charts + chart: kube-prometheus-stack + targetRevision: 83.2.0 + helm: + skipCrds: true + valuesObject: + # rev 2 (2026-07-21): silenciadas 4 alertas que llevaban meses disparadas sin + # significar nada — ver kubeControllerManager/kubeScheduler/kubeProxy y + # defaultRules.disabled. + # rev 3 (2026-07-21): grafana.extraConfigmapMounts. El montaje del ConfigMap + # grafana-alerting (reglas + contact points + política → Telegram) se había + # añadido A MANO al Deployment y NO lo generaba el chart: sobrevivía solo por + # el three-way merge de Helm. Reconstruir el release desde este fichero se + # habría llevado por delante TODO el alerting, en silencio. + # rev 4 (2026-07-21): grafana.admin.existingSecret y FUERA adminPassword. + # El valor en git era "admin", que no abría nada: la cuenta admin local se + # configuró el 26-abr y no se ha usado desde entonces (se entra por Authentik). + # Los sidecars de dashboards y datasources SÍ la usaban y recibían 401 al + # intentar recargar por API. Ahora la contraseña es aleatoria, vive solo en el + # Secret grafana-admin (creado fuera del chart, nunca en git) y la cuenta queda + # como acceso de emergencia. Leerla: + # kubectl get secret grafana-admin -n monitoring \ + # -o jsonpath='{.data.admin-password}' | base64 -d; echo + # rev 5 (2026-07-22): fuera la datasource Alertmanager. OJO: NO la apaga + # alertmanager.enabled=false — el chart la provisiona igual, apuntando a un + # servicio que no existe (error 500 al consultarla). El interruptor real es + # grafana.sidecar.datasources.alertmanager.enabled. Y quitarla del fichero de + # provisioning NO la borra de grafana.db: hay que provisionar el borrado con + # una directiva deleteDatasources de un solo uso (se hizo y se retiró). + # rev 7 (2026-07-22): recuperado el SSO de Authentik, roto desde hace ~15 días + # (último acceso de akadmin). Grafana ignoraba la cabecera X-authentik-username + # -> Authentik te dejaba pasar y Grafana te pedía credenciales igual. El bloque + # auth.proxy existía en el ConfigMap vivo en abril (está en el export de git), + # pero el chart NUNCA lo ha generado en ninguna de sus 6 revisiones, así que + # era otro retoque a mano que se perdió por el camino. Ahora va declarado. + # Junto con él, la anotación del middleware de Traefik: tampoco la generaba el + # chart, y sin ella Grafana queda expuesta sin Authentik delante. + # rev 6 (2026-07-22): grafana.envValueFrom. Mismo caso que rev 3, encontrado al + # auditar el Deployment contra el render: TELEGRAM_BOT_TOKEN y TELEGRAM_CHAT_ID + # estaban puestas A MANO y el chart no las generaba. El contact point de + # alerting usa ${TELEGRAM_BOT_TOKEN}: reconstruir el release desde cero habría + # dejado las alertas de Telegram mudas. Se declaran aquí sin tocar nada vivo. + # (CHAT_ID hoy no lo usa nadie —el chatid del contact point va literal— pero se + # deja para que el render coincida exactamente con lo desplegado.) + alertmanager: + enabled: false + defaultRules: + create: true + disabled: + PrometheusNotConnectedToAlertmanagers: true + rules: + alertmanager: false + grafana: + admin: + existingSecret: grafana-admin + envValueFrom: + TELEGRAM_BOT_TOKEN: + secretKeyRef: + key: TELEGRAM_BOT_TOKEN + name: grafana-telegram-infisical + TELEGRAM_CHAT_ID: + secretKeyRef: + key: TELEGRAM_CHAT_ID + name: grafana-telegram-infisical + extraConfigmapMounts: + - configMap: grafana-alerting + mountPath: /etc/grafana/provisioning/alerting + name: grafana-alerting + grafana.ini: + # SSO con Authentik. Traefik autentica en el borde con el middleware de + # abajo y reenvía X-authentik-username; esto es lo que hace que Grafana se + # fíe de esa cabecera y no vuelva a pedir credenciales. Sin este bloque el + # SSO queda a medias: Authentik te deja pasar y Grafana te enseña SU + # formulario. auto_sign_up crea el usuario (así nació akadmin). + # disable_login_form cierra el formulario local: por la web sólo se entra + # por Authentik. NO cierra la auth básica de la API, que es de donde cuelgan + # los dos sidecars y la puerta de emergencia. + # + # SI EL SSO SE CAE Y NO PUEDES ENTRAR POR LA WEB: + # 1. La API sigue abierta con la contraseña del secret grafana-admin: + # kubectl get secret grafana-admin -n monitoring \ + # -o jsonpath='{.data.admin-password}' | base64 -d; echo + # y contra localhost:3000 dentro del pod, o por port-forward. + # 2. Para recuperar el formulario, poner disable_login_form: false aquí, + # commit y push: ArgoCD lo sincroniza solo. Tarda un minuto y no + # pierde nada. (Ya NO se hace con helm upgrade: manda ArgoCD.) + auth: + disable_login_form: true + auth.proxy: + auto_sign_up: true + enabled: true + header_name: X-authentik-username + header_property: username + server: + root_url: https://grafana.chemavx.xyz + ingress: + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + # Sin esta anotación Grafana queda expuesta sin Authentik delante. Estaba + # puesta a mano en el Ingress vivo y el chart no la generaba: sobrevivía + # sólo por el three-way merge, como el montaje de alerting (rev 3). + traefik.ingress.kubernetes.io/router.middlewares: authentik-authentik-forward-auth@kubernetescrd + traefik.ingress.kubernetes.io/router.entrypoints: websecure + enabled: true + hosts: + - grafana.chemavx.xyz + ingressClassName: traefik + tls: + - hosts: + - grafana.chemavx.xyz + secretName: grafana-tls + persistence: + enabled: true + size: 5Gi + storageClassName: local-path + sidecar: + datasources: + alertmanager: + enabled: false + kubeControllerManager: + enabled: false + kubeProxy: + enabled: false + kubeScheduler: + enabled: false + prometheus: + prometheusSpec: + retention: 30d + storageSpec: + volumeClaimTemplate: + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi + storageClassName: local-path + destination: + server: https://kubernetes.default.svc + namespace: monitoring + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + # Ver la nota de ServerSideDiff arriba: ArgoCD se declara un field manager + # mas y solo toca lo suyo, en vez de arrasar con lo que no aparece en el + # last-applied. Imprescindible al adoptar un release Helm ya vivo. + - ServerSideApply=true diff --git a/monitoring/argocd-app.yaml b/monitoring/argocd-app.yaml index 99376b6..bf00a37 100644 --- a/monitoring/argocd-app.yaml +++ b/monitoring/argocd-app.yaml @@ -4,11 +4,13 @@ # mezcla TRES dueños distintos y solo uno es nuestro. # # A) Lo que escribimos nosotros -> el include de aquí abajo, ArgoCD manda. -# B) Lo que genera el chart -> kube-prometheus-stack (release Helm viva). -# Meterlo aquí daría DOS dueños al mismo objeto y reproduciría la deriva -# del three-way merge que ya se comió el alerting y el token de Telegram. -# Adoptarlo va aparte (source Helm + values-kube-prometheus-stack.yaml, -# borrando de git los ~40 renderizados). Pendiente = Fase 2. +# B) Lo que genera el chart -> kube-prometheus-stack. Sigue SIN estar en +# esta lista, y con razón: gestionarlo desde aquí como manifiestos sueltos +# daría DOS dueños al mismo objeto y reproduciría la deriva del three-way +# merge que ya se comió el alerting y el token de Telegram. Desde la Fase 2 +# (2026-07-25) lo gestiona su propia Application con source Helm -> +# argocd-app-kube-prometheus-stack.yaml, y los ~40 renderizados que había +# volcados en git se han borrado. Un solo dueño, el chart. # C) Lo que fabrican los controladores solos -> serviceaccount-default, # configmap-kube-root-ca.crt y el rulefiles-0 (lo genera el # prometheus-operator desde los PrometheusRule). En git no pintan nada; @@ -44,7 +46,7 @@ spec: # Lista blanca explícita. Al añadir un fichero nuestro a monitoring/ hay # que añadirlo AQUÍ o ArgoCD lo ignora. Y al revés: sacar un fichero de # esta lista con prune:true activo BORRA el recurso del cluster. - include: "{argocd-app.yaml,configmap-grafana-alerting.yaml,configmap-chemavx-homelab-overview.yaml,deployment-uptime-kuma.yaml,service-uptime-kuma.yaml,pvc-uptime-kuma-pvc.yaml,ingress-uptime-kuma.yaml,ingress-uptime-kuma-redirect.yaml,ingress-prometheus.yaml,infisical-grafana-telegram.yaml}" + include: "{argocd-app.yaml,argocd-app-kube-prometheus-stack.yaml,configmap-grafana-alerting.yaml,configmap-chemavx-homelab-overview.yaml,deployment-uptime-kuma.yaml,service-uptime-kuma.yaml,pvc-uptime-kuma-pvc.yaml,ingress-uptime-kuma.yaml,ingress-uptime-kuma-redirect.yaml,ingress-prometheus.yaml,infisical-grafana-telegram.yaml}" destination: server: https://kubernetes.default.svc namespace: monitoring diff --git a/monitoring/values-kube-prometheus-stack.yaml b/monitoring/values-kube-prometheus-stack.yaml deleted file mode 100644 index c4b853e..0000000 --- a/monitoring/values-kube-prometheus-stack.yaml +++ /dev/null @@ -1,138 +0,0 @@ -# Valores del release Helm kube-prometheus-stack (chart 83.2.0, ns monitoring). -# -# Este namespace NO está bajo ArgoCD: se gestiona a mano. Este fichero es la -# fuente de verdad del release; para aplicarlo: -# -# helm upgrade kube-prometheus-stack prometheus-community/kube-prometheus-stack \ -# --version 83.2.0 -n monitoring -f values-kube-prometheus-stack.yaml -# -# rev 2 (2026-07-21): silenciadas 4 alertas que llevaban meses disparadas sin -# significar nada — ver kubeControllerManager/kubeScheduler/kubeProxy y -# defaultRules.disabled. -# rev 3 (2026-07-21): grafana.extraConfigmapMounts. El montaje del ConfigMap -# grafana-alerting (reglas + contact points + política → Telegram) se había -# añadido A MANO al Deployment y NO lo generaba el chart: sobrevivía solo por -# el three-way merge de Helm. Reconstruir el release desde este fichero se -# habría llevado por delante TODO el alerting, en silencio. -# rev 4 (2026-07-21): grafana.admin.existingSecret y FUERA adminPassword. -# El valor en git era "admin", que no abría nada: la cuenta admin local se -# configuró el 26-abr y no se ha usado desde entonces (se entra por Authentik). -# Los sidecars de dashboards y datasources SÍ la usaban y recibían 401 al -# intentar recargar por API. Ahora la contraseña es aleatoria, vive solo en el -# Secret grafana-admin (creado fuera del chart, nunca en git) y la cuenta queda -# como acceso de emergencia. Leerla: -# kubectl get secret grafana-admin -n monitoring \ -# -o jsonpath='{.data.admin-password}' | base64 -d; echo -# rev 5 (2026-07-22): fuera la datasource Alertmanager. OJO: NO la apaga -# alertmanager.enabled=false — el chart la provisiona igual, apuntando a un -# servicio que no existe (error 500 al consultarla). El interruptor real es -# grafana.sidecar.datasources.alertmanager.enabled. Y quitarla del fichero de -# provisioning NO la borra de grafana.db: hay que provisionar el borrado con -# una directiva deleteDatasources de un solo uso (se hizo y se retiró). -# rev 7 (2026-07-22): recuperado el SSO de Authentik, roto desde hace ~15 días -# (último acceso de akadmin). Grafana ignoraba la cabecera X-authentik-username -# -> Authentik te dejaba pasar y Grafana te pedía credenciales igual. El bloque -# auth.proxy existía en el ConfigMap vivo en abril (está en el export de git), -# pero el chart NUNCA lo ha generado en ninguna de sus 6 revisiones, así que -# era otro retoque a mano que se perdió por el camino. Ahora va declarado. -# Junto con él, la anotación del middleware de Traefik: tampoco la generaba el -# chart, y sin ella Grafana queda expuesta sin Authentik delante. -# rev 6 (2026-07-22): grafana.envValueFrom. Mismo caso que rev 3, encontrado al -# auditar el Deployment contra el render: TELEGRAM_BOT_TOKEN y TELEGRAM_CHAT_ID -# estaban puestas A MANO y el chart no las generaba. El contact point de -# alerting usa ${TELEGRAM_BOT_TOKEN}: reconstruir el release desde cero habría -# dejado las alertas de Telegram mudas. Se declaran aquí sin tocar nada vivo. -# (CHAT_ID hoy no lo usa nadie —el chatid del contact point va literal— pero se -# deja para que el render coincida exactamente con lo desplegado.) -alertmanager: - enabled: false -defaultRules: - create: true - disabled: - PrometheusNotConnectedToAlertmanagers: true - rules: - alertmanager: false -grafana: - admin: - existingSecret: grafana-admin - envValueFrom: - TELEGRAM_BOT_TOKEN: - secretKeyRef: - key: TELEGRAM_BOT_TOKEN - name: grafana-telegram-infisical - TELEGRAM_CHAT_ID: - secretKeyRef: - key: TELEGRAM_CHAT_ID - name: grafana-telegram-infisical - extraConfigmapMounts: - - configMap: grafana-alerting - mountPath: /etc/grafana/provisioning/alerting - name: grafana-alerting - grafana.ini: - # SSO con Authentik. Traefik autentica en el borde con el middleware de - # abajo y reenvía X-authentik-username; esto es lo que hace que Grafana se - # fíe de esa cabecera y no vuelva a pedir credenciales. Sin este bloque el - # SSO queda a medias: Authentik te deja pasar y Grafana te enseña SU - # formulario. auto_sign_up crea el usuario (así nació akadmin). - # disable_login_form cierra el formulario local: por la web sólo se entra - # por Authentik. NO cierra la auth básica de la API, que es de donde cuelgan - # los dos sidecars y la puerta de emergencia. - # - # SI EL SSO SE CAE Y NO PUEDES ENTRAR POR LA WEB: - # 1. La API sigue abierta con la contraseña del secret grafana-admin: - # kubectl get secret grafana-admin -n monitoring \ - # -o jsonpath='{.data.admin-password}' | base64 -d; echo - # y contra localhost:3000 dentro del pod, o por port-forward. - # 2. Para recuperar el formulario, poner disable_login_form: false aquí y - # helm upgrade. Tarda un minuto y no pierde nada. - auth: - disable_login_form: true - auth.proxy: - auto_sign_up: true - enabled: true - header_name: X-authentik-username - header_property: username - server: - root_url: https://grafana.chemavx.xyz - ingress: - annotations: - cert-manager.io/cluster-issuer: letsencrypt-prod - # Sin esta anotación Grafana queda expuesta sin Authentik delante. Estaba - # puesta a mano en el Ingress vivo y el chart no la generaba: sobrevivía - # sólo por el three-way merge, como el montaje de alerting (rev 3). - traefik.ingress.kubernetes.io/router.middlewares: authentik-authentik-forward-auth@kubernetescrd - traefik.ingress.kubernetes.io/router.entrypoints: websecure - enabled: true - hosts: - - grafana.chemavx.xyz - ingressClassName: traefik - tls: - - hosts: - - grafana.chemavx.xyz - secretName: grafana-tls - persistence: - enabled: true - size: 5Gi - storageClassName: local-path - sidecar: - datasources: - alertmanager: - enabled: false -kubeControllerManager: - enabled: false -kubeProxy: - enabled: false -kubeScheduler: - enabled: false -prometheus: - prometheusSpec: - retention: 30d - storageSpec: - volumeClaimTemplate: - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 20Gi - storageClassName: local-path