Files
service-catalog/doc/monitoring-deploy-test.md
T
2026-06-25 15:01:57 +09:00

18 KiB

VictoriaMetrics 모니터링 스택 배포 테스트

카탈로그의 VM 옵저버빌리티 스택(11개 Application)을 ArgoCD 전체 GitOps 경로로 배포·검증하는 절차와 기록 문서. 테스트 완료 후 본 문서를 바탕으로 이슈에 결과를 정리한다.

표기 규칙 (중요)

🔧 수동 설정 — dip-console/ArgoCD가 아니라 사람이 스크립트·명령어로 직접 설정한 항목. 이 마커가 붙은 내용은 모두 **§7 수동 설정 목록**에 모이며, 이슈 정리 + 향후 dip-console 자동화 대상이다. 새 수동 작업이 생기면 같은 마커로 표기하고 §7에 추가한다.

테스트 중 각 단계의 [ ] 체크박스와 결과란을 채운다.


1. 테스트 환경 / 전제

항목 값 / 상태
클러스터 (기입: context / API 도달 여부)
ArgoCD (기입: 버전 / namespace)
StorageClass longhorn (전제)
Ingress / cert-manager apisix / root-ca-issuer (전제)
Keycloak paasup realm, 도달 URL (기입)
카탈로그 repo gitea.example.org/dip/service-catalog @ master
값(values) repo (기입: URL @ branch)
사전조건 준비 방식 수동 목(mock) — dip-console 미사용

경로: ArgoCD ApplicationSet 이중 소스(카탈로그 + 값 repo) + syncWave 0→1→2. 사전조건은 수동 목으로 준비한다.


2. 0단계 — 로컬 정적 검증 (클러스터 불필요)

  • 오버레이 $VAR를 테스트값으로 치환 후 2단 병합 렌더 확인 — 11/11 PASS
    # 차트마다: 베이스(custom-values) + 렌더된 오버레이
    helm template <release> manifests/helm/<chart>/<ver> \
      -f manifests/helm/<chart>/<ver>/custom-values.yaml \
      -f /tmp/<chart>-overlay-rendered.yaml > /dev/null
    
  • vmauth 렌더 config에 issuer·{{.MetricsAccountID}}·로그 AccountID 헤더 포함 확인 — PASS
  • applicationset.yaml / perses-dashboards.yaml / perses-provisioning.yaml YAML 유효성 — PASS

결과: 0단계 통과 (2026-06-25)

  • 2단 병합 렌더 11/11 OK (vmcluster·vlogs·ksm·node-exporter·alertmanager·vmauth·vmagent·otelcol·otelcol-events·vmalert·perses). otelcol 로그전용(trace 포트 0), perses ingress 포함(docs=14).
  • vmauth 렌더 auth.yml: issuer realms/paasup, 메트릭 경로 {{.MetricsAccountID}}, 로그 헤더 AccountID: {{.LogsAccountID}}, unauthorized_user accountID 0 모두 확인($VAR 치환 후에도 vmauth 플레이스홀더 보존).
  • 매니페스트 3종 YAML 유효(applicationset / perses-dashboards / perses-provisioning).
  • 테스트 치환값: $STORAGE_CLASS=longhorn, $KEYCLOAK_URL=https://keycloak.example.org, $KEYCLOAK_REALM=paasup, $DOMAIN=example.org (검증용 임시값, 실제 배포는 dip-console 렌더).

3. 1단계 — 사전조건 수동 목 준비

🔧 수동 설정 — 이 단계 전체가 수동 목. dip-console이 대신할 작업들이다. 3.1~3.5 모두 실행 완료(2026-06-25). 명령·결과는 각 절 + §7에 집계.

테스트 환경값: DOMAIN=example.org, KEYCLOAK_URL=https://keycloak.example.org, KEYCLOAK_REALM=paasup, STORAGE_CLASS=longhorn. root-ca-cert 소스 = cert-manager/root-ca-secret(ca.crt).

