skywalking+elk集成

介绍

Apache SkywalkingApache基金下由国人开发的分布式链路追踪系统,Skeywalking可以为微服务,云原生应用提供可视化链路追踪服务,通过Skywalking可以方便的查看服务的调用链路和性能瓶颈。 ELK是三个项目首字母的缩写,分别为Elasticsearch、Logstash 和 Kibana,Elasticsearch 是一个搜索和分析引擎。Logstash 是服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到诸如 Elasticsearch 等“存储库”中。Kibana 则可以让用户在 Elasticsearch 中使用图形和图表对数据进行可视化。通过ELK用户可以在Kibana中方便查询应用日志,借助Elasticsearch强大的搜索能力,对于大数据量的日志也能进行快速检索。

Skywalking和ELK的集成,实际上就是将日志和调用链路关联起来,在交易出问题需要排查时,可以在Skeywalking查看其调用链路以及调用时长,到具体日志,需要到ELK中进行查看,两个系统需要有个标识符能够将其关联起来,这个标识符就是Trace id,skywalking会为每一笔交易生成一个traceid,在服务的调用链中会将该id传递到各个调用节点,服务在打印日志时,需要将该id带上,这样就能将其关联起来。

实现

我们以spring boot项目集成skywalking和elk为例介绍具体的做法

skywalking接入

spring boot需要先集成Skywalking,从官网下载skywalking压缩包并解压,其中包含一个agent目录

agent
├── activations
├── bootstrap-plugins
├── config
├── logs
├── optional-plugins
├── optional-reporter-plugins
├── plugins
└── skywalking-agent.jar

在spring boot应用程序启动命令及上以下参数

java -javaagent:/you/path/apache-skywalking-apm-bin/agent/skywalking-agent.jar -jar app.jar

默认情况下skywalking-agent会读取config文件夹下文件agent.config作为配置文件,其中collector.backend_service指定了OAP的地址,使用的是OAP的gRPC端口,默认为11800

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

如果不想使用配置文件,可以直接使用以下命令

java -javaagent:/you/path/apache-skywalking-apm-bin/agent/skywalking-agent.jar  -Dskywalking.agent.service_name=appName -Dskywalking.collector.backend_service=127.0.0.1:11800 -jar app.jar

maven依赖

添加maven依赖

<dependency>
     <groupId>net.logstash.logback</groupId>
     <artifactId>logstash-logback-encoder</artifactId>
     <version>6.6</version>
 </dependency>
 <dependency>
     <groupId>org.apache.skywalking</groupId>
     <artifactId>apm-toolkit-logback-1.x</artifactId>
     <version>8.6.0</version>
 </dependency>

logstash-logback-encoder可以将日志输出到logstash中,apm-toolkit-logback-1.x可以在日志中插入traceid

logback配置

在resources目录下新加文件logback-spring.xml,如果项目已创建则可直接用,完整配置如下

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <springProperty scope="local" name="appName" source="spring.application.name" />
<!--    <include resource="org/springframework/boot/logging/logback/base.xml"/>-->
    <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>elk.definesys.com:4567</destination>
        <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
            <provider class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.logstash.TraceIdJsonProvider">
            </provider>
            <customFields>{"app":"${appName}"}</customFields>
        </encoder>
    </appender>
    <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{HH:mm:ss.SSS}  %-5level logger_name:%logger{36} - [%tid] : %msg%n</pattern>
            </layout>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="LOGSTASH"/>
        <appender-ref ref="STDOUT"/>
    </root>
    <!-- 日志过滤 -->
    <logger name="org.apache.catalina.core.ContainerBase*" level="WARN"/>
    <logger name="org.springframework.web.servlet.DispatcherServlet" level="WARN"/>
    <logger name="com.zaxxer.hikari.HikariDataSource" level="WARN"/>
</configuration>
这里重新定义了控制台的日志格式ConsoleAppender方便演示,实际环境中,可根据需求进行定制

destination为logstash地址端口,logstash配置如下

input{
        tcp {
                mode => "server"
                host => "0.0.0.0"
                port => 4567
                codec => json_lines
        }
}
output{
        elasticsearch{
                hosts=>["localhost:9200"]
                index => "log-%{+YYYY.MM.dd}"
                }
}

测试验证

@GetMapping(value = "orderDetail")
private Response orderDetail(@RequestParam(value = "orderNo") String orderNo) {
    Logger logger = LoggerFactory.getLogger(ProjectController.class);
    logger.info("order no===>{}", orderNo);
    return Response.ok().data(projectService.queryOrderDetail(orderNo));
}

启动应用,访问接口,在控制台可以看到以下日志

21:53:02.170  INFO  logger_name:c.d.d.s.p.c.ProjectController - [TID:eacb092e76594dcdb413adf5e37d4fae.97.16269619821690001] : order no===>DFS0009845

其中TID就是traceid,登录skywalking,在追踪界面通过traceid进行查询

可以看到调用链路,以及每个节点耗时。

登录kibana,输入TID:"eacb092e76594dcdb413adf5e37d4fae.97.16269619821690001"进行查询

