자체 운영 PostgreSQL
pg_tracing 확장을 사용하여 PostgreSQL이 직접 스팬을 전송하도록 구성합니다. 이미지 빌드, 서버 설정, 검증 절차와 앱 키 제약을 설명합니다.
데이터베이스 프로세스를 직접 운영하는 환경에서는 pg_tracing 확장을 설치할 수 있습니다. PostgreSQL이 애플리케이션 트레이스에 자체 실행계획을 포함시키므로, 쿼리 지연을 확인하는 데 그치지 않고 인덱스를 사용하지 않은 순차 스캔과 같은 원인까지 확인할 수 있습니다.
동작 방식
pg_tracing은 애플리케이션이 쿼리에 SQLCommenter 주석으로 전달한 W3C traceparent를 해석합니다. 해당 트레이스의 하위 스팬으로 서버 측 스팬을 생성하며, 전송은 백그라운드 워커가 OTLP로 직접 수행합니다. SDK를 경유하지 않습니다.
스팬은 두 계층으로 생성됩니다. track이 생성하는 문장 단위 스팬과 planstate_spans가 생성하는 실행계획 노드 단위 스팬입니다. 후자를 비활성화하면 SeqScan, Hash Join과 같은 노드는 수집되지 않고 문장 전체의 소요 시간만 기록됩니다.
요구 사항
| 항목 | 값 |
|---|---|
| PostgreSQL | 14, 15, 16 — 17은 미지원 |
| 확장 | pg_tracing (DataDog) |
| 빌드 | 서버 헤더를 대상으로 컴파일하는 C 확장이므로 postgresql-server-dev-<버전>과 libcurl이 필요합니다 |
| 런타임 | OTLP 익스포터가 libcurl로 전송합니다. https 엔드포인트를 사용하는 경우 ca-certificates가 필요합니다 |
이미지 빌드
확장은 배포판 패키지로 제공되지 않으므로 직접 빌드해야 합니다. Sophonz는 다음 Dockerfile로 빌드한 이미지를 사용합니다.
FROM postgres:16-bookworm AS build
ARG PG_TRACING_REF=v0.1.3
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
build-essential git ca-certificates postgresql-server-dev-16 \
libcurl4-openssl-dev; \
git clone --depth 1 --branch "${PG_TRACING_REF}" \
https://github.com/DataDog/pg_tracing.git /tmp/pg_tracing; \
make -C /tmp/pg_tracing install; \
rm -rf /var/lib/apt/lists/* /tmp/pg_tracing
FROM postgres:16-bookworm
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends libcurl4 ca-certificates; \
rm -rf /var/lib/apt/lists/*
COPY --from=build /usr/lib/postgresql/16/lib/pg_tracing.so /usr/lib/postgresql/16/lib/
COPY --from=build /usr/share/postgresql/16/extension/pg_tracing* /usr/share/postgresql/16/extension/Debian 기반 이미지를 사용하는 이유는 pgxs 툴체인이 지원되는 환경이기 때문입니다. Alpine에서는 헤더와 툴체인을 별도로 구성해야 합니다.
서버 설정
shared_preload_libraries는 서버 시작 시점에 로드되어야 하므로 서버 인자로 전달합니다.
- -c
- shared_preload_libraries=pg_tracing
- -c
- compute_query_id=on
- -c
- pg_tracing.track=all
- -c
- pg_tracing.planstate_spans=on
- -c
- pg_tracing.otel_naptime=2000
- -c
- pg_tracing.otel_service_name=my-postgres
- -c
- pg_tracing.otel_endpoint=https://in.sophonz.ai/v1/traces| 설정 | 의미 |
|---|---|
shared_preload_libraries | 확장을 로드합니다. 서버 재시작이 필요합니다 |
compute_query_id | 스팬에 안정적인 쿼리 ID를 부여하여 동일한 쿼리를 그룹화할 수 있게 합니다 |
track | all은 최상위 문과 중첩 문을 모두 포함합니다 |
planstate_spans | 실행계획 노드를 개별 스팬으로 기록합니다 |
otel_naptime | 전송 주기(ms) |
otel_service_name | 트레이스에 표시될 서비스 이름 |
otel_endpoint | OTLP 트레이스 엔드포인트 (경로 포함) |
설치 후 데이터베이스에서 확장을 활성화합니다.
CREATE EXTENSION IF NOT EXISTS pg_tracing;샘플링
기본값은 sample_rate = 0, caller_sample_rate = 1입니다. PostgreSQL은 샘플링된 traceparent와 함께 전달된 쿼리만 추적하며, 그 외의 쿼리는 추적하지 않습니다. 데이터베이스가 독자적으로 샘플링을 결정하지 않으므로, 애플리케이션 트레이스에 포착된 요청은 데이터베이스 스팬을 함께 포함합니다.
sample_rate를 상향하면 애플리케이션과 무관하게 데이터베이스가 자체적으로 추적을 시작합니다. 이때 생성되는 스팬은 상위 스팬이 없으므로 별도의 트레이스로 기록됩니다.
애플리케이션 설정
PostgreSQL이 traceparent를 해석하려면 애플리케이션이 쿼리에 이를 포함하여 전달해야 합니다. 이 역할을 하는 것이 SQLCommenter입니다. 특정 언어의 기능이 아니라 여러 OpenTelemetry 구현이 공유하는 규약이지만, 기본값으로 켜져 있지 않으므로 명시적으로 활성화해야 합니다.
Node.js SDK에서는 instrumentations 옵션으로 하위 계측에 전달합니다.
init({
service: 'my-api',
apiKey: process.env.SOPHONZ_API_KEY,
instrumentations: {
'@opentelemetry/instrumentation-pg': {
addSqlCommenterCommentToQueries: true,
},
},
});이 설정이 없으면 쿼리에 주석이 붙지 않고, PostgreSQL은 추적할 근거를 받지 못합니다. 확장은 정상 동작하지만 스팬은 생성되지 않습니다.
CAUTION — 이름 있는 프리페어드 스테이트먼트를 사용하는 경우
주석에는 쿼리마다 다른 트레이스 ID가 들어가므로 쿼리 텍스트가 매번 달라집니다. 문장에 이름을 붙여 재사용하는 구성에서는 캐시 효율이 떨어질 수 있습니다. 이름 없는 문장을 쓰는 구성에서는 해당하지 않습니다.
앱 키 제약
WARNING — 자체 호스팅 환경에서는 스팬이 수집되지 않을 수 있습니다
Sophonz 수집기는 service.key 리소스 속성으로 테넌트를 판별하며, 확인되지 않은 경우 해당 리소스를 폐기합니다. 그러나 pg_tracing이 제공하는 설정은 otel_service_name과 otel_endpoint 두 가지뿐이므로, 임의의 리소스 속성을 지정할 수 없습니다.
리소스가 폐기된 경우에도 수집기는 200을 반환하므로, PostgreSQL 로그에는 오류가 나타나지 않습니다.
해결 방법은 배포 형태에 따라 다릅니다.
- Sophonz 관리형 — 이미 적용되어 있습니다. 수집기에 내부 전용 수신 포트를 두고, 수신 시점에 키를 부여합니다.
- 온프레미스·자체 호스팅 — 도입 시 함께 구성합니다. 엔터프라이즈 개요를 참고하거나 문의해 주세요.
NOTE — 확장 자체를 수정하는 것이 근본적인 해결입니다
리소스 속성을 지정할 수 있는 GUC를 확장에 추가하면 이 우회 구성이 필요하지 않게 됩니다. 이미지를 직접 빌드하고 있으므로 패치 적용이 가능하며, 업스트림에 기여할 수 있는 변경입니다.
검증
확장이 스팬을 생성하여 전송하고 있는지 먼저 확인합니다.
SELECT * FROM pg_tracing_info();처리된 스팬 수와 마지막 전송 시각을 반환합니다. 이 값이 증가하는데도 트레이스가 비어 있다면, 유실은 데이터베이스가 아니라 수집 경로에서 발생한 것입니다.
실제로 적용된 설정은 pg_settings에서 확인합니다. 오타가 포함된 GUC는 경고 없이 무시되므로, 서버 인자만으로 적용 여부를 판단해서는 안 됩니다.
SELECT name, setting FROM pg_settings
WHERE name LIKE 'pg_tracing%' ORDER BY name;마지막으로 단일 트레이스에 포함된 서비스를 확인합니다.
SELECT serviceName, count()
FROM sophonz_traces.distributed_sophonz_index_v2
WHERE traceID = '...'
GROUP BY 1;애플리케이션과 데이터베이스가 모두 조회되어야 합니다. 데이터베이스만 누락된 경우에는 컨텍스트 전파가 아니라 수집 단계의 문제입니다. 전파 여부는 별도로 확인할 수 있습니다. 애플리케이션 응답의 Server-Timing 헤더에 요청 시 전송한 것과 동일한 트레이스 ID가 포함되어 있으면 전파는 정상입니다.