3.1 값(values) repo — 실행 완료 (2026-06-25)

  • 오버레이 11종을 $VAR 치환(문자열 치환만, {{.MetricsAccountID}} 등 보존) → ~/project/tenant-catalog/victoria-metrics/에 생성 후 push
    # dip-catalog 오버레이 → tenant-catalog 렌더 (sed 문자열 치환)
    for f in manifests/applicationset/victoria-metrics/1.0.0/*-values.yaml; do
      sed -e 's#$STORAGE_CLASS#longhorn#g' -e 's#$KEYCLOAK_URL#https://keycloak.example.org#g' \
          -e 's#$KEYCLOAK_REALM#paasup#g' -e 's#$DOMAIN#example.org#g' \
          -e 's#$INFISICAL_PERSES_CLIENT_SECRET#perses-mock-secret-changeme#g' \
          -e 's#$INFISICAL_ALERTMANAGER_SECRET#alertmanager-notify#g' "$f" \
        > ~/project/tenant-catalog/victoria-metrics/$(basename "$f")
    done
    cd ~/project/tenant-catalog && git add -A && git commit -m "Add victoria-metrics values (mock)" && git push origin master
    
    결과: dip/tenant-catalog @ master 510aa8e, victoria-metrics/*-values.yaml 11개.

생성된 값 파일 내용 (환경 고유값 있는 차트만; 나머지 6종 kube-state-metrics/node-exporter/otelcol/otelcol-events/vmagent/vmalert-values.yaml = {} — 베이스 custom-values만 사용)

vmauth-values.yaml

config:
  users:
    - jwt:
        oidc:
          issuer: "https://keycloak.example.org/realms/paasup"
      url_map:
        - src_paths: ["/api/v1/.*"]
          url_prefix: "http://vmcluster-victoria-metrics-cluster-vmselect.monitoring.svc.cluster.local:8481/select/{{.MetricsAccountID}}/prometheus"
        - src_paths: ["/select/logsql/.*"]
          url_prefix: "http://vlogs-victoria-logs-cluster-vlselect.monitoring.svc.cluster.local:9471"
          headers:
            - "AccountID: {{.LogsAccountID}}"
            - "ProjectID: 0"
  unauthorized_user:
    url_prefix: "http://vmcluster-victoria-metrics-cluster-vmselect.monitoring.svc.cluster.local:8481/select/0/prometheus"

perses-values.yaml

config:
  security:
    enable_auth: true
    authentication:
      providers:
        oidc:
          - slug_id: keycloak
            name: Keycloak
            client_id: perses
            client_secret: "perses-mock-secret-changeme"
            issuer: "https://keycloak.example.org/realms/paasup"
            redirect_uri: "https://perses.example.org/api/auth/providers/oidc/keycloak/callback"
            scopes: [openid, profile, email]
ingress:
  enabled: true
  ingressClassName: "apisix"
  annotations:
    cert-manager.io/cluster-issuer: "root-ca-issuer"
  hosts:
    - host: "perses.example.org"
      paths: [{ path: /, pathType: Prefix }]
  tls:
    - secretName: perses-tls
      hosts: ["perses.example.org"]

alertmanager-values.yaml

extraSecretMounts:
  - name: alertmanager-notify
    mountPath: /etc/alertmanager/secrets
    subPath: ""
    secretName: "alertmanager-notify"
    readOnly: true

vmcluster-values.yaml / vlogs-values.yaml

# vmcluster
vmstorage: { persistentVolume: { storageClass: "longhorn" } }
# vlogs
vlstorage: { persistentVolume: { storageClass: "longhorn" } }

3.2 Keycloak (paasup realm) — 실행 완료 (2026-06-25)

확인됨: paasup realm 존재(HTTP 200), perses/perses-vmauth client·vm-access scope 없음(신규 생성 대상).

# ── 관리자 토큰 (platform Keycloak) ──
KC=https://keycloak.example.org; REALM=paasup
PW=$(kubectl get secret -n platform keycloak -o jsonpath='{.data.admin-password}' | base64 -d)
TOKEN=$(curl -sk -X POST "$KC/realms/master/protocol/openid-connect/token" \
  -d grant_type=password -d client_id=admin-cli -d username=admin --data-urlencode "password=$PW" | jq -r .access_token)
H="Authorization: Bearer $TOKEN"; JSON="Content-Type: application/json"

# ── 1) Client Scope vm-access + 매퍼 2종 ──
curl -sk -X POST "$KC/admin/realms/$REALM/client-scopes" -H "$H" -H "$JSON" \
  -d '{"name":"vm-access","protocol":"openid-connect"}'
SID=$(curl -sk "$KC/admin/realms/$REALM/client-scopes" -H "$H" | jq -r '.[]|select(.name=="vm-access").id')
for m in metrics logs; do
  curl -sk -X POST "$KC/admin/realms/$REALM/client-scopes/$SID/protocol-mappers/models" -H "$H" -H "$JSON" \
    -d "{\"name\":\"vm-$m-account\",\"protocol\":\"openid-connect\",\"protocolMapper\":\"oidc-usermodel-attribute-mapper\",\"config\":{\"user.attribute\":\"vm_${m}_account_id\",\"claim.name\":\"vm_access.${m}_account_id\",\"jsonType.label\":\"int\",\"access.token.claim\":\"true\",\"id.token.claim\":\"false\"}}"
done

# ── 2) Client perses (로그인) — secret은 perses-values.yaml의 client_secret과 일치 ──
curl -sk -X POST "$KC/admin/realms/$REALM/clients" -H "$H" -H "$JSON" -d '{
  "clientId":"perses","enabled":true,"publicClient":false,"standardFlowEnabled":true,
  "secret":"perses-mock-secret-changeme",
  "redirectUris":["https://perses.example.org/api/auth/providers/oidc/keycloak/callback"]}'

# ── 3) Client perses-vmauth (datasource service-account) ──
curl -sk -X POST "$KC/admin/realms/$REALM/clients" -H "$H" -H "$JSON" -d '{
  "clientId":"perses-vmauth","enabled":true,"publicClient":false,
  "standardFlowEnabled":false,"serviceAccountsEnabled":true,"secret":"perses-vmauth-secret-changeme"}'
CID=$(curl -sk "$KC/admin/realms/$REALM/clients?clientId=perses-vmauth" -H "$H" | jq -r '.[0].id')
curl -sk -X PUT "$KC/admin/realms/$REALM/clients/$CID/default-client-scopes/$SID" -H "$H"   # vm-access 연결
SAU=$(curl -sk "$KC/admin/realms/$REALM/clients/$CID/service-account-user" -H "$H" | jq -r .id)
curl -sk -X PUT "$KC/admin/realms/$REALM/users/$SAU" -H "$H" -H "$JSON" \
  -d '{"attributes":{"vm_metrics_account_id":["0"],"vm_logs_account_id":["0"]}}'  # 플랫폼 조회(account 0)

# ── (선택) 테스트 유저 attribute: 테넌트 격리 검증용 ──
# UID=$(curl -sk "$KC/admin/realms/$REALM/users?username=<user>" -H "$H" | jq -r '.[0].id')
# curl -sk -X PUT "$KC/admin/realms/$REALM/users/$UID" -H "$H" -H "$JSON" \
#   -d '{"attributes":{"vm_metrics_account_id":["1"],"vm_logs_account_id":["1"]}}'

3.3 시크릿 (평문 목 — external-secrets/Infisical 대체) — 실행 완료 (2026-06-25)

monitoring ns 선생성 필요(ArgoCD CreateNamespace는 sync 시점 → 사전 생성). perses OIDC client_secret은 perses-values.yaml에 인라인이라 별도 Secret 불필요(목). root-ca-cert·alertmanager-notify만 필요.

kubectl create namespace monitoring --dry-run=client -o yaml | kubectl apply -f -

# alertmanager 알림 자격증명 (base custom-values가 /etc/alertmanager/secrets/webhook-url 참조)
kubectl create secret generic alertmanager-notify -n monitoring \
  --from-literal=webhook-url='https://discord.com/api/webhooks/<MOCK>'

# Keycloak TLS 신뢰용 CA (perses base custom-values가 secretName: root-ca-cert, key ca.crt 마운트)
#   사내 CA 소스: cert-manager/root-ca-secret (ca.crt, 1261 bytes)
kubectl get secret root-ca-secret -n cert-manager -o jsonpath='{.data.ca\.crt}' | base64 -d > /tmp/ca.crt
kubectl create secret generic root-ca-cert -n monitoring --from-file=ca.crt=/tmp/ca.crt

생성된 Secret (ns=monitoring)

Secret 값 / 출처
alertmanager-notify webhook-url https://discord.com/api/webhooks/MOCK/MOCK (mock)
root-ca-cert ca.crt cert-manager/root-ca-secretca.crt 복사 (1261 bytes)

3.4 ConfigMap — 실행 완료 (2026-06-25)

vmagent base custom-values가 vmagent-relabel-configs(테넌트별 demo01/demo02/platform) 마운트. 목은 빈 규칙으로도 기동 가능.

# 테넌트별 write_relabel_configs (예: 최소 빈 규칙). 실제 규칙은 dip-console이 테넌트 온보딩 시 채움.
for t in demo01 demo02 platform; do echo "[]" > /tmp/$t.yaml; done
kubectl create configmap vmagent-relabel-configs -n monitoring \
  --from-file=demo01.yaml=/tmp/demo01.yaml --from-file=demo02.yaml=/tmp/demo02.yaml --from-file=platform.yaml=/tmp/platform.yaml

생성된 ConfigMap vmagent-relabel-configs — 3개 키, 각 내용(mock):

# demo01.yaml
[]
# demo02.yaml
[]
# platform.yaml
[]

3.5 대시보드 — 실행 완료 (2026-06-25)

정적 대시보드 ConfigMap(사이드카 로드). 테스트에선 임시 apply, 정식은 ArgoCD directory 소스 Application.

kubectl apply -n monitoring -f manifests/helm/perses/0.21.0/files/perses-dashboards.yaml
# (데이터소스/시크릿은 perses-provisioning.yaml — 환경별이라 dip-console/Infisical 관리)

생성된 ConfigMap perses-dashboards — label perses.dev/resource: "true", data 키 4종(대시보드 JSON): dashboard-k8s-nodes.json, dashboard-k8s-pod-diagnostics.json, dashboard-k8s-pod-history.json, dashboard-k8s-workloads.json. 원본: perses-dashboards.yaml.


4. 2단계 — ArgoCD 배포 — 실행 완료 (2026-06-25)

ArgoCD는 platform ns에 설치(release argo-cd). ApplicationSet도 platform ns에 apply.

  • repo 등록: service-catalog는 기등록(insecure=true). tenant-catalog는 기존 repo secret 복제로 신규 등록

    🔧 수동 설정: kubectl get secret repo-3724134660 -n platform -o json | jq '.metadata.name="repo-tenant-catalog" | .data.url=("https://gitea.example.org/dip/tenant-catalog"|@base64)' | kubectl apply -f - → §7

  • applicationset.yaml 조정: ns argocdplatform, repoURL service-catalog/tenant-catalog, targetRevision: master
  • 로컬 변경분 service-catalog push (d1f7c77) 후 kubectl apply -f .../applicationset.yaml
  • wave 0→1→2 순서 동기화, perses(wave2) vmauth 이후 기동 확인

결과: 15/15 파드 Running, PVC 3개 Bound, 11 Application 전부 Healthy(9 Synced + 2 OutOfSync).

Application wave Sync Health
vmcluster 0 ⚠️ OutOfSync Healthy
vlogs 0 ⚠️ OutOfSync Healthy
kube-state-metrics 0 Synced Healthy
node-exporter 0 Synced Healthy
alertmanager 0 Synced Healthy
vmauth 1 Synced Healthy
vmagent 1 Synced Healthy
otelcol 1 Synced Healthy
otelcol-events 1 Synced Healthy
vmalert 1 Synced Healthy
perses 2 Synced Healthy

⚠️ vmcluster/vlogs OutOfSync (무해, 기능 정상) — 원인: vmstorage/vlstorage **StatefulSet volumeClaimTemplates**에 K8s가 자동 추가하는 volumeMode: Filesystem이 차트 렌더본엔 없어 diff 발생. volumeClaimTemplates는 immutable이라 sync로 제거 불가 → 영구 OutOfSync(ArgoCD+StatefulSet 전형). 파드 Running·PVC Bound로 기능 영향 없음. 개선책: ApplicationSet에 ignoreDifferences(StatefulSet /spec/volumeClaimTemplates) 추가 → §8 backlog.


5. 3단계 — E2E 기능 검증

vm-stack test/ 스크립트(verify-metrics.sh/verify-logs.sh) 패턴(port-forward + curl) 적응.

# 항목 검증 방법 결과
1 파드 monitoring 전 파드 Running
2 메트릭 vmagent targets up>0, vmselect query=count(up)>0
3 인증 라우팅 Keycloak 토큰으로 vmauth /api/v1/query 성공
4 로그 otelcol→vlinsert 적재, vmauth AccountID 헤더로 vlselect 조회
5 멀티테넌시 accountID별 메트릭(경로)·로그(헤더) 격리
6 perses Keycloak 로그인, 대시보드 4종 로드, datasource 쿼리
7 알림 vmalert 규칙 로드, 테스트 알림 → alertmanager → 채널 수신

🔧 수동 설정(검증용 임시 토큰 발급/포트포워드 등 일회성 명령): 기록 → §7


6. 4단계 — 정리 (teardown)

  • ArgoCD Application/ApplicationSet 삭제(또는 prune)
  • 비-helm CR(있다면) → helm/Application → monitoring ns
  • longhorn PV/volume 명시 삭제(Retain)
  • Keycloak 목 리소스(perses client·테스트 그룹) 삭제

    🔧 수동 설정: 삭제 명령 기록 → §7


7. 수동 설정 목록 (이슈/자동화 backlog)

🔧 항목에서 실제 실행한 명령·스크립트를 여기에 모은다. 이슈 정리 시 이 표를 그대로 사용하고, 각 항목은 dip-console 자동화 후보다.

# 단계 대상 상태 명령 위치 dip-console 이관
1 3.1 값 repo 렌더·push 실행(2026-06-25) tenant-catalog@510aa8e §3.1 예정
2 3.2 Keycloak vm-access scope(+매퍼2) + perses/perses-vmauth client + SA attr(acct 0) 실행(2026-06-25) paasup realm §3.2 예정
3 3.3 monitoring ns + alertmanager-notify + root-ca-cert(←cert-manager/root-ca-secret) 실행(2026-06-25) §3.3 예정(Infisical)
4 3.4 vmagent-relabel-configs(demo01/demo02/platform, 빈 규칙) 실행(2026-06-25) §3.4 예정
5 3.5 perses-dashboards ConfigMap apply 실행(2026-06-25) §3.5 (ArgoCD 경로)
6 4 ArgoCD tenant-catalog repo secret 등록 + ApplicationSet apply(platform ns) 실행(2026-06-25) §4 -
7 6 teardown 미수행 §6 -

진행 메모(2026-06-25): 0~2단계 완료. 1단계 mock 값(perses=perses-mock-secret-changeme, perses-vmauth=perses-vmauth-secret-changeme, alertmanager webhook·relabel placeholder). 2단계: ArgoCD=platform ns, service-catalog@d1f7c77/tenant-catalog@510aa8e, 11 App Healthy(15/15 파드). 남은 것: 3단계 E2E, 4단계 정리.


8. 테스트 결과 요약 (이슈용)

테스트 완료 후 채운다.

  • 일시 / 수행자: (기입)
  • 환경: (클러스터/ArgoCD/Keycloak)
  • 결과: 11 Application Synced+Healthy ( / ), E2E ( /7 PASS)
  • 발견 이슈 / 개선사항:
    • vmcluster/vlogs 영구 OutOfSync — StatefulSet volumeClaimTemplatesvolumeMode 자동 기본값 diff(immutable). 기능 무해. → ApplicationSet에 ignoreDifferences(StatefulSet /spec/volumeClaimTemplates) 추가 권장.
    • ArgoCD 설치 위치가 argocd가 아니라 platform ns — ApplicationSet metadata.namespace/template...namespaceplatform으로 조정함.
    • (E2E 단계에서 추가 기입)
  • 자동화 backlog: §7 수동 설정 목록 참조 (1단계 전부 dip-console 이관 대상)