Java服务通过动态开关 Profiling 实现关键问题定位

    2023公众号封面_最佳实践.jpg

    简介


    Profile 通过收集和分析应用程序运行过程中 CPU、内存和 I/O 相关的数据,可以识别应用程序的性能瓶颈和错误,帮助我们更好地了解程序的运行情况。Profile 是一种非常有价值的技术,它可以实现:

    • 识别性能瓶颈:Profiling 可以帮助发现程序的性能瓶颈,即程序中最耗费时间的部分。通过识别性能瓶颈,以便我们采取措施来优化这部分性能。

    • 优化程序性能:通过 Profiling 收集的数据,我们可以知道程序的哪些部分需要进行优化。通过优化这些部分,可以提高程序的性能,减少资源的消耗。

    • 排查程序错误:在应用程序中,有时会出现各种错误。通过 Profiling 分析程序的运行情况,我们可以找到错误的原因,并及时进行修复。

    利用观测云可以快速收集应用程序 Profile 数据,利用 Profile 火焰图查看器分析 Java 、Python 、 Go 、C++、NodeJS不同语言环境下应用程序运行过程中的动态性能数据,直观地查看每一个方法、类和线程的调用关系和执行效率。通过关联链路,获取链路相关 span 的关联代码执行片段,实现方法级代码性能追踪,帮助开发人员发现代码优化方向。

    注意:收集应用程序 Profile 数据,会对应用程序本身性能造成一定的影响,所以一般情况下不建议在生产环境实时启动 Profile 数据收集。


    本文以在 Kubernetes 部署的 Java 应用程序为例,介绍通过动态开关打开 Profile 数据采集的方法,并展示对应用程序的关键问题定位的效果。

    前置条件

    安装 DataKit

    注册并登录观测云控制台,选择「集成」-「DataKit」-「Kubernetes」,按照所提示的安装步骤下载 datakit.yaml,并配置 DataWay 数据网关地址。

    打开 Profile 采集器

    1.修改 datakit.yaml 文件,configMap 加入 Profile 采集配置

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: datakit-conf
      namespace: datakit
    data:
      profile.conf: |-
        [[inputs.profile]]
          ## profile Agent endpoints register by version respectively.
          ## Endpoints can be skipped listen by remove them from the list.
          ## Default value set as below. DO NOT MODIFY THESE ENDPOINTS if not necessary.
          endpoints = ["/profiling/v1/input"]
    
          ## set true to enable election, pull mode only
          election = true
    
        ## go pprof config
        ## collect profiling data in pull mode
        #[[inputs.profile.go]]
          ## pprof url
          #url = "http://localhost:6060"
    
          ## pull interval, should be greater or equal than 10s
          #interval = "10s"
    
          ## service name
          #service = "go-demo"
    
          ## app env
          #env = "dev"
    
          ## app version
          #version = "0.0.0"
    
          ## types to pull
          ## values: cpu, goroutine, heap, mutex, block
          #enabled_types = ["cpu","goroutine","heap","mutex","block"]
    
        #[inputs.profile.go.tags]
          # tag1 = "val1"
    
        ## pyroscope config
        #[[inputs.profile.pyroscope]]
          ## listen url
          #url = "0.0.0.0:4040"
    
          ## service name
          #service = "pyroscope-demo"
    
          ## app env
          #env = "dev"
    
          ## app version
          #version = "0.0.0"
    
        #[inputs.profile.pyroscope.tags]
          #tag1 = "val1"
    

    2.DaemonSet 添加相应的挂载配置

    volumeMounts:
        - mountPath: /usr/local/datakit/conf.d/profile/profile.conf
          name: datakit-conf
          subPath: profile.conf
    

    3.修改完成之后,安装 datakit.yaml 配置

    kubectl apply -f datakit.yaml
    

    应用配置

    安装 Datakit Operator

    Datakit Operator 可以通过 K8s 的 Admission Controller(准入控制器)功能,会向指定的 Pod 注入应用程序启动所需要的 dd-lib 探针文件,这样就不需要应用程序打包镜像时加入对应的探针文件,减少配置从而提高效率。

    1.下载 datakit-operator.yaml 文件并执行安装

    wget https://static.guance.com/datakit-operator/datakit-operator.yaml
    kubectl apply -f datakit-operator.yaml
    

    2.验证所有配置是否安装完成

    正常会有 datakit 和 datakit-operator 两种类型的 Pod

    kubectl get pod -n datakit
    

    修改 Dockerfile

    注意:java8 版本需要高于 8u262+,或者使用 java11 及以上版本。

    修改应用 Dockerfile,暴露启动参数:

    FROM openjdk:8u292
    
    RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
    RUN echo 'Asia/Shanghai' >/etc/timezone
    
    ENV jar my-service.jar
    ENV workdir /data/app/
    RUN mkdir -p ${workdir}
    COPY ${jar} ${workdir}
    WORKDIR ${workdir}
    ENTRYPOINT ["sh", "-ec", "exec java ${JAVA_OPTS} -jar ${jar} ${PARAMS} 2>&1 > /dev/null"]
    

    应用部署 yaml

    1.添加以下环境变量,通过 ENV_PROFILE 环境变量来动态控制 Profile 数据采集:

    env:
    - name: DD_AGENT_HOST
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: status.hostIP
    - name: ENABLE_PROFILE
      value: "true"
    - name: JAVA_OPTS
      value: |-
        -javaagent:/datadog-lib/dd-java-agent.jar -Ddd.service=my-service  -Ddd.profiling.enabled=$(ENABLE_PROFILE) -Ddd.profiling.allocation.enabled=$(ENABLE_PROFILE) -Ddd.env=env -Ddd.agent.port=9529
    

    2.添加 annotation,位置 spec.template.metadata 下:

    annotations:
      admission.datakit/java-lib.version: ""
    

    生产环境部署时,ENV_PROFILE 环境变量可以设置为 false,需要排查性能问题时,再打开 Profile 开关收集数据,从而实现动态采集 Profile 数据的功能。

    效果展示

    配置成功后,再结合观测云控制台强大的可视化数据分析能力,可以显著提高研发人员定位问题、分析问题、解决问题的处理时效,从而提高业务系统整体健康度。

    附录

    完整 yaml 参考:

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      namespace: profile
      labels:
        app: my-service
    spec:
      selector:
        app: my-service
      ports:
        - protocol: TCP
          port: 9299
          nodePort: 30001
          targetPort: 9299
      type: NodePort
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-service
      namespace: profile
      labels:
        app: my-service
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: my-service
      template:
        metadata:
          labels:
            app: my-service
          annotations:
            admission.datakit/java-lib.version: ""
        spec:
          containers:
            - name: my-container
              env:
              - name: DD_AGENT_HOST
                valueFrom:
                  fieldRef:
                    apiVersion: v1
                    fieldPath: status.hostIP
              - name: ENABLE_PROFILE
                value: "true"
              - name: JAVA_OPTS
                value: |-
                  -javaagent:/datadog-lib/dd-java-agent.jar -Ddd.service=my-service  -Ddd.profiling.enabled=$(ENABLE_PROFILE) -Ddd.profiling.allocation.enabled=$(ENABLE_PROFILE) -Ddd.env=env -Ddd.agent.port=9529
              image: my-service:v1.0
              imagePullPolicy: IfNotPresent
              ports:
              - containerPort: 9299
                protocol: TCP
          restartPolicy: Always
    

    联系我们

    加入社区

    微信扫码
    加入官方交流群

    立即体验

    在线开通,按量计费,真正的云服务!

    立即开始

    选择观测云版本

    代码托管平台