콘텐츠로 이동

OpenTelemetry PHP 서비스 관측 가능성 모범 사례


서문

이 문서에서는 GuanceCloud/opentelemetry-php-instrumentation을 기반으로 PHP 서비스를 Guance에 연결하고, 로그, PHP-FPM 메트릭 및 프로파일링을 보완하여 즉시 적용 가능한 관측 가능성 솔루션을 구성하는 방법을 소개합니다.

이 솔루션은 PHP-FPM, CLI Worker, Supervisor, 컨테이너 내 PHP 프로세스 등 일반적인 배포 방식에 적합합니다. 트레이스 데이터는 기본적으로 DataKit 4317 포트를 통해 OTLP gRPC 방식으로 전송됩니다. 연동 및 문제 해결 단계에서는 임시로 9529/otelHTTP OTLP 방식으로 전환하여 트레이스 연결이 정상적인지 빠르게 확인할 수 있습니다.

환경 정보

  • 시스템 환경: Linux
  • 개발 언어: PHP 8.2
  • 배포 방식: PHP-FPM / CLI
  • APM 확장: GuanceCloud/opentelemetry-php-instrumentation 1.3.1-gtrace
  • 수집기: DataKit

구현 목표

  • 애플리케이션 트레이스 연결
  • 애플리케이션 로그 연결
  • PHP-FPM 메트릭 연결
  • 프로파일링 연결 (선택 사항)

연결 솔루션

GuanceCloud Fork 기반 설명

이 문서의 연결 방식은 GuanceCloud/opentelemetry-php-instrumentation을 기반으로 하며, 이는 OpenTelemetry 공식 저장소에서 fork된 확장 저장소입니다.

명확히 구분해야 할 두 가지 경계가 있습니다.

  • 확장의 출처는 GuanceCloud fork의 릴리스 에셋을 사용할 수 있습니다.
  • composer require로 설치하는 open-telemetry/sdk, open-telemetry/exporter-otlp, open-telemetry/opentelemetry-auto-*는 여전히 공식 PHP 패키지입니다.

즉, 현재 fork의 변경 사항은 주로 PHP 확장 레이어에 있으며, 전체 PHP SDK, exporter 및 auto instrumentation 에코시스템이 fork된 것은 아닙니다.

구성 요소 목록

OpenTelemetry PHP를 적용하려면 최소한 다음 구성 요소가 필요합니다.

  • DataKit: 트레이스, 로그, 메트릭, 프로파일 데이터를 수신합니다.
  • PHP opentelemetry 확장: 자동 계측 기능을 담당합니다.
  • OpenTelemetry PHP SDK: 데이터 구성 및 내보내기를 담당합니다.
  • OTLP Exporter: gRPC 또는 HTTP를 통해 DataKit에 데이터를 전송합니다.
  • 해당 프레임워크 또는 구성 요소의 계측 라이브러리: Laravel, Slim, Symfony, PDO, Guzzle 등의 자동 계측을 담당합니다.

opentelemetry 확장만 설치하는 것으로는 충분하지 않으며, 프로젝트 내에 SDK + exporter + 해당 자동 계측 패키지를 반드시 설치해야 합니다. 그렇지 않으면 확장이 로드되어도 완전한 트레이스 데이터를 생성할 수 없습니다.

사전 준비

DataKit 설치

호스트에 DataKit을 먼저 설치하고, 해당 호스트에서 127.0.0.1:4317 또는 127.0.0.1:9529에 접근 가능한지 확인하세요.

OpenTelemetry 수집기 활성화

DataKit 설치 디렉터리로 이동하여 sample 구성을 복사하고 DataKit을 재시작합니다.

mkdir -p /usr/local/datakit/conf.d/opentelemetry
cp /usr/local/datakit/conf.d/samples/opentelemetry.conf.sample \
  /usr/local/datakit/conf.d/opentelemetry/opentelemetry.conf
systemctl restart datakit

