클러스터 관측성을 올리기 위해 Prometheus + Grafana를 얹은 기록. full kube-prometheus-stack은 이 홈랩 규모엔 과해서 처음부터 배제하고, 경량 구성으로 직접 짰다.

정정: 이 문서 초안엔 "Headless-Migration-Amphetamine-to-Caffeinate.md로 확보한 메모리 여유를 활용해서"라고 써있었는데 틀렸다. 호스트(macOS) 메모리 정리와 k3s VM(게스트) 메모리는 서로 다른 풀이라 호스트 쪽에서 아무리 회수해도 VM에 잡힌 고정 할당량은 안 바뀐다. 실제로 이 스택이 들어간 자리는 원래부터 있던 worker1의 여유 공간이었다 — 자세한 설명은 Host-vs-Cluster-Memory-Two-Separate-Pools.md 참고.

왜 kube-prometheus-stack을 안 썼나

Helm으로 흔히 까는 kube-prometheus-stack(Prometheus Operator + Alertmanager + node-exporter + kube-state-metrics + Grafana)은 보통 1.5~2.5GB를 먹는다. 배포 직전 기준 워커 두 대(worker1 630MB, worker2 560MB) 합쳐 여유가 1.2GB뿐이었어서, 이걸 깔면 여유를 통째로 날리고 kafka/qdrant처럼 이미 빡빡한 워크로드까지 OOM 위험에 노출시킬 수 있었다.

구성 — 뺀 것과 남긴 것

리소스 사이징

배치 스케줄링은 기존 패턴을 그대로 따랐다 — nodeAffinity로 control-plane(master) 회피, podAntiAffinity(preferred)로 kafka/qdrant/target-tracking-service/postgres가 있는 노드를 피함. 실제로 둘 다 여유가 가장 많던 worker1에 배치됐다.

스크랩 대상

이미 계측이 끝나 있어서 순수 인프라만 얹으면 됐다 — 새 앱 코드는 하나도 안 건드렸다:

배포 과정

# 1. namespace + Grafana admin 비밀번호(git에 커밋하지 않음 -- target-tracking-secrets와 동일 패턴)
kubectl create namespace monitoring
kubectl create secret generic grafana-admin -n monitoring \
  --from-literal=admin-password='<생성된 랜덤 비밀번호>'

# 2. apps/monitoring/ 매니페스트 git push 후 ArgoCD Application 최초 1회 등록
#    (이 리포는 app-of-apps/ApplicationSet이 없어서 새 Application은 수동 kubectl apply 필요)
kubectl apply -f argocd/applications/monitoring.yaml

검증

kubectl get pods -n monitoring
# grafana     1/1 Running
# prometheus  1/1 Running

curl http://<k3s-master-tailscale-ip>:30090/api/v1/targets
# kubernetes-cadvisor x3 (노드별) + target-tracking-service + threat-intel-ai-service -- 전부 up

curl http://<k3s-master-tailscale-ip>:30300/api/health
# 200

kubectl top nodes

배포 전후 worker1 메모리: 807Mi(55%) → 1005Mi(69%) — 약 200MB 증가, 예상 범위(최대 350~450MB) 안쪽. CPU는 초기 기동 직후 38%까지 튀었다가 곧 15%로 안정.

접속 정보

기존 Qdrant(30333), Postgres(30432) NodePort와 동일하게 Tailscale 네트워크 밖으로는 노출되지 않는다.

Grafana admin 비밀번호 변경

방법 1 — UI에서 직접 (일상적으로 쓰는 방법): 로그인 후 우측 상단 프로필 아이콘 → Change Password. Grafana 내부 DB(PVC에 저장됨)에 반영되고 파드 재시작에도 유지된다.

방법 2 — kubectl로 강제 리셋 (로그인 자체가 안 될 때):

kubectl exec -n monitoring deploy/grafana -- grafana-cli admin reset-admin-password '새비밀번호'