본문 바로가기
WIKI 기술 지식 베이스

PHP 애플리케이션 트레이싱 (Tracing PHP Applications)

원문 보기 위키 갱신

PHP 애플리케이션에 Datadog PHP tracer(dd-trace-php)를 설치하고 계측해 트레이스를 Datadog으로 보내는 방법이에요. 설치, 자동 계측, 업그레이드·제거, 문제 해결을 다룹니다.

출처: 문서

본문

호환성 요구사항 (Compatibility requirements)

최신 dd-trace-php 버전의 최소 PHP 버전 요구사항은 PHP 7이에요. PHP 5를 사용 중이라면 0.99 버전까지 PHP tracer를 계속 사용할 수 있어요. PHP 5는 PHP 라이브러리 1.0 버전부터 EOL(수명 종료)이에요.

Datadog의 PHP 버전·프레임워크 지원(레거시·유지보수 버전 포함) 전체 목록은 호환성 요구사항 페이지를 참고하세요.

시작하기 (Getting started)

시작하기 전에 Agent를 이미 설치·구성했는지 확인하세요.

확장 설치하기 (Install the extension)

공식 설치 프로그램을 다운로드하세요.

curl -LO https://github.com/DataDog/dd-trace-php/releases/latest/download/datadog-setup.php

Alpine Linux를 사용한다면 설치 프로그램을 실행하기 전에 libgcc_s를 설치해야 해요.

apk add libgcc

활성화하려는 기능의 플래그만 전달해 설치 프로그램을 실행하세요.

# APM만
php datadog-setup.php --php-bin=all

# APM + AAP
php datadog-setup.php --php-bin=all --enable-appsec

# APM + Profiling
php datadog-setup.php --php-bin=all --enable-profiling

# 전체 설치: APM + AAP + Profiling
php datadog-setup.php --php-bin=all --enable-appsec --enable-profiling

참고: Windows에서는 APM만 지원돼요. Windows에서 PHP 애플리케이션을 트레이싱할 때는 --enable-appsec과 --enable-profiling 플래그를 사용하지 마세요.

이 명령은 호스트나 컨테이너에서 찾은 모든 PHP 바이너리에 확장을 설치해요. --php-bin을 생략하면 설치 프로그램이 인터랙티브 모드로 실행되어 설치할 바이너리를 선택하라고 사용자에게 묻습니다. dd-trace-php를 특정 바이너리에만 설치해야 한다면 --php-bin의 값은 특정 바이너리 경로가 될 수 있어요.

PHP(PHP-FPM 또는 Apache SAPI)를 다시 시작하고 애플리케이션의 트레이싱 활성화 엔드포인트를 방문하세요. 생성된 트레이스를 보려면 APM Traces 페이지로 가세요.

--enable-appsec를 지정하지 않으면 AppSec 확장은 시작 시 잠깐 로드되고 기본적으로 활성화되지 않아요. 즉시 short-circuit되어 무시할 만한 성능 오버헤드를 일으켜요.

트레이스가 UI에 나타나기까지 몇 분 걸릴 수 있어요. 몇 분이 지나도 트레이스가 나타나지 않으면 호스트 머신에서 phpinfo() 페이지를 만들고 ddtrace까지 스크롤하세요. 실패한 진단 검사가 이 섹션에 나타나 문제를 식별하는 데 도움을 줘요.

Apache ZTS: PHP CLI 바이너리가 NTS(비스레드 안전)로 빌드되었는데 Apache가 ZTS(Zend 스레드 안전) 버전의 PHP를 사용한다면, ZTS 바이너리의 확장 로드를 수동으로 변경해야 해요. /path/to/php-zts --ini를 실행해 Datadog .ini 파일의 위치를 찾은 다음 파일 이름에 -zts 접미사를 추가하세요. 예를 들어 extension=ddtrace-20210902.so에서 extension=ddtrace-20210902-zts.so로 바꾸면 돼요.

SELinux: 호스트에 httpd SELinux 정책이 구성되어 있으면, SELinux 구성에서 임시 파일의 쓰기·실행을 명시적으로 허용하지 않는 한 SDK 기능이 제한될 수 있어요: allow httpd_t httpd_tmpfs_t:file { execute execute_no_trans };

