Skip to content

Commit 813fb62

Browse files
authored
fix: Kubernetes 플랫폼 설정 정비 (#761)
* fix: vLLM 배치 토큰 한도 상향 * feat(kubernetes): CNPG 전용 Longhorn 스토리지 클래스 추가 * fix: Prometheus 단일 복제본 전환 * fix: Grafana 알림 한글화
1 parent c833b9c commit 813fb62

13 files changed

Lines changed: 223 additions & 191 deletions

File tree

docs/content/developer/infra/observability-improvement-plan/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ P0는 `group_wait=10s`, `repeat_interval=15m`로 Discord에 빠르게 전달합
145145

146146
P1은 `group_wait=30s`, `repeat_interval=1h`를 사용합니다. prod와 cluster-global P1은 Discord로 전달하고 dev P1은 muted receiver로 분리합니다.
147147

148-
- `PrometheusHADegraded`: ready Prometheus replica가 2개 미만인 상태 1분 지속.
148+
- `PrometheusUnavailable`: 단일 Prometheus replica가 ready 상태가 아니거나 readiness metric이 없는 상태 1분 지속.
149149
- `ApiLatencyHigh`: p95 latency 2초 초과 5분 지속.
150150
- `JudgeWaitingQueueBacklog`: `waiting_queue` 5 초과 3분 지속.
151151
- `CeleryWorkerRestarting`: worker restart 3회 이상/15분.

kubernetes/base/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ resources:
1010
- ./backend/pvc.yaml
1111

1212
# Database and Cache
13+
- ./postgres/longhorn-cnpg-storageclass.yaml
1314
- ./postgres/postgres.yaml
1415
- ./redis/redis.yaml
1516

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: storage.k8s.io/v1
2+
kind: StorageClass
3+
metadata:
4+
name: longhorn-cnpg
5+
provisioner: driver.longhorn.io
6+
allowVolumeExpansion: true
7+
reclaimPolicy: Delete
8+
volumeBindingMode: Immediate
9+
parameters:
10+
numberOfReplicas: "1"
11+
staleReplicaTimeout: "30"
12+
fromBackup: ""
13+
fsType: ext4
14+
dataLocality: best-effort
15+
unmapMarkSnapChainRemoved: ignored
16+
disableRevisionCounter: "true"
17+
dataEngine: v1
18+
backupTargetName: default

kubernetes/base/postgres/postgres.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ spec:
1818
memory: 2Gi
1919

2020
storage:
21-
storageClass: longhorn
21+
storageClass: longhorn-cnpg
2222
size: 30Gi
2323

2424
bootstrap:

kubernetes/base/vllm/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ kubectl describe pod -n code-place-prod -l app=vllm
3838
- 이미지: `vllm/vllm-openai:v0.24.0`
3939
- 모델: `nvidia/Qwen3.6-35B-A3B-NVFP4`
4040
- 포트: `8000`
41-
- 주요 옵션: `--dtype auto`, `--gpu-memory-utilization 0.9`, `--max-model-len 4096`, `--kv-cache-dtype fp8`, `--calculate-kv-scales`, `--enable-prefix-caching`, `--enable-chunked-prefill`, `--max-num-seqs 60`
41+
- 주요 옵션: `--dtype auto`, `--gpu-memory-utilization 0.9`, `--max-model-len 4096`, `--max-num-batched-tokens 4096`, `--kv-cache-dtype fp8`, `--calculate-kv-scales`, `--enable-prefix-caching`, `--enable-chunked-prefill`, `--max-num-seqs 60`
4242
- 리소스: `cpu: 16`, `memory: 64Gi`, `nvidia.com/gpu: 1`
4343
- PVC: `vllm-hf-cache`, `storageClassName: longhorn`, `30Gi`, `ReadWriteOnce`
4444

kubernetes/base/vllm/deployment.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ spec:
4646
- "0.9"
4747
- --max-model-len
4848
- "4096"
49+
- --max-num-batched-tokens
50+
- "4096"
4951
- --kv-cache-dtype
5052
- fp8
5153
- --calculate-kv-scales

kubernetes/monitoring/alertmanager-config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ spec:
7070
apiURL:
7171
name: alertmanager-contact-points
7272
key: webhook-url
73-
title: '[{{ if eq .Status "firing" }}활성{{ else }}해결{{ end }}][P0] {{ .CommonLabels.alertname }}{{ with .CommonLabels.service }} ({{ . }}){{ end }}'
73+
title: '[{{ if eq .Status "firing" }}활성{{ else }}해결{{ end }}][P0] {{ .CommonAnnotations.summary }}{{ with .CommonLabels.service }} ({{ . }}){{ end }}'
7474
content: '{{ .CommonAnnotations.summary }}'
7575
message: |-
7676
{{ range .Alerts }}
77-
대상: {{ if .Labels.service }}{{ .Labels.service }}{{ else }}{{ .Labels.alertname }}{{ end }}{{ with .Labels.instance }} ({{ . }}){{ end }}
77+
대상: {{ if .Labels.service }}{{ .Labels.service }}{{ else }}{{ .Annotations.summary }}{{ end }}{{ with .Labels.instance }} ({{ . }}){{ end }}
7878
{{ if or .Labels.pod .Labels.container .Labels.persistentvolumeclaim .Labels.pvc .Labels.pvc_namespace .Labels.deployment .Labels.daemonset .Labels.node .Labels.volume .Labels.disk .Labels.reason .Labels.condition .Labels.collector .Labels.check .Labels.scope .Labels.status .Labels.Hostname .Labels.gpu .Labels.UUID }}
7979
문맥:{{ with .Labels.pod }} pod={{ . }}{{ end }}{{ with .Labels.container }} container={{ . }}{{ end }}{{ with .Labels.persistentvolumeclaim }} pvc={{ . }}{{ end }}{{ with .Labels.pvc }} pvc={{ . }}{{ end }}{{ with .Labels.pvc_namespace }} pvc_namespace={{ . }}{{ end }}{{ with .Labels.deployment }} deployment={{ . }}{{ end }}{{ with .Labels.daemonset }} daemonset={{ . }}{{ end }}{{ with .Labels.node }} node={{ . }}{{ end }}{{ with .Labels.volume }} volume={{ . }}{{ end }}{{ with .Labels.disk }} disk={{ . }}{{ end }}{{ with .Labels.reason }} reason={{ . }}{{ end }}{{ with .Labels.condition }} condition={{ . }}{{ end }}{{ with .Labels.collector }} collector={{ . }}{{ end }}{{ with .Labels.check }} check={{ . }}{{ end }}{{ with .Labels.scope }} scope={{ . }}{{ end }}{{ with .Labels.status }} status={{ . }}{{ end }}{{ with .Labels.Hostname }} host={{ . }}{{ end }}{{ with .Labels.gpu }} gpu={{ . }}{{ end }}{{ with .Labels.UUID }} gpu_uuid={{ . }}{{ end }}
8080
{{ end }}
@@ -101,11 +101,11 @@ spec:
101101
apiURL:
102102
name: alertmanager-contact-points
103103
key: webhook-url
104-
title: '[{{ if eq .Status "firing" }}활성{{ else }}해결{{ end }}][P1] {{ .CommonLabels.alertname }}{{ with .CommonLabels.service }} ({{ . }}){{ end }}'
104+
title: '[{{ if eq .Status "firing" }}활성{{ else }}해결{{ end }}][P1] {{ .CommonAnnotations.summary }}{{ with .CommonLabels.service }} ({{ . }}){{ end }}'
105105
content: '{{ .CommonAnnotations.summary }}'
106106
message: |-
107107
{{ range .Alerts }}
108-
대상: {{ if .Labels.service }}{{ .Labels.service }}{{ else }}{{ .Labels.alertname }}{{ end }}{{ with .Labels.instance }} ({{ . }}){{ end }}
108+
대상: {{ if .Labels.service }}{{ .Labels.service }}{{ else }}{{ .Annotations.summary }}{{ end }}{{ with .Labels.instance }} ({{ . }}){{ end }}
109109
{{ if or .Labels.pod .Labels.container .Labels.persistentvolumeclaim .Labels.pvc .Labels.pvc_namespace .Labels.deployment .Labels.daemonset .Labels.node .Labels.volume .Labels.disk .Labels.reason .Labels.condition .Labels.collector .Labels.check .Labels.scope .Labels.status .Labels.Hostname .Labels.gpu .Labels.UUID }}
110110
문맥:{{ with .Labels.pod }} pod={{ . }}{{ end }}{{ with .Labels.container }} container={{ . }}{{ end }}{{ with .Labels.persistentvolumeclaim }} pvc={{ . }}{{ end }}{{ with .Labels.pvc }} pvc={{ . }}{{ end }}{{ with .Labels.pvc_namespace }} pvc_namespace={{ . }}{{ end }}{{ with .Labels.deployment }} deployment={{ . }}{{ end }}{{ with .Labels.daemonset }} daemonset={{ . }}{{ end }}{{ with .Labels.node }} node={{ . }}{{ end }}{{ with .Labels.volume }} volume={{ . }}{{ end }}{{ with .Labels.disk }} disk={{ . }}{{ end }}{{ with .Labels.reason }} reason={{ . }}{{ end }}{{ with .Labels.condition }} condition={{ . }}{{ end }}{{ with .Labels.collector }} collector={{ . }}{{ end }}{{ with .Labels.check }} check={{ . }}{{ end }}{{ with .Labels.scope }} scope={{ . }}{{ end }}{{ with .Labels.status }} status={{ . }}{{ end }}{{ with .Labels.Hostname }} host={{ . }}{{ end }}{{ with .Labels.gpu }} gpu={{ . }}{{ end }}{{ with .Labels.UUID }} gpu_uuid={{ . }}{{ end }}
111111
{{ end }}

kubernetes/monitoring/kube-prometheus-stack-values.yaml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,9 @@ kubeScheduler:
3030

3131
prometheus:
3232
prometheusSpec:
33-
replicas: 2
34-
podAntiAffinity: soft
35-
podAntiAffinityTopologyKey: kubernetes.io/hostname
36-
replicaExternalLabelNameClear: true
33+
# A second Prometheus replica stores an independent full TSDB copy. This
34+
# cluster uses one durable Longhorn-backed instance to keep metric storage bounded.
35+
replicas: 1
3736
evaluationInterval: 15s
3837
scrapeInterval: 15s
3938
retention: 15d

kubernetes/monitoring/logs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ Keep these invariants unless the storage design changes intentionally:
105105
- Loki uses filesystem storage on a Longhorn PVC.
106106
- Alloy runs on every schedulable node. Loki and Tempo remain single-writer services on Longhorn PVCs, while stateless collectors, Loki gateway, and probe exporters can be replicated across nodes.
107107
- If a node uses a custom NoSchedule taint, add the matching Alloy toleration before relying on the `AlloyDaemonSetUnavailable` alert for full-node log coverage.
108-
- kube-prometheus-stack Prometheus and Alertmanager run with 2 replicas. Prometheus clears the replica external label so HA replicas do not create replica-labeled duplicate Alertmanager notifications. Prometheus, Alertmanager, and Grafana use Longhorn PVCs so metric data, silences, and UI state survive pod rescheduling.
108+
- kube-prometheus-stack Prometheus runs as one replica to avoid storing two independent copies of every time series. Alertmanager keeps 2 replicas. Prometheus, Alertmanager, and Grafana use Longhorn PVCs so metric data, silences, and UI state survive pod rescheduling.
109109
- Alloy keeps namespace-based collection for `code-place-dev`, `code-place-prod`, and `monitoring`; do not depend on `app.kubernetes.io/name` for CodePlace app logs because the application manifests primarily use `app`.
110110
- Alloy keeps `alloy.mounts.varlog=true`; otherwise the `/var/log/pods` targets are discovered but cannot be read.
111111
- The PVC size is explicit at `50Gi`, and `LokiPVCAlmostFull` alerts at 85%.

kubernetes/monitoring/prometheus-rules.test.yaml

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ tests:
4747
service: hub-auth
4848
severity: critical
4949
exp_annotations:
50-
summary: prod hub-auth 공개 엔드포인트가 응답하지 않습니다
51-
description: https://hub-auth.code-place-dev.site/healthz HTTPS synthetic probe가 1분 동안 실패했습니다.
50+
summary: 운영 hub-auth 공개 엔드포인트가 응답하지 않습니다
51+
description: https://hub-auth.code-place-dev.site/healthz의 HTTPS 가상 점검이 1분 동안 실패했습니다.
5252

5353
- name: transient prod failure recovers before the one minute threshold
5454
interval: 15s
@@ -93,8 +93,8 @@ tests:
9393
service: hub-auth
9494
severity: warning
9595
exp_annotations:
96-
summary: dev hub-auth 공개 엔드포인트가 응답하지 않습니다
97-
description: https://hub-auth-dev.code-place-dev.site/healthz HTTPS synthetic probe가 2분 동안 실패했습니다.
96+
summary: 개발 hub-auth 공개 엔드포인트가 응답하지 않습니다
97+
description: https://hub-auth-dev.code-place-dev.site/healthz의 HTTPS 가상 점검이 2분 동안 실패했습니다.
9898

9999
- name: missing prod hub-auth telemetry is not endpoint downtime
100100
interval: 15s
@@ -120,8 +120,8 @@ tests:
120120
service: hub-auth
121121
severity: warning
122122
exp_annotations:
123-
summary: prod hub-auth 공개 probe metric이 없습니다
124-
description: Prometheus가 prod hub-auth synthetic probe 결과를 2분 동안 찾지 못했습니다. 공개 endpoint 장애가 아니라 probe 수집 경로 문제입니다.
123+
summary: 운영 hub-auth 공개 점검 메트릭이 없습니다
124+
description: Prometheus가 2분 동안 운영 hub-auth의 가상 점검 결과를 찾지 못했습니다. 공개 엔드포인트 장애가 아니라 점검 수집 경로의 문제입니다.
125125

126126
- name: unrelated prod-labelled probe cannot trigger the CodePlace prod alert
127127
interval: 15s
@@ -168,8 +168,8 @@ tests:
168168
service: backend
169169
severity: critical
170170
exp_annotations:
171-
summary: dev backend metrics target이 내려갔습니다
172-
description: Prometheus가 dev backend /metrics 엔드포인트를 1분 동안 scrape하지 못했거나 기대 target 2개를 모두 발견하지 못했습니다.
171+
summary: 개발 백엔드 메트릭 수집 대상에 연결할 수 없습니다
172+
description: Prometheus가 1분 동안 개발 백엔드의 /metrics 엔드포인트를 수집하지 못했거나, 필요한 수집 대상 2개를 모두 찾지 못했습니다.
173173

174174
- name: backend scrape failure preserves the failed instance
175175
interval: 15s
@@ -202,8 +202,8 @@ tests:
202202
service: backend
203203
severity: critical
204204
exp_annotations:
205-
summary: dev backend metrics target이 내려갔습니다
206-
description: Prometheus가 dev backend /metrics 엔드포인트를 1분 동안 scrape하지 못했거나 기대 target 2개를 모두 발견하지 못했습니다.
205+
summary: 개발 백엔드 메트릭 수집 대상에 연결할 수 없습니다
206+
description: Prometheus가 1분 동안 개발 백엔드의 /metrics 엔드포인트를 수집하지 못했거나, 필요한 수집 대상 2개를 모두 찾지 못했습니다.
207207

208208
- name: backend rolling surge does not trigger target coverage
209209
interval: 15s
@@ -258,8 +258,8 @@ tests:
258258
service: backend
259259
severity: warning
260260
exp_annotations:
261-
summary: dev Backend telemetry replica coverage가 불완전합니다
262-
description: dev Backend target별 custom collector 4종 또는 Sentinel check 2종의 metric series가 5분 동안 누락됐습니다. Backend rollout 버전과 target별 /metrics 출력을 확인해야 합니다.
261+
summary: 개발 백엔드 원격 측정 복제본 수집 범위가 불완전합니다
262+
description: 개발 백엔드 수집 대상별 사용자 정의 수집기 4종 또는 Sentinel 점검 2종의 메트릭 시계열이 5분 동안 누락되었습니다. 백엔드 배포 버전과 대상별 /metrics 출력을 확인해야 합니다.
263263

264264
- name: partial OTel target discovery is not healthy
265265
interval: 15s
@@ -276,8 +276,8 @@ tests:
276276
service: otel-collector
277277
severity: warning
278278
exp_annotations:
279-
summary: OpenTelemetry Collector를 scrape할 수 없습니다
280-
description: OpenTelemetry Collector target이 5분 동안 없거나 내려갔거나 기대 target 2개보다 적습니다. dev/prod trace 수집 상태를 확인해야 합니다.
279+
summary: OpenTelemetry Collector 메트릭 수집 대상을 사용할 수 없습니다
280+
description: OpenTelemetry Collector 수집 대상이 5분 동안 없거나 내려갔거나 필요한 수집 대상 2개보다 적습니다. 개발·운영 추적 수집 상태를 확인해야 합니다.
281281

282282
- name: partial datastore and Blackbox metric coverage is not healthy
283283
interval: 15s
@@ -326,8 +326,8 @@ tests:
326326
service: postgres
327327
severity: warning
328328
exp_annotations:
329-
summary: dev PostgreSQL metric collector가 응답하지 않습니다
330-
description: dev CNPG collector가 5분 동안 PostgreSQL에 접근하지 못했거나 기대 instance 3개의 collector metric을 모두 제공하지 못했습니다.
329+
summary: 개발 PostgreSQL 메트릭 수집기가 응답하지 않습니다
330+
description: 개발 CNPG 수집기가 5분 동안 PostgreSQL에 접근하지 못했거나, 필요한 인스턴스 3개의 수집 메트릭을 모두 제공하지 못했습니다.
331331
- eval_time: 5m15s
332332
alertname: RedisExporterCoverageIncomplete
333333
exp_alerts:
@@ -337,8 +337,8 @@ tests:
337337
service: redis
338338
severity: warning
339339
exp_annotations:
340-
summary: dev Redis exporter coverage가 불완전합니다
341-
description: dev Redis/Sentinel exporter가 5분 동안 실패했거나 기대 target 6개의 redis_up metric을 모두 제공하지 못했습니다.
340+
summary: 개발 Redis Exporter 수집 범위가 불완전합니다
341+
description: 개발 Redis/Sentinel Exporter가 5분 동안 실패했거나 필요한 수집 대상 6개의 redis_up 메트릭을 모두 제공하지 못했습니다.
342342
- eval_time: 5m15s
343343
alertname: BlackboxExporterCoverageIncomplete
344344
exp_alerts:
@@ -348,8 +348,8 @@ tests:
348348
service: blackbox-exporter
349349
severity: warning
350350
exp_annotations:
351-
summary: Blackbox exporter coverage가 불완전합니다
352-
description: Blackbox exporter가 5분 동안 실패했거나 기대 target 2개보다 적습니다. public synthetic probe 수집 경로를 확인해야 합니다.
351+
summary: Blackbox Exporter 수집 범위가 불완전합니다
352+
description: Blackbox Exporter가 5분 동안 실패했거나 필요한 수집 대상 2개보다 적습니다. 공개 가상 점검 수집 경로를 확인해야 합니다.
353353

354354
- name: failed collector checks preserve target context
355355
interval: 15s
@@ -379,8 +379,8 @@ tests:
379379
service: redis
380380
severity: critical
381381
exp_annotations:
382-
summary: dev Redis Sentinel master 또는 quorum을 확인할 수 없습니다
383-
description: Redis/Sentinel Pod readiness와 별개로 mymaster 탐색 또는 CKQUORUM 검사가 1분 동안 실패했습니다.
382+
summary: 개발 Redis Sentinel의 주 노드 또는 정족수를 확인할 수 없습니다
383+
description: Redis/Sentinel Pod의 준비 상태와 별개로 mymaster 탐색 또는 CKQUORUM 검사가 1분 동안 실패했습니다.
384384
- eval_time: 5m15s
385385
alertname: CodePlaceCollectorFailed
386386
exp_alerts:
@@ -393,5 +393,5 @@ tests:
393393
service: backend
394394
severity: warning
395395
exp_annotations:
396-
summary: dev Backend custom metric collector가 실패했습니다
397-
description: dev Backend custom metric collector가 5분 동안 Redis 기반 metric을 수집하지 못했거나 metric이 사라졌습니다. 실패 collector label을 확인해야 합니다.
396+
summary: 개발 백엔드 사용자 정의 메트릭 수집기가 실패했습니다
397+
description: 개발 백엔드 사용자 정의 메트릭 수집기가 5분 동안 Redis 기반 메트릭을 수집하지 못했거나 메트릭이 사라졌습니다. 실패한 수집기 레이블을 확인해야 합니다.

0 commit comments

Comments
 (0)