본문으로 건너뛰기

OpenTelemetry Collector 아키텍처

이 회고는 2026년 1월 13일 observability 설계가 Prometheus-first scraping에서 OpenTelemetry collector-first 모델로 이동한 이유를 설명합니다.

원래 기록은 여전히 가치가 있지만, checked-in topology는 이후 바뀌었습니다. 이 문서는 지속되는 아키텍처 결정과 drift된 세부사항을 분리합니다.

소스 경로

  • todo/incidents/trouble/opentelemetry-collector-architecture-2026-01-13.md
  • lumie-infra/observability/kustomization.yaml
  • lumie-infra/observability/opentelemetry/manifests/collector.yaml
  • lumie-infra/observability/opentelemetry/manifests/targetallocator-rbac.yaml
  • lumie-infra/observability/opentelemetry-operator/helm-values.yaml
  • lumie-infra/observability/prometheus/helm-values.yaml
  • lumie-infra/security/kyverno/manifests/policies/remove-cpu-limits.yaml

배경

이전 mental model은 Prometheus를 기본 collector로 봤습니다. Scrape-oriented metric에는 작동했지만 Lumie에는 다음도 필요했습니다.

  • application OTLP trace
  • application/container log
  • monitor resource를 통해 discover되는 Kubernetes metric
  • log는 Loki, trace는 Tempo로 보내는 더 명확한 경로
  • observability component마다 중복되는 scraping logic 감소

지속되는 결정은 OpenTelemetry를 shared collection layer로 두고, 각 backend가 signal type별 storage/query를 소유하게 하는 것입니다.

지속 설계 결정

1월 아키텍처 기록의 세 결정은 여전히 repo와 맞습니다.

결정현재 의미
OpenTelemetry가 shared collection layer입니다app OTLP traffic, Kubernetes scrape target, container log가 collector를 통과합니다
Prometheus는 대부분 workload metric의 기본 scraper가 아닙니다Prometheus는 metric을 저장하고 제공하지만, collection work의 상당 부분은 collector-managed pipeline이 담당합니다
Storage는 signal-specific으로 남습니다metric은 Prometheus, log는 Loki, trace는 Tempo가 맡습니다

Repo-level observability kustomization은 이 결정의 중요한 결과를 보존합니다.

# promtail removed - OTel filelog receiver handles log collection

Source: lumie-infra/observability/kustomization.yaml

현재 repo-managed contract

현재 topology는 하나의 OpenTelemetryCollector custom resource로 정의됩니다.

구성요소현재 모드checked-in 책임
otel-collectordaemonsetOTLP traffic을 받고, /var/log/pods를 tail하며, Target Allocator가 discover한 target을 scrape합니다
Target Allocatorcollector spec 안에서 enabledServiceMonitorPodMonitor를 읽고 scrape work를 collector pod에 할당합니다
Prometheussingle replicaOTLP receiver를 enable하고 자체 scrape는 scrape-by: prometheus-only target으로 제한합니다
LokiOTLP HTTP endpointcollector에서 log를 받습니다
TempoOTLP gRPC endpointcollector에서 trace를 받습니다

다음 excerpt가 live contract를 보여줍니다.

spec:
mode: daemonset
targetAllocator:
enabled: true
allocationStrategy: per-node

Source: lumie-infra/observability/opentelemetry/manifests/collector.yaml

enableOTLPReceiver: true
enableRemoteWriteReceiver: false
serviceMonitorSelector:
matchLabels:
scrape-by: prometheus-only

Source: lumie-infra/observability/prometheus/helm-values.yaml

1월 기록에서 달라진 점

몇 가지 원래 세부사항은 더 이상 checked-in contract가 아닙니다.

1월 기록현재 repo 상태
별도 otel-metrics, otel-logs collector하나의 otel-collector DaemonSet이 metrics, logs, traces를 처리합니다
metrics StatefulSet 전반의 Target Allocator consistent hashingcollector manifest는 allocationStrategy: per-node를 설정합니다
Prometheus를 remote-write receiver로 사용Prometheus는 native OTLP receiver를 enable하고 remote write receiver를 disable합니다
Thanos가 active query path 일부observability kustomization은 thanos/argocd.yaml를 주석 처리하며, 현재 footprint plan은 Thanos를 disable 상태로 둡니다

