skywalking简介

skywalking是一个开源的可观测平台,用于收集、分析、聚合和可视化来自服务和云原生基础设施的数据。skywalking提供了一种简单的方式来维护您的分布式系统的清晰视图,甚至跨云。它是一个现代化的APM,专门为本地云、基于容器的分布式系统设计。
skywalking通过Java 字节码注入技术以无代码入侵的方式实现对java应用的服务调用追踪。
skywalking的jave agent对应用性能影响也非常的小,参考 官网结果可以得知,模拟300 ~ 500并发的情况下,仅仅增加了6% ~ 10%左右的cpu,2%的内存消耗,对响应时延和TPS几乎没有影响。

业务场景分析

已有业务架构使用的k8s+springcloud微服务,采使用prometheus做监控,elasticesearch+fluentd做日志采集。前期由于业务调用简单就没有做服务调用追踪,后续业务复杂度增加,生产环境出现业务问题越来多,需要用于服务调用追踪工具来帮助快速定位问题。
对比多家产品,zipkin功能简单纯粹,比较容易部署和使用,但由于业务使用了rocketmq,发现目前只有skywalking实现了对rocketmq消息生产和消费的追踪。
skywalking 9.0的定位是一个可观测平台,除了服务调用追踪的功能之外,还包含了监控告警、分布式日志采集等功能,而这些功能就目前来说使用prometheus+efk是主流。因此项目实践中并没有将应用的监控数据和日志数据采集到skywalking中。

部署skywalking

使用helm部署,由于当前环境已经部署有es集群,因此直接使用该es集群做为skywalking的存储。

# helm添加skywaking repo
helm repo add skywalking https://apache.jfrog.io/artifactory/skywalking-helm 
# 生成部署skywalking.yaml文件,当前最新版本为9.10.0,elasticsearch.config.host 表示已有的es集群地址
helm template prod skywalking/skywalking  -n kube-public \
--set elasticsearch.enabled=false \
--set oap.storageType=elasticsearch \
--set elasticsearch.config.host=elasticsearch.kube-system  \
--set ui.image.tag=9.1.0 --set oap.image.tag=9.1.0 >skywalking.yaml
# 修改skywaking.yaml,将Service prod-skywalking-ui的type改成NodePort
vi skywalking.yaml
……
apiVersion: v1
kind: Service
metadata:
  labels:
    app: prod
    chart: skywalking-4.2.0
    component: "ui"
    heritage: Helm
    release: prod
  name: prod-skywalking-ui
spec:
  type: NodePort  # 此处由ClusterIp改成NodePort
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
  selector:
    app: prod
    component: "ui"
    release: prod
……
# kubectl部署
kubectl apply -f skywaking.yaml -n kube-public

部署成功后通过nodeport访问skywalking的界面

helm安装skywalking接入外部es skywalking jvm_xml

java应用容器接入skywalking

使用官方提供的 SkyWalking Java Agent,以initContainer的方式在业务容器启动时将java agent安装好。
示例:

kind: Deployment
apiVersion: apps/v1
metadata:
  name: pumpkin-user-api
  namespace: pumkin-dev
  labels:
    k8s-app: pumpkin-user-api
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: pumpkin-user-api
  template:
    metadata:
      labels:
        k8s-app: pumpkin-user-api
    spec:
      volumes:
        - name: skywalking-agent
          emptyDir: {}
        - name: customize-enhance-config
          configMap:
            name: pumpkin-user-api
            defaultMode: 420
      initContainers:
        - name: agent-container
          image: 'apache/skywalking-java-agent:8.10.0-java8'
          command:
            - /bin/sh
          args:
            - '-c'
            - >-
              cp -R /skywalking/agent /agent/ && mv -f
              /agent/agent/optional-plugins/apm-trace-ignore-plugin-8.10.0.jar
              /agent/agent/plugins/ &&mv -f
              /agent/agent/optional-plugins/apm-customize-enhance-plugin-8.10.0.jar
              /agent/agent/plugins/ && rm -f
              /agent/agent/plugins/apm-lettuce-5.x-plugin-8.10.0.jar
              /agent/agent/plugins/apm-hikaricp-3.x-4.x-plugin-8.10.0.jar
          resources: {}
          volumeMounts:
            - name: skywalking-agent
              mountPath: /agent
      containers:
        - name: pumpkin-user-api
          image: pumpkin-user-api-dev:trunk.29732.1052
          ports:
            - name: http
              containerPort: 5000
              protocol: TCP
          env:
            - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES
              value: 'prod-skywalking-oap.kube-public:11800'  # skywalking server 访问地址
            - name: SW_LOGGING_DIR
              value: /app/logs   # skywalking日志输出目录,修改成一个有写入权限的目录
            - name: SW_SERVICE_NAME
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: "metadata.labels['k8s-app']"
            - name: SW_NAMESPACE
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
            - name: SW_AGENT_NAME
              value: '${SW_NAMESPACE}::${SW_SERVICE_NAME}'
            - name: JAVA_OPTIONS
              value: >-
                -javaagent:/skywalking/agent/skywalking-agent.jar
                -Dskywalking.trace.ignore_path=/*/actuator/health
                -Dskywalking.plugin.jdbc.trace_sql_parameters=true
                -Dskywalking.plugin.customize.enhance_file=/skywalking/agent/config/customize_enhance.xml
          volumeMounts:
            - name: skywalking-agent
              mountPath: /skywalking
            - name: customize-enhance-config
              mountPath: /skywalking/agent/config/customize_enhance.xml
              subPath: customize_enhance.xml

说明:

  1. 环境变量SW_AGENT_NAME表示该应用在skywalking中的服务名,如果采用<group name>::<logic name>的格式,则将group name中存放到服务的元数据中去。
  2. 环境变量JAVA_OPTIONS=-javaagent:/skywalking/agent/skywalking-agent.jar -Dskywalking.xxx…,该业务镜像在dockerfile支持接受JAVA_OPTIONS,加入到jvm启动参数中去,此处加入-javaagent:/skywalking/agent/skywalking-agent.jar 参数,接入skywalking agent,其余的-Dskywalking.xxx是一些skywaking agent plugin的参数,后续另外说明
  3. initContainers中除了复制了/skywalking/agent目录所有文件,同时还从optional-plugins目录中将文件移到了plugins目录中,这表示启用一些可选的plugin。相反,从plugins目录中删除了文件,表示关闭了一些默认的plugin

以下是服务接入skywalking后的效果图:

服务列表

helm安装skywalking接入外部es skywalking jvm_xml_02


调用拓扑图

helm安装skywalking接入外部es skywalking jvm_微服务_03


调用追踪

helm安装skywalking接入外部es skywalking jvm_jar_04

在日志中输出traceId

在应用日志中输出traceId,需要在pom文件中引入skywalking的日志jar包,以logback为例:

<dependency>
         <groupId>org.apache.skywalking</groupId>
         <artifactId>apm-toolkit-logback-1.x</artifactId>
         <version>8.10.0}</version>
     </dependency>

修改logback.xml文件

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
        <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
            <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</Pattern>
        </layout>
    </encoder>
</appender>

日志效果类似:

2022-06-22 16:57:19.113 [TID:014519ae75fe4d51a1a7ecd4d8a90ddf.178.16558882391093573] [pool-8-thread-1] INFO  site region tenant handler, 没有获取到site region, orders x.f.c.t.SiteRegionTenantHandler.doTableFilter(SiteRegionTenantHandler.java:58)

忽略不重要接口的调用追踪

实际应用场景中经常会使用spring actuator的health接口进行服务健康检查,这类与业务无关的接口大家可能不需要关注,skywalking可以启动apm-trace-ignore-plugin可选插件忽略指定路径接口的调用追踪。在应用的部署yaml中的initContainers中,将optional-plugins目录中的apm-trace-ignore-plugin-8.10.0.jar文件移到plugins目录

mv -f /agent/agent/optional-plugins/apm-trace-ignore-plugin-8.10.0.jar /agent/agent/plugins/

配置skywalking.trace.ignore_path参数,指定要忽略的路径,如果有多个可以用逗号分隔。以jvm参数的方式指定该参数:

-Dskywalking.trace.ignore_path=/*/actuator/health

