PHP Profiling 最佳实践

更新时间:
复制 MD 格式

dd-trace-php 是 Datadog 开源的 PHP APM 扩展,支持持续性能剖析(Profiling)数据采集。配合 ARMS 持续剖析服务的 pprof-agent 接收器,可将 PHP 应用的 CPU、内存分配等维度的 Profiling 数据持续上报至 ARMS 进行分析。

方案架构

ARMS 通过 pprof-agent 接收器接收 dd-trace-php 上报的 Profiling 数据,并存储到持续剖析服务。整体数据流如下:

PHP 应用(dd-trace-php 扩展)→ pprof-agent 接收器 → ARMS 持续剖析服务

安装 Profiling 接收器

ACK 集群

  1. 登录容器服务管理控制台,在左侧导航栏选择集群列表,单击目标集群名称。

  2. 在左侧导航栏选择组件管理,找到 arms-obi 组件并安装,installMode 选择 profiling

其他集群

手动部署以下 YAML。安装前替换以下变量:

  • ARMS_REGION_ID:填写需要上报的 region。

  • ARMS_LICENSE_KEY:填写 UID 对应的 LicenseKey。

  • image 中的 ARMS_REGION_ID:替换为当前所在 region。

---
apiVersion: v1
kind: Namespace
metadata:
  name: obi-system
  labels:
    app: pprof-agent

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: pprof-agent-receiver-config
  namespace: obi-system
  labels:
    app: pprof-agent
    mode: receiver
data:
  config.yaml: |
    collector:
      enabled: false            # 纯推模式,不做 Go 拉取(无需 RBAC / NODE_NAME)
    receiver:
      enabled: true
      listen: ":8126"           # PHP 侧 DD_TRACE_AGENT_URL 指向此端口
      path: "/profiling/v1/input"
      max_body_size: 20480      # 单次上报 body 上限(KB)
      service_tag: "service"    # 取 tags_profiler 的 service 作为 appName(= PHP 侧 DD_SERVICE)
      profile_type: "php"       # 回退用类型标识(已被 profile_types 取代)
      profile_types:            # 同一份 pprof 按这些维度各发布一份,ARMS 各视图才能读到
        - cpu
        - mem
      go_compatible: true       # 把 dd-trace-php 的 sample type 名改写为 ARMS GoProfileAnalyzer 认识的 Go 名字
    storage:
      type: "oss"
      oss:
        # region_id 留空 → 从 env ARMS_REGION_ID 读取,两者都空时默认 cn-hangzhou。
        region_id: ""
        bucket: "arms-profiling"
        endpoint: ""            # 为空时由 region_id + internal 推导
        path_prefix: "profiling/"
        internal: true          # 生产建议 true(VPC 内网);跨区域/无内网时改 false
    arms:
      # license_key 留空 → 从 env ARMS_LICENSE_KEY 读取(下方 Secret 注入)。
      # uid 无需填写:代码会从 license_key 自动反解(GetUserId)。
      # region_id 留空 → 回退到 storage.oss.region_id(即 env ARMS_REGION_ID)。
      license_key: ""
      region_id: ""
      app_name_label: "app"
      workspace: ""
      endpoint: ""

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pprof-agent-receiver
  namespace: obi-system
  labels:
    app: pprof-agent
    mode: receiver
spec:
  # 推模式由 Service 负载均衡,每个上报只命中一个副本,不会重复上传,可按吞吐独立伸缩。
  replicas: 1
  selector:
    matchLabels:
      app: pprof-agent
      mode: receiver
  template:
    metadata:
      labels:
        app: pprof-agent
        mode: receiver
    spec:
      terminationGracePeriodSeconds: 70
      containers:
        - name: agent
          image: registry-{ARMS_REGION_ID}-vpc.ack.aliyuncs.com/acs/obi:pprof-agent-0.10.2
          imagePullPolicy: Always
          command: ["/usr/local/bin/pprof-agent"]
          args:
            - "--config"
            - "/etc/pprof-agent/config.yaml"
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            # ARMS 凭证经 Secret 注入(凭证不落 ConfigMap)。
            # 只需 license_key;uid 由 license_key 自动反解,无需注入。
            - name: ARMS_LICENSE_KEY
              value: "xxxxxxx"
            # 区域:同时决定 OSS 上传区域与 ARMS 元数据区域;不设则默认 cn-hangzhou。
            - name: ARMS_REGION_ID
              value: "xxxxxxxx"
          volumeMounts:
            - name: config
              mountPath: /etc/pprof-agent
              readOnly: true
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: "1"
              memory: 512Mi
          ports:
            - name: intake
              containerPort: 8126
              protocol: TCP
            - name: health
              containerPort: 8080
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 30
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
      volumes:
        - name: config
          configMap:
            name: pprof-agent-receiver-config
            items:
              - key: config.yaml
                path: config.yaml

