Adjust VM ApplicationSet for test env + per-URL tenant routing rationale

- applicationset: ns argocd→platform, repoURL service-catalog/tenant-catalog @master
- vmagent custom-values/CUSTOM-README: per-URL 채택 근거 정정(인라인 가능하나 보안·격리 위해 per-URL)
- architecture doc: §5.1 테넌트 라우팅 결정 기록 + monitoring 배포 테스트 문서

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
wbsong111
2026-06-25 14:36:58 +09:00
parent 6290322f1b
commit d1f7c77201
5 changed files with 356 additions and 7 deletions
+325
View File
@@ -0,0 +1,325 @@
# VictoriaMetrics 모니터링 스택 배포 테스트
카탈로그의 VM 옵저버빌리티 스택(11개 Application)을 **ArgoCD 전체 GitOps 경로**로 배포·검증하는 절차와 기록 문서. 테스트 완료 후 본 문서를 바탕으로 이슈에 결과를 정리한다.
- 아키텍처: [victoria-metrics-architecture.md](victoria-metrics-architecture.md)
- ApplicationSet: [manifests/applicationset/victoria-metrics/1.0.0/](../manifests/applicationset/victoria-metrics/1.0.0/)
- 카탈로그(테스트 업로드): `https://gitea.example.org/dip/service-catalog` (branch `master`)
## 표기 규칙 (중요)
> 🔧 **수동 설정** — dip-console/ArgoCD가 아니라 **사람이 스크립트·명령어로 직접 설정**한 항목. 이 마커가 붙은 내용은 모두 **[§7 수동 설정 목록](#7-수동-설정-목록-이슈자동화-backlog)**에 모이며, 이슈 정리 + 향후 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단계 — 로컬 정적 검증 (클러스터 불필요)
- [x] 오버레이 `$VAR`를 테스트값으로 치환 후 2단 병합 렌더 확인 — **11/11 PASS**
```sh
# 차트마다: 베이스(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
```
- [x] vmauth 렌더 config에 issuer·`{{.MetricsAccountID}}`·로그 `AccountID` 헤더 포함 확인 — **PASS**
- [x] `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)
- [x] 오버레이 11종을 `$VAR` 치환(문자열 치환만, `{{.MetricsAccountID}}` 등 보존) → `~/project/tenant-catalog/victoria-metrics/`에 생성 후 push
```sh
# 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`
```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`
```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`
```yaml
extraSecretMounts:
- name: alertmanager-notify
mountPath: /etc/alertmanager/secrets
subPath: ""
secretName: "alertmanager-notify"
readOnly: true
```
`vmcluster-values.yaml` / `vlogs-values.yaml`
```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 **없음**(신규 생성 대상).
```sh
# ── 관리자 토큰 (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`만 필요.
```sh
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-secret`의 `ca.crt` 복사 (1261 bytes) |
### 3.4 ConfigMap — ✅ 실행 완료 (2026-06-25)
vmagent base custom-values가 `vmagent-relabel-configs`(테넌트별 demo01/demo02/platform) 마운트. 목은 빈 규칙으로도 기동 가능.
```sh
# 테넌트별 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):
```yaml
# demo01.yaml
[]
# demo02.yaml
[]
# platform.yaml
[]
```
### 3.5 대시보드 — ✅ 실행 완료 (2026-06-25)
정적 대시보드 ConfigMap(사이드카 로드). 테스트에선 임시 apply, 정식은 ArgoCD directory 소스 Application.
```sh
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](../manifests/helm/perses/0.21.0/files/perses-dashboards.yaml).
---
## 4. 2단계 — ArgoCD 배포
- [ ] repo 등록: 카탈로그 repo + 값 repo (`argocd repo add`, 사내 CA insecure)
> 🔧 **수동 설정**: `argocd repo add` 명령 기록 → §7
- [ ] `applicationset.yaml`의 `repoURL`→`dip/service-catalog`, `targetRevision`→`master`로 수정 확인
- [ ] `kubectl apply -f manifests/applicationset/victoria-metrics/1.0.0/applicationset.yaml`
- [ ] `argocd app list`로 wave 0→1→2 순서 Synced/Healthy, perses가 vmauth 이후 기동
결과 (Application별 Sync/Health):
| Application | wave | Synced | Healthy |
|-------------|------|--------|---------|
| vmcluster | 0 | | |
| vlogs | 0 | | |
| kube-state-metrics | 0 | | |
| node-exporter | 0 | | |
| alertmanager | 0 | | |
| vmauth | 1 | | |
| vmagent | 1 | | |
| otelcol | 1 | | |
| otelcol-events | 1 | | |
| vmalert | 1 | | |
| perses | 2 | | |
---
## 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](#31-값values-repo--✅-실행-완료-2026-06-25) | ✅ 예정 |
| 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 repo add + ApplicationSet | ⬜ 미수행 (ArgoCD 위치 확인 필요 — platform ns 추정) | §4 | - |
| 7 | 6 | teardown | ⬜ 미수행 | §6 | - |
> 진행 메모(2026-06-25): **1단계(3.1~3.5) 전부 실행 완료.** 모두 mock 값. client_secret: perses=`perses-mock-secret-changeme`, perses-vmauth=`perses-vmauth-secret-changeme`. alertmanager webhook·relabel은 mock placeholder. 다음은 2단계(ArgoCD) — `platform` ns에 argocd-tls 존재 → ArgoCD 설치 위치부터 확인 필요.
---
## 8. 테스트 결과 요약 (이슈용)
테스트 완료 후 채운다.
- **일시 / 수행자**: (기입)
- **환경**: (클러스터/ArgoCD/Keycloak)
- **결과**: 11 Application Synced+Healthy ( / ), E2E ( /7 PASS)
- **발견 이슈 / 개선사항**:
- (예: perses ingress 누락 보강, 시크릿 이름 불일치, `$VAR` 렌더 방식 등)
- **자동화 backlog**: §7 수동 설정 목록 참조
+18
View File
@@ -128,6 +128,24 @@ flowchart TB
> **비대칭**: 메트릭은 `/select/multitenant/`로 전체 테넌트 집계가 가능하나, VictoriaLogs는 합산 엔드포인트가 없어 `(AccountID, ProjectID)` 단위로만 조회된다.
### 5.1 메트릭 쓰기 경로 테넌트 라우팅 결정 (per-URL)
위 표는 **조회(read)** 경로다. **쓰기(write)** 경로에서 vmagent가 테넌트를 어떻게 가르는지는 두 가지 선택지가 있고, **per-URL을 채택**한다.
| 방식 | 동작 | 온보딩 | 격리 |
|------|------|--------|------|
| **per-URL (채택)** | 테넌트마다 별도 remoteWrite `/insert/{accountID}/` + `urlRelabelConfig` keep 필터 | 새 테넌트 = vmagent 값 + relabel 수정 → **Git/PR** | **강** — URL이 테넌시 강제 |
| multitenant+라벨 (대안) | 단일 `/insert/multitenant/` + relabel로 `vm_account_id` 라벨 set | ConfigMap만 수정 | 약 — 라벨(=relabel 규칙)에 의존 |
**per-URL 채택 근거 (보안·격리 우선)**
- **스푸핑 차단**: 테넌시가 URL로 고정 → 스크레이프 대상이 `vm_account_id` 라벨을 노출해도 테넌트 위장 불가.
- **변경 통제**: 새 테넌트(=새 accountID) 추가가 Git/PR 리뷰·승인을 거침(ConfigMap 한 줄 수정보다 통제 강함).
- **장애·오설정 격리**: remoteWrite 큐가 테넌트별로 분리 → 한 테넌트 문제나 relabel 오타의 cross-tenant 파급 제한.
**트레이드오프(감수)**: 새 테넌트 추가 시 vmagent 값(remoteWrite)도 함께 수정·재배포해야 한다. (multitenant+라벨은 ConfigMap만으로 온보딩되지만 테넌시가 relabel 규칙에 의존해 격리가 약함 — 채택 안 함.)
> 같은 테넌트의 새 네임스페이스(`demo01-*`)는 prefix 정규식이 커버하므로 **무변경**. 변경이 필요한 건 **새 accountID(테넌트) 추가** 시뿐이다. 차트는 인라인 `urlRelabelConfig`도 지원하나, 위 근거로 per-tenant URL + 별도 relabel ConfigMap을 쓴다.
## 6. 인증/인가 (Keycloak `paasup` realm)
```mermaid
@@ -2,7 +2,7 @@ apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: victoria-metrics
namespace: argocd
namespace: platform
spec:
goTemplate: true
goTemplateOptions: ["missingkey=error"]
@@ -71,15 +71,15 @@ spec:
template:
metadata:
name: "{{ .name }}"
namespace: argocd
namespace: platform
annotations:
argocd.argoproj.io/sync-wave: "{{ .syncWave }}"
spec:
project: default
sources:
# Source 1: 카탈로그 repo (Helm 차트)
- repoURL: https://gitea.example.org/dip/catalog-test
targetRevision: main
- repoURL: https://gitea.example.org/dip/service-catalog
targetRevision: master
path: "{{ .chartPath }}"
helm:
releaseName: "{{ .name }}"
@@ -87,8 +87,8 @@ spec:
- "{{ .baseValues }}" # 카탈로그 차트의 정적 베이스값(custom-values)
- $values/{{ .valuesPath }} # Source 2의 환경 오버레이 (나중 = 우선)
# Source 2: 값(values) repo — dip-console이 렌더한 환경별 값
- repoURL: https://gitea.example.org/dip/values-test
targetRevision: main
- repoURL: https://gitea.example.org/dip/tenant-catalog
targetRevision: master
ref: values
destination:
server: https://kubernetes.default.svc
@@ -30,7 +30,9 @@ helm upgrade vmagent ./ -f custom-values.yaml --install -n monitoring
| 9000 | platform (kafka-system, kubeflow 등) | `/relabel/platform.yaml` |
| 0 | fallback (미매칭) | 없음 |
> 쓰기 경로는 vmauth를 경유하지 않고 vminsert(`:8480`)에 직접 쓴다. `writeRelabelConfigs`(camelCase)는 CLI 미지원이라 파일 마운트(`urlRelabelConfig`) 방식을 사용한다.
> 쓰기 경로는 vmauth를 경유하지 않고 vminsert(`:8480`)에 직접 쓴다.
>
> **테넌트 라우팅 방식 결정 (per-URL)**: 차트는 `urlRelabelConfig`에 relabel 규칙을 **인라인**으로 줘도 자동으로 ConfigMap을 생성·마운트한다(기술적으로 인라인 가능). 그럼에도 **테넌트마다 별도 remoteWrite URL(`/insert/{accountID}/`) + 별도 relabel 파일**을 쓰는 이유는 **보안·격리** 때문이다: ① 테넌시를 URL이 강제하므로 데이터 라벨 스푸핑이 불가, ② 새 테넌트 추가가 Git/PR 변경(감사·승인)을 거침, ③ remoteWrite 큐가 테넌트별로 분리돼 장애·오설정의 cross-tenant 파급이 제한됨. (대안인 `/insert/multitenant/` + `vm_account_id` 라벨 매핑은 온보딩이 ConfigMap만으로 가능하나 테넌시가 relabel 규칙에 의존 → 격리가 약함. 트레이드오프는 아키텍처 문서 참조.)
### 2.2 scrape_configs
@@ -7,6 +7,10 @@
# accountID 매핑(예시): 0=플랫폼 공통(fallback) / 1=demo01 / 2=demo02 / 9000=platform
# 네임스페이스별 분기는 urlRelabelConfig(ConfigMap: vmagent-relabel-configs) 마운트로 처리.
# 해당 ConfigMap은 온보딩 시 별도 생성한다(CUSTOM-README 참조).
#
# 테넌트 라우팅 = per-URL 의도적 채택(보안·격리). 차트는 인라인 urlRelabelConfig도 지원하나,
# 테넌시를 URL로 강제(스푸핑 차단)하고 테넌트 추가를 Git/PR로 통제하기 위해 per-tenant URL을 쓴다.
# → 새 테넌트 = remoteWrite 항목 추가(이 파일 수정) + relabel 파일 추가. 상세: CUSTOM-README / 아키텍처 문서.
# =============================================================================
# remoteWrite — 최소 1개 필요. accountID별 엔드포인트 + urlRelabelConfig 파일 분기