자동 계측 (Automatic instrumentation)

트레이싱은 기본적으로 자동 활성화돼요. 확장이 설치되면 ddtrace가 애플리케이션을 트레이싱하고 트레이스를 Agent로 보내요.

Datadog은 모든 웹 프레임워크를 기본 지원해요. 자동 계측은 PHP 런타임을 수정해 특정 함수·메서드를 래핑해 트레이싱하는 방식으로 동작해요. PHP tracer는 여러 라이브러리의 자동 계측을 지원해요.

자동 계측은 다음을 포착해요.

  • 메서드 실행 시간.
  • 웹 요청의 URL·상태 응답 코드나 데이터베이스 접근의 SQL 쿼리 같은 관련 트레이스 데이터.
  • 가능한 경우 스택 트레이스를 포함한 처리되지 않은 예외.
  • 시스템을 통과하는 트레이스(예: 웹 요청)의 총 개수.

구성 (Configuration)

필요하다면 Unified Service Tagging 설정을 포함해 원하는 대로 애플리케이션 성능 텔레메트리 데이터를 보내도록 SDK를 구성하세요. 자세한 내용은 라이브러리 구성을 읽어보세요.

서비스나 리소스별로 트레이스 수집을 제어하려면(리소스 이름에 와일드카드 사용 포함) 리소스 기반 샘플링으로 트레이스 수집 제어를 참고하세요.

짧고 긴 CLI 스크립트 트레이싱 (Tracing short- and long-running CLI scripts)

CLI 스크립트 계측에는 추가 단계가 필요해요. 자세한 내용은 PHP CLI 스크립트 트레이싱을 읽어보세요.

업그레이드 (Upgrading)

PHP tracer를 업그레이드하려면 최신 릴리스를 다운로드하고 확장 설치와 같은 단계를 따르세요.

설치가 완료되면 PHP(PHP-FPM 또는 Apache SAPI)를 다시 시작하세요.

참고: opcache.file_cache 매개변수를 설정해 OPcache에서 2차 캐싱을 사용하고 있다면 캐시 폴더를 제거하세요.

제거 (Removing)

PHP tracer를 제거하려면:

  1. php-fpm이라면 php-fpm 서비스를 중지하고, 그렇지 않으면 Apache 웹 서버를 중지하세요.
  2. php 구성 폴더에서 98-ddtrace.ini와 99-ddtrace-custom.ini 파일의 링크를 해제하세요.
  3. php-fpm이라면 php-fpm 서비스를 다시 시작하고, 그렇지 않으면 Apache 웹 서버를 다시 시작하세요.

참고: opcache.file_cache 매개변수를 설정해 OPcache에서 2차 캐싱을 사용하고 있다면 캐시 폴더를 제거하세요.

애플리케이션 크래시 문제 해결 (Troubleshooting an application crash)

PHP tracer 때문에 애플리케이션이 크래시하는 드문 경우(보통 세그멘테이션 폴트)에 가장 좋은 방법은 코어 덤프나 Valgrind 트레이스를 얻어 Datadog 지원에 연락하는 거예요.

디버그 심볼 설치하기 (Install debug symbols)

코어 덤프를 읽을 수 있으려면 PHP를 실행하는 시스템에 PHP 바이너리용 디버그 심볼이 설치되어 있어야 해요.

PHP나 PHP-FPM에 디버그 심볼이 설치되어 있는지 확인하려면 gdb를 사용하세요.

gdb를 설치하세요:

apt|yum install -y gdb

관심 있는 바이너리로 gdb를 실행하세요. 예를 들어 PHP-FPM의 경우:

gdb php-fpm

gdb 출력에 아래 텍스트와 비슷한 줄이 있으면 디버그 심볼이 이미 설치된 거예요.

...
Reading symbols from php-fpm...Reading symbols from /usr/lib/debug/path/to/some/file.debug...done.
...

gdb 출력에 아래 텍스트와 비슷한 줄이 있으면 디버그 심볼을 설치해야 해요.

...
Reading symbols from php-fpm...(no debugging symbols found)...done.
...

CentOS

프로그램 debuginfo-install을 제공하는 패키지 yum-utils를 설치하세요.

yum install -y yum-utils

