auto instrumentation은 Application Code 수정 없이 Opentelemetry로 trace, metric, log 데이터를 수집할 때 사용합니다.
Application을 작성한 언어를 기반으로, 미리 만들어져있는 라이브러리를 주입하는 방식입니다.
Docker image에서 해당 라이브러리를 주입하는 방식도 존재하지만, kubernetes 환경에서는 더욱 간단하게 application pod에 initcontainer로 라이브러리를 주입하도록 할 수 있습니다.
참조 링크 :
https://opentelemetry.io/docs/k8s-operator/automatic/
저는 otlp http protocol을 이용하여 trace data를 수집하기 위한 테스트를 진행했습니다.
1. Opentelemetry-operator 배포
Auto instrumentation을 적용하기 위해 필요합니다.
operator를 설치해야 자동으로 application pod에 opentelemetry에 대한 내용이 주입되기 때문입니다.
Helm chart : https://artifacthub.io/packages/helm/opentelemetry-helm/opentelemetry-operator
Helm chart에서 Affinity 이외에 특별히 수정한 부분 없이 설치했습니다.
# otel-operator.yaml
replicaCount: 1
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: role
operator: In
values:
- grafana-mimir
CMD
helm repo add opentelemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm upgrade -i otel-operator opentelemetry/opentelemetry-operator --version=0.26.3 -f otel-operator.yaml -n otel --create-namespace
2. Opentelemetry-collector 배포
공식 문서에도 나와 있듯이, collector는 옵션입니다.
collector를 사용해서 수집하는 것이 best practice이고, 여러 application에서 수집해서 export하는 것에 대해 수정이 필요한 경우 collector만 수정하면 되기 때문에 사용했습니다.
동일한 cluster에 tempo가 설치되어 있는 경우의 otel-collector yaml입니다.
otel-operator를 설치하면서 opentelemetrycollector라는 CRD가 함께 생성되기 때문에 아래와 같이 kind가 OpenTelemetryCollector이며, 이 리소스를 생성하면 otel-operator가 자동으로 이에 맞는 deployment를 생성해 줄 것입니다.
→ metric 표출이 기본 적용이기 때문에, pipeline에서 metric에 대한 지정을 하지 않으면 metric export error 가 지속해서 발생할 수 있습니다. 만약 metric을 사용하지 않고자 한다면, 뒤 과정에서 생성, 배포하는 instrumentation에서 metric을 disable해야 합니다.
# otelcol.yaml
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
name: test-otel
namespace: otel
spec:
config: |
receivers:
otlp:
protocols:
http:
processors:
batch:
exporters:
otlphttp:
endpoint: http://tempo-release-distributor.tempo.svc.cluster.local:4318
headers: {'X-Scope-OrgId': 'test-org'}
logging:
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [logging]
telemetry:
metrics:
address: ":8888"
receivers : trace, metric, log 데이터를 받을 형식을 지정합니다.
위의 설정은 opentelemetry http 방식으로 데이터를 받기 위해 설정했습니다.
이 형식은 데이터를 collector로 보낼 application이 어떤 방식으로 데이터를 내보내는지를 확인하고 그에 맞게 설정해야 합니다.
reference : https://opentelemetry.io/docs/collector/configuration/#receivers
여기에 설정한 내용에 따라 otel collector가 구동하는 서버가 달라지고, 노출시키는 서비스 포트가 달라집니다.
여러 개를 설정 할 수도 있습니다.
processors : receiver로부터 데이터를 받고 exporter로 내보내는 과정 사이에 수행되는 작업에 대한 설정입니다.
reference : https://opentelemetry.io/docs/collector/configuration/#processors
특정 trace, metric, log를 exclude 시킬 수 있습니다.
exporters : 데이터를 내보낼 때의 설정입니다.
trace, metric, log 데이터를 받을 주체에 대한 설정을 할 수 있습니다.
데이터를 받는 쪽의 endpoint 주소나 tls 관련 설정을 주로 합니다.
위 설정은 같은 클러스터 내에 존재하는 tempo의 distributor의 endpoint와 otlp http로 데이터를 받는 포트를 함께 명시했습니다.
receiver와 마찬가지로, 데이터를 받는 주체가 지원하는 protocol과 포트를 확인해서 설정해야 합니다.
reference : https://opentelemetry.io/docs/collector/configuration/#exporters
protocol이 grpc의 경우에는 앞부분의 http 없이 선언하고, http는 http이기 때문에 앞에 http를 붙여서 선언해야 합니다.
service : 앞서 설정한 내용을 기반으로 pipeline, extension, telemetry 등을 설정합니다.
pipeline을 선언하여 어떤 데이터를 어떤 receiver, processor, exporter를 통해 전달 할 지 선언합니다.
pipeline에는 traces, metrics, logs를 지정 할 수 있고, 각각 pipeline을 설정하면 됩니다.
위의 설정은 trace 데이터는 otlp → batch → otlphttp를 통해 전달하고, metric 데이터는 otlp → batch → logging을 통해 전달하도록 했습니다.
→ Metric 데이터는 사용하지 않을 예정이지만, auto-instrumentation으로 metric 데이터도 생성되는데 선언이 없으면 metric 데이터 전송 에러 메시지가 application에 계속 쌓이기 때문에 선언했습니다.
참고로, otel-collector는 deployment 형식의 설치가 default이고, 이밖에도 statefulset, daemonset으로도 구성할 수 있습니다.
CMD
kubectl apply -f otelcol.yaml
3. Instrumentation 배포
auto instrumentation을 위해 instrumentation이 필요합니다.
otel-operator 배포시 함께 배포된 CRD를 참조하여 생성하게 됩니다.
이 리소스를 생성하면 정의에 맞게 otel-operator가 자동으로 Instrumentation이 가능하도록 app application을 수정합니다.
→ metric을 disable하려면 env에 OTEL_METRICS_EXPORTER=none 를 추가해야 합니다.
# instrumentation.yaml
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: test-instrumentation
namespace: otel
spec:
exporter:
endpoint: http://test-otel-collector.otel.svc.cluster.local:4318
propagators:
- tracecontext
- baggage
sampler:
type: parentbased_traceidratio
argument: "1"
java:
env:
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: http/protobuf
- name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL
value: http/protobuf
- name: OTEL_EXPORTER_OTLP_METRICS_PROTOCOL
value: http/protobuf
exporter : Application에서 otel-collector로 데이터를 전송해야 하므로 otel-collector의 endpoint를 명시해야 합니다.
위의 설정은 데이터를 내보낼 때 grpc가 아닌 http로 하도록 설정했기 때문에 앞에 http가 붙어있고, grpc를 사용할 때에는 붙이지 않아도 됩니다.
sampler : trace sampling을 설정하는 부분입니다. 아래 링크를 참조하여 원하는 sampling 종류, 값을 설정합니다.
env : Application에서 동작 할 opentelemetry 관련 작업에서 추가 설정 할 내용을 선언합니다.
JAVA의 경우 auto-instrumentation을 이용하여 데이터 전송 시 default로 grpc를 사용합니다. 따라서 grpc로 데이터를 전송한다면 env를 추가로 선언하지 않아도 됩니다.
저는 http protocol을 통해 데이터를 전송하는 것을 테스트해야 했으므로 위와 같이 protocol을 정의했습니다.
reference : https://opentelemetry.io/docs/instrumentation/ 에서 개발 언어 - Automatic - Configuration을 참조
https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/
CMD
kubectl apply -f instrumentation.yaml
4. Application Deployment Annotation 추가
Application pod에 auto-instrumentation을 할 것이라는 표시로 annotation을 추가해야 합니다.
# Application pod의 manifest 윗 부분
apiVersion: v1
kind: Pod
metadata:
annotations:
instrumentation.opentelemetry.io/inject-java: "otel/test-instrumentation"
위와 같이 pod에 annotation이 붙을 수 있도록 deployment의 spec.template.metadata.annotations에 annotation을 추가하는 식으로 적용 할 수 있습니다.
<namespace>/<instrumentation name>
reference : https://opentelemetry.io/docs/k8s-operator/automatic/#add-annotations-to-existing-deployments
Deployment 예시 :
apiVersion: apps/v1
kind: Deployment
metadata:
name: testtomcat
namespace: dummyapps
spec:
template:
metadata:
annotations:
instrumentation.opentelemetry.io/inject-java: otel/test-instrumentation
Test Application : tomcat 배포
저는 테스트용 Application으로 dummy tomcat을 사용했습니다.
# testtomcat.yaml
replicaCount: 1
service:
type: ClusterIP
podAnnotations:
instrumentation.opentelemetry.io/inject-java: "otel/test-instrumentation"
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: role
operator: In
values:
- grafana-mimir
helm upgrade -i testtomcat bitnami/tomcat -f testtomcat.yaml -n dummyapps --create-namespace
배포가 완료되고 pod의 yaml을 조회해보면 아래와 같이 init container가 추가되고 기존 컨테이너에 env가 추가된 것을 확인할 수 있습니다.
init container는 opentelemetry를 위한 라이브러리를 가져오는 작업을 수행합니다.
apiVersion: v1
kind: Pod
metadata:
annotations:
instrumentation.opentelemetry.io/inject-java: otel/test-instrumentation
...
name: testtomcat-578dcdf7f5-xj6vn
namespace: dummyapps
...
spec:
...
containers:
- env:
...
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: http/protobuf
- name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL
value: http/protobuf
- name: OTEL_EXPORTER_OTLP_METRICS_PROTOCOL
value: http/protobuf
- name: JAVA_TOOL_OPTIONS
value: ' -javaagent:/otel-auto-instrumentation/javaagent.jar'
- name: OTEL_SERVICE_NAME
value: testtomcat
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: <http://test-otel-collector.otel.svc.cluster.local:4318>
- name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
- name: OTEL_PROPAGATORS
value: tracecontext,baggage
- name: OTEL_TRACES_SAMPLER
value: parentbased_traceidratio
- name: OTEL_TRACES_SAMPLER_ARG
value: "1"
- name: OTEL_RESOURCE_ATTRIBUTES
value: k8s.container.name=tomcat,k8s.deployment.name=testtomcat,k8s.namespace.name=dummyapps,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),k8s.replicaset.name=testtomcat-578dcdf7f5
image: docker.io/bitnami/tomcat:10.1.8-debian-11-r7
...
name: tomcat
...
volumeMounts:
...
- mountPath: /otel-auto-instrumentation
name: opentelemetry-auto-instrumentation
...
initContainers:
- command:
- cp
- /javaagent.jar
- /otel-auto-instrumentation/javaagent.jar
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:1.23.0
imagePullPolicy: IfNotPresent
name: opentelemetry-auto-instrumentation
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /otel-auto-instrumentation
name: opentelemetry-auto-instrumentation
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-qmdcd
readOnly: true
...
volumes:
...
- emptyDir: {}
name: opentelemetry-auto-instrumentation
status:
...
'Monitoring' 카테고리의 다른 글
Opentelemetry - Collector filtering span (0) | 2023.08.28 |
---|---|
Opentelemetry - auto instrumentation with specific libraries (0) | 2023.08.28 |
Prometheus-adapter를 이용한 custom.metrics.k8s.io API 사용 (for HPA v2beta2 target) (1) | 2023.08.28 |
Loki Deployment to k8s (0) | 2023.02.14 |
Prometheus ISSUE (0) | 2023.02.14 |