如果所有的日志汇总到同一个索引下,那么可以查到所有节点上的相关日志,我们注意到日志里面有两个字段

这个是logback配置文件中配置了自定义字段

<springProperty scope="local" name="appName" source="spring.application.name" />
....
<customFields>{"app":"${appName}"}</customFields>

appName引用了application.properties中的spring.application.name属性,建议加上该字段,可以根据应用名称进行日志搜索

DevOps

上面介绍了spring boot中集成skywalking和elk的方案,但在实际生产中,如何将这个方案做到通用,做到通用有以下几个要求

  • 通用,所有应用都可以通过统一的步骤,统一的模板进行集成
  • 透明,日志模块相对较为独立,并且方案较为统一,上面的方法是和应用绑定在一起,耦合性较强,并不推荐在应用中直接集成,应用只需负责输出log即可,集成的步骤可以在devops 打包阶段由脚本完成

目前系统devops流水线包括以下工具

  • 应用会被制作成docker镜像运行在docker中
  • jenkins负责执行脚本进行编译打包部署
  • kubernetes作为应用运行平台
  • elk和skywalking都是运行在kubernetes环境中

应用打包

我们只需将以下三个文件打入应用中就可以将一个未集成的spring boot应用改造成集成好的应用

  • logback-spring.xml文件
  • logstash-logback-encoder-xx.jar logstash插件包
  • apm-toolkit-logback-1.x skywalking插件包

创建一个空目录,把涉及到的资源放到该目录下

apm-toolkit-logback-1.x-8.1.0.jar
app.jar
logback-spring.xml
logstash-logback-encoder-6.6.jar

在该目录下创建bash脚本package.sh脚本如下

mkdir -p BOOT-INF/lib/
mkdir -p BOOT-INF/classes/
cp -rf apm-toolkit-logback-1.x-8.1.0.jar BOOT-INF/lib/
cp -rf logstash-logback-encoder-6.6.jar BOOT-INF/lib/
cp -rf logback-spring.xml BOOT-INF/classes/
jar -uvf0 app.jar BOOT-INF/lib/apm-toolkit-logback-1.x-8.1.0.jar
jar -uvf0 app.jar BOOT-INF/lib/logstash-logback-encoder-6.6.jar
jar -uvf0 app.jar BOOT-INF/classes/logback-spring.xml

执行./package.sh对应用进行重新打包,将相关资源更新到应用中,脚本执行完执行以下命令启动应用,登录skywalking和kibana验证结果

java -javaagent:/you/path/skywalking-agent/skywalking-agent.jar -jar app.jar

docker镜像

我们需要把skywalking的agent目录放到镜像中,可以创建一个空目录,把涉及到的资源全部放到该目录下

.
├── Dockerfile
├── skywalking-agent
│   ├── activations
│   ├── bootstrap-plugins
│   ├── config
│   ├── logs
│   ├── optional-plugins
│   ├── optional-reporter-plugins
│   ├── plugins
│   └── skywalking-agent.jar
└── starfire-service-1.0.jar
  • Dockerfile

dockerfile文件如下,基础镜像为openjdk:8-jdk-alpine,在dockerfile中做了日期处理,将容器的日期改为中国上海时区

FROM openjdk:8-jdk-alpine
MAINTAINER definesys.com
RUN apk add --no-cache tzdata && \
        cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
        echo "Asia/Shanghai" > /etc/timezone && \
        apk add --update-cache curl bash libc6-compat vim
COPY skywalking-agent/ /usr/local/skywalking-agent
ENV LANG=zh_CN.UTF-8
ENV TZ=Asia/Shanghai
VOLUME /tmp
ADD starfire-service-1.0.jar app.jar
RUN echo "Asia/Shanghai" > /etc/timezone
EXPOSE 8080
ENTRYPOINT ["java","-javaagent:/usr/local/skywalking-agent/skywalking-agent.jar","-Djava.security.egd=file:/dev/./urandom","-Dfile.encoding=UTF-8", "-jar","app.jar"]
  • starfire-service-1.0.jar这个为应用文件
  • skywaling-agent/config文件中的agent.config文件后期可以通过configmap挂载进行覆盖
  • 容器入口命令ENTRYPOINT指定了skywalking的javaagent

执行以下命令构建镜像

docker build -t starfire-service:v1.0  .

构建完后启动应用进行测试

➜ docker run -it --rm starfire-service:v1.0 
DEBUG 2021-07-23 11:49:41:939 main AgentPackagePath : The beacon class location is jar:file:/usr/local/skywalking-agent/skywalking-agent.jar!/org/apache/skywalking/apm/agent/core/boot/AgentPackagePath.class. 
INFO 2021-07-23 11:49:41:950 main SnifferConfigInitializer : Config file found in /usr/local/skywalking-agent/config/agent.config. 
....

从日志可以看出skywalking配置已经生效,但如果每个应用在构建镜像时都把skywalking构建到镜像中其实违反了我们上面提到的通用原则,要做到通用,建议**构建一个包含skywalking agent的基础镜像,应用从该基础镜像再进行构建**

,
Trackback

no comment untill now

Add your comment now