OpenTelemetry Operator, Collector Kubernetes 배포 가이드
이 포스트는 Kubernetes 클러스터에 OpenTelemetry Operator, Collector를 배포하여 클러스터 내부의 node, pod, container의 메트릭을 수집하고, Prometheus 형식으로 메트릭을 노출하는 방법에 관해 설명합니다.
먼저 OpenTelemetry Operator을 배포하고 OpenTelemetry Collector의 구조와 작동 방식을 알아본 후, 실제로 OpenTelemetry Collector를 배포하여 Prometheus를 통해 수집한 메트릭 데이터를 확인하도록 하겠습니다.
목차
1. OpenTelemetry란?
1-1. OpenTelemetry Operator란?
1-2. OpenTelemetry Collector란?
2. 버전 정보
3. OpenTelemetry Operator 배포
4. OpenTelemetry Collector 배포
4-1. OpenTelemetry Collector의 구조
4-2. 예시 Collector 배포 (Deployment)
4-3. Daemonset Collector 구성
4-4. Prometheus 구성
4-5. Kubernetes 메트릭 수집 확인
5. 결론
1. OpenTelemetry란?

OpenTelemetry는 관측성(Observability)을 위한 프레임워크/툴킷으로 트레이싱(Tracing), 메트릭(Metric), 로그(Log) 데이터를 관리하도록 설계되었습니다. OpenTelemetry는 이러한 원격 측정 데이터(telemetry data)의 생성/수집, 관리, 내보내기에 중점을 두고 있습니다.
원격 측정 데이터의 저장, 시각화와 같은 기능은 자체적으로 지원하지 않으며, 다른 백엔드 툴을 통해서 구현해야 합니다.
OpenTelemetry는 수집한 데이터를 Prometheus, Jaeger, Elastic, Grafana와 같이 널리 사용되는 오픈소스 시스템뿐만 아니라 New Relic, Datadog, 그리고 Google Cloud, AWS, Azure 등 CSP의 모니터링 도구와 같은 다양한 상용 백엔드 시스템과도 연동하여 사용할 수 있습니다.
1-1. OpenTelemetry Operator란?
OpenTelemetry Operator는 Kubernetes의 Operator 패턴을 기반으로, OpenTelemetry 구성 요소를 쉽게 관리하고 배포할 수 있는 도구입니다. Kubernetes의 Operator 패턴은 Custom Resource Definition(CRD)을 사용하여 복잡한 애플리케이션의 설치, 구성, 관리를 자동화합니다.
OpenTelemetry Operator는 opentelemetrycollectors, instrumentations Custom Resource에 정의된 내용에 따라 OpenTelemetry Collector와 auto-instrumentation 리소스를 생성하고, 관리합니다.
1-2. OpenTelemetry Collector란?
OpenTelemetry Collector는 다양한 애플리케이션의 성능과 동작을 모니터링하기 위한 로그, 메트릭, 트레이스 데이터를 수집하고, 측정한 데이터를 설정에 따라 처리한 후, 다양한 백엔드 시스템으로 해당 데이터를 내보내는 역할을 합니다.
OpenTelemetry Collector는 주로 Receivers, Processors, Exporters로 구성되며, 최종적으로 Pipeline을 통해 앞서 구성한 세 구성 요소를 연결합니다. 각 구성 요소는 다음과 같은 역할을 합니다.
- Receivers (수신기)
다양한 애플리케이션 소스에서 데이터를 수집하는 역할. - Processors (프로세서)
데이터를 필터링, 변환하는 등 다양한 처리를 수행. - Exporters (내보내기)
데이터를 최종 백엔드로 전송.
OpenTelemetry Collector에는 다양한 배포판이 있으며, 배포판마다 지원하는 컴포넌트에 차이가 있습니다. 각 배포판이 지원하는 컴포넌트는 opentelemetry-collector-releases GitHub 리포지토리에서 확인하실 수 있습니다. (배포판 디렉터리 내부 manifest.yaml 파일)
2. 버전 정보
- Kubernetes : v1.30.3
- cert-manager : v1.16.2
- OpenTelemetry Operator : 0.116.0
- Prometheus : 3.1.0
3. OpenTelemetry Operator 배포
OpenTelemetry Operator 배포를 위해선 Kubernetes 클러스터에 cert-manager 설치가 필요합니다.
cert-manager 공식 설치 문서를 참고하여 클러스터에 설치하고, 모든 Pod의 준비 상태를 확인 후 진행합니다.
$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml
최신 버전은 공식 설치 문서 링크를 확인하세요.
$ kubectl get po -n cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-57d855897b-rlsvk 1/1 Running 0 26s
cert-manager-cainjector-5c7f79b84b-wbltw 1/1 Running 0 26s
cert-manager-webhook-657b9f664c-8rbwn 1/1 Running 0 26s
다음 명령어를 사용하여 최신 버전의 Operator를 배포합니다.
opentelemetry-operator-system 네임스페이스를 생성하여 관련 리소스가 해당 네임스페이스에 배포됩니다.
$ kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml
namespace/opentelemetry-operator-system created
customresourcedefinition.apiextensions.k8s.io/instrumentations.opentelemetry.io created
customresourcedefinition.apiextensions.k8s.io/opampbridges.opentelemetry.io created
customresourcedefinition.apiextensions.k8s.io/opentelemetrycollectors.opentelemetry.io created
serviceaccount/opentelemetry-operator-controller-manager created
role.rbac.authorization.k8s.io/opentelemetry-operator-leader-election-role created
clusterrole.rbac.authorization.k8s.io/opentelemetry-operator-manager-role created
clusterrole.rbac.authorization.k8s.io/opentelemetry-operator-metrics-reader created
clusterrole.rbac.authorization.k8s.io/opentelemetry-operator-proxy-role created
rolebinding.rbac.authorization.k8s.io/opentelemetry-operator-leader-election-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/opentelemetry-operator-manager-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/opentelemetry-operator-proxy-rolebinding created
service/opentelemetry-operator-controller-manager-metrics-service created
service/opentelemetry-operator-webhook-service created
deployment.apps/opentelemetry-operator-controller-manager created
certificate.cert-manager.io/opentelemetry-operator-serving-cert created
issuer.cert-manager.io/opentelemetry-operator-selfsigned-issuer created
mutatingwebhookconfiguration.admissionregistration.k8s.io/opentelemetry-operator-mutating-webhook-configuration created
validatingwebhookconfiguration.admissionregistration.k8s.io/opentelemetry-operator-validating-webhook-configuration created
최초 구성 시 opentelemetry-operator-system 네임스페이스에 다음과 같이 리소스가 생성됩니다.
$ kubectl get all -n opentelemetry-operator-system
NAME READY STATUS RESTARTS AGE
pod/opentelemetry-operator-controller-manager-6fb67d9fbc-nwk4p 2/2 Running 0 45s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/opentelemetry-operator-controller-manager-metrics-service ClusterIP 10.96.107.45 <none> 8443/TCP 46s
service/opentelemetry-operator-webhook-service ClusterIP 10.97.121.58 <none> 443/TCP 46s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/opentelemetry-operator-controller-manager 1/1 1 1 45s
NAME DESIRED CURRENT READY AGE
replicaset.apps/opentelemetry-operator-controller-manager-6fb67d9fbc 1 1 1 45s
4. OpenTelemetry Collector 배포
OpenTelemetry Collector의 구조와 동작 방식을 알아보고, 예시 collector를 배포하여 어떤 리소스가 생성되는지 확인해 보겠습니다.
4-1. OpenTelemetry Collector의 구조
OpenTelemetry Collector는 크게 Receivers, Processors, Exporters, Pipelines의 구조로 이루어져 있습니다.
Receivers 설정을 통해 다양한 대상으로부터 원격 측정 데이터를 수집하고, Processors 설정에 따라 수집한 데이터를 처리하며 마지막으로 Exporters를 통해 처리된 데이터를 노출/전송합니다.
Pipelines는 Receivers, Processors, Exporters를 연결하는 역할을 합니다.
Receivers, Processors, Exporters는 설정에 명시하는 것만으로 적용되지 않으며, 모두 Pipeline 설정에 정의되어야 적용됩니다.
Receivers
Receivers는 OpenTelemetry Collector의 데이터 수집을 구성하는 부분입니다. 수집하는 데이터의 타입에 따라 대상으로부터 데이터를 직접 수집하거나, 대상이 전송하는 데이터를 수신하여 수집할 수 있습니다.
Receivers는 1개 이상 구성되어야 합니다.
receivers:
jaeger:
protocols:
grpc: # 수신 프로토콜
endpoint: 0.0.0.0:4317 # 수신 포트
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
otlp/other:
protocols:
grpc:
endpoint: 0.0.0.0:14317
http:
endpoint: 0.0.0.0:14318
prometheus:
config: # Prometheus 메트릭 수집 설정
scrape_configs:
- job_name: otel-collector
scrape_interval: 5s
scrape_configs:
- targets: [prometheus.svc:8888] # Prometheus 메트릭 수집 대상 설정
위 예시의 경우 jaeger, otlp 타입의 데이터는 명시된 프로토콜(grpc, http)과 포트를 통해 데이터를 수신하여 수집하고, prometheus 타입의 경우 명시된 타겟(localhost:8888)을 통해 데이터를 수집합니다.
동일한 타입의 receiver는 여러 개 구성될 수 있으며, 예시의 otlp, otlp/other와 같이 <receiver 타입>/<이름> 형태로 구성할 수 있습니다.
Processors
Processors는 receivers가 수집한 데이터를 exporters로 내보내기 전에 수정하고 변환하는 작업을 합니다. Processors 설정을 통해 데이터 필터링, 삭제, 이름 변경과 같은 작업을 할 수 있습니다. 또한 Memory Limiter Processor를 통해 Collector의 메모리 부족을 방지하거나, Batch Processor를 통해 데이터를 batch에 배치하여 전송하도록 구성할 수 있습니다.
Processors 구성은 필수적이지 않지만, memory_limiter와 batch 설정은 권장됩니다.
processors:
memory_limiter:
check_interval: 1s # 메모리 사용량을 확인하는 주기
limit_percentage: 75 # 메모리 사용량의 hard limit %를 정의
spike_limit_percentage: 15 # 메모리 사용량의 soft limit을 정의 (limit_percentage - spike_limit_percentage): 75 - 15 = 60%
batch:
send_batch_size: 10000 # batch 사이즈(한 번에 보내는 데이터양)
timeout: 10s # batch가 가득 차지 않아도 10초 뒤 전송
Exporters
Exporters는 데이터를 하나 이상의 백엔드나 목적지로 전송합니다. Exporters를 통한 데이터 전송은 데이터 타입에 따라 직접 전송 혹은 대상이 Collector로부터 수집하도록 구성할 수 있습니다.
Recievers처럼 동일한 타입의 Exporters는 여러 개 구성할 수 있으며, <exporter 타입>/<이름> 형태로 구성할 수 있습니다.
Exporter는 하나 이상 구성되어야 합니다.
exporters:
file:
path: ./filename.json
otlp/jaeger:
endpoint: jaeger-server:4317
debug:
verbosity: detailed
prometheus:
endpoint: 0.0.0.0:8889
prometheusremotewrite:
endpoint: http://prometheus.example.com:9411/api/prom/push
Pipelines
Pipelines(파이프라인)는 앞서 구성한 3개의 Receivers, Processors, Exporters 설정을 연결하여 traces, metrics, logs 데이터로 내보내는 역할을 합니다. 여러 파이프라인에서 동일한 Receivers, Processors, Exporters를 사용할 수 있으며 동일한 processor가 여러 파이프라인에 정의된 경우, 각 파이프라인은 별개의 processor 인스턴스를 토해 처리합니다.
Pipeline에 다수의 processors가 구성된 경우 데이터 처리는 정의된 순서에 따라 처리됩니다.
service:
pipelines:
metrics:
receivers: [opencensus, prometheus]
processors: [batch]
exporters: [opencensus, prometheus]
traces:
receivers: [opencensus, jaeger]
processors: [batch, memory_limiter] # batch 프로세스 이후 memory_limiter 처리
exporters: [opencensus, zipkin]
4-2. 예시 Collector 배포 (Deployment)
예시 OpenTelemetryCollector 리소스를 Kubernetes 클러스터에 배포하여, 실제로 어떻게 구성되는지 확인해 보겠습니다.
예시 yaml 파일은 다음과 같습니다.
example.yaml
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
name: example
spec:
config:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
prometheus: # Collector의 Prometheus 메트릭 수집 설정
config:
scrape_configs:
- job_name: 'otel-collector'
scrape_interval: 5s
static_configs:
- targets: ['nginx-prometheus-svc.nginx-ingress:9113'] # 수집 대상 엔드포인트(NGINX Plus Ingress Controller)
processors:
memory_limiter:
check_interval: 1s # 메모리 사용량을 확인하는 주기
limit_percentage: 75 # 메모리 사용량의 hard limit %를 정의
spike_limit_percentage: 15 # 메모리 사용량의 soft limit을 정의 (limit_percentage - spike_limit_percentage): 75 - 15 = 60%
batch:
send_batch_size: 10000 # batch 사이즈(한 번에 보내는 데이터양)
timeout: 10s # batch가 가득 차지 않아도 10초 뒤 전송
exporters:
debug: {} # Collector의 로그를 콘솔로 출력해 Pod 로그로 확인 가능
prometheus:
endpoint: "0.0.0.0:9113" # 메트릭 데이터를 prometheus 형태로 노출하는 설정
service:
pipelines:
metrics:
receivers: [otlp, prometheus]
processors: [memory_limiter, batch]
exporters: [debug, prometheus]
receivers의 prometheus 항목은 예시를 위해 클러스터에 사전 배포된 NGINX Plus Ingress Controller의 Prometheus 메트릭 노출 엔드포인트에서 수집하도록 구성했습니다.
$ kubectl apply -f example.yaml -n otel
opentelemetrycollector.opentelemetry.io/example created
$ kubectl get opentelemetrycollectors.opentelemetry.io -n otel
NAME MODE VERSION READY AGE IMAGE MANAGEMENT
example deployment 0.116.1 1/1 17s ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector:0.116.1 managed
otel 네임스페이스에 배포하였으며, 위와 같이 opentelemetrycollectors 리소스가 생성됩니다.
$ kubectl get all -n otel
NAME READY STATUS RESTARTS AGE
pod/example-collector-58d8dcd46f-qdnb9 1/1 Running 0 10s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/example-collector ClusterIP 10.99.227.17 <none> 4317/TCP,4318/TCP,9113/TCP 10s
service/example-collector-headless ClusterIP None <none> 4317/TCP,4318/TCP,9113/TCP 10s
service/example-collector-monitoring ClusterIP 10.109.201.26 <none> 8888/TCP 10s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/example-collector 1/1 1 1 10s
NAME DESIRED CURRENT READY AGE
replicaset.apps/example-collector-58d8dcd46f 1 1 1 10s
생성한 opentelemetrycollectors 리소스에 정의된 내용에 따라 Deployment/replicaset/pod 1개와, 3개의 Service가 배포됩니다. 각 리소스는 생성한 opentelemetrycollectors 리소스의 이름인 example 뒤에 -collector가 추가된 이름으로 생성됩니다.
Service 리소스 중 example-collector는 설정에 따라 데이터를 수집/노출하는 역할을 하며, example-collector-monitoring은 collector Pod 자체의 데이터를 8888 포트를 통해 노출합니다.
Name: example-collector
Namespace: otel
Labels: app.kubernetes.io/component=opentelemetry-collector
app.kubernetes.io/instance=otel.example
app.kubernetes.io/managed-by=opentelemetry-operator
app.kubernetes.io/name=example-collector
app.kubernetes.io/part-of=opentelemetry
app.kubernetes.io/version=latest
operator.opentelemetry.io/collector-service-type=base
Annotations: <none>
Selector: app.kubernetes.io/component=opentelemetry-collector,app.kubernetes.io/instance=otel.example,app.kubernetes.io/managed-by=opentelemetry-operator,app.kubernetes.io/part-of=opentelemetry
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.99.227.17
IPs: 10.99.227.17
Port: otlp-grpc 4317/TCP
TargetPort: 4317/TCP
Endpoints: 10.244.1.182:4317
Port: otlp-http 4318/TCP
TargetPort: 4318/TCP
Endpoints: 10.244.1.182:4318
Port: prometheus 9113/TCP
TargetPort: 9113/TCP
Endpoints: 10.244.1.182:9113
Session Affinity: None
Events: <none>
example-collector Service는 collector 설정에 따라 4317/4318 포트를 통해 otlp grpc/http 데이터를 수신하고, exporter 설정에 따라 9113 포트를 통해 Prometheus 형식의 메트릭을 노출합니다.
$ kubectl logs example-collector-58d8dcd46f-qdnb9
2025-01-20T00:27:09.525Z info service@v0.116.0/service.go:164 Setting up own telemetry...
2025-01-20T00:27:09.525Z warn service@v0.116.0/service.go:213 service::telemetry::metrics::address is being deprecated in favor of service::telemetry::metrics::readers
2025-01-20T00:27:09.525Z info telemetry/metrics.go:70 Serving metrics {"address": "0.0.0.0:8888", "metrics level": "Normal"}
2025-01-20T00:27:09.526Z info builders/builders.go:26 Development component. May change in the future. {"kind": "exporter", "data_type": "metrics", "name": "debug"}
......
2025-01-20T01:02:00.728Z info Metrics {"kind": "exporter", "data_type": "metrics", "name": "debug", "resource metrics": 2, "metrics": 132, "data points": 2910}
2025-01-20T01:02:10.786Z info Metrics {"kind": "exporter", "data_type": "metrics", "name": "debug", "resource metrics": 2, "metrics": 132, "data points": 2910}
Collector Pod의 로그를 확인해보면, 위와 같이 debug exporter 설정에 따라 메트릭 데이터 관련 출력이 나타납니다.
$ kubectl run nginx --image=nginx -n otel
$ kubectl exec -n otel -it nginx -- curl example-collector:9113/metrics
# HELP nginx_ingress_controller_ingress_resources_total Number of handled ingress resources
# TYPE nginx_ingress_controller_ingress_resources_total gauge
nginx_ingress_controller_ingress_resources_total{class="nginx",instance="nginx-prometheus-svc.nginx-ingress:9113",job="otel-collector",type="master"} 0
nginx_ingress_controller_ingress_resources_total{class="nginx",instance="nginx-prometheus-svc.nginx-ingress:9113",job="otel-collector",type="minion"} 0
......
otel 네임스페이스에 서비스로 요청을 전송하기 위해 nginx pod를 생성하고, collector가 Prometheus 메트릭을 노출하는 example-collector:9113/metrics로 요청을 전송하면, 위와 같이 collector가 수집한 NGINX Ingress Controller의 메트릭을 응답하는 것을 확인할 수 있습니다.
$ kubectl exec -n otel -it nginx -- curl example-collector-monitoring:8888/metrics
# HELP otelcol_exporter_send_failed_metric_points Number of metric points in failed attempts to send to destination. [alpha]
# TYPE otelcol_exporter_send_failed_metric_points counter
otelcol_exporter_send_failed_metric_points{exporter="debug",service_instance_id="c5dff308-b5e7-4793-b2e4-ae133b20455f",service_name="otelcol",service_version="0.116.0"} 0
otelcol_exporter_send_failed_metric_points{exporter="prometheus",service_instance_id="c5dff308-b5e7-4793-b2e4-ae133b20455f",service_name="otelcol",service_version="0.116.0"} 0
......
example-collector-monitoring:8888/metrics로 요청을 전송 시, 위와 같이 collector 인스턴스의 메트릭 데이터를 확인할 수 있습니다.
4-3. Daemonset Collector 구성
OpenTelemetry Collector는 Kubernetes 모니터링을 위한 다양한 receivers와 processors를 지원합니다.
이번 과정에서는 Kubeletstats Receiver를 통해 kubelet의 API 서버로부터 node, pod, container의 메트릭을 수집하고, Kubernetes Attributes Processor를 통해서 수집한 메트릭에 Kubernetes 메타데이터를 추가하도록 하겠습니다.
1. Collector를 위한 Kubernetes의 ServiceAccount 리소스를 생성합니다.
Kubernetes API를 통해서 collector는 다양한 네임스페이스의 Pod와 노드의 메트릭/메타데이터를 수집하기 때문에, 권한 설정을 위한 ServiceAccount 구성이 필수적입니다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: collector # SA 리소스 이름 설정
namespace: monitoring # 네임스페이스 설정
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: otel-collector
rules:
- apiGroups:
- ''
resources:
- 'pods'
- 'namespaces'
- 'nodes'
- 'nodes/stats'
verbs:
- 'get'
- 'watch'
- 'list'
- apiGroups:
- 'apps'
resources:
- 'replicasets'
verbs:
- 'get'
- 'list'
- 'watch'
- apiGroups:
- 'extensions'
resources:
- 'replicasets'
verbs:
- 'get'
- 'list'
- 'watch'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: otel-collector
subjects:
- kind: ServiceAccount
name: collector # SA 리소스 이름 설정
namespace: monitoring # 네임스페이스 설정
roleRef:
kind: ClusterRole
name: otel-collector
apiGroup: rbac.authorization.k8s.io
Collector가 배포될 네임스페이스를 ServiceAccount에 명시하고, ServiceAccount의 이름도 원하는 이름으로 설정합니다. 이 포스트에서는 monitoring 네임스페이스에 collector ServiceAccount로 구성했습니다.
$ kubectl apply -f otelcol-sa.yaml
serviceaccount/collector created
clusterrole.rbac.authorization.k8s.io/otel-collector created
clusterrolebinding.rbac.authorization.k8s.io/otel-collector created
2. 배포할 collector 리소스를 정의하는 파일을 생성합니다.
구성별 상세 설명은 아래에 이어집니다.
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
name: daemonset
spec:
mode: daemonset
image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-k8s
serviceAccount: collector
env:
- name: KUBE_NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
config:
receivers:
kubeletstats:
collection_interval: 10s
auth_type: 'serviceAccount'
endpoint: '${env:K8S_NODE_NAME}:10250'
insecure_skip_verify: true
metric_groups:
- node
- pod
- container
processors:
memory_limiter:
check_interval: 1s # 메모리 사용량을 확인하는 주기
limit_percentage: 75 # 메모리 사용량의 hard limit %를 정의
spike_limit_percentage: 15 # 메모리 사용량의 soft limit을 정의 (limit_percentage - spike_limit_percentage): 75 - 15 = 60%
batch:
send_batch_size: 10000 # 배치 사이즈(한 번에 보내는 데이터 양)
timeout: 10s # 배치 사이즈에 도달하지 않아도 timeout이 발생하면 보냄
k8sattributes:
auth_type: 'serviceAccount'
filter:
node_from_env_var: KUBE_NODE_NAME
extract:
metadata:
- k8s.namespace.name
- k8s.deployment.name
- k8s.statefulset.name
- k8s.daemonset.name
- k8s.cronjob.name
- k8s.job.name
- k8s.node.name
- k8s.pod.name
- k8s.pod.uid
- k8s.pod.start_time
- k8s.container.name
passthrough: false
pod_association:
- sources:
- from: resource_attribute
name: k8s.pod.ip
- sources:
- from: resource_attribute
name: k8s.pod.uid
- sources:
- from: connection
exporters:
debug: {}
otlphttp:
endpoint: "http://prometheus-svc:9090/api/v1/otlp"
tls:
insecure: true
service:
pipelines:
metrics:
receivers: [kubeletstats]
processors: [memory_limiter, k8sattributes, batch]
exporters: [otlphttp, debug]
각 구성 요소에 대해서 알아보도록 하겠습니다.
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
name: daemonset # 리소스 이름 설정
spec:
mode: daemonset # 배포 방식을 Daemonset으로 구성(기본값: Deployment)
image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-k8s # Collector의 배포판을 k8s로 설정(기본값: otelcol)
serviceAccount: collector # collector에 적용할 ServiceAccount 설정
env: # K8S Node 이름 추출을 위한 변수 설정
- name: KUBE_NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
Kubeletstats Receiver를 통해 node의 메트릭을 수집할 때, Deployment로 배포 시 collector가 배포되는 node의 메트릭만 수집을 할 수 있습니다. 모든 node에 collector를 배포하기 위해 Daemonset으로 배포를 진행합니다.
기본 배포판의 경우 K8S의 메트릭 수집을 위한 receiver, processor을 지원하지 않아 otelcol-k8s 배포판을 사용하기 위해 image를 통해 배포판을 지정합니다.
Daemonset Pod의 권한을 위해 serviceAccount를 앞서 구성한 serviceAccount 이름으로 설정하고, node 이름 추출을 위한 변수 설정을 적용합니다.
config:
receivers:
kubeletstats:
collection_interval: 10s # 수집 주기 설정
auth_type: 'serviceAccount' # Kubelet 인증을 위한 설정
endpoint: '${env:K8S_NODE_NAME}:10250' # 수집할 엔드포인트 설정. 노드의 10250 포트(kubelet)
insecure_skip_verify: true
metric_groups:
- node
- pod
- container
클러스터의 메트릭 수집을 위한 Kubeletstats Receiver 설정입니다. 해당 receiver 구성을 통해 클러스터의 node, pod, container 메트릭을 수집합니다.
기본적으로 수집할 수 있는 메트릭과, 선택적으로 수집할 수 있는 메트릭은 kubeletstatsreceiver 문서를 확인하세요.
processors:
memory_limiter:
check_interval: 1s # 메모리 사용량을 확인하는 주기
limit_percentage: 75 # 메모리 사용량의 hard limit %를 정의
spike_limit_percentage: 15 # 메모리 사용량의 soft limit을 정의 (limit_percentage - spike_limit_percentage): 75 - 15 = 60%
batch:
send_batch_size: 10000 # 배치 사이즈(한 번에 보내는 데이터 양)
timeout: 10s # 배치 사이즈에 도달하지 않아도 timeout이 발생하면 보냄
k8sattributes:
auth_type: 'serviceAccount' # ServiceAccount 설정
filter:
node_from_env_var: KUBE_NODE_NAME # 상단에서 구성한 env.name을 통한 이름 추출
extract:
metadata: # 추출할 메타데이터 설정. 필요에 따라 조정
- k8s.namespace.name
- k8s.deployment.name
- k8s.statefulset.name
- k8s.daemonset.name
- k8s.cronjob.name
- k8s.job.name
- k8s.node.name
- k8s.pod.name
- k8s.pod.uid
- k8s.pod.start_time
- k8s.container.name
pod_association: # 수집한 데이터를 Pod와 연결하는 방법 정의
- sources:
- from: resource_attribute # 1순위 : Pod IP를 통해 연결
name: k8s.pod.ip
- sources:
- from: resource_attribute # 2순위 : Pod UID를 통해 연결
name: k8s.pod.uid
- sources:
- from: connection # 3순위 : 요청의 연결 정보를 통해 Pod IP 확인
Memory limiter 구성을 통해 collector의 메모리 부족으로 인한 문제를 방지하고, batch 설정을 통해 효율적으로 데이터를 전송합니다.
k8sattributes 설정을 통해 수집한 메트릭에 포함할 K8S 메타데이터를 정의합니다. 구성할 수 있는 전체 메타데이터와, Pod의 label과 annotation에서 값을 추출하는 방법은 k8sattributesprocessor 문서를 참고하세요.
exporters:
debug: {} # 디버깅을 위한 설정
otlphttp: # otlp 데이터를 http 프로토콜로 지정한 엔드포인트로 전송
endpoint: "http://prometheus-svc:9090/api/v1/otlp" # Prometheus Service로 지정
service:
pipelines:
metrics:
receivers: [kubeletstats]
processors: [memory_limiter, k8sattributes, batch]
exporters: [otlphttp, debug]
exporters 구성을 통해 디버깅을 위한 debug exporter와, Prometheus Pod로 메트릭 전송을 위한 otlphttp exporter를 구성합니다. Prometheus Pod와 Service를 아직 배포하지 않았으나, 추후 배포할 엔드포인트로 구성했습니다.
service.pipelines 구성을 통해 receivers, processors, exporters를 하나로 묶어 최종적으로 구성합니다.
4-4. Prometheus 구성
OpenTelemetry Collector가 전송하는 otlp 데이터를 수신하기 위한 Prometheus Deployment를 구성합니다.
1. Collector로부터 메트릭 데이터를 수신하는 Prometheus Pod의 설정 파일을 작성합니다.
Pod로 마운트하기 위해 설정 파일을 ConfigMap으로 구성합니다.
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: monitoring
data:
prometheus.yml: |
global:
scrape_interval: 15s
otlp:
# Recommended attributes to be promoted to labels.
promote_resource_attributes:
- k8s.cronjob.name
- k8s.daemonset.name
- k8s.deployment.name
- k8s.job.name
- k8s.namespace.name
- k8s.pod.name
- k8s.statefulset.name
- k8s.node.name
- k8s.pod.uid
- k8s.pod.start_time
otlp.promote_resource_attributes 항목 하단에, processors에서 정의한 메타데이터를 추가합니다.
위 설정이 없을 시 Prometheus가 Opentelemetry Collector에서 추출한 메타데이터를 인식하지 못합니다.
2. Prometheus가 otlp 데이터를 수신할 수 있도록 Deployment에 args 설정을 적용합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
containers:
- name: prometheus-container
image: prom/prometheus
ports:
- containerPort: 9090
args:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--web.enable-otlp-receiver" # OTLP receiver 활성화
- "--web.listen-address=:9090"
- "--web.route-prefix=/"
volumeMounts: # ConfigMap으로 구성한 설정 파일 마운트
- name: prometheus-config
mountPath: /etc/prometheus/prometheus.yml
subPath: prometheus.yml
volumes:
- name: prometheus-config
configMap:
name: prometheus-config
해당 설정을 적용하면 otlp 데이터를 수신할 수 있는 엔드포인트가 활성화됩니다.
3. Prometheus ConfigMap, Deployment, Service와 Collector 리소스를 배포하고, Prometheus 웹 UI 접근을 위한 VirtualServer 리소스를 배포합니다.
배포에 사용된 파일들은 NGINX STORE GitHub 리포지토리에서 확인하실 수 있습니다.
$ kubectl apply -f prometheus-config.yaml,prometheus-deploy.yaml,prometheus-svc.yaml,prometheus-vs.yaml -n monitoring
configmap/prometheus-config created
deployment.apps/prometheus created
service/prometheus-svc created
virtualserver.k8s.nginx.org/protmeteheus-vs created
$ kubectl apply -f otelcol-daemonset.yaml -n monitoring
opentelemetrycollector.opentelemetry.io/daemonset created
$ kubectl get all -n monitoring
NAME READY STATUS RESTARTS AGE
pod/daemonset-collector-5flqt 1/1 Running 0 18s
pod/daemonset-collector-8f586 1/1 Running 0 18s
pod/prometheus-777967db4f-5ghwg 1/1 Running 0 50s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/daemonset-collector-monitoring ClusterIP 10.106.214.35 <none> 8888/TCP 18s
service/prometheus-svc ClusterIP 10.100.120.121 <none> 9090/TCP 50s
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/daemonset-collector 2 2 2 2 2 <none> 18s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/prometheus 1/1 1 1 50s
NAME DESIRED CURRENT READY AGE
replicaset.apps/prometheus-777967db4f 1 1 1 50s
4-5. Kubernetes 메트릭 수집 확인
1. Daemonset Collector 로그를 확인하여 정상적으로 메트릭을 수집하는지 확인합니다.
......
2025-01-20T07:26:18.026Z info service@v0.117.0/service.go:230 Starting otelcol-k8s... {"Version": "0.117.0", "NumCPU": 4}
2025-01-20T07:26:18.026Z info extensions/extensions.go:39 Starting extensions...
2025-01-20T07:26:18.034Z info kube/client.go:141 k8s filtering {"kind": "processor", "name": "k8sattributes", "pipeline": "metrics", "labelSelector": "", "fieldSelector": "spec.nodeName=worker"}
2025-01-20T07:26:18.034Z info service@v0.117.0/service.go:253 Everything is ready. Begin running and processing data.
2025-01-20T07:26:28.029Z info Metrics {"kind": "exporter", "data_type": "metrics", "name": "debug", "resource metrics": 57, "metrics": 630, "data points": 676}
아래와 같은 에러가 발생할 수 있습니다. 해당 에러는 receivers 설정에 따라 node 이름으로 DNS 질의가 이루어지는데, Pod의 DNS 질의가 이루어지는 coredns에 node의 이름과 IP 정보가 없어 발생하는 에러입니다.
2025-01-20T07:50:41.936Z error scraperhelper@v0.117.0/obs_metrics.go:61 Error scraping metrics {"kind": "receiver", "name": "kubeletstats",
"data_type": "metrics", "scraper": "kubeletstats", "error": "Get \"https://worker2:10250/stats/summary\": dial tcp: lookup worker2 on 10.96.0.10:53: no such
host"}
coredns에 node의 이름과 IP 정보를 추가하여 해결할 수 있습니다.
1-1. node의 이름, IP를 확인합니다.
$ kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master Ready control-plane 290d v1.30.3 192.168.200.171 <none> Ubuntu 22.04.4 LTS 5.15.0-130-generic containerd://1.7.2
worker Ready <none> 205d v1.30.3 192.168.200.91 <none> Ubuntu 22.04.3 LTS 5.15.0-130-generic containerd://1.7.2
worker2 Ready <none> 6d5h v1.30.3 192.168.200.92 <none> Ubuntu 22.04.4 LTS 5.15.0-130-generic containerd://1.7.25
1-2. coredns Configmap에 node의 이름, IP를 추가합니다.
$ kubectl edit cm -n kube-system coredns
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
hosts {
192.168.200.91 worker
192.168.200.92 worker2
fallthrough
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
kind: ConfigMap
metadata:
creationTimestamp: "2024-04-05T02:40:20Z"
name: coredns
namespace: kube-system
resourceVersion: "3124034"
uid: 8a6754f4-be94-4009-945e-641002cfd039
위와 같이 coredns ConfigMap을 수정하여 hosts 항목을 새로 만들고, node의 이름과 IP를 환경에 맞게 추가합니다.
수정된 ConfigMap이 적용되면 collector Pod에서 정상적인 로그가 출력됩니다.
2. Prometheus UI로 접속하여 메트릭을 확인합니다.




Processor에서 추출한 메타데이터를 통해 메트릭을 구분하며 확인할 수 있습니다.
5. 결론
이번 포스트에서는 Kubernetes 클러스터에 OpenTelemetry Operator, Collector를 배포하여 클러스터 내부의 node, pod, container의 메트릭을 수집하고, Prometheus 형식으로 노출하는 방법을 알아봤습니다.
OpenTelemetry Operator를 Kubernetes 클러스터에 배포하여, OpenTelemetry Collector를 정의하는 yaml파일 하나를 통해 클러스터에 손쉽게 OpenTelemetry Collector를 배포할 수 있었습니다.
그리고 OpenTelemetry Collector의 구성 요소, 동작 방식을 알아봤으며 실제 OpenTelemetry Collector를 클러스터에 배포하여 클러스터의 node, pod, container의 메트릭 수집을 확인했습니다.
OpenTelemetry Collector는 이번 포스트에서 다룬 메트릭 데이터 뿐만이 아니라, 애플리케이션의 로그/트레이싱 데이터도 다룹니다. OpenTelemetry를 활용하면 Kubernetes와 같은 분산 시스템 환경에서 애플리케이션의 성능과 동작을 다양한 백엔드 시스템과 통합하여 모니터링 할 수 있습니다.
Kubernetes 클러스터의 NGINX Ingress Controller의 NGINX Plus API 데이터를 Prometheus 형식으로 노출하고, OpenTelemetry Collector와의 통합을 체험해 보고 싶으시다면, NGINX STORE를 통해 문의해 NGINX의 상업용 구독을 무료로 체험해 보세요.
댓글을 달려면 로그인해야 합니다.