PHP 바이너리의 패키지 이름을 찾으세요. PHP 설치 방법에 따라 다를 수 있어요.

yum list installed | grep php

디버그 심볼을 설치하세요. 예를 들어 php-fpm 패키지의 경우:

debuginfo-install -y php-fpm

참고: PHP 바이너리를 제공하는 저장소가 기본적으로 활성화되어 있지 않다면 debuginfo-install 명령 실행 시 활성화할 수 있어요. 예를 들어:

debuginfo-install --enablerepo=remi-php74 -y php-fpm

Debian

Sury Debian DPA에서 설치한 PHP

PHP를 Sury Debian DPA에서 설치했다면 디버그 심볼이 이미 DPA에서 사용 가능해요. 예를 들어 PHP-FPM 7.2의 경우:

apt update
apt install -y php7.2-fpm-dbgsym
다른 패키지에서 설치한 PHP

Debian 프로젝트는 디버그 심볼 설치 지침이 있는 위키 페이지를 유지 관리해요.

파일 /etc/apt/sources.list를 편집하세요:

# ... 기존 패키지 모두 여기에 남겨두세요

# `deb` deb http://deb.debian.org/debian-debug/ $RELEASE-debug main 추가
# 예를 들어 buster의 경우
deb http://deb.debian.org/debian-debug/ buster-debug main

apt를 업데이트하세요:

apt update

먼저 디버그 심볼의 표준 패키지 이름을 시도하세요. 예를 들어 패키지 이름이 php7.2-fpm이라면:

apt install -y php7.2-fpm-dbgsym

# 위가 안 되면

apt install -y php7.2-fpm-dbg

디버그 심볼을 찾을 수 없으면 유틸리티 도구 find-dbgsym-packages를 사용하세요. 바이너리를 설치하세요:

apt install -y debian-goodies

바이너리의 전체 경로나 실행 중인 프로세스의 프로세스 ID에서 디버그 심볼을 찾아보세요:

find-dbgsym-packages /usr/sbin/php-fpm7.2

찾으면 결과 패키지 이름을 설치하세요:

apt install -y php7.2-fpm-{package-name-returned-by-find-dbgsym-packages}

Ubuntu

ppa:ondrej/php에서 설치한 PHP

PHP를 ppa:ondrej/php에서 설치했다면 apt 소스 파일 /etc/apt/sources.list.d/ondrej-*.list를 편집해 main/debug 컴포넌트를 추가하세요.

Before: deb http://ppa.launchpad.net/ondrej/php/ubuntu <version> main

After: deb http://ppa.launchpad.net/ondrej/php/ubuntu <version> main main/debug

업데이트하고 디버그 심볼을 설치하세요. 예를 들어 PHP-FPM 7.2의 경우:

apt update
apt install -y php7.2-fpm-dbgsym
다른 패키지에서 설치한 PHP

PHP 바이너리의 패키지 이름을 찾으세요. PHP 설치 방법에 따라 다를 수 있어요.

apt list --installed | grep php

참고: 어떤 경우 php-fpm은 실제 패키지를 가리키는 metapackage일 수 있어요. 예를 들어 PHP-FPM 7.2의 경우 php7.2-fpm입니다. 이 경우 패키지 이름은 후자예요.

먼저 디버그 심볼의 표준 패키지 이름을 시도하세요. 예를 들어 패키지 이름이 php7.2-fpm이라면:

apt install -y php7.2-fpm-dbgsym

# 위가 안 되면

apt install -y php7.2-fpm-dbg

-dbg와 -dbgsym 패키지를 찾을 수 없으면 ddebs 저장소를 활성화하세요. ddebs에서 디버그 심볼을 설치하는 방법에 대한 자세한 정보는 Ubuntu 문서에서 확인할 수 있어요.

예를 들어 Ubuntu 18.04+에서는 ddebs 저장소를 활성화하세요:

echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse" | tee -a /etc/apt/sources.list.d/ddebs.list

echo "deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse" | tee -a /etc/apt/sources.list.d/ddebs.list

서명 키를 import하세요(서명 키가 올바른지 확인):

apt install ubuntu-dbgsym-keyring
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys <SIGNING KEY FROM UBUNTU DOCUMENTATION>
apt update

