• VictoriaMetrics Stack 완전해부 - 2

    2026. 6. 9.

    by. Daramu

    이전 포스팅에 이어 2편으로 이어가겠다.

     

    4. vmlogs-values.yaml

    server:
      retentionPeriod: 30d

    수집된 로그를 30일까지 저장하겠다는 선언이다.

    이전 포스팅의 VMSingle은 기본 단위가 개월이었지만, 로그는 일단위이니 d를 사용했다.

    로그데이터는 숫자인 메트릭에 비해 텍스트 정보라 용량이 훨씬 크도 폭팔적이다. 그래서 타이트하게 잡는 것이 보통이다.

     

    근데 여기서 왜 메트릭은 두개인데 로그와 트레이스는 한개인지 의문이 들 수 있다.

    그 이유는 메트릭은 이전부터 엄청난 영향력을 행사한 Prometheus가 Pull방식을 사용하기 때문이고, 나머지는 push방식을 사용하기 때문이다.

     

    pull 방식이기 때문에 VMSingle은 클러스터 전체를 돌아다니며 pod를 감시하고, 가져오는 중노동을 해야하기 때문에 저장소와 수집소를 두개로 쪼갠 것이다.

     

    그런데, 잘 생각해보면 어차피 메트릭,로그,트레이스의 모든 수집은 otel이 담당한다.

    그럼 당연히 "otel이 수집해서 보내면 되는거 아니야?" 하는 생각이 들 수 있다.

     

    바로 다음 포스팅으로 otel 설정에 vmagent 설정을 고스란히 이사해서 vmagent를 없애는 방식에 대한 포스팅을 다룰 것이다.

     

      persistentVolume:
        enabled: true
        size: 20Gi

    동일하게 스토리지 볼륨은 20Gi로 사용하겠다는 설정이다.

     

    5. jaeger-values.yaml

    allInOne:
      enabled: true

    Jeager는 수집기, 쿼리엔진, 웹UI, 봇 등 수많은 pod가 떠야하는 서비스이다.

    하지만 위 allinone 옵션을 켜면 단 하나의 pod에 위 기능을 전부 집어넣게 된다. 자원을 아끼기 위한 노력이라고 봐주면 된다.

     

      extraEnv:
        - name: BADGER_EPHEMERAL
          value: "false"
        - name: BADGER_DIRECTORY_VALUE
          value: "/badger/data"
        - name: BADGER_DIRECTORY_KEY
          value: "/badger/key"
     
      persistence:
        enabled: true
        size: 10Gi

    이것도 자원을 아끼기 위한 비슷한 옵션이다. all in one은 기본적으로 메모리 저장소를 쓴다. 즉, pod가 재실행 되면 모든 데이터가 사라진다.

     

    그것을 막기 위해 메모리 동작을 막고 영구 볼륨 10GB짜리를 붙이겠다는 설정이다.

    실제 운영 환경에서는 allinone을 제거하는 것으로 jeager의 엄청난 확장성과 성능을 모두 누릴 수 있을 것이다.

     

    6. otel-daemonset-values.yaml & otel-gateway-values.yaml

    사실 현 스택에서 가장 중요하다고 봐도 무방한 OTel 진영이다.

    두 파일은 DaemonSet으로 데이터 수집과 Deployment(Gateway)로 물류 허브 처럼 각 백엔드로 나눠주는 투트랙 기반이다.

     

    otel-daemonset: 모든 워커마다 1개씩 떠서, 노드 내부의 날것의 로그를 전부 긁어온다.

    otel-gateway: 단일 지점(deployment)로 존재하며, 애플리케이션이 직접 쏘는 매트릭/트레이스와 Daemonset이 보낸 로그를 한대 모아 각 백엔드로 분기 전달한다.

     

    우선 daemonset먼저 살펴보겠다.

    config:
      receivers:
        filelog:
          include:
            - /var/log/pods/*/*/*.log
    ...(생략)

    스프링 부트 같은 컨테이너들의 로그는 쿠버네티스는 /var/log/pods/아래에 저장한다. 이 로그를 모두 가져오는 역할을 한다.

     

      processors:
        batch: {}
        memory_limiter:
          limit_mib: 256
        k8sattributes:
          auth_type: serviceAccount
          extract:
            metadata:
              - k8s.pod.name
              - k8s.namespace.name
              - k8s.node.name
              - k8s.deployment.name

    위에서 가져온 로그는 기본적으로 구분이 없는데, 이 로그들이 어떠한 pod에서 왔는지, 어떤 deployment에서 왔는지, 어떤 ns인지등 태그를 자동으로 주입해주는 역할을 한다.

     

      exporters:
        otlp/gateway:
          endpoint: otel-gw-opentelemetry-collector.monitoring.svc.cluster.local:4317
          tls:
            insecure: true

    위에서 수집한 로그를 어디에 보낼지이다. 위에서 언급했지만 otel-gateway가 중간 물류창고처럼 모든 데이터를 받아감으로, 바로 다음에 설치할 gateway로 위치를 잡는 작업이다.

     

    다음으로 gateway이다.

    config:
      receivers:
        otlp:
          protocols:
            grpc:
              endpoint: 0.0.0.0:4317
            http:
              endpoint: 0.0.0.0:4318

    외부에서 들어오는OTel 표준 규격 데이터의 통합 입구이다.

    모든 데이터는 위 4317, 4318 포트로 보내진다. 그렇기에 deployment 설정에서 쿠버네티스 FQDN을 사용해 이 서비스로 보내는 설정이 있었다.

     

      exporters:
        otlphttp/vmagent:
          endpoint: http://vmagent-victoria-metrics-agent.monitoring.svc.cluster.local:8429/opentelemetry/api/v1/push
    ...
     
        # Logs → VictoriaLogs
        otlphttp/vmlogs:
          endpoint: http://vmlogs-victoria-logs-single-server.monitoring.svc.cluster.local:9428/insert/opentelemetry
    ...
     
        # Traces → Jaeger
        otlp/jaeger:
          endpoint: jaeger.monitoring.svc.cluster.local:4317
    ...

    바로 이곳이 모든 데이터의 분기점이다.

     

    각 데이터별 흐름은 다음과 같다.

     

    메트릭(Metrics):

    OTLP 입력 -> otlphttp/vmagent 전송

    도착지:http://vmagent-victori....8429/opentelemetry/api/v1/push

    애플리케이션이 push 한 메트릭을 받아서 일단 VMAgent로 밀어 넣는다. 물론, AMAgent는 이걸 받아서 VMSingle 를 통해 저장할 것이다.

     

    로그(Logs):

    OTel DaemonSet이 준 로그 -> otlphttp/vmlogs 전송

    도착지: http://vmlogs-...:9428/insert/opentelemetry

    Daemonset이 전달해준 로그의 메타데이터를 확인하고, 로그 전문 창고인 VictoriaLogs로 보낸다.

     

    트레이드(Traces):

    스프링 부트가 직접 쏜 트레이스 -> otlp/jaeger 전송

    도착지: jaeger.monitoring.svc.cluster.local:4317

    애플리케이션 안에서 요청의 흐름을 추적하는 트레이스를 받아서 Jeager의 gRPC 포트로 밀어 넣는다.

     

    여기서 궁금증이 생긴다.

    메트릭은 스프링 부트로 설정해서 VMAgent가 수집하고, 로그는 DaemonSet이 가져온다.

     

    그런데 당신은 트레이스는 설정한 적이 없다.

    근데 어떻게 스프링 부트가 트레이스를 전송한단 말인가?

     

    그건 구축 단계 포스팅에도 말했었지만, OpenTelemetry Operator 방식의 자동 주입으로 설정할 수 있다.
    (https://daramu.tistory.com/86)

     

    # instrumentation.yaml
    apiVersion: opentelemetry.io/v1alpha1
    kind: Instrumentation
    metadata:
      name: otel-instrumentation
      namespace: default
    spec:
      exporter:
        endpoint: http://otel-daemonset-collector.monitoring.svc:4317
      propagators:
        - tracecontext
        - baggage
      sampler:
        type: parentbased_traceidratio
        argument: "1.0"
      java:
        image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:latest
        env:
          - name: OTEL_EXPORTER_OTLP_PROTOCOL
            value: grpc

    이 파일은 쿠버네티스에게 앞으로  자바(Spring Boot)앱에 OTel 에이전트를 장착할 표준 규격서(CRD)를 등록하는 파일이다.

    kind: Instrumentation은 OpenTelemetry Operator를 설치하면 사용할 수 있는 쿠버네티스의 새로운 확장 기능이다.

     

    spec:
      exporter:
        endpoint: http://otel-daemonset-collector.monitoring.svc.cluster.local:4317

    이 룰북을 장착한 스프링 부트 앱들이 트레이스 데이터를 어디로 보낼지에 대한 설정이다.

    보면 알 수 있지만, 데이터 수집인 otel-daemonset으로 보내는걸 알 수있다.

     

    sampler:
        type: parentbased_traceidratio
        argument: "1.0"

    트레이스 데이터를 모두(100%)수집하겠다는 설정이다. 트레이스 데이터가 너무 많다면 0.1(10%)와 같이 줄일 수 있다.

     

      java:
        image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:latest

    쿠버네티스가 이 이미지를 기반으로 자바 에이전트 파일을 주입해서 트레이스를 보낼 수 있는 의존성 파일을 몰래 주입한다.

     

    이렇게 설정했다면 스프링 부트 deployment에게 "너는 위에서 설정한 표준서의 주입 대상이다." 라는 것을 알려야 한다.

    이 방식은 몇가지가 있지만, 가장 많이 사용되는 방식은 deployment에  annotaion으로 넣어두면 된다.

     

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: new-spring-app
    spec:
      replicas: 1
      template:
        metadata:
          annotations:
            # 바로 이 부분이다.
            instrumentation.opentelemetry.io/inject-java: "otel-instrumentation"
            
            # 메트릭 수집용 주석도 잊지 말 것
            prometheus.io/scrape: "true"
            prometheus.io/path: "/actuator/prometheus"
            prometheus.io/port: "8080"
        spec:
          containers:
    ...(생략)

     

    이전 포스팅에는 CLI로 하는 법을 적었지만, 아무래도 yaml파일로 직접 수정하는 것이 일회용이 아니라 원하는 트레이스만 뽑아쓰기 좋을 것이다.

    댓글