[인턴] Datadog으로 서비스 상태 모니터링하고 분석하기
Datadog으로 서비스 상태 모니터링하고 분석하기
이전 글 요약
이전 글에서는 NestJS의 @Module + @Injectable 패턴으로
에러 모니터링 설정을 주입하는 구조를 만들었습니다.
MonitoringModule.forRoot({
serviceName: "api-server",
environment: process.env.NODE_ENV ?? "development",
defaultLabels: { team: "backend" },
});
이 구조 덕분에 에러를 일관된 포맷으로 모니터링 시스템에 전송할 수 있었습니다. 이번 글에서는 그 모니터링 시스템 중 하나인 Datadog을 실제로 어떻게 설정하고 분석에 활용했는지 정리합니다.
Datadog이란
Datadog은 클라우드 환경의 인프라, 애플리케이션, 로그를 통합 관리하는 관측성(Observability) 플랫폼입니다.
핵심 구성 요소는 세 가지입니다.
- Metrics: 시계열 수치 데이터 (CPU, 메모리, 요청 수, 에러율 등)
- Logs: 애플리케이션에서 출력한 로그 텍스트
- Traces: 요청이 여러 서비스를 거치는 흐름 (APM)
이 세 가지를 하나의 UI에서 연결해서 볼 수 있다는 점이 Datadog의 가장 큰 강점입니다.
Agent 설치와 기본 설정
Datadog은 서버에 Agent를 설치해서 데이터를 수집합니다.
Docker 환경
# docker-compose.yml
services:
datadog-agent:
image: gcr.io/datadoghq/agent:7
environment:
- DD_API_KEY=${DD_API_KEY}
- DD_SITE=datadoghq.com
- DD_LOGS_ENABLED=true
- DD_APM_ENABLED=true
- DD_DOGSTATSD_NON_LOCAL_TRAFFIC=true
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /proc/:/host/proc/:ro
- /sys/fs/cgroup/:/host/sys/fs/cgroup:ro
NestJS 앱에 APM 연동
// main.ts — 반드시 다른 import보다 먼저 실행
import tracer from "dd-trace";
tracer.init({
service: "api-server",
env: process.env.NODE_ENV,
version: process.env.APP_VERSION,
logInjection: true, // 로그에 trace_id 자동 주입
});
logInjection: true를 설정하면 로그에 dd.trace_id가 자동으로 포함되어,
나중에 로그와 트레이스를 연결해서 볼 수 있습니다.
Metrics 수집과 활용
기본 제공 Metrics
Agent를 설치하면 아래 Metrics는 별도 설정 없이 자동 수집됩니다.
| Metric 이름 | 설명 |
|---|---|
system.cpu.user |
CPU 사용률 |
system.mem.used |
메모리 사용량 |
nginx.net.request_per_s |
초당 요청 수 |
docker.cpu.usage |
컨테이너 CPU 사용률 |
커스텀 Metrics (DogStatsD)
애플리케이션에서 직접 수치를 Datadog으로 전송할 수 있습니다.
import StatsD from "hot-shots";
const dogstatsd = new StatsD({
host: process.env.DD_AGENT_HOST ?? "localhost",
port: 8125,
globalTags: {
env: process.env.NODE_ENV,
service: "api-server",
},
});
// 카운터: 이벤트 발생 횟수
dogstatsd.increment("order.created", 1, { paymentMethod: "card" });
// 게이지: 현재 상태 값
dogstatsd.gauge("queue.pending_jobs", pendingCount);
// 히스토그램: 분포 측정 (p50, p95, p99 자동 계산)
dogstatsd.histogram("order.processing_time_ms", elapsedMs);
히스토그램은 평균값 대신 백분위수(percentile) 기준으로 분석할 수 있어, 일부 느린 요청이 평균을 왜곡하는 문제를 피할 수 있습니다.
Logs 분석
로그 수집 설정
# /etc/datadog-agent/conf.d/app.d/conf.yaml
logs:
- type: docker
service: api-server
source: nodejs
tags:
- env:production
구조화 로그 출력
Datadog에서 로그를 필터링하려면 JSON 포맷으로 출력하는 것이 효과적입니다.
import winston from "winston";
const logger = winston.createLogger({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json(), // JSON 포맷으로 출력
),
transports: [new winston.transports.Console()],
});
// 출력 예시
// {"level":"error","message":"DB connection failed","orderId":"abc123","timestamp":"2026-03-17T10:00:00.000Z","dd.trace_id":"12345"}
logger.error("DB connection failed", { orderId: "abc123" });
Log Explorer에서 분석하기
Datadog의 Log Explorer에서 쿼리 문법으로 로그를 검색할 수 있습니다.
# 에러 레벨 로그 중 특정 서비스 필터링
service:api-server status:error
# 특정 필드 값으로 필터링
service:api-server @orderId:abc123
# 최근 1시간 내 에러 로그 수 집계
service:api-server status:error | stats count by @http.status_code
@ 접두사는 JSON 로그의 커스텀 필드를 의미합니다.
status, service 등 기본 필드는 @ 없이 사용합니다.
APM (Application Performance Monitoring)
APM은 요청이 여러 서비스와 함수를 거치는 전체 흐름(Trace) 을 추적합니다.
자동 계측
dd-trace는 Express, NestJS, Prisma, Redis 등 주요 라이브러리를 자동으로 계측합니다.
별도 코드 없이 HTTP 요청, DB 쿼리, 캐시 호출이 모두 트레이스로 기록됩니다.
커스텀 Span 추가
자동 계측이 되지 않는 내부 비즈니스 로직에는 직접 Span을 추가할 수 있습니다.
import tracer from "dd-trace";
async function processOrder(orderId: string) {
// 커스텀 Span 생성
const span = tracer.startSpan("order.process", {
tags: {
"order.id": orderId,
"order.type": "standard",
},
});
try {
const result = await doHeavyWork(orderId);
span.setTag("order.result", "success");
return result;
} catch (error) {
span.setTag("error", true);
span.setTag("error.message", error.message);
throw error;
} finally {
span.finish(); // 반드시 종료 처리
}
}
Trace와 Log 연결
logInjection: true 설정 시, 로그에 dd.trace_id가 자동 삽입됩니다.
APM의 특정 트레이스에서 “View related logs” 버튼을 누르면,
해당 요청 중 발생한 로그만 바로 필터링해서 볼 수 있습니다.
Dashboard 구성
Dashboard는 여러 Metrics와 로그 집계를 한 화면에 배치해 서비스 상태를 한눈에 파악하는 도구입니다.
실용적인 위젯 구성
| 위젯 유형 | 활용 예시 |
|---|---|
| Timeseries | 시간대별 요청 수, 에러율 추이 |
| Top List | 에러가 많이 발생한 엔드포인트 순위 |
| Query Value | 현재 활성 사용자 수 (단일 수치) |
| Heatmap | 응답 시간 분포 |
| Log Stream | 실시간 에러 로그 스트림 |
템플릿 변수 활용
Dashboard 상단에 템플릿 변수를 추가하면, 환경이나 서비스를 드롭다운으로 전환할 수 있습니다.
# Dashboard 설정 예시
템플릿 변수: $env (값: production, staging, development)
위젯 쿼리: avg:system.cpu.user{env:$env}
하나의 Dashboard로 환경별 상태를 전환하며 비교할 수 있어 유용합니다.
Monitor와 Alert 설정
Monitor는 Metrics나 로그가 특정 조건을 만족하면 자동으로 알림을 보내는 기능입니다.
Metric Monitor 예시
# 에러율 기준 알림 설정
쿼리: sum:trace.express.request.errors{service:api-server}.as_count() /
sum:trace.express.request.hits{service:api-server}.as_count() * 100
경고(Warning): > 1 (에러율 1% 초과)
위험(Critical): > 5 (에러율 5% 초과)
알림 대상: Slack #alerts 채널
Log Monitor 예시
# 특정 에러 메시지가 5분 내 10회 이상 발생 시 알림
쿼리: service:api-server status:error "DB connection failed"
조건: count > 10 (last 5 minutes)
알림: PagerDuty on-call 담당자
알림 메시지 템플릿
🔴 [Critical] 에러율 급등
- 현재 에러율: %
- 임계값: %
- 확인 링크: )}}
✅ [Recovered] 에러율 정상화
실제 분석 흐름 예시
아래는 “특정 시간대에 응답 지연이 발생했다”는 상황에서 실제로 사용한 분석 순서입니다.
1단계: Dashboard에서 이상 구간 포착
- Timeseries 위젯에서
p95응답 시간이 평소보다 높아진 구간 확인 - 같은 시간대에 DB 쿼리 수가 급증한 것도 함께 확인
2단계: APM에서 느린 Trace 탐색
APM > Service > api-server
정렬: Duration 내림차순
필터: duration > 3s
- 특정 엔드포인트(
POST /orders)에서 느린 트레이스 발견 - Trace 상세 보기에서 어느 Span이 오래 걸렸는지 확인 → DB 쿼리 Span이 2.8초 소요
3단계: Log Explorer에서 해당 요청 로그 확인
@dd.trace_id:1234567890 service:api-server
- Trace ID로 해당 요청의 로그를 바로 필터링
- “table scan detected” 경고 로그 발견 → 인덱스 누락이 원인
정리
Datadog을 실제 업무에 적용하면서 얻은 핵심 배움을 정리합니다.
- Agent + APM 설정으로 인프라 Metrics와 애플리케이션 트레이스를 모두 수집 가능
- JSON 구조화 로그 +
logInjection으로 로그와 트레이스를 연결하면 디버깅 속도가 크게 향상 - 커스텀 Metrics는 비즈니스 관점의 수치(주문 수, 대기열 크기 등)를 직접 측정하는 데 유용
- Dashboard 템플릿 변수로 환경별 비교가 용이해져 배포 전후 상태 확인에 활용
- Monitor + 알림은 사람이 직접 보지 않아도 이상 상태를 자동으로 감지하는 핵심 장치
이전 글들에서 만든 에러 분류 체계와 NestJS 모듈 패턴이 “무엇을, 어떻게 보낼 것인가” 에 대한 답이었다면, 이번 글의 Datadog 활용은 “보내진 데이터를 어떻게 보고, 분석하고, 대응할 것인가” 에 대한 답이라고 생각합니다.
Leave a comment