skywalking链路追踪简易指导

skywalking链路追踪简易指导

  1. 简介

skywalking是可观察性分析平台和应用程序性能管理系统。提供分布式跟踪,服务网格遥测分析,度量聚合和可视化等多合一解决方案。

个人理解skywalking可以分为3个部分:

  1. UI部分(webappService),即webapp文件夹,主要负责数据展示;
  2. 数据收集部分(oapService),这部分涉及到的是config和oap-lib,一个是配置相关,一个是oap需要依赖的jar包;
  3. 代理模块,即agent文件夹。当然把1和2合起来看成一个模块也没问题。

 

skywalking链路追踪简易指导

如上图所描述:

    1. 可以将数据存储在Es、H2等存储中,但是直接和数据打交道的是Collector。
    2. 客户端是通过Agent,与Collector相连接,然后Collector将数据存储在Es、H2等存储中。
    3. 监控页面是连接的Collector,Collector从Es、H2等存储中将数据查询出来。
  1. 安装ES
    1. 下载地址

官网下载地址:https://www.elastic.co/cn/downloads/elasticsearch

建议使用7.6.1,本文中均是采用该版本。

    1. 安装部署

下载解压后可不做任何修改直接使用,可以设置集群,本文中为进行集群配置。

启动:sh bin/elasticsearch 或者 bin/elasticsearch.bat

验证是否启动成功:http://127.0.0.1:9200

可以设置elasticsearch的登录密码。

Elasticsearch的配置:

config/elasticsearch.yml增加如下配置:

skywalking链路追踪简易指导

 

Elasticsearch修改密码:

命令行输入:

bin/elasticsearch-setup-passwords.bat interactive

回车后进行密码修改。

Kibana配置:

config/kibana.yml修改如下配置:

skywalking链路追踪简易指导

 

  1. Skywalking安装

注意事项:es存在写入瓶颈,需要注意(https://www.easyice.cn/archives/207)。

    1. 下载地址

官网下载地址:http://skywalking.apache.org/downloads/

下载的安装包解压后有如下内容:

skywalking链路追踪简易指导

 

    1. 默认端口

端口号

说明

8080

web端口

11800

gRPC追踪信息收集器端口

12800

rest追踪信息收集器端口

    1. Skywalking collector配置

OAP(Collector)链路数据归集器,主要用于数据落地,大部分都会选择 Elasticsearch,OAP配置文件为 config/application.yml,配置单点的 OAP(Collector)配置如下:

cluster:

  # 单节点模式

  standalone:

  # zk用于管理collector集群协作.

  # zookeeper:

    # 多个zk连接地址用逗号分隔.

    # hostPort: localhost:2181

    # sessionTimeout: 100000

  # 分布式 kv 存储设施,类似于zk,但没有zk重型(除了etcd,consul、Nacos等都是类似功能)

  # etcd:

    # serviceName: ${SW_SERVICE_NAME:"SkyWalking_OAP_Cluster"}

    # 多个节点用逗号分隔, 如: 10.0.0.1:2379,10.0.0.2:2379,10.0.0.3:2379

    # hostPort: ${SW_CLUSTER_ETCD_HOST_PORT:localhost:2379}

core:

  default:

     # 混合角色:接收代理数据,1级聚合、2级聚合

    # 接收者:接收代理数据,1级聚合点

    # 聚合器:2级聚合点

     role: ${SW_CORE_ROLE:Mixed} # Mixed/Receiver/Aggregator

     # rest 服务地址和端口

     restHost: ${SW_CORE_REST_HOST:localhost}

     restPort: ${SW_CORE_REST_PORT:12800}

     restContextPath: ${SW_CORE_REST_CONTEXT_PATH:/}

     # gRPC 服务地址和端口

     gRPCHost: ${SW_CORE_GRPC_HOST:localhost}

     gRPCPort: ${SW_CORE_GRPC_PORT:11800}

     downsampling:

       - Hour

       - Day

       - Month

     # 设置度量数据的超时。超时过期后,度量数据将自动删除.

     # 单位分钟

     recordDataTTL: ${SW_CORE_RECORD_DATA_TTL:90}

     # 单位分钟

     minuteMetricsDataTTL: ${SW_CORE_MINUTE_METRIC_DATA_TTL:90}

     # 单位小时

     hourMetricsDataTTL: ${SW_CORE_HOUR_METRIC_DATA_TTL:36}

     # 单位天

     dayMetricsDataTTL: ${SW_CORE_DAY_METRIC_DATA_TTL:45}

     # 单位月

     monthMetricsDataTTL: ${SW_CORE_MONTH_METRIC_DATA_TTL:18}

storage:

  elasticsearch7:

     # elasticsearch 的集群名称

     nameSpace: ${SW_NAMESPACE:"local-ES"}

    # elasticsearch 集群节点的地址及端口

    clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:192.168.2.10:9200}

    # elasticsearch 的用户名和密码

    user: ${SW_ES_USER:""}

    password: ${SW_ES_PASSWORD:""}

    # 设置 elasticsearch 索引分片数量

    indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:2}

    # 设置 elasticsearch 索引副本数

    indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:0}

    # 批量处理配置

    # 每2000个请求执行一次批量

    bulkActions: ${SW_STORAGE_ES_BULK_ACTIONS:2000}

    # 无论请求的数量如何,每10秒刷新一次堆

    flushInterval: ${SW_STORAGE_ES_FLUSH_INTERVAL:10}

    # 并发请求的数量

    concurrentRequests: ${SW_STORAGE_ES_CONCURRENT_REQUESTS:2}

    # elasticsearch 查询的最大数量

    metadataQueryMaxSize: ${SW_STORAGE_ES_QUERY_MAX_SIZE:5000}

    # elasticsearch 查询段最大数量

    segmentQueryMaxSize: ${SW_STORAGE_ES_QUERY_SEGMENT_SIZE:200}

 

    1. Skywalking webApp 配置