디버그 심볼의 표준 패키지 이름을 추가해 보세요. 예를 들어 패키지 이름이 php7.2-fpm이라면:

apt install -y php7.2-fpm-dbgsym

# 위가 안 되면

apt install -y php7.2-fpm-dbg

디버그 심볼을 찾을 수 없으면 유틸리티 도구 find-dbgsym-packages를 사용하세요. 바이너리를 설치하세요:

apt install -y debian-goodies

바이너리의 전체 경로나 실행 중인 프로세스의 프로세스 ID에서 디버그 심볼을 찾아보세요:

find-dbgsym-packages /usr/sbin/php-fpm7.2

찾으면 결과 패키지 이름을 설치하세요:

apt install -y php7.2-fpm-{package-name-returned-by-find-dbgsym-packages}

코어 덤프 얻기 (Obtaining a core dump)

PHP 애플리케이션의 코어 덤프를 얻는 것은 특히 PHP-FPM에서 까다로울 수 있어요. 코어 덤프를 얻는 데 도움이 되는 몇 가지 팁이에요.

  1. 애플리케이션 오류 로그를 보고 PHP-FPM이 코어 덤프를 생성했는지 확인하세요.
    • (SIGSEGV - core dumped)를 검색하세요. 이런 메시지는 덤프가 생성됐다는 뜻이에요: WARNING: [pool www] child <pid> exited on signal 11 (SIGSEGV - core dumped) after <duration> seconds from start.
    • (SIGSEGV)를 검색하세요. 이런 메시지는 코어가 덤프되지 않았다는 뜻이에요: WARNING: [pool www] child <pid> exited on signal 11 (SIGSEGV) after <duration> seconds from start.
  2. cat /proc/sys/kernel/core_pattern을 실행해 코어 덤프를 찾으세요. 기본값은 보통 core인데, 웹 루트 폴더에 core라는 이름의 파일이 생성된다는 뜻이에요.

