OpenTelemetry Node.js¶
OpenTelemetry Node.js 자동 계측 모듈은 시작 매개변수를 통해 사전 로드되어 애플리케이션 의존성이 로드되기 전에 Hook을 등록합니다. 비즈니스 코드를 수정할 필요 없이 지원되는 웹 프레임워크, HTTP 클라이언트, 데이터베이스 및 메시지 큐 호출을 수집할 수 있습니다.
표준 OpenTelemetry 기능 외에도 Guance는 Node.js에 Profile 확장 기능을 제공합니다. 이 기능은 @cloudcare/profiler-nodejs 및 @datadog/pprof를 기반으로 하며, wall, heap profile을 수집하고 DataKit의 /profiling/v1/input 엔드포인트를 통해 전송합니다. Profile 확장은 OpenTelemetry 공식 표준 기능이 아니므로, 이 문서에서는 코드 없는 방식 외의 추가 방안으로 소개합니다.
이 문서에서는 DataKit의 OpenTelemetry 수집기를 사용하여 OTLP 데이터를 수신하고 Guance로 전달합니다:
전제 조건¶
- OpenTelemetry JavaScript에서 현재 지원하는 Node.js LTS 버전 사용
- 애플리케이션이 npm, pnpm 또는 Yarn을 사용하여 의존성 관리
- DataKit이 설치되어 있고 대상 Guance 워크스페이스에 연결되어 있음
- Node.js 애플리케이션에서 DataKit으로의 네트워크 연결 가능: OTLP/HTTP는 DataKit HTTP 포트
9529사용, OTLP/gRPC는 기본적으로4317사용
버전 및 확장 지원¶
- Node.js: 자동 계측 패키지는 일반적으로
^18.19.0또는>=20.6.0을 요구하며, 프로덕션 환경에서는 현재 지원되는 LTS 버전을 사용하고 실제 설치된 패키지의engines선언을 기준으로 합니다. - OpenTelemetry: 현재 안정 버전을 사용하고 lockfile을 통해 실제 배포 버전을 고정하는 것이 좋습니다.
- Profile 확장 패키지:
@cloudcare/profiler-nodejs - 기본 Profiling 전송 주소:
http://127.0.0.1:9529/profiling/v1/input
1. OpenTelemetry 수집기 활성화¶
DataKit 설치 디렉터리의 conf.d/opentelemetry로 이동합니다. 수집기 설정 파일이 아직 없으면 예제 파일을 복사합니다:
opentelemetry.conf에 최소한 다음 수신 설정이 포함되어 있는지 확인합니다:
[[inputs.opentelemetry]]
# Guance에서 태그로 유지할 사용자 정의 속성을 화이트리스트에 추가합니다.
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 + Protobuf | Trace | http://<DataKit-IP>:9529/otel/v1/traces |
| OTLP/HTTP + Protobuf | Metric | http://<DataKit-IP>:9529/otel/v1/metrics |
| OTLP/HTTP + Protobuf | Log | http://<DataKit-IP>:9529/otel/v1/logs |
| OTLP/gRPC | Trace, Metric, Log | http://<DataKit-IP>:4317 |
Node.js 애플리케이션과 DataKit가 동일한 호스트에 있지 않은 경우, 실제 배포 환경에 맞게 DataKit 리스닝 주소, 방화벽 또는 기타 네트워크 액세스 제어를 조정해야 합니다. gRPC의 경우 addr을 애플리케이션에서 액세스 가능한 리스닝 주소(예: 0.0.0.0:4317)로 변경할 수 있습니다. OTLP 수신 포트를 공용 네트워크에 직접 노출하지 마십시오.
DataKit를 재시작하고 서비스를 확인합니다:
2. 애플리케이션에 OpenTelemetry 연결¶
자동 계측 모듈 설치¶
애플리케이션 프로젝트 디렉터리에서 npm 공식 저장소를 통해 OpenTelemetry 공식 패키지를 설치합니다:
@opentelemetry/auto-instrumentations-node에는 Node.js SDK, 자동 계측 라이브러리 및 일반적인 exporter가 포함되어 있습니다. 이 패키지는 지원되고 실제로 로드된 라이브러리에 대해서만 텔레메트리 데이터를 생성합니다. 애플리케이션에서 사용하는 프레임워크나 클라이언트가 지원 목록에 없으면 해당 Span이 자동으로 생성되지 않습니다.
환경 변수를 통한 시작¶
다음은 OTLP/HTTP + Protobuf를 예로 들어, 기본적으로 Trace는 활성화하고 Metric과 Log는 비활성화합니다:
export OTEL_SERVICE_NAME="order-service"
export OTEL_RESOURCE_ATTRIBUTES="env=prod,version=1.0.0,team=backend"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="none"
export OTEL_LOGS_EXPORTER="none"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://127.0.0.1:9529/otel"
export OTEL_PROPAGATORS="tracecontext,baggage"
export NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
node app.js
OTEL_EXPORTER_OTLP_ENDPOINT는 기본 주소입니다. OTLP/HTTP exporter는 데이터 유형에 따라 자동으로 /v1/traces, /v1/metrics 또는 /v1/logs를 추가하여 최종적으로 DataKit의 /otel/v1/* 경로에 매핑됩니다.
애플리케이션에 이미 NODE_OPTIONS가 설정되어 있는 경우, 기존 매개변수를 유지하면서 --require @opentelemetry/auto-instrumentations-node/register를 추가하고 기존 값을 덮어쓰지 않도록 주의하십시오. PM2, systemd, Supervisor 또는 npm scripts 시나리오에서는 OTEL_* 및 NODE_OPTIONS를 실제 애플리케이션 프로세스의 시작 환경에 작성해야 합니다.
시작 매개변수를 통한 로드¶
NODE_OPTIONS를 설정하지 않고 Node.js 시작 명령에서 직접 모듈을 사전 로드할 수도 있습니다:
자동 계측 모듈은 애플리케이션 코드 및 해당 의존성이 로드되기 전에 실행되어야 합니다. node app.js 시작 후에 동적으로 모듈을 로드하지 마십시오. 그렇지 않으면 이미 로드된 라이브러리가 계측되지 않을 수 있습니다.
일반적인 시작 방법¶
npm script를 통해 시작:
PM2를 통해 시작하는 경우, ecosystem.config.js의 env 설정에 변수를 넣거나 프로세스 관리 플랫폼의 환경 변수 기능을 통해 주입할 수 있습니다. 설정을 변경한 후에는 애플리케이션 프로세스를 재시작해야 합니다. 핫 리로드만으로는 사전 로드 모듈이 다시 로드되지 않을 수 있습니다.
OTLP/gRPC 사용¶
OTLP/gRPC로 변경하려면 프로토콜과 endpoint만 변경하면 됩니다:
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://127.0.0.1:4317"
gRPC endpoint에는 /v1/traces와 같은 HTTP 경로를 추가할 수 없습니다.
3. 데이터 전송 매개변수¶
기본 매개변수¶
| 환경 변수 | 설명 | 권장값 또는 예시 |
|---|---|---|
OTEL_SERVICE_NAME |
service.name을 설정합니다. 설정하지 않으면 서비스를 안정적으로 식별할 수 없습니다. |
order-service, 프로덕션 환경에서는 반드시 명시적으로 설정해야 합니다. |
OTEL_RESOURCE_ATTRIBUTES |
리소스 속성, 쉼표로 구분된 key=value 형식입니다. |
env=prod,version=1.0.0,team=backend |
OTEL_TRACES_EXPORTER |
Trace 내보내기 도구입니다. | DataKit로 전송할 때는 otlp로 설정하고, 비활성화할 때는 none으로 설정합니다. |
OTEL_METRICS_EXPORTER |
Metric 내보내기 도구입니다. | 메트릭을 전송해야 하는 경우 otlp로 설정하고, 그렇지 않으면 none으로 설정합니다. |
OTEL_LOGS_EXPORTER |
Log 내보내기 도구입니다. | 로그를 전송해야 하는 경우 otlp로 설정하고, 그렇지 않으면 none으로 설정합니다. |
OTEL_PROPAGATORS |
서비스 간 컨텍스트 전파 형식입니다. | 기본값 tracecontext,baggage; 전체 링크에서 호환성을 유지해야 합니다. |
OTEL_SDK_DISABLED |
OpenTelemetry SDK를 비활성화합니다. | 기본값 false; 긴급하게 비활성화해야 하는 경우 true로 설정합니다. |
service.name은 Guance에서 서비스 소속을 식별하는 데 사용됩니다. env와 version을 함께 설정하여 환경 및 버전별로 필터링하는 것이 좋습니다. 기타 사용자 정의 리소스 속성은 DataKit customer_tags 화이트리스트에 추가된 후에만 태그로 유지됩니다. 속성 이름의 .은 _로 변환됩니다.
Node.js 자동 계측 매개변수¶
| 환경 변수 | 설명 | 기본값 또는 예시 |
|---|---|---|
NODE_OPTIONS |
애플리케이션 의존성 로드 전에 자동 계측 등록 모듈을 사전 로드합니다. | --require @opentelemetry/auto-instrumentations-node/register |
OTEL_NODE_RESOURCE_DETECTORS |
특정 리소스 탐지기를 활성화합니다. 여러 개는 쉼표로 구분합니다. | 기본값 all; env,host,os,process 또는 none으로 설정 가능합니다. |
OTEL_NODE_ENABLED_INSTRUMENTATIONS |
나열된 계측만 활성화합니다. 이름에 @opentelemetry/instrumentation- 접두사를 포함하지 않습니다. |
http,express,pg |
OTEL_NODE_DISABLED_INSTRUMENTATIONS |
기본 목록에서 특정 계측을 비활성화합니다. | fs,grpc |
OTEL_LOG_LEVEL |
OpenTelemetry 내부 진단 로그 레벨입니다. | 프로덕션 환경에서는 info 권장; 문제 해결 시에만 일시적으로 debug 사용합니다. |
활성화 및 비활성화 목록을 동시에 설정하는 경우, 먼저 OTEL_NODE_ENABLED_INSTRUMENTATIONS가 적용된 후 비활성화 목록이 적용됩니다. 동일한 계측이 두 목록에 모두 있는 경우 최종적으로 비활성화됩니다. 복잡한 단일 계측 설정은 환경 변수만으로 완전히 구성할 수 없으며, 이 문서의 코드 없는 연결 범위를 벗어납니다.
OTLP 매개변수¶
| 환경 변수 | 설명 | 권장값 또는 예시 |
|---|---|---|
OTEL_EXPORTER_OTLP_PROTOCOL |
모든 신호의 OTLP 프로토콜입니다. | DataKit는 http/protobuf 또는 grpc를 지원합니다. |
OTEL_EXPORTER_OTLP_ENDPOINT |
모든 신호에 공통으로 사용되는 기본 주소입니다. | HTTP: http://datakit-host:9529/otel; gRPC: http://datakit-host:4317. |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT |
Trace 전용 주소로, 공통 주소보다 우선합니다. | http://datakit-host:9529/otel/v1/traces |
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT |
Metric 전용 주소로, 공통 주소보다 우선합니다. | http://datakit-host:9529/otel/v1/metrics |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT |
Log 전용 주소로, 공통 주소보다 우선합니다. | http://datakit-host:9529/otel/v1/logs |
OTEL_EXPORTER_OTLP_HEADERS |
모든 OTLP 요청에 포함되는 요청 헤더입니다. 여러 값은 쉼표로 구분합니다. | x-tenant=tenant-a; DataKit expected_headers와 일치해야 합니다. |
OTEL_EXPORTER_OTLP_COMPRESSION |
OTLP 요청 압축 방식입니다. | 호스트 간 전송 시 gzip으로 설정할 수 있습니다. |
OTEL_EXPORTER_OTLP_TIMEOUT |
단일 내보내기 타임아웃(밀리초)입니다. | 10000 |
DataKit의 OTLP/HTTP 수집은 Protobuf만 지원합니다. http/protobuf를 사용하고 http/json은 사용하지 마십시오. 공통 endpoint와 특정 데이터 유형의 endpoint가 동시에 존재하는 경우, 특정 데이터 유형의 설정이 우선합니다.
샘플링 및 배치 전송 매개변수¶
| 환경 변수 | 설명 | 기본값 또는 예시 |
|---|---|---|
OTEL_TRACES_SAMPLER |
Trace 헤더 샘플러입니다. | 기본값 parentbased_always_on; 비율 샘플링은 parentbased_traceidratio를 사용합니다. |
OTEL_TRACES_SAMPLER_ARG |
샘플러 매개변수입니다. | 0.1은 루트 Trace의 10%를 샘플링함을 의미합니다. |
OTEL_BSP_SCHEDULE_DELAY |
Span 배치 내보내기 간격(밀리초)입니다. | 기본값 5000. |
OTEL_BSP_MAX_QUEUE_SIZE |
내보내기 대기 중인 Span 큐의 최대 크기입니다. | 기본값 2048. |
OTEL_BSP_MAX_EXPORT_BATCH_SIZE |
배치당 최대 내보내기 Span 수입니다. | 기본값 512. |
OTEL_BSP_EXPORT_TIMEOUT |
Span 배치 내보내기 타임아웃(밀리초)입니다. | 기본값 30000. |
OTEL_METRIC_EXPORT_INTERVAL |
Metric 내보내기 간격(밀리초)입니다. | 기본값 60000. |
프로덕션 환경에서는 트래픽과 데이터 예산에 따라 샘플링 비율을 설정해야 합니다. 애플리케이션 측 헤더 샘플링과 DataKit 측 샘플링을 동시에 활성화하면 최종 유지율이 중첩되어 낮아지므로, 샘플링 위치를 통일하여 계획해야 합니다.
추가: 코드 방식 연결¶
이 문서에서는 앞서 설명한 코드 없는 방식을 우선 권장합니다. Resource, Exporter 또는 개별 계측을 정밀하게 제어해야 하는 경우 기존 SDK 코드 방식도 사용할 수 있습니다.
의존성 설치¶
npm install --save \
@opentelemetry/api \
@opentelemetry/sdk-node \
@opentelemetry/resources \
@opentelemetry/exporter-trace-otlp-proto \
@opentelemetry/instrumentation-http
예제 코드¶
OpenTelemetry는 비즈니스 모듈이 로드되기 전에 초기화되어야 합니다:
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { resourceFromAttributes } = require('@opentelemetry/resources');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const {
OTLPTraceExporter,
} = require('@opentelemetry/exporter-trace-otlp-proto');
const resource = resourceFromAttributes({
'service.name': 'orders-api',
'service.version': '1.0.0',
'deployment.environment.name': 'prod',
});
const sdk = new NodeSDK({
resource,
traceExporter: new OTLPTraceExporter({
url: 'http://127.0.0.1:9529/otel/v1/traces',
}),
instrumentations: [new HttpInstrumentation()],
});
async function main() {
await sdk.start();
require('./app');
}
process.once('SIGTERM', async () => {
await sdk.shutdown();
process.exit(0);
});
main().catch(error => {
console.error(error);
process.exit(1);
});
코드 방식과 NODE_OPTIONS=--require .../register를 동시에 사용하여 두 개의 SDK를 중복 초기화하지 않도록 주의하십시오. 코드 방식을 선택한 경우, 코드 없는 방식의 등록 모듈 사전 로드 매개변수를 제거해야 합니다.
추가: Node.js Profile 확장¶
Node.js Profile은 표준 OpenTelemetry Trace 및 Metric 외에 성능 프로파일링 데이터를 보완하는 데 사용됩니다. 현재 지원되는 profile 유형:
wall;heap.
실제 연결 시에는 먼저 wall을 활성화하여 수집 오버헤드와 전송 링크의 안정성을 확인한 후 필요에 따라 heap을 활성화하는 것이 좋습니다.
Profile 확장 설치¶
최소 연결 예제¶
const { resourceFromAttributes } = require('@opentelemetry/resources');
const {
DatakitProfilingExporter,
NodeProfiling,
} = require('@cloudcare/profiler-nodejs');
const profiler = new NodeProfiling({
resource: resourceFromAttributes({
'service.name': 'orders-api',
'service.version': '1.2.3',
'deployment.environment.name': 'prod',
}),
exporter: new DatakitProfilingExporter({
endpoint: 'http://127.0.0.1:9529/profiling/v1/input',
}),
profileTypes: ['wall'],
cpuProfilingEnabled: true,
});
async function main() {
await profiler.start();
require('./app');
}
process.once('SIGTERM', async () => {
await profiler.shutdown();
process.exit(0);
});
main().catch(error => {
console.error(error);
process.exit(1);
});
OpenTelemetry SDK와 함께 사용¶
애플리케이션이 이전 섹션의 SDK 코드 방식을 사용하는 경우, 동일한 Resource를 재사용할 수 있습니다:
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { resourceFromAttributes } = require('@opentelemetry/resources');
const {
DatakitProfilingExporter,
NodeProfiling,
} = require('@cloudcare/profiler-nodejs');
const resource = resourceFromAttributes({
'service.name': 'orders-api',
'service.version': '1.2.3',
'deployment.environment.name': 'prod',
});
const sdk = new NodeSDK({ resource });
const profiling = new NodeProfiling({
resource,
exporter: new DatakitProfilingExporter({
endpoint: 'http://127.0.0.1:9529/profiling/v1/input',
}),
});
async function main() {
await sdk.start();
await profiling.start();
require('./app');
}
process.once('SIGTERM', async () => {
await profiling.shutdown();
await sdk.shutdown();
process.exit(0);
});
main().catch(error => {
console.error(error);
process.exit(1);
});
링크를 확인할 때 수동으로 한 번 수집을 트리거할 수 있습니다:
Profile 권장 설정¶
| 매개변수 | 기본값 | 설명 |
|---|---|---|
endpoint |
http://127.0.0.1:9529/profiling/v1/input |
Profile 업로드 주소입니다. |
profileTypes |
['wall', 'heap'] |
수집 유형입니다. 먼저 ['wall']을 사용하는 것이 좋습니다. |
intervalMillis |
60000 |
주기적 수집 간격입니다. |
wallDurationMillis |
10000 |
단일 wall profile 지속 시간입니다. |
기본적으로 확장은 60초마다 한 번씩 수집을 수행하며, wall profile은 10초 동안 지속되고 데이터를 /profiling/v1/input으로 전송합니다. 현재 exporter는 wall.pprof, space.pprof 및 이번 수집을 설명하는 event.json을 전송합니다. 여기서 profiler는 ddtrace, family는 nodejs, format은 pprof이며, Guance의 Node.js Profile 구문 분석 링크와의 호환성을 위해 사용됩니다.
실행 환경에 globalThis.fetch가 없는 경우 fetch 구현을 명시적으로 제공해야 합니다. 프로세스 종료 전에 profile이 손실되는 것을 방지하려면 종료 신호 처리기에서 shutdown()을 호출해야 합니다. Trace와 Metric만 필요한 경우 Profile 확장을 설치할 필요가 없습니다.
연결 확인¶
지원되는 계측 라이브러리를 통해 처리된 애플리케이션 경로를 요청한 후, DataKit 호스트에서 수신 로그를 확인합니다:
/otel/v1/traces에 대한 POST 요청이 나타나고 응답 코드가 200이면 DataKit이 Trace를 수신한 것입니다. 그런 다음 Guance의 애플리케이션 성능 모니터링(APM) > 분산 추적에서 service:order-service로 조회합니다.
데이터가 없는 경우, 자동 계측 패키지가 애플리케이션 프로젝트에 설치되었는지, NODE_OPTIONS가 실제 Node.js 프로세스에 전달되었는지, 등록 모듈이 애플리케이션 의존성보다 먼저 로드되었는지, 코드 경로가 지원되는 계측 라이브러리에 해당하는지, OTLP endpoint에 연결할 수 있는지 순서대로 확인합니다. 문제 해결 시 일시적으로 OTEL_LOG_LEVEL=debug로 설정할 수 있으며, 확인이 완료되면 info로 복원합니다.
Profile 인터페이스 연결 가능성을 확인하려면 다음을 실행합니다:
Profile 전송이 성공하면 확장 디버그 로그에 일반적으로 Datakit profiling export succeeded가 나타납니다. 그런 다음 Guance에서 서비스별로 Node.js Profile 데이터를 확인할 수 있습니다.