DataKit이 정상적으로 작동하는지 확인합니다.

curl http://127.0.0.1:9529/v1/ping
ss -lntp | grep -E '4317|9529'
Warning

애플리케이션이 http://127.0.0.1:9529/otel/v1/traces로 전송했을 때 404가 반환되면, 일반적으로 PHP 측 문제가 아니라 DataKit의 opentelemetry 입력이 실제로 활성화되지 않은 것입니다. 먼저 opentelemetry.conf가 존재하고 DataKit 재시작과 함께 적용되었는지 확인하세요.

트레이스 연결

1. OTEL 확장 설치 및 활성화

OpenTelemetry 공식 zero-code 문서에 따르면, 가장 간단한 경로는 처음부터 컴파일하는 것이 아니라 "시스템 패키지" 또는 "이미지 설치"를 우선 선택하는 것입니다.

권장 순서는 다음과 같습니다.

  1. Linux 패키지 관리자 설치
  2. Docker 이미지 설치
  3. 호스트에 pecl이 있는 경우 pecl 사용
  4. 마지막으로 소스 컴파일 고려
Warning

Linux 호스트에 항상 pecl이 있다고 가정하지 마세요. 예를 들어, 이 문서에서 검증에 사용된 호스트의 2026-07-30 실제 상태는 "phpize는 있지만 pecl은 없음"이었습니다.

또한 yum install php-pecl-opentelemetry 또는 pecl install opentelemetry로 설치되는 것은 일반적으로 공식 upstream 확장이며, GuanceCloud fork의 gtrace 버전이 아닙니다. 1.3.1-gtrace를 검증하거나 제공하려는 경우 설치 출처를 GuanceCloud fork의 릴리스 에셋 또는 내부 빌드 산출물로 변경해야 합니다.

배포판에 기성 패키지가 있다면 우선 직접 설치하세요.

CentOS/RHEL 계열 시스템은 공식 권장 Remi 저장소 방식을 참고할 수 있습니다.

yum update -y
yum install -y epel-release yum-utils
yum install -y http://rpms.remirepo.net/enterprise/remi-release-7.rpm
yum-config-manager --enable remi-php81
yum install -y php php-pecl-opentelemetry
php --ri opentelemetry

Alpine은 APK 패키지를 직접 설치할 수 있습니다.

echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
apk add php php81-pecl-opentelemetry@testing
php --ri opentelemetry

Docker 공식 PHP 이미지:

install-php-extensions opentelemetry

호스트에 이미 pecl이 있는 경우 다음을 직접 실행할 수 있습니다.

pecl install opentelemetry

호스트에 pecl이 없는 경우 다음 중 하나를 우선 선택하세요.

  • 플랫폼 측에서 PHP 버전과 일치하는 opentelemetry.so를 직접 제공
  • 시스템 패키지 관리자를 사용하여 설치
  • 컨테이너 이미지 내에서 install-php-extensions를 통해 설치

GuanceCloud fork의 경우 현재 엔터프라이즈 적용에 더 권장되는 방식은 다음과 같습니다.

  • Windows: 릴리스에 이미 패키징된 zip 에셋을 직접 사용
  • Linux: CI에서 해당 PHP 마이너 버전의 opentelemetry.so를 빌드한 후, 운영 또는 아티팩트 저장소를 통해 통합 배포

이렇게 해야 설치된 것이 실제로 gtrace 버전임을 보장할 수 있으며, 공식 upstream 확장이 아닙니다.

Windows:

  • GuanceCloud/opentelemetry-php-instrumentation 릴리스 페이지에서 현재 PHP 마이너 버전, ts/nts, 컴파일러와 일치하는 zip 패키지를 다운로드합니다.
  • php_opentelemetry.dll을 PHP ext/ 디렉터리에 넣습니다.
  • php.ini에서 확장을 활성화합니다.