server:

  port: 9000

collector:

  path: /graphql

  ribbon:

    ReadTimeout: 10000

    # 指向所有后端collector 的 restHost:restPort 配置,多个使用, 分隔

    listOfServers: localhost:12800

 

 

    1. Skywalking Agent 配置

# 设置Agent命名空间,它用来隔离追踪和监控数据,当两个应用使用不同的名称空间时,跨进程传播链会中断。

agent.namespace=${SW_AGENT_NAMESPACE:default-namespace}

# 设置服务名称,会在 Skywalking UI 上显示的名称

agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}

# 每 3秒采集的样本跟踪比例,如果是负数则表示 100%采集

agent.sample_n_per_3_secs=${SW_AGENT_SAMPLE:-1}

# 启用 Debug ,如果为 true 则将把所有检测到的类文件保存在"/debug"文件夹中

# agent.is_open_debugging_class = ${SW_AGENT_OPEN_DEBUG:true}

# 后端的 collector 端口及地址

collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:192.168.2.215:11800}

# 日志级别

logging.level=${SW_LOGGING_LEVEL:INFO}

注:可以通过启动命令行参数的方式进行属性添加。

例如:agent.service_name使用SW_AGENT_NAME进行配置。

插件:插件目录在agent下面;agent/plugins目录下默认存放了大部分插件;在agent/optional-plugins目录下存在的部分非必要插件,可以按需从此目录移到agent/plugins目录下即可,例如spring-cloud-gateway需要从agent/optional-plugins目录下拖动插件到agent/plugins目录下。

 

  1. traceId与日志组件的集成

以logback为例,只要在日志配置xml中增加以下配置,则在打印日志的时候,自动把当前上下文中的traceId加入到日志中去。

<appender name="console" 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} [%thread] %-5level %logger{36} - %tid - %msg%n</pattern>

        </layout>

        <charset>UTF-8</charset>

</encoder>

</appender>

在工程的pom.xml中增加如下jar依赖

<!-- https://mvnrepository.com/artifact/org.apache.skywalking/apm-toolkit-logback-1.x -->

<dependency>

    <groupId>org.apache.skywalking</groupId>

    <artifactId>apm-toolkit-logback-1.x</artifactId>

    <version>7.0.0</version>

</dependency>

  1. Skywalking nginx lua使用

示例:

http {

    lua_package_path "/Path/to/.../skywalking-nginx-lua/lib/skywalking/?.lua;;";

    # Buffer represents the register inform and the queue of the finished segment

    lua_shared_dict tracing_buffer 100m;

    # Init is the timer setter and keeper

    # Setup an infinite loop timer to do register and trace report.

    init_worker_by_lua_block {

        local metadata_buffer = ngx.shared.tracing_buffer

        -- Set service name

        metadata_buffer:set('serviceName', 'User Service Name')

        -- Instance means the number of Nginx deloyment, does not mean the worker instances

        metadata_buffer:set('serviceInstanceName', 'User Service Instance Name')

        require("client"):startBackendTimer("http://127.0.0.1:12800")

    }

 

    server {

        listen 80;

        location /ingress {

            rewrite_by_lua_block {

                require("tracer"):start("gateway", "agent.namespace")

            }

            proxy_pass http://127.0.0.1:8080/gateway;

 

            body_filter_by_lua_block {

                if ngx.arg[2] then

                    require("tracer"):finish()

                end

            }

            log_by_lua_block {

                require("tracer"):prepareForReport()

            }

        }

        # ------------------------------------------------------

        # -- Mock OAP server to provide register and trace collection

        # -- 这里往下是不能少的,否则没办法跟oap server连接

        # ------------------------------------------------------

        location /v2/service/register {

            default_type text/html;

            lua_need_request_body on;

            content_by_lua_block {

                local cjson = require('cjson')

                ngx.log(ngx.DEBUG, 'Service register request = ', ngx.req.get_body_data())

                local param = cjson.decode(ngx.req.get_body_data())

                local registeredInfo = {}

                registeredInfo[1] = {key=param.services[1].serviceName, value=1}

                ngx.say(cjson.encode(registeredInfo))

            }

        }

        location /v2/instance/register {

            default_type text/html;

            lua_need_request_body on;

            content_by_lua_block {

                local cjson = require('cjson')

                ngx.log(ngx.DEBUG, 'Service instance register request = ', ngx.req.get_body_data())

                local param = cjson.decode(ngx.req.get_body_data())

                local registeredInfo = {}

                registeredInfo[1] = {key=param.instances[1].instanceUUID, value=1}

                ngx.say(cjson.encode(registeredInfo))

            }

        }

        location /v2/instance/heartbeat {

            default_type text/html;

            lua_need_request_body on;

            content_by_lua_block {

                local cjson = require('cjson')

                --ngx.log(ngx.DEBUG, 'Service instance ping request = ', ngx.req.get_body_data())

            }

        }

        location /v2/segments {

            default_type text/html;

            lua_need_request_body on;

            content_by_lua_block {

                local cjson = require('cjson')

                ngx.log(ngx.DEBUG, 'Received segment = ', ngx.req.get_body_data())

            }

        }

    }

}

 

由于默认header存储的key为[sw6]如果在微服务(javaagent启动的时候) 配置了namespace的情况下需要修改lua脚本(tracer.lua、span.lua)中的[sw6]为[namespace-sw6]来保证整个链路是完整的。

 

特别注意:由于header中存在“.”、“_”的key会在过nginx的时候被过滤掉,切忌在header中用带有这种特殊符号的key。

默认情况下nginx的underscores_in_headers的配置是off的,如果非要使用“_”需要改成on