--- title: VictoriaLogs (K3s 로그 저장) updated: 2026-04-08 Grafana 통합 tags: [k3s, logging, vector, observability, grafana] --- # VictoriaLogs K3s 클러스터의 raw 로그 저장소. 메트릭은 [[victoriametrics-stack|VictoriaMetrics]], 로그는 VictoriaLogs로 분리. 같은 VM 팀 제품이라 운영 패턴 일치. ## 접속 | 항목 | 값 | |------|------| | Web UI | https://vl.inouter.com/select/vmui/ | | HTTP API | https://vl.inouter.com/ | | 클러스터 내부 | http://vlogs-victoria-logs-single-server.logging.svc.cluster.local:9428 | | 노출 방식 | LAN 직접 (Cloudflare DNS → k3s.inouter.com → MetalLB 192.168.9.53 → Traefik IngressRoute), BunnyCDN 우회 | | TLS | wildcard-inouter-tls (reflector로 logging ns에 자동 복제) | | 인증 | 없음 (LAN/Tailscale 안에서만 접근 가능) | ## 설치 ```bash helm repo add vm https://victoriametrics.github.io/helm-charts/ helm install vlogs vm/victoria-logs-single -n logging -f vlogs-values.yaml ``` values 핵심: ```yaml server: retentionPeriod: 14d persistentVolume: enabled: true storageClassName: longhorn size: 50Gi resources: requests: { cpu: 100m, memory: 256Mi } limits: { cpu: 1000m, memory: 1Gi } ``` - 단일 노드 (`victoria-logs-single`), 14일 보존, longhorn 50GiB PVC - 네임스페이스: `logging` (Vector와 같은 곳) ## Traefik IngressRoute `logging` ns: ```yaml apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: vlogs namespace: logging spec: entryPoints: [websecure] routes: - match: Host(`vl.inouter.com`) kind: Rule services: - name: vlogs-victoria-logs-single-server port: 9428 tls: secretName: wildcard-inouter-tls ``` ## 데이터 파이프라인 ### APISIX 로그 (현재 가동 중) ``` APISIX (apisix ns, 2 replica) stdout (nginx access log 형식) → Vector daemonset (logging ns, 3 노드) - source: apisix_logs (kubernetes_logs, label=app.kubernetes.io/name=apisix) - transform: parse_apisix (정규식 파싱 → method/path/status/request_time/host/UA 등 구조화) - sink: vlogs (elasticsearch bulk API) → VictoriaLogs (/insert/elasticsearch/_bulk) ``` Vector helm values는 [[vector|vector]] 문서 또는 `helm get values vector -n logging` 참조. ### 다른 잠재 source - Traefik 로그는 별도 sink로 [[crowdsec-safeline|CrowdSec]]에 전송 (vector의 다른 transform/sink). VictoriaLogs로는 안 보내고 있음. 필요하면 sink inputs에 `parse_traefik` 추가하면 됨. ## Grafana 통합 vm-stack의 Grafana(`monitoring/vm-stack-grafana`)에 등록되어 있음. Explore에서 data source를 `VictoriaLogs`로 선택하면 LogsQL 쿼리 가능. ### 등록 방법 두 단계로 구성: 1. **플러그인 설치** — vm-stack helm values의 `grafana.plugins`에 `victoriametrics-logs-datasource` 추가하고 `helm upgrade vm-stack`. Grafana 시작 시 자동 다운로드/설치 (현재 v0.26.3). 2. **datasource 등록** — `grafana.additionalDataSources`는 vm-stack chart 구조상 sub-chart로 전달이 안 되므로 무시됨. 대신 **sidecar ConfigMap 패턴** 사용: ```yaml apiVersion: v1 kind: ConfigMap metadata: name: vlogs-grafana-datasource namespace: monitoring labels: grafana_datasource: "1" # ← grafana-sc-datasources sidecar가 자동 발견 data: vlogs.yaml: | apiVersion: 1 datasources: - name: VictoriaLogs type: victoriametrics-logs-datasource access: proxy url: http://vlogs-victoria-logs-single-server.logging.svc.cluster.local:9428 isDefault: false editable: true ``` `kubectl apply -f` 한 번이면 sidecar가 즉시 picks up하고 grafana가 hot reload (`Writing /etc/grafana/provisioning/datasources/vlogs.yaml`). ### 주의 - Grafana PVC가 ReadWriteOnce이므로 helm upgrade 시 새 pod이 다른 노드에 스케줄되면 Multi-Attach error → 기존 pod 수동 삭제 필요. (또는 Grafana Deployment의 strategy를 Recreate로 변경) - helm values의 `grafana.additionalDataSources`는 무시되지만 plugin 설치는 작동하므로, plugin 라인은 helm values에 두는 게 맞음. ## LogsQL 쿼리 예시 ```logsql # 모든 APISIX 로그 program:apisix # 5xx 에러만 program:apisix status:>=500 # 특정 path program:apisix path:/apisix/admin/routes # 느린 요청 (1초 이상) program:apisix request_time:>1.0 # 특정 클라이언트 외부 IP (real_ip 적용 후 진짜 IP) program:apisix remote_addr:211.211.28.97 # 특정 호스트 program:apisix host:juiceshop.keepanker.cv # 특정 호스트의 4xx/5xx program:apisix host:juiceshop.keepanker.cv status:>=400 # 특정 노드 pod program:apisix _stream:{kubernetes.pod_name="apisix-f5764d796-kdj2x"} ``` > [!info] 진짜 IP 추적 > 2026-04-08 [[apisix#real_ip 설정 (2026-04-08 patch 반영)|APISIX real_ip patch]] 이후 `remote_addr`에 BunnyCDN edge가 전달한 진짜 외부 IP가 찍힘. `xff`/`xrip` 필드로 X-Forwarded-For/X-Real-IP 헤더 원본도 보존. 그 이전 로그는 K3s pod CIDR(10.42.x.x)만 찍혀 있음. > [!warning] parse_apisix 정규식과 APISIX log format은 짝 > APISIX의 `access_log_format`을 바꾸면 vector helm values의 `parse_apisix` 정규식도 같이 업데이트해야 함. 안 그러면 매치 실패해서 `log_type=raw`로 떨어지고 구조화 필드(method/status/path 등) 추출이 안 됨. UI에서 직접 쿼리하거나 HTTP API: ```bash curl -s "https://vl.inouter.com/select/logsql/query?query=program:apisix+status:>=500&limit=20" ``` ## 운영 명령 ```bash # 메트릭 (ingest 통계) curl -s https://vl.inouter.com/metrics | grep vl_rows_ingested_total # 디스크 사용량 kubectl exec -n logging vlogs-victoria-logs-single-server-0 -- du -sh /storage # Vector 상태 kubectl logs -n logging -l app.kubernetes.io/name=vector --tail=50 # Vector configmap 직접 보기 kubectl get cm vector -n logging -o jsonpath='{.data.vector\.yaml}' ``` ## 향후 작업 - [x] Grafana data source 추가 (2026-04-08 완료, 위 "Grafana 통합" 섹션 참조) - [ ] CrowdSec와 통합 (APISIX 로그 → CrowdSec 시나리오) - [ ] AI 분석 컴포넌트 (CronJob 또는 streaming, 새 공격 패턴 자동 차단 룰 생성) - [ ] Traefik 로그도 VL로 동시 sink (현재는 CrowdSec only)