Span Metrics Connector는 span data로부터 RED(Request, Error, Duration) 메트릭을 만들어낸다.
생성되는 메트릭들은 다음의 dimension들을 최소한 가지게 된다.
- service.name
- span.name
- span.kind
- status.code
1. 설정값
histogram : Span duration 측정기준으로부터 record를 계산하기 위한 히스토그램 (explicit, exponential)
dimensions : service.name, span.name, span.kind, status.code 외에 생성되는 메트릭들의 label로 등록하고 싶은 리스트
예를 들어 시계열 메트릭의 Label로 IP , Region 등을 추가하고 싶다면 dimensions에 추가하면 된다.
exclude_dimensions : service.name, span.name, span.kind, status.code 중 제외하고 싶은 dimension
dimensions_cache_size : Collector의 메모리 사용량을 향상시키기 위한 Dimension 전용 캐시 사이즈 (default : 1000)
resource_metrics_cache_size : 서비스를 위해 메트릭을 보관하는 캐시 사이즈이며 대부분 메모리 누수를 방지하고 메트릭 타임스탬프 재설정을 수정하기 위한 누적 시간과 관련이 있음 (default : 1000)
namespace : 생성된 메트릭들의 네임스페이스를 정의한다. 만약 네임스페이스가 제공되었다면 생성된 메트릭 네임들이 해당 네임스페이스에 추가된다.
metrics_flush_interval : 생성된 메트릭들을 flush 하는 주기 (default : 15s)
exemplars : examplars를 히스토그램에 어떻게 붙일지 설정하는 섹션
enabled: true | false (default : false)
events : events 메트릭을 설정
enabled: true | false (default : false)
dimensions : (true일 경우 필수값) event metric에 dimention으로써 추가하고 싶은 span event의 attribute 리스트
resource_metrics_key_attributes : 리소스 attribute를 필터링하며 resource metrics key map hash를 만들어내는데 사용한다.
이는 서비스 재시작 전반에 걸쳐 리소스 속성이 변경되어 메트릭 카운터가 중단(및 중복)될 수 있는 상황을 방지하는 데 사용된다.
모든 Attribute를 가질 필요는 없으며 고유 리소스를 적절하게 식별하거나 둘 이상의 서비스 및 스팬으로부터의 데이터를 집계하는 위험을 발생시키기에 충분한 속성을 포함하면 된다.
아래 Github issue에 관련 내용이 나오며, resource_metrics_key_attributes 설정을 통해 Counter 값이 초기화되는 이슈를 해결할 수 있다고 하니 꼭 설정해주자.
https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/21101
전체 설정에 대한 정보는 아래 config.go에서 확인할 수 있다.
2. 예시
docker-compose.yaml
version: '2'
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:0.92.0
volumes:
- ./otel-collector-config.yml:/etc/otelcol-contrib/config.yaml
ports:
- 4318:4318 # OTLP http receiver
- 55679:55679 # zpages
otel-collector-config.yaml
receivers:
otlp:
protocols:
http:
grpc:
exporters:
logging:
loglevel: debug
# prometheusremotewrite:
# endpoint: http://localhost:9090/api/v1/write
# target_info:
# enabled: true
connectors:
spanmetrics:
histogram:
explicit:
buckets: [50ms, 100ms, 200ms, 500ms, 1s, 5s]
aggregation_temporality: "AGGREGATION_TEMPORALITY_CUMULATIVE"
metrics_flush_interval: 15s
exemplars:
enabled: false
events:
enabled: true
dimensions:
- name: exception.type
- name: exception.message
resource_metrics_key_attributes:
- service.name
- telemetry.sdk.language
- telemetry.sdk.name
service:
extensions: [zpages]
pipelines:
traces:
receivers: [otlp]
exporters: [spanmetrics]
metrics:
receivers: [spanmetrics]
exporters: [logging]
extensions:
zpages:
endpoint: 0.0.0.0:55679
위 설정을 좀 더 자세히 살펴보자.
우선 우리가 설정한 파이프라인이 어떻게 구성되어있는지 간단히 확인하기위해 zpages 라는 extention을 추가해주었다.
zpages는 otel pipeline을 구성하면서 디버깅할 때 사용될 수 있는 익스텐션이다.
파이프라인 구성은 아래와 같다. 로컬에서는 보통 디버깅을 위해 logging exporter를 많이 사용하게 되며 당연히 실제 k8s에 올릴 때는 prometheusremotewrite를 사용하게 된다.
- traces : otlp(receiver) -> spanmetrics(connector type exporter)
- metrics : spanmetrics(connector type receiver) -> logging(exporter)
참고로 zpages에 대한 경로는 아래와 같다.
3. 테스트
아래 명령을 통해 OTEL Javaagent의 경로 및 otel config 값 등을 주입해준다.
OTEL_LOGS_EXPORTER=none OTEL_METRICS_EXPORTER=none \
<JAVA_PATH> -Dspring.profiles.active=local -javaagent:./opentelemetry-javaagent.jar \
-jar <PATH/TO/my-app.jar> -Dotel.resource.attributes=service.name=MYAPP-LOCAL \
-Dotel.traces.exporter=otlp -Dotel.exporter.otlp.endpoint=localhost:4317
무슨 설정값을 넣어줘야 하는지는 아래에서 확인 가능함
기 만들어둔 springboot에 otel java autoinstrument로 otel javaagent를 설정해주었다.
아래 latency에 접근하면 1.1s동안 sleep 한 뒤 client에게 응답을 해주게 된다.
이렇게 하면 다음의 내용이 logging exporter를 통해 찍히는걸 확인할 수 있다.
총 3가지가 보이는데 calls, duration, events이다.
calls는 호출 별로 어떤 endpoint로 STATUS_CODE가 무엇인지 등 정보를 확인할 수 있으며 이 메트릭을 통해 다음의 정보들을 구할 수 있다.
R (Request)
E (Error)
duration은 다음의 정보를 구할 수 있다.
D (Duration)
events는 어떤 dimension을 넣을거냐에 따라 다르지만 보통 Exeption이 발생할 때 어떤 Exception Type인지, Exception Message가 어떻게 되는지 등을 확인할 수 있다.
다만 Exception Message는 굉장히 긴 문자열로 구성되는게 일반적이기 때문에 Cardinality를 고려하면 Exception Type만 설정하는게 옳은 방향일 것으로 보인다.
otel-collector-1 | 2024-01-26T03:08:39.600Z info MetricsExporter {"kind": "exporter", "data_type": "metrics", "name": "logging", "resource metrics": 1, "metrics": 3, "data points": 2}
otel-collector-1 | 2024-01-26T03:08:39.602Z info ResourceMetrics #0
otel-collector-1 | Resource SchemaURL:
otel-collector-1 | Resource attributes:
otel-collector-1 | -> host.arch: Str(aarch64)
otel-collector-1 | -> host.name: Str(XXXXXX)
otel-collector-1 | -> os.description: Str(Mac OS X 14.2)
otel-collector-1 | -> os.type: Str(darwin)
otel-collector-1 | -> process.executable.path: Str(/Users/XXXXXX/Library/Java/JavaVirtualMachines/corretto-11.0.17/Contents/Home/bin/java)
otel-collector-1 | -> process.pid: Int(97819)
otel-collector-1 | -> process.runtime.description: Str(Amazon.com Inc. OpenJDK 64-Bit Server VM 11.0.17+8-LTS)
otel-collector-1 | -> process.runtime.name: Str(OpenJDK Runtime Environment)
otel-collector-1 | -> process.runtime.version: Str(11.0.17+8-LTS)
otel-collector-1 | -> service.name: Str(xxxxxx-0.0.1-SNAPSHOT)
otel-collector-1 | -> telemetry.distro.name: Str(opentelemetry-java-instrumentation)
otel-collector-1 | -> telemetry.distro.version: Str(2.0.0)
otel-collector-1 | -> telemetry.sdk.language: Str(java)
otel-collector-1 | -> telemetry.sdk.name: Str(opentelemetry)
otel-collector-1 | -> telemetry.sdk.version: Str(1.34.1)
otel-collector-1 | ScopeMetrics #0
otel-collector-1 | ScopeMetrics SchemaURL:
otel-collector-1 | InstrumentationScope spanmetricsconnector
otel-collector-1 | Metric #0
otel-collector-1 | Descriptor:
otel-collector-1 | -> Name: calls
otel-collector-1 | -> Description:
otel-collector-1 | -> Unit:
otel-collector-1 | -> DataType: Sum
otel-collector-1 | -> IsMonotonic: true
otel-collector-1 | -> AggregationTemporality: Cumulative
otel-collector-1 | NumberDataPoints #0
otel-collector-1 | Data point attributes:
otel-collector-1 | -> service.name: Str(xxxxxxx-0.0.1-SNAPSHOT)
otel-collector-1 | -> span.name: Str(GET /latency)
otel-collector-1 | -> span.kind: Str(SPAN_KIND_SERVER)
otel-collector-1 | -> status.code: Str(STATUS_CODE_UNSET)
otel-collector-1 | StartTimestamp: 2024-01-26 03:08:30.544939961 +0000 UTC
otel-collector-1 | Timestamp: 2024-01-26 03:08:39.599184507 +0000 UTC
otel-collector-1 | Value: 1
otel-collector-1 | Metric #1
otel-collector-1 | Descriptor:
otel-collector-1 | -> Name: duration
otel-collector-1 | -> Description:
otel-collector-1 | -> Unit: ms
otel-collector-1 | -> DataType: Histogram
otel-collector-1 | -> AggregationTemporality: Cumulative
otel-collector-1 | HistogramDataPoints #0
otel-collector-1 | Data point attributes:
otel-collector-1 | -> service.name: Str(xxxxxxxxx-0.0.1-SNAPSHOT)
otel-collector-1 | -> span.name: Str(GET /latency)
otel-collector-1 | -> span.kind: Str(SPAN_KIND_SERVER)
otel-collector-1 | -> status.code: Str(STATUS_CODE_UNSET)
otel-collector-1 | StartTimestamp: 2024-01-26 03:08:30.544939961 +0000 UTC
otel-collector-1 | Timestamp: 2024-01-26 03:08:39.599204423 +0000 UTC
otel-collector-1 | Count: 1
otel-collector-1 | Sum: 1113.759917
otel-collector-1 | ExplicitBounds #0: 50.000000
otel-collector-1 | ExplicitBounds #1: 100.000000
otel-collector-1 | ExplicitBounds #2: 200.000000
otel-collector-1 | ExplicitBounds #3: 500.000000
otel-collector-1 | ExplicitBounds #4: 1000.000000
otel-collector-1 | ExplicitBounds #5: 5000.000000
otel-collector-1 | Buckets #0, Count: 0
otel-collector-1 | Buckets #1, Count: 0
otel-collector-1 | Buckets #2, Count: 0
otel-collector-1 | Buckets #3, Count: 0
otel-collector-1 | Buckets #4, Count: 0
otel-collector-1 | Buckets #5, Count: 1
otel-collector-1 | Buckets #6, Count: 0
otel-collector-1 | Metric #2
otel-collector-1 | Descriptor:
otel-collector-1 | -> Name: events
otel-collector-1 | -> Description:
otel-collector-1 | -> Unit:
otel-collector-1 | -> DataType: Sum
otel-collector-1 | -> IsMonotonic: true
otel-collector-1 | -> AggregationTemporality: Cumulative
otel-collector-1 | {"kind": "exporter", "data_type": "metrics", "name": "logging"}
이렇게 간단하게 Opentelemetry Collector의 spanmetrics connector를 알아보았다.
여기에 이제 prometheusremotewrite를 추가로 덧붙이면 시계열저장소에 저장한 뒤 그라파나에서 대시보드 및 Alert를 할 수 있게 된다.
정보 1)
여기서 이제 prometheusremotewrite exporter를 통해 보내게되는데 불필요한 RED 메트릭들이 다수 보내질 수 있다.
따라서 중간에 processor를 두어 불필요한 metric을 안보내게 할 수도 있음을 참고하자.
Ex. span.kind=server 인 메트릭만 보내기, ..
정보 2)
exemplars를 사용하려면 prometheus 호환 저장소에서도 exemplars 사용하겠다는 설정을 해줘야 한다.
ex. --enable-feature=exemplar-storage
'Log,Monitorings' 카테고리의 다른 글
[Opentelemetry Collector] RED Metric 생성과 샘플링 정책 완벽히 구성하기 (10) | 2024.03.19 |
---|---|
[Log] Grafana Loki 로그 Write 시 참고 사항 (3) | 2024.02.17 |
[Monitoring] Grafana Tempo 알아보기 (3) | 2023.11.23 |
[EKS] Data Transfer 비용 절감 작업 (istio destination rule, topology aware hints) (2) | 2023.09.14 |
[Loki] Python으로 Loki에 로그 보내는 방법 (0) | 2023.08.26 |