코어 덤프가 생성되지 않았다면 다음 구성을 확인하고 필요에 따라 변경하세요.

  1. /proc/sys/kernel/core_pattern에 중첩 디렉터리를 포함한 경로가 있으면 전체 디렉터리 경로가 존재하는지 확인하세요.
  2. PHP-FPM 풀 워커를 실행하는 사용자가 root가 아니면(흔한 사용자 이름은 www-data) 그 사용자에게 코어 덤프 디렉터리에 대한 쓰기 권한을 주세요.
  3. /proc/sys/fs/suid_dumpable의 값이 0이 아닌지 확인하세요. PHP-FPM 워커 풀을 root로 실행하지 않는 한 1 또는 2로 설정하세요. 시스템 관리자에게 옵션을 확인하세요.
  4. PHP-FPM 풀 구성 섹션에 적절한 rlimit_core가 있는지 확인하세요. unlimited로 설정할 수 있어요: rlimit_core = unlimited.
  5. 시스템에 적절한 ulimit가 설정되어 있는지 확인하세요. unlimited로 설정할 수 있어요: ulimit -c unlimited.
  6. 애플리케이션이 Docker 컨테이너에서 실행된다면 /proc/sys/* 변경은 호스트 머신에서 해야 해요. 사용 가능한 옵션을 알려면 시스템 관리자에게 문의하세요. 가능하면 테스트 또는 스테이징 환경에서 문제를 재현해 보세요.

Docker 컨테이너 안에서 코어 덤프 얻기

Docker 컨테이너에서 코어 덤프를 얻는 데 아래 정보를 사용하세요.

  1. Docker 컨테이너는 privileged 컨테이너로 실행되어야 하고, 코어 파일의 ulimit 값은 아래 예시처럼 최대로 설정해야 해요.
    • docker run 명령을 사용한다면 --privileged와 --ulimit core=99999999999 인자를 추가하세요.
    • docker compose를 사용한다면 docker-compose.yml 파일에 다음을 추가하세요.
privileged: true
ulimits:
  core: 99999999999

컨테이너를 실행할 때(PHP 애플리케이션 시작 전) 다음 명령을 실행해야 해요.

ulimit -c unlimited
echo '/tmp/core' > /proc/sys/kernel/core_pattern
echo 1 > /proc/sys/fs/suid_dumpable

Valgrind 트레이스 얻기 (Obtaining a Valgrind trace)

크래시에 대한 더 많은 세부 정보를 얻으려면 Valgrind로 애플리케이션을 실행하세요. 코어 덤프와 달리 이 방법은 권한이 없는 컨테이너에서도 항상 동작해요.

참고: Valgrind로 실행하는 애플리케이션은 네이티브 실행보다 수십 배 느려요. 이 방법은 프로덕션이 아닌 환경에 권장돼요.

패키지 관리자로 Valgrind를 설치하세요. 요청 몇 개를 생성할 만큼 Valgrind로 애플리케이션을 실행하세요.

CLI 애플리케이션의 경우:

USE_ZEND_ALLOC=0 valgrind -- php path/to/script.php

php-fpm을 실행할 때:

USE_ZEND_ALLOC=0 valgrind --trace-children=yes -- php-fpm -F --fpm-config <CONFIG_FILE_PATH> <MORE_OPTIONS>

Apache를 사용할 때:

(. /etc/apache2/envvars; USE_ZEND_ALLOC=0 valgrind --trace-children=yes -- apache2 -X)`

결과 Valgrind 트레이스는 기본적으로 표준 오류로 출력돼요. 다른 대상으로 출력하려면 공식 문서를 참고하세요. PHP-FPM 프로세스의 예상 출력은 아래 예시와 비슷해요.

==322== Conditional jump or move depends on uninitialised value(s)
==322==    at 0x41EE82: zend_string_equal_val (zend_string.c:403)
==322==    ...
==322==    ...
==322==
==322== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==322==    at 0x73C8657: kill (syscall-template.S:81)
==322==    by 0x1145D0F2: zif_posix_kill (posix.c:468)
==322==    by 0x478BFE: ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER (zend_vm_execute.h:1269)
==322==    by 0x478BFE: execute_ex (zend_vm_execute.h:53869)
==322==    by 0x47D9B0: zend_execute (zend_vm_execute.h:57989)
==322==    by 0x3F6782: zend_execute_scripts (zend.c:1679)
==322==    by 0x394F0F: php_execute_script (main.c:2658)
==322==    by 0x1FFE18: main (fpm_main.c:1939)
==322==
==322== Process terminating with default action of signal 11 (SIGSEGV)
==322==    ...
==322==    ...
==322==
==322== HEAP SUMMARY:
==322==     in use at exit: 3,411,619 bytes in 22,428 blocks
==322==   total heap usage: 65,090 allocs, 42,662 frees, 23,123,409 bytes allocated
==322==
==322== LEAK SUMMARY:
==322==    definitely lost: 216 bytes in 3 blocks
==322==    indirectly lost: 951 bytes in 32 blocks
==322==      possibly lost: 2,001,304 bytes in 16,840 blocks
==322==    still reachable: 1,409,148 bytes in 5,553 blocks
==322==                       of which reachable via heuristic:
==322==                         stdstring          : 384 bytes in 6 blocks
==322==         suppressed: 0 bytes in 0 blocks
==322== Rerun with --leak-check=full to see details of leaked memory
==322==
==322== Use --track-origins=yes to see where uninitialised values come from
==322== For lists of detected and suppressed errors, rerun with: -s
==322== ERROR SUMMARY: 18868 errors from 102 contexts (suppressed: 0 from 0)

strace 얻기 (Obtaining a strace)

일부 문제는 외부 요인 때문에 발생하므로 strace를 확보하는 것이 유용할 수 있어요.

참고: strace로 실행하는 애플리케이션은 네이티브 실행보다 수십 배 느려요. 이 방법은 프로덕션이 아닌 환경에 권장돼요.

패키지 관리자로 strace를 설치하세요. Datadog 지원에 보낼 strace를 생성할 때 자식 프로세스를 따라가도록 -f 옵션을 사용해야 해요.

CLI 애플리케이션의 경우:

strace -f php path/to/script.php

php-fpm의 경우:

strace -f php-fpm -F --fpm-config <CONFIG_FILE_PATH> <MORE_OPTIONS>

Apache의 경우:

(. /etc/apache2/envvars; strace -f apache2 -X)

더 알아보기 (Learn more)

도움이 되는 추가 문서, 링크, 글: