OpenTelemetry Go SDK¶
OpenTelemetry Go SDK는 API, SDK 및 프레임워크 계측 라이브러리를 통해 Go 애플리케이션에 대한 원격 측정 데이터를 수집합니다. Java Agent와 같은 런타임 자동 계측 솔루션과 달리 Go 애플리케이션은 일반적으로 코드에서 SDK를 초기화하고 웹 프레임워크, HTTP 클라이언트, 데이터베이스 또는 메시지 대기열을 해당 계측 라이브러리로 래핑해야 합니다.
이 문서에서는 DataKit의 OpenTelemetry 수집기를 사용하여 OTLP 데이터를 수신하고 Guance로 전달합니다.
이 문서의 예제는 OTLP/HTTP + Protobuf를 통해 Trace와 Metric을 보고합니다. OpenTelemetry Go의 Trace와 Metric은 안정화되었지만, Log SDK의 성숙도와 생태계 지원 수준은 계속 바뀔 수 있으므로 프로덕션 환경에 적용하기 전에 최신 공식 상태를 확인해야 합니다.
전제 조건¶
- Go 1.23 이상;
- DataKit이 설치되고 DataKit이 대상 Guance 작업 공간에 연결됩니다.
- DataKit에 대한 Go 애플리케이션의 네트워크 연결성: OTLP/HTTP는 DataKit HTTP 포트 '9529'를 사용하고, OTLP/gRPC는 기본적으로 '4317'을 사용합니다.
- 애플리케이션에서 사용하는 프레임워크나 구성요소에 해당 OpenTelemetry Go 계측 라이브러리 가 있는 것으로 확인되었거나, OpenTelemetry API를 통해 Span 및 Metric을 수동으로 생성할 계획입니다.
1. OpenTelemetry 수집기 활성화¶
DataKit 설치 디렉터리에 'conf.d/opentelemetry'를 입력하세요. 수집기 구성이 아직 생성되지 않은 경우 샘플 파일을 복사합니다.
opentelemetry.conf에 최소한 다음 수신 구성이 포함되어 있는지 확인하세요.
[[inputs.opentelemetry]]
# Guance에서 사용자 정의 속성을 태그로 유지하려면 이 화이트리스트에 추가하세요.
# 속성 이름의 점은 밑줄로 변환됩니다. 예: team.name -> team_name.
customer_tags = ["team", "project"]
[inputs.opentelemetry.http]
http_status_ok = 200
trace_api = "/otel/v1/traces"
metric_api = "/otel/v1/metrics"
logs_api = "/otel/v1/logs"
[inputs.opentelemetry.grpc]
addr = "127.0.0.1:4317"
max_payload = 16777216
위 구성을 사용하면 다음 수신 주소가 활성화됩니다.
| 프로토콜 | 데이터 유형 | DataKit 수신 주소 |
|---|---|---|
| OTLP/HTTP + 프로토부프 | 추적 | http://<DataKit-IP>:9529/otel/v1/traces |
| OTLP/HTTP + 프로토부프 | 미터법 | http://<DataKit-IP>:9529/otel/v1/metrics |
| OTLP/HTTP + 프로토부프 | 로그 | http://<DataKit-IP>:9529/otel/v1/logs |
| OTLP/gRPC | 추적, 측정항목, 로그 | http://<DataKit-IP>:4317 |
애플리케이션과 DataKit이 동일한 호스트에 있지 않으면 DataKit HTTP 수신 주소, 방화벽 또는 기타 네트워크 액세스 제어를 실제 배포에 따라 조정해야 합니다. OTLP/gRPC를 사용하는 경우 addr을 0.0.0.0:4317과 같이 애플리케이션이 액세스할 수 있는 수신 주소로 변경해야 합니다. OTLP 수신 포트를 공용 네트워크에 직접 노출하지 마십시오.
구성을 적용하려면 DataKit을 다시 시작하십시오.
DataKit HTTP 서비스에 연결할 수 있는지 확인하세요.
2. OpenTelemetry {#instrument-application}에 대한 애플리케이션 액세스¶
Go SDK 및 계측 라이브러리 {#install-sdk} 설치¶
Go 프로젝트 디렉터리에 공식 SDK, OTLP/HTTP 내보내기 및 net/http 계측 라이브러리를 설치합니다.
go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/sdk
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
go get go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp
go get go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
go mod tidy
go.mod 및 go.sum은 의존성 버전을 기록합니다. 프로덕션 환경에서는 OpenTelemetry 의존성을 업그레이드한 뒤 이 두 파일을 커밋하고 컴파일, 단위 테스트, 트레이스 회귀 테스트를 수행해야 합니다.
SDK {#initialize-sdk} 초기화¶
다음 예제는 두 가지를 모두 완료합니다.
OTEL_SERVICE_NAME및OTEL_RESOURCE_ATTRIBUTES에서 리소스 속성을 읽습니다.- OTLP/HTTP 추적 내보내기 및 메트릭 내보내기를 생성합니다.
- 전역
TracerProvider,MeterProvider및 W3C 컨텍스트 전파자를 등록합니다. otelhttp로 HTTP 핸들러를 감싸고 비즈니스 child Span 및 사용자 정의 Counter를 생성합니다.- 버퍼의 데이터 손실을 방지하려면 프로세스가 종료될 때 공급자를 새로 고치고 닫습니다.
package main
import (
"context"
"errors"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func setupOTelSDK(ctx context.Context) (func(context.Context) error, error) {
res, err := resource.New(
ctx,
resource.WithFromEnv(),
resource.WithTelemetrySDK(),
resource.WithHost(),
resource.WithOS(),
resource.WithProcess(),
)
if err != nil {
return nil, fmt.Errorf("create resource: %w", err)
}
traceExporter, err := otlptracehttp.New(ctx)
if err != nil {
return nil, fmt.Errorf("create trace exporter: %w", err)
}
tracerProvider := sdktrace.NewTracerProvider(
sdktrace.WithResource(res),
sdktrace.WithSampler(
sdktrace.ParentBased(sdktrace.TraceIDRatioBased(1.0)),
),
sdktrace.WithBatcher(traceExporter),
)
metricExporter, err := otlpmetrichttp.New(ctx)
if err != nil {
_ = tracerProvider.Shutdown(ctx)
return nil, fmt.Errorf("create metric exporter: %w", err)
}
meterProvider := sdkmetric.NewMeterProvider(
sdkmetric.WithResource(res),
sdkmetric.WithReader(
sdkmetric.NewPeriodicReader(
metricExporter,
sdkmetric.WithInterval(30*time.Second),
),
),
)
otel.SetTracerProvider(tracerProvider)
otel.SetMeterProvider(meterProvider)
otel.SetTextMapPropagator(
propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
),
)
shutdown := func(ctx context.Context) error {
return errors.Join(
meterProvider.Shutdown(ctx),
tracerProvider.Shutdown(ctx),
)
}
return shutdown, nil
}
func main() {
ctx, stop := signal.NotifyContext(
context.Background(),
os.Interrupt,
syscall.SIGTERM,
)
defer stop()
shutdown, err := setupOTelSDK(ctx)
if err != nil {
log.Fatal(err)
}
defer func() {
shutdownCtx, cancel := context.WithTimeout(
context.Background(),
5*time.Second,
)
defer cancel()
if err := shutdown(shutdownCtx); err != nil {
log.Printf("shutdown OpenTelemetry: %v", err)
}
}()
meter := otel.Meter("example/order-service")
requestCounter, err := meter.Int64Counter("app.request.count")
if err != nil {
log.Fatal(err)
}
mux := http.NewServeMux()
mux.Handle("/hello", otelhttp.NewHandler(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestCtx, span := otel.Tracer("example/order-service").Start(
r.Context(),
"prepare-response",
)
defer span.End()
span.SetAttributes(attribute.String("app.route", "/hello"))
requestCounter.Add(requestCtx, 1)
_, _ = fmt.Fprintln(w, "hello from OpenTelemetry Go SDK")
}),
"GET /hello",
))
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
}
go func() {
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(
context.Background(),
5*time.Second,
)
defer cancel()
if err := server.Shutdown(shutdownCtx); err != nil {
log.Printf("shutdown HTTP server: %v", err)
}
}()
log.Println("listening on http://127.0.0.1:8080")
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
log.Fatal(err)
}
}
실제 프로젝트에서는 사용되는 구성 요소에 해당하는 계측 라이브러리를 설치해야 합니다. 예를 들어, 표준 라이브러리 net/http는 otelhttp를 사용합니다. 다른 웹 프레임워크, 데이터베이스 또는 메시지 대기열은 OpenTelemetry Registry에서 일치하는 패키지를 선택하고 해당 패키지의 지침에 따라 핸들러, 전송, 클라이언트 또는 드라이버를 래핑해야 합니다.
보고된 주소를 구성하고 {#configure-and-run}를 시작합니다.¶
다음으로 OTLP/HTTP + Protobuf를 사용하여 로컬 DataKit에 보고합니다. OTEL_EXPORTER_OTLP_ENDPOINT는 기본 주소입니다. Trace 및 Metric 내보내기는 각각 /v1/traces 및 /v1/metrics를 추가하며 이는 궁극적으로 DataKit의 /otel/v1/* 경로에 해당합니다.
export OTEL_SERVICE_NAME="order-service"
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment.name=prod,service.version=1.0.0,team=backend"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://127.0.0.1:9529/otel"
export OTEL_EXPORTER_OTLP_INSECURE="true"
export OTEL_EXPORTER_OTLP_COMPRESSION="gzip"
go run .
추적 및 측정항목 생성을 요청합니다.
메트릭은 기본적으로 30초마다 내보내지며, 이 예제에서는 sdkmetric.WithInterval(30*time.Second)로 제어합니다. 한 번의 내보내기 주기를 기다린 뒤 Guance에서 service=order-service의 트레이스를 확인하고 app.request.count 메트릭을 조회할 수 있습니다.
OTLP/gRPC {#use-grpc} 사용¶
Go SDK의 OTLP 전송은 코드에 사용된 내보내기 패키지에 의해 결정됩니다. 이 예에서는 otlptracehttp 및 otlpmetrichttp를 직접 사용합니다. OTEL_EXPORTER_OTLP_PROTOCOL=grpc 설정만으로는 gRPC로 전환되지 않습니다.
OTLP/gRPC를 사용하려면 gRPC 내보내기를 설치하세요.
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
go get go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc
go mod tidy
그런 다음 코드의 내보내기 패키지 및 초기화 함수를 다음으로 바꿉니다.
그리고 /v1/traces, /v1/metrics 경로 없이 gRPC 주소를 사용하세요.
export OTEL_EXPORTER_OTLP_ENDPOINT="http://127.0.0.1:4317"
export OTEL_EXPORTER_OTLP_INSECURE="true"
3. 데이터 보고 매개변수¶
리소스 매개변수¶
이 예에서는 resource.WithFromEnv()를 통해 다음 표준 환경 변수를 읽습니다.
| 환경 변수 | 설명 | 제안된 값 또는 예 |
|---|---|---|
OTEL_SERVICE_NAME |
리소스 속성 service.name에 해당하는 서비스 이름입니다. |
주문 서비스; 프로덕션 환경에서는 명시적으로 설정해야 합니다. |
OTEL_RESOURCE_ATTRIBUTES |
쉼표로 구분된 '키=값' 형식의 리소스 속성입니다. | deployment.environment.name=prod,service.version=1.0.0,team=backend |
Guance에서는 서비스 속성, 환경 필터링, 버전 분석을 위해 최소한 service.name, deployment.environment.name, service.version을 설정하는 것이 좋습니다. 사용자 정의 리소스 속성을 태그로 유지하려면 먼저 DataKit customer_tags 화이트리스트에 추가해야 합니다. 속성 이름의 .는 _로 변환됩니다.
OTLP/HTTP 매개변수¶
otlptracehttp.New() 및 otlpmetrichttp.New()는 다음 환경 변수를 직접 읽습니다. 신호별 매개변수는 일반 매개변수보다 우선합니다.
| 일반 환경 변수 | 신호별 환경 변수 | 설명 | DataKit 예제 |
|---|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT |
일반 매개변수는 기본 URL이며, 내보내기는 자동으로 신호 경로를 추가합니다. 신호별 매개변수는 원래 값에 따라 사용되는 전체 URL입니다. | 유니버설: http://127.0.0.1:9529/otel;Trace:http://127.0.0.1:9529/otel/v1/traces;Metric:http://127.0.0.1:9529/otel/v1/metrics |
OTEL_EXPORTER_OTLP_INSECURE |
OTEL_EXPORTER_OTLP_TRACES_INSECURE, OTEL_EXPORTER_OTLP_METRICS_INSECURE |
전송 계층 TLS를 끌지 여부입니다. | DataKit이 일반 텍스트 HTTP를 사용하는 경우 true로 설정합니다. |
OTEL_EXPORTER_OTLP_HEADERS |
OTEL_EXPORTER_OTLP_TRACES_HEADERS, OTEL_EXPORTER_OTLP_METRICS_HEADERS |
쉼표로 구분된 key=value 형식의 요청 헤더입니다. |
DataKit에서 expected_headers를 구성할 때 해당 값을 설정합니다. |
OTEL_EXPORTER_OTLP_TIMEOUT |
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT |
단일 내보내기 시간 초과, 값은 밀리초입니다. | 10000 등 네트워크 상황에 따라 설정하세요. |
OTEL_EXPORTER_OTLP_COMPRESSION |
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION, OTEL_EXPORTER_OTLP_METRICS_COMPRESSION |
OTLP 요청 압축 방법. | gzip으로 설정할 수 있습니다. 압축하지 않으려면 비워두세요. |
OTEL_EXPORTER_OTLP_CERTIFICATE |
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE |
서버 인증서를 확인하는 데 사용되는 PEM CA 파일의 경로입니다. | HTTPS를 통해 OTLP를 수신할 때 인증서별로 설정을 배포합니다. |
앱과 DataKit이 동일한 호스트에 있지 않으면 예제의 127.0.0.1을 앱이 액세스할 수 있는 DataKit 주소로 바꿔야 합니다.
SDK 코드 매개변수¶
다음 매개변수는 Go SDK 초기화 코드에 의해 제어되며 동일한 이름의 공통 환경 변수를 설정해도 자동으로 적용되지 않습니다.
| 구성 항목 | 샘플 코드 | 설명 |
|---|---|---|
| 추적 샘플링 | sdktrace.ParentBased(sdktrace.TraceIDRatioBased(1.0)) |
'1.0'은 루트 추적 전체 샘플링을 의미합니다. 생산 환경은 용량에 따라 '0.1'과 같은 비율로 조정될 수 있으며, 'ParentBased'를 통해 업스트림 샘플링 결정을 따를 수 있습니다. |
| 스팬 일괄 내보내기 | sdktrace.WithBatcher(traceExporter) |
프로덕션 환경에서는 일괄 내보내기가 권장됩니다. WithMaxQueueSize, WithMaxExportBatchSize, WithBatchTimeout 및 WithExportTimeout을 통해 추가로 조정할 수 있습니다. |
| 측정항목 내보내기 기간 | sdkmetric.WithInterval(30*time.Second) |
주기적 내보내기 간격을 제어합니다. 너무 짧으면 애플리케이션, 네트워크 및 스토리지 오버헤드가 증가합니다. |
| 컨텍스트 전파 | TraceContext{}, 수하물{} |
W3C 'traceparent', 'tracestate' 및 'baggage'를 사용하세요. 콜 체인의 서비스는 전파 형식 호환성을 유지해야 합니다. |
| 자원탐지 | resource.WithHost(), WithOS(), WithProcess() |
호스트, 운영 체제 및 프로세스 속성을 자동으로 보완합니다. 프로세스 매개변수 및 리소스 속성의 키와 같은 민감한 정보를 저장하지 마세요. |
이 예에서는 OTLP 내보내기를 직접 생성하므로 특별한 주의가 필요합니다.- OTEL_EXPORTER_OTLP_PROTOCOL은 코드에서 이미 선택한 HTTP 또는 gRPC 내보내기를 변경하지 않습니다.
- OTEL_TRACES_EXPORTER, OTEL_METRICS_EXPORTER는 이 예에서 직접 생성된 내보내기를 닫지 않습니다.
- OpenTelemetry Go 핵심 SDK는 현재 모든 공통 SDK 환경 변수를 자동으로 적용하지 않습니다. 특히 'OTEL_SDK_DISABLED', 'OTEL_TRACES_SAMPLER' 또는 'OTEL_PROPAGATORS'가 사용자 지정 초기화 코드에 자동으로 적용된다고 가정하지 마세요.
- 표준 환경 변수를 통해 내보내기를 동적으로 선택하고 초기화해야 하는 경우 공식 Contrib의 autoexport 패키지를 평가하고 테스트 환경에서 해당 동작을 확인할 수 있습니다.
확인 및 문제 해결¶
curl http://127.0.0.1:9529/v1/ping를 실행하여 애플리케이션이 DataKit에 접근할 수 있는지 확인합니다.- 애플리케이션을 시작하고
/hello에 액세스하여 애플리케이션 로그에추적 내보내기 생성,메트릭 내보내기 생성또는 OTLP 내보내기 오류가 없는지 확인합니다. - 하나 이상의 메트릭 내보내기 주기를 기다립니다.
- Guance의 APM 서비스 목록에서
order-service의 Trace를 조회합니다. - 데이터를 쿼리할 수 없는 경우 DataKit
opentelemetry수집기 구성, DataKit에 적용된 네트워크, 보고 URL 및 DataKit 로그를 확인합니다. - 중복 Span이 발생하는 경우 동일한 Handler, Transport, Database Client 또는 Driver가 반복적으로 패키징되어 있는지 확인합니다.