어떤 방식을 사용하든 확장 패키지를 선택할 때는 다음 조건을 엄격히 일치시켜야 합니다.

  • PHP 마이너 버전 (예: 8.1/8.2/8.3/8.4)
  • ts/nts
  • 플랫폼 및 컴파일러
  • 대상 실행 방식에 해당하는 PHP 바이너리

php.ini에서 확장을 활성화합니다.

[opentelemetry]
extension=opentelemetry.so
opentelemetry.attr_hooks_enabled = On

확장이 로드되었는지 확인합니다.

php --ri opentelemetry

2. SDK 및 자동 계측 의존성 설치

공식 문서의 핵심 사항은 확장만 설치한다고 해서 트레이스가 생성되지 않는다는 것입니다. 프로젝트 내에 SDK + exporter + 계측 라이브러리를 반드시 설치해야 합니다.

Slim + PSR-18을 예로 들면, 최소 의존성은 다음과 같습니다.

composer config allow-plugins.php-http/discovery false
composer require \
  open-telemetry/sdk \
  open-telemetry/exporter-otlp \
  php-http/guzzle7-adapter \
  nyholm/psr7

애플리케이션이 Slim이 아닌 다른 프레임워크나 구성 요소를 사용하는 경우, 실제 기술 스택에 맞는 자동 계측 패키지를 설치하세요. 일반적인 예시는 다음과 같습니다.

# PSR-18 HTTP Client
composer require open-telemetry/opentelemetry-auto-psr18

# Slim
composer require open-telemetry/opentelemetry-auto-slim

# Laravel
composer require open-telemetry/opentelemetry-auto-laravel

# Symfony
composer require open-telemetry/opentelemetry-auto-symfony

# PDO
composer require open-telemetry/opentelemetry-auto-pdo

# Guzzle
composer require open-telemetry/opentelemetry-auto-guzzle

최소 원칙은 다음과 같습니다.

  • 확장 설치
  • sdk 설치
  • exporter-otlp 설치
  • 현재 프레임워크 및 미들웨어와 일치하는 auto-* 패키지 설치

그렇지 않으면 불완전하거나 빈 트레이스 데이터만 얻을 수 있습니다.

3. 트레이스 전송 파라미터 구성

로컬 DataKit 4317에 직접 연결하려면 OTLP gRPC를 사용하는 것이 좋습니다.

OTEL_PHP_AUTOLOAD_ENABLED=true \
OTEL_SERVICE_NAME=my-php-service \
OTEL_SERVICE_VERSION=1.3.1-gtrace \
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod \
OTEL_TRACES_EXPORTER=otlp \
OTEL_METRICS_EXPORTER=none \
OTEL_LOGS_EXPORTER=none \
OTEL_EXPORTER_OTLP_PROTOCOL=grpc \
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4317 \
OTEL_PROPAGATORS=baggage,tracecontext

문제 해결 시 HTTP OTLP로 전환할 수 있습니다.

OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:9529/otel

PHP-FPM, Apache, Supervisor, systemd 또는 컨테이너를 통해 PHP를 시작하는 경우, 위 환경 변수를 현재 셸 세션에만 설정하지 말고 프로세스 시작 환경에 구성하세요.

권장 최소 구성은 다음과 같습니다.

OTEL_PHP_AUTOLOAD_ENABLED=true
OTEL_SERVICE_NAME=my-php-service
OTEL_SERVICE_VERSION=1.3.1-gtrace
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=none
OTEL_LOGS_EXPORTER=none
OTEL_EXPORTER_OTLP_PROTOCOL=grpc
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4317

환경을 구분해야 하는 경우 다음을 추가하세요.

OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod

4. 비즈니스 계측 추가

프레임워크 자동 계측은 일반적인 진입점만 다룰 수 있습니다. 주문, 결제, 외부 API 호출, 배치 처리 등 주요 비즈니스 노드에는 WithSpan을 사용하여 계측을 추가하는 것이 좋습니다.

<?php

declare(strict_types=1);