开启数据库sql语句参数记录

skywalking会追踪本地数据库的调用,会记录执行的sql语句,但默认情况下不会记录传入的sql参数。如果需要记录sql参数,配置plugin.jdbc.trace_sql_parameters参数为true即可。以jvm参数的方式指定该参数:

-Dskywalking.plugin.jdbc.trace_sql_parameters=true

效果如下:

helm安装skywalking接入外部es skywalking jvm_kubernetes_05

无代码入侵方式自定义追踪点

skywalking支持自定义追踪,可以在业务逻辑中加入追踪代码或者通过自定义插件是实现。入侵现有代码肯定不友好,编写skywalking插件太难,因此skywaking提供一个自定义追踪增强的插件customize-enhance-trace,可以通过配置文件的方式进行各种方法级别的调用追踪。

customize-enhance-trace插件支持的一些配置语法:

helm安装skywalking接入外部es skywalking jvm_微服务_06


配置示例:

在应用的部署yaml中的initContainers中,将optional-plugins目录中的apm-customize-enhance-plugin-8.10.0.jar文件移到plugins目录

mv -f /agent/agent/optional-plugins/apm-customize-enhance-plugin-8.10.0.jar

编写customize_enhance.xml文件,以下以k8s的configmap方式创建该文件

kind: ConfigMap
apiVersion: v1
metadata:
  name: pumpkin-user-api
  namespace: pumkin-dev
data:
  customize_enhance.xml: |-
    <?xml version="1.0" encoding="UTF-8"?>
    <enhanced>
       <class class_name="xingkong.framework.tool.ExcelTool" >
            <method method="exportDataToExcel(java.util.List,java.lang.String)" operation_name="/exportExcel" static="true">
                <tag key="title">arg[1]</tag>
                <log key="list1">arg[0].[0]</log>
            </method>
        </class>
    </enhanced>

将configmap的内容作为volume挂载到/skywalking/agent/config目录中customize_enhance.xml文件中,配置skywalking.plugin.customize.enhance_file参数指定插件读取该配置文件 。以jvm参数的方式指定该参数:

-Dskywalking.plugin.customize.enhance_file=/skywalking/agent/config/customize_enhance.xml

效果图:

helm安装skywalking接入外部es skywalking jvm_jar_07


自定义追踪点中按照配置文件中配置以tag或者log的方式记录了exportDataToExcel(list,string)方法的入参值

helm安装skywalking接入外部es skywalking jvm_kubernetes_08


实际使用中,这个功能可以很方便的帮助定位线上问题。目前我没有去确认这个插件是否不需要应用重启支持动态的修改配置文件。如果支持,那这个插件非常实用。

总结

相比于zipkin,skywalking的功能更加丰富,当然使用自然变复杂了。skywalking的国内开源的,它比较好支持的国内的一些中间件和框架,比如rocketmq、ShardingSphere等等。skywalking agent的插件机制让skywalking有非常好的扩展性,当然官方提供的那些可选插件的使用对应用性能的影响是不确定的,这些需要有实际使用时去注意。
本文只是简单的尝试了skywalking在java服务调用追踪方面的一些功能,skywalking其它方面的功能没有做介绍,后续有时间再补上。