---
apiVersion: v1
kind: Service
metadata:
  name: arms-obi-pprof-agent-receiver
  namespace: obi-system
  labels:
    app: pprof-agent
    mode: receiver
spec:
  selector:
    app: pprof-agent
    mode: receiver
  ports:
    - name: intake
      port: 8126
      targetPort: 8126
      protocol: TCP

安装 dd-trace-php

执行以下命令安装 dd-trace-php 扩展并启用 Profiling。该扩展支持 PHP 7.1+(含 PHP 7.4、8.x),运行环境为 64 位 Linux。

curl -sSL -o /tmp/datadog-setup.php \
  https://github.com/DataDog/dd-trace-php/releases/download/1.23.0/datadog-setup.php
php /tmp/datadog-setup.php --php-bin all --enable-profiling
php -m | grep -i datadog          # 应看到 datadog-profiling
说明

--enable-profiling 是关键参数:不加该参数只安装 tracer,不会安装 profiler。建议将安装步骤写进 Dockerfile 一次性完成,避免每次容器启动时联网下载。安装遇到问题,参考 dd-trace-php 开源仓库

配置 php-fpm

php-fpm 默认 clear_env = yes,会清空 worker 进程的环境变量,导致设置的 DD_* 变量无法传递到 profiler,表现为"安装了扩展但不上报数据"。必须在 pool 配置中关闭该选项:

; /usr/local/etc/php-fpm.d/www.conf
clear_env = no
说明

CLI 或 mod_php 等其它运行方式不受影响,仅 php-fpm 需要此配置。

配置环境变量

基础配置(必填)

变量

作用

建议值

DD_PROFILING_ENABLED

Profiling 总开关(0/1),不开则完全不采集

1

DD_SERVICE

服务名,用于区分不同应用

实际服务名

DD_ENV

环境标(prod/staging…)

prod

DD_VERSION

版本标

1.0.0

DD_TRACE_AGENT_URL

接收器地址,profiler 上报到该地址

http://arms-obi-pprof-agent-receiver.obi-system.svc:8126

DD_TRACE_AGENT_URL 为 pprof-agent 接收器的 Service 全限定域名,Profiler 实际上报路径为 {DD_TRACE_AGENT_URL}/profiling/v1/input。该地址支持跨 namespace 访问,应用与接收器不在同一 namespace 也可正常通信。

采集维度开关

变量

作用

默认

DD_PROFILING_ALLOCATION_ENABLED

内存分配采样

开(1

DD_PROFILING_EXCEPTION_ENABLED

异常采样(抛异常时的调用栈)

DD_PROFILING_EXCEPTION_SAMPLING_DISTANCE

异常采样间隔(每 N 次采一次),越大越省开销

100

DD_PROFILING_TIMELINE_ENABLED

时间线(按时间排布事件,含 GC/IO 等)

建议显式设为 1

说明

CPU 与 wall time 是基础维度,随总开关 DD_PROFILING_ENABLED 一起开启,无需单独配置。

上报与调试

变量

作用

默认

DD_PROFILING_UPLOAD_PERIOD

上报周期(秒),越小越快看到数据

60(验证期可调小到 15

DD_PROFILING_LOG_LEVEL

profiler 日志级别:off/error/warn/info/debug

off

DD_TRACE_ENABLED

链路追踪开关。只需要 Profiling 而不需要 trace 时设为 false

true

说明

Profiling 与 Tracing 相互独立:DD_TRACE_ENABLED=false 时 Profiling 照常工作。

完整示例

K8s 环境,仅开启 Profiling:

env:
  - { name: DD_PROFILING_ENABLED, value: "1" }
  - { name: DD_SERVICE,           value: "my-php-app" }
  - { name: DD_ENV,               value: "prod" }
  - { name: DD_VERSION,           value: "1.0.0" }
  - { name: DD_TRACE_AGENT_URL,   value: "http://arms-obi-pprof-agent-receiver.obi-system.svc:8126" }
  - { name: DD_PROFILING_ALLOCATION_ENABLED, value: "1" }
  - { name: DD_PROFILING_UPLOAD_PERIOD,      value: "15" }
  - { name: DD_PROFILING_LOG_LEVEL,          value: "info" }
  - { name: DD_TRACE_ENABLED,                value: "false" }

常见问题

现象

原因及处理

php -m 没有 datadog

安装时漏加 --enable-profiling,或安装失败。重新执行安装命令并确认输出无报错。

安装了扩展但收不到数据

php-fpm 未设置 clear_env = no(环境变量传不进 worker 进程)——最常见原因。

profiler 日志报连接失败

执行 curl http://arms-obi-pprof-agent-receiver.obi-system.svc:8126/info 测试连通性。

上报太慢看不到数据

等待一个 DD_PROFILING_UPLOAD_PERIOD 周期,验证期可将该值调小到 15

启动卡在下载安装包

到 GitHub 网络慢,改为在 Dockerfile 中预装 dd-trace-php。