use OpenTelemetry\API\Instrumentation\SpanAttribute;
use OpenTelemetry\API\Instrumentation\WithSpan;

#[WithSpan('order.submit')]
function submitOrder(#[SpanAttribute] string $orderNo): void
{
    // 비즈니스 로직
}

로그 연결

애플리케이션에서 JSON 형식으로 로그를 출력하고 service, env, version을 명시적으로 포함하는 것이 좋습니다. 로그와 트레이스를 연동해야 하는 경우 현재 trace_idspan_id를 함께 출력할 수 있습니다.

use OpenTelemetry\API\Trace\Span;

$context = Span::getCurrent()->getContext();
$traceId = $context->isValid() ? $context->getTraceId() : '';
$spanId = $context->isValid() ? $context->getSpanId() : '';

DataKit 측에서 logging 수집기를 활성화합니다. 예시 구성은 다음과 같습니다.

[[inputs.logging]]
  logfiles = ["/var/log/php-app/*.log"]
  source = "php"
  service = "my-php-service"
  pipeline = "php-json.p"

로그 필드에는 최소한 다음이 포함되어야 합니다.

  • message
  • status
  • service
  • env
  • version
  • trace_id
  • span_id

PHP-FPM 메트릭 연결

애플리케이션이 PHP-FPM을 통해 실행되는 경우 phpfpm 수집기도 함께 활성화하는 것이 좋습니다.

  1. www.conf에서 상태 페이지를 활성화합니다.
pm.status_path = /status
  1. Nginx 또는 Apache를 통해 /status를 노출합니다.

  2. DataKit에서 phpfpm 수집기를 활성화합니다.

[[inputs.phpfpm]]
  status_url = "http://127.0.0.1/status"
  use_fastcgi = false

이렇게 하면 다음을 동시에 관찰할 수 있습니다.

  • 활성 프로세스 수
  • 유휴 프로세스 수
  • 연결 큐 길이
  • 느린 요청 수
  • 단일 프로세스 요청 처리 시간 및 메모리 소비

프로파일링 연결 (선택 사항)

트레이스 외에도 CPU, 메모리 할당 및 핫 함수 문제를 파악해야 하는 경우 PHP 프로파일링을 별도로 활성화할 수 있습니다.

현재 더 안정적인 방법은 다음과 같습니다.

  1. DataKit에서 profile 수집기를 활성화합니다.
  2. PHP에 dd-trace-php를 설치하고 프로파일링을 활성화합니다.
  3. PHP 프로세스에 다음과 같은 환경 변수를 구성합니다.
DD_PROFILING_ENABLED=true
DD_SERVICE=my-php-service
DD_ENV=prod
DD_VERSION=1.3.1-gtrace
DD_AGENT_HOST=127.0.0.1
DD_TRACE_AGENT_PORT=9529

프로파일링은 트레이스를 보완하는 기능이므로 필요에 따라 활성화하고, 모든 서비스에서 기본적으로 전부 켤 필요는 없습니다.

최소 연결 순서

트레이스를 먼저 연결하고 점차적으로 다른 관측 데이터를 보완하려는 경우 다음 순서로 연결하는 것이 좋습니다.

  1. DataKit opentelemetry 입력 활성화
  2. PHP opentelemetry 확장 설치 및 활성화
  3. 프로젝트 내 sdk + exporter-otlp + 해당 auto-* 의존성 설치
  4. OTEL_* 프로세스 환경 변수 구성
  5. 실제 요청을 한 번 보내 트레이스가 Guance에 들어왔는지 확인
  6. 그런 다음 로그 수집, PHP-FPM 메트릭 및 프로파일링 추가

이렇게 하면 트레이스 연동 단계를 최소한으로 줄이고 처음부터 너무 많은 구성 요소를 동시에 처리하는 것을 피할 수 있습니다.

현재 Fork의 한계

OpenTelemetry 공식 zero-code 경험과 완전히 동일하게 맞추려면 현재 GuanceCloud fork에는 몇 가지 명확한 격차가 있습니다.

  • Linux용 사전 컴파일된 바이너리 에셋이 없어 Linux 측 설치 경로가 충분히 짧지 않습니다.
  • 독립적으로 식별 가능한 패키지 배포 채널이 없어 pecl install opentelemetry가 upstream이 아닌 gtrace로 연결되지 않습니다.
  • 릴리스 에셋과 공식 문서의 "시스템 패키지 설치" 경로가 완전히 연결되지 않아 문서에서 공식 설치 명령을 그대로 사용할 수 없습니다.
  • "확장은 fork에서 제공되지만 SDK 및 auto instrumentation 의존성은 여전히 공식 Composer 패키지에서 제공된다"는 점을 별도로 설명해야 합니다.

향후 연결 경험을 계속 개선해야 한다면 우선 순위는 다음과 같습니다.

  1. Linux용 일반적인 PHP 마이너 버전의 사전 컴파일된 확장 에셋 추가
  2. PHP 버전, ts/nts 및 확장 디렉터리를 자동으로 감지하는 설치 스크립트 제공
  3. 릴리스 페이지에서 "어떤 PHP 버전/실행 모드에 적합한지"에 대한 에셋 설명을 고정적으로 제공
  4. fork README에서 "upstream 설치 경로"와 "GuanceCloud gtrace 설치 경로"를 명확히 구분

실습 효과

위 연결을 완료하면 일반적으로 다음과 같은 관측 기능을 얻을 수 있습니다.

  • 애플리케이션 성능 모니터링(APM)에서 PHP 서비스 진입점 요청, 데이터베이스 호출, 다운스트림 HTTP 호출 등의 트레이스 정보 확인
  • 로그에서 service/env/version 기준으로 필터링하고 trace_id를 기반으로 동일한 요청 추적
  • 인프라스트럭처 또는 사용자 정의 보기에서 PHP-FPM의 활성 프로세스, 대기열, 느린 요청 등의 실행 상태 관찰
  • 프로파일링 보기에서 CPU 핫스팟, 메모리 할당 핫스팟 및 느린 함수 파악

모범 사례 권장 사항

  • 리소스 속성 통일: 최소한 service.name, service.version, deployment.environment를 고정합니다.
  • 릴리스 명명 통일: 확장 버전, 애플리케이션 버전, 릴리스 태그를 최대한 일관되게 유지하여 버전 추적을 용이하게 합니다.
  • 4317/gRPC 우선 사용: 프로덕션 환경에서는 OTLP gRPC를 우선 사용하고, 9529/otel은 연동 및 문제 해결에 더 적합합니다.
  • 기성 설치 방식 우선 사용: 이미지 사전 설치, 빌드된 아티팩트 배포 또는 pecl이 있는 환경에서의 PECL 설치를 우선하며, 소스 컴파일을 주 권장 경로로 사용하지 않습니다.
  • 프로세스 수준 환경 변수 사용: 로그인 셸에서만 OTEL_*을 설정하지 말고, php-fpm, systemd, 컨테이너 또는 시작 스크립트에 작성합니다.
  • 속성 카디널리티 제어: 전화번호, 전체 주문 번호, 원시 SQL, 초장기 URL 등을 span attribute에 전체를 기록하지 않습니다.
  • 주요 비즈니스 수동 계측: 자동 계측이 다루지 못하는 비즈니스 노드는 WithSpan을 사용하여 정확하게 보완합니다.
  • 로그에 트레이스 필드 유지: trace_id/span_id를 통일적으로 출력하지 않으면 로그와 트레이스가 안정적으로 연동되지 않습니다.
  • DataKit 입력 먼저 확인: 트레이스가 연결되지 않을 때는 opentelemetry, logging, phpfpm, profile 수집기가 활성화되었는지 먼저 확인합니다.

참고 자료

문서 평가

이 페이지가 도움이 되었나요?