掘金 后端 ( ) • 2024-03-22 00:10

docker化应用接入skywalking Agent

本文柯苏远写于2024年3月21日15点50分

1. SkyWalking 架构简介

sw-%E6%9E%B6%E6%9E%84%E5%9B%BE.png

2. 背景

服务容器化的过程中想要引入sw来监控整个系统的信息。

目前运维同学已经在开发环境上搭建好了oap以及存储层和sw-ui层。

我们需要将探针层和应用层接入整个sw架构。

3. 问题 & 解决思路

3.1 问题描述

在我们应用中接入探针层主要是在jar的启动命令上指定agent的jar包以及oap地址,格式如下:

# skywalking agent jar包路径
-javaagent:/sw/skywalking-agent/skywalking-agent.jar

# 服务名称
-Dskywalking.agent.service_name=provider

# sw-oap地址
-Dskywalking.collector.backend_service=127.0.0.1:11800

这里涉及两个变动点:

  • -javaagent 指定的agent包路径。
  • -Dskywalking.collector.backend_service 指定sw-oap服务地址。

由于现在服务容器化,这两个点都需要在dockerfile文件中体现,可是dockerfile文件又不支持这两个属性动态变化(我调研的结果是dockerfile里不支持写一些if之类逻辑的)。

由于整个部署是cicd的,所以dev、test、uat以及prod都是用这一个dockerfile,那么如何实现各个环境基于同一个dockerfile来满足上面说的两个变动点呢?

3.2 解决思路

  • 将动的变成不动的。-javaagent
  • 将一定要动的抽取出来。-Dskywalking.collector.backend_service

3.3 具体方案

探针的配置优先级:

  1. 启动命令上的。
  2. config/agent.config 配置文件里的配置

根据这个优先级我们可以将-Dskywalking.collector.backend_service 从dockerfile文件里抽取出来放在 agent.config 里。

Untitled.png

下面是对于 -javaagent 动变不动的具体方案:

3.3.1 复制一份agent文件到docker容器内

关键点:直接将探针copy一份到服务对应的容器里,然后在dockerfile里 -javaagent 指向容器里的探针地址。

缺点:

  • 每个服务对应的容器里都有一份探针,冗余了很多份。
  • 从宿主机往容器里拷贝agent的时候有一个docker构建上下文环境,被拷贝的文件只能在dockerfile所处的当前目录以及当前目录的子目录。不在这个范围的会报文件找不到,所以对于复制的路径不够灵活。

3.3.2 将一份agent文件分别挂载到多个docker容器内

关键点:将探针文件挂载在每个docker容器里,同一个服务器上的docker容器用同一份探针文件。

优点:

  • 容器内没有冗余探针文件,共用一份,节省物理资源。
  • 在挂载的时候不需要将探针放在当前dockerfile目录或者是子目录下,可以放置在服务器任意地址进行挂载操作,更加灵活。

4. 可行性实践

有两个服务:consumerprovider,都注册在 nacos 上。 consumer 对外暴露了一个接口:/echo/{str}

整个调用链就是:consumerprovidermysql

4.1 复制到容器里的方式

  • 首先将我们的两个jar包以及本地编写的两个dockerfile文件,以及我们要用到的sw-agent上传的dev服务器上,在服务器上的目录如下所示:

Untitled 1.png

  • 具体的 consumer-dockerfile 文件内容如下所示:

    # 使用官方提供的Spring Boot基础镜像  
    FROM openjdk:8-jdk-alpine
    
    # 设置工作目录为/app  
    WORKDIR /app
    
    # 将当前目录内容复制到容器的/app内  
    COPY consumer.jar /app/consumer.jar
    
    # 拷贝宿主机的skywalking-agent/ 文件夹下的内容到容器的/skywalking-agent目录下  
    COPY skywalking-agent/ /skywalking-agent/
    
    # 使用容器内在上一步复制进来的agent地址
    ENV JAVA_OPTS="-javaagent:/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=consumer"
    
    # 暴露容器的 8082 端口  
    EXPOSE 8082
    
    # 设置容器启动时运行jar包  
    ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app/consumer.jar"]
    
  • 将consumer-dockerfile文件打包成镜像

    docker build -f Consumer-Dockerfile -t consumer:1.0 .
    
  • 运行镜像生成对应容器

    docker run -t -d -i -p 8082:8082 --name=consumer consumer:1.0
    
  • Provider是相同的操作

    • dockerfile

      FROM openjdk:8-jdk-alpine
      
      # 设置工作目录为/app  
      WORKDIR /app
      
      # 将当前目录内容复制到容器的/app内  
      COPY provider.jar /app/provider.jar
      
      # 拷贝宿主机的skywalking-agent/ 文件夹下的内容到容器的/skywalking-agent目录下  
      COPY skywalking-agent/ /skywalking-agent/
      
      # 使用容器内的agent地址
      ENV JAVA_OPTS="-javaagent:/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=provider"
      
      # 暴露容器的 8081 端口  
      EXPOSE 8081
      
      # 设置容器启动时运行jar包  
      ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app/provider.jar"]
      
    • 构建镜像 & 运行容器

       docker build -f Provider-Dockerfile -t provider:1.0 .
       
       docker build -f Consumer-Dockerfile -t consumer:1.0 .
      
  • 测试

    • 暴露出来的接口是:http://192.24.100.118:8082/echo/{str} , {str} 是路径参数,循环调用100次,去sw-ui界面看看是否有对应的跟踪链路。

Untitled 2.png

复制方案的测试结论是可行的。

4.2 挂载到容器里的方式

去调研了下dockerfile的语法,发现在dockerfile里是不可以直接进行挂载的,只能指定卷,挂载操作要在镜像起容器的时候才可以。

  • 首先服务还是上面两个jar包,只不过这次不是copy探针了,而是挂载,所以我们要对应修改dockerfile文件,分别注释掉copy指令,更改容器暴露端口,以及启动时候在命令行指定端口。

    复制了一份到另外一个目录,然后进行了修改dockerfile,目录详情。

Untitled 3.png

  • Consumer-dockerfile & Provider-dockerfile

    # 使用官方提供的Spring Boot基础镜像  
    FROM openjdk:8-jdk-alpine
    
    # 设置工作目录为/app  
    WORKDIR /app
    
    # 将当前目录内容复制到容器的/app内  
    COPY consumer.jar /app/consumer.jar
    
    # 拷贝宿主机的skywalking-agent/ 文件夹下的内容到容器的/skywalking-agent目录下  
    # COPY skywalking-agent/ /skywalking-agent/
    
    # 使用容器内挂载agent的地址
    ENV JAVA_OPTS="-javaagent:/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=consumer"
    
    # 暴露容器的 8083 端口  
    EXPOSE 8083
    
    # 设置容器启动时运行jar包  
    ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app/consumer.jar --server.port=8083"]
    
    FROM openjdk:8-jdk-alpine
    
    # 设置工作目录为/app  
    WORKDIR /app
    
    # 将当前目录内容复制到容器的/app内  
    COPY provider.jar /app/provider.jar
    
    # 拷贝宿主机的skywalking-agent/ 文件夹下的内容到容器的/skywalking-agent目录下  
    # COPY skywalking-agent/ /skywalking-agent/
    
    # 使用容器内的agent地址
    ENV JAVA_OPTS="-javaagent:/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=provider"
    
    # 暴露容器的 8084 端口  
    EXPOSE 8084
    
    # 设置容器启动时运行jar包  
    ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app/provider.jar --server.port=8084"]
    
  • 构建并运行 consumer & provider

    这里的 -v 是挂载 宿主机目录到容器里的参数

    docker build -f Consumer-Dockerfile -t consumer:2.0 .
    docker run -t -d -i -p 8083:8083 -v /sw/mount/skywalking-agent:/skywalking-agent --name=consumer2 consumer:2.0
     
    docker build -f Provider-Dockerfile -t provider:2.0 .
    docker run -t -d -i -p 8084:8084 -v /sw/mount/skywalking-agent:/skywalking-agent --name=provider2 provider:2.0
    
  • 测试

    • 暴露出来的接口是:http://192.24.100.118:8083/echo/{str} , {str} 是路径参数,循环调用100次,去sw-ui界面看看是否有对应的跟踪链路。

挂载方案的测试结论是可行的。

5. 总结

关于如何将探针和我们的应用接入sw架构,主要是两种方式:

  1. 将探针复制到应用容器内。
  2. 将探针挂载到应用容器内。

推荐使用第二种