skywalking链路追踪简易指导
skywalking链路追踪简易指导
- 简介
skywalking是可观察性分析平台和应用程序性能管理系统。提供分布式跟踪,服务网格遥测分析,度量聚合和可视化等多合一解决方案。
个人理解skywalking可以分为3个部分:
- UI部分(webappService),即webapp文件夹,主要负责数据展示;
- 数据收集部分(oapService),这部分涉及到的是config和oap-lib,一个是配置相关,一个是oap需要依赖的jar包;
- 代理模块,即agent文件夹。当然把1和2合起来看成一个模块也没问题。
如上图所描述:
-
- 可以将数据存储在Es、H2等存储中,但是直接和数据打交道的是Collector。
- 客户端是通过Agent,与Collector相连接,然后Collector将数据存储在Es、H2等存储中。
- 监控页面是连接的Collector,Collector从Es、H2等存储中将数据查询出来。
- 安装ES
- 下载地址
官网下载地址:https://www.elastic.co/cn/downloads/elasticsearch
建议使用7.6.1,本文中均是采用该版本。
-
- 安装部署
下载解压后可不做任何修改直接使用,可以设置集群,本文中为进行集群配置。
启动:sh bin/elasticsearch 或者 bin/elasticsearch.bat
验证是否启动成功:http://127.0.0.1:9200
可以设置elasticsearch的登录密码。
Elasticsearch的配置:
config/elasticsearch.yml增加如下配置:
Elasticsearch修改密码:
命令行输入:
bin/elasticsearch-setup-passwords.bat interactive
回车后进行密码修改。
Kibana配置:
config/kibana.yml修改如下配置:
- Skywalking安装
注意事项:es存在写入瓶颈,需要注意(https://www.easyice.cn/archives/207)。
-
- 下载地址
官网下载地址:http://skywalking.apache.org/downloads/
下载的安装包解压后有如下内容:
-
- 默认端口
端口号 |
说明 |
8080 |
web端口 |
11800 |
gRPC追踪信息收集器端口 |
12800 |
rest追踪信息收集器端口 |
-
- 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} |
-
- Skywalking webApp 配置
server: port: 9000 collector: path: /graphql ribbon: ReadTimeout: 10000 # 指向所有后端collector 的 restHost:restPort 配置,多个使用, 分隔 listOfServers: localhost:12800
|
-
- 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目录下。
- 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> |
- 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