lumie-infra/observability/prometheus/helm-values.yaml에도 작은 comment drift가 있습니다. Comment는 여전히 collector remote write를 언급하지만, collector.yaml의 active exporter는 otlphttp/prometheus이고 enableRemoteWriteReceiverfalse입니다. Active exporter/receiver setting이 기준입니다.

운영상 의미

Collector-first observability는 failure domain을 바꿉니다.

의미운영 해석
하나의 collector DaemonSet이 여러 signal을 처리합니다collector rollout 문제가 metrics, logs, traces를 함께 저하시킬 수 있습니다
Target Allocator가 scrape assignment를 소유합니다ServiceMonitor만으로 특정 collector pod가 target을 scrape한다고 증명할 수 없습니다
Prometheus scrape role이 좁아집니다Prometheus-native scraping이 필요한 target은 scrape-by: prometheus-only로 opt in해야 합니다
CPU throttling이 중요합니다collector/operator values는 CPU limit을 생략하고, Kyverno가 remove-cpu-limits로 일반 pod CPU limit을 제거합니다

Collector는 sidecar convenience가 아니라 infrastructure dependency가 됐습니다.

검증

현재 계약은 read-only check로 검증합니다.

kubectl get applications.argoproj.io -n argocd opentelemetry opentelemetry-operator prometheus
kubectl get opentelemetrycollectors.opentelemetry.io -n opentelemetry
kubectl get pods -n opentelemetry -o wide
kubectl logs -n opentelemetry daemonset/otel-collector-collector --tail=200
kubectl get clusterpolicy remove-cpu-limits -o yaml

성공 기준은 다음입니다.

  • collector와 operator Application이 존재합니다.
  • OpenTelemetryCollector custom resource가 존재합니다.
  • DaemonSet이 node에서 실행 중입니다.
  • collector log가 live pipeline을 보여줍니다.
  • CPU-limit removal policy가 설치되어 있습니다.

잘된 점

  • Collection과 storage/query backend를 분리했습니다.
  • Log, metric, trace가 서로 무관한 세 mechanism이 아니라 하나의 collection plane으로 이동했습니다.
  • Prometheus는 명확한 역할을 유지하면서도 모든 scrape path를 책임지지 않게 됐습니다.
  • 현재 manifest state와 역사적 note를 구분할 만큼 repo 정보가 문서화됐습니다.

아쉬웠던 점

  • 원본 기록과 현재 manifest가 collector 수, target allocation strategy, Prometheus receiver mode, Thanos 상태에서 drift됐습니다.
  • Collector-first design은 collector outage의 blast radius를 키웁니다.
  • 구현이 바뀐 뒤에도 일부 comment가 stale하게 남았습니다.
  • Operator는 일반 Kubernetes monitor resource뿐 아니라 Target Allocator visibility도 봐야 합니다.

액션 아이템

작업소유 영역상태증거
Observability topology가 바뀌면 이 문서를 collector.yaml과 Prometheus values에 맞춰 유지합니다.lumie-document, lumie-infra지속이 회고는 1월 기록과의 known drift를 기록합니다.
Observability manifest를 수정할 때 stale Prometheus values comment를 고칩니다.lumie-infra/observability/prometheus/helm-values.yaml계획Active config는 remote write receiver가 아니라 OTLP receiver를 사용합니다.
CPU-limit removal policy를 보존하거나 예외를 명시적으로 문서화합니다.lumie-infra/security/kyverno지속remove-cpu-limits는 현재 운영 계약의 일부입니다.

교훈

  • Collector-first design은 collector failure domain을 shared infrastructure로 다룰 때만 이해하기 쉬워집니다.
  • Historical architecture note는 manifest가 바뀌면 drift section이 필요합니다.
  • Prometheus는 metrics backend로 남을 수 있지만 universal metrics collector로 남을 필요는 없습니다.
  • Values file의 comment는 오해를 만들 수 있습니다. Active manifest와 receiver/exporter setting이 기준입니다.

관련 문서

  • OpenTelemetry operations reference
  • OpenTelemetry Operator operations reference
  • Prometheus operations reference