Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SpringBoot Admin2.0集成Arthas实践 #1736

Closed
password36 opened this issue Mar 15, 2021 · 18 comments
Closed

SpringBoot Admin2.0集成Arthas实践 #1736

password36 opened this issue Mar 15, 2021 · 18 comments

Comments

@password36
Copy link

前言

项目最初使用Arthas主要有两个目的:

  1. 通过arthas解决实现测试环境、性能测试环境以及生产环境性能问题分析工具的问题;
  2. 通过使用jad、mc、redefine功能组合实现生产环境部分节点代码热更新的能力;

技术选型相关

因为公司还未能建立起较为统一的生产微服务配置以及状态管理的能力,各自系统的研发运维较为独立。
同时现在项目使用了Spring Cloud以及Eureka的框架结构,和SBA的基础支撑能力较为匹配,同时,SBA已经可以提供服务感知,日志级别配置管理,以及基于actuator的JVM、Spring容器的众多管理插件,可以满足基础使用的需求。
在调研期间,Arthas整体版本为3.4.5,提供了基于Webconsole的Tunner Server模式,通过前面链接文章已经实践,与SBA已经可以实现集成。
因为项目本身没有历史包袱,在实际集成的过程中采用了SBA 2.0版本以提供更多的管理功能和图形界面能力。
其他优点:

  • web console界面嵌入SBA整体密码登录和网页权限管理,实现登陆SBA后才可以使用相关arthas web console的功能。
  • 基于SBA 客户端依赖的jolokia-core开放目标服务进程的jmx管理,通过实现jmx接口复用SBA的相关操作界面,减少前端界面开发能力的要求。

整体结构

整体结构
几个关键点,使用JVM内置Arthas Spring Boot插件,参考工商银行的模式建立完善的客户端下载以及修改脚本实现远程控制。
内置方案工作开发量小,只需要集成相关的开源组件即可实现相关的远程使用的模式并兼顾安全。工银的方案大而全适合整体架构规划后配置专有研发团队之城。
内置方案同时包含通过JMX的启停操作(基于3.4.5的Spring Boot插件无法获得相关句柄,暂时无法实现。),默认不启动。
通过远程JMX开通后,JVM新增相关线程8个,新增虚拟机内存30MB左右,和本文参考的SBA1.0方案相同,需要考虑在线开启前JVM内存是否可以支持。

实现效果

SBA 2.0 最大的方便就是提供了配置化链接外部网页的能力,同时如果网页实现在当前JVM进程,可以实现Spring-Security的本地权限管理,在生产环境下只有在登录SBA后才能使用相关集成的arthas功能。

  1. 登录界面
    SBA登录界面

  2. 外嵌连接位置
    首页banner

  3. JMX的使用
    JMX菜单1
    JMX启动-Arthas Agent

  4. 跳转arthas web console
    arthas_web_console

改造方案

参考原文-SpringBoot Admin集成Arthas实践 #1601中实现的几个步骤

  1. 整体工程结构
    整体工程结构

整体工程修改自SBA开源项目的example工程,具体使用custom-ui的工程链接如下:
[spring-boot-admin-sample-custom-ui] (https://github.com/codecentric/spring-boot-admin/tree/master/spring-boot-admin-samples/spring-boot-admin-sample-custom-ui)
红色框的部分是arthas web console的全部静态文件,通过Maven Resource的指定配置打入指定目录,实现SBA启动时的自定义加载。
maven resource配置--下:

            <resource>
                <directory>static</directory>
                <targetPath>${project.build.directory}/classes/META-INF/spring-boot-admin-server-ui/extensions/arthas
                </targetPath>
                <filtering>false</filtering>
            </resource>

最终构建的jar中META-INFO中包含相关的文件即可在SBA自带的tomcat启动后加载到相关的静态资源,最后的url和自定义实现的arthas console配置的外部URL对应即可。
2. 外部链接配置
SBA 2.0 开始已经使用vue全家桶了,扩展集成均比较方便。
其中,官方文档给出了外嵌连接的配置方式[Linking / Embedding External Pages] (https://codecentric.github.io/spring-boot-admin/2.3.1/#customizing-external-views)

参考sba example工程的application.yml配置即可

    # tag::customization-external-views[]
    spring:
      boot:
        admin:
          ui:
            external-views:
              - label: "Arthas Console"
                url: http://21.129.49.153:8080/
                order: 1900
    # end::customization-external-views[]
  1. 对应Spring MVC controller实现
    参考引用原实现的 SBA集成部分,该部分主要修改实现如下功能:

    • 实现tunnel server已经加载实例列表的刷新并展示到前段 AgentID框供选择点击链接;
    • 实现自定义IP地址的刷新(解决生产环境双生产IP和运维段IP不一致的问题)
  2. Arthas Spring Boot插件修改和配置
    参考引用原实现的 SBA集成中插件修改以及客户端配置application.yml
    对原版Spring boot插件修改主要在于原有插件是通过Spring的@ConditionalOnMissingBean实现自动加载。
    修改主要是通过修改这部分实现通过配置文件默认不启动,然后使用时通过远程启动相关agent线程。

  3. 基于Spring Actuator的JMX实现。
    SBA client在maven引入中会默认引入jolokia-core.jar,如果没有因为SBA client依赖可以自行引入该包,可以实现通过actuator开放基于http的jmx操作能力和SBA控制台的相关功能无缝配合。
    application.yml中开放management相关配置,根据自身环境情况,也可以开在客户端侧开启Spring security认证,SBA也可以很好的支持通过服务发现实现密码保护actuator端点的访问。

        #放开management
        management:
          endpoints:
            web:
              exposure:
                # 这里用* 代表暴露所有端点只是为了观察效果,实际中按照需进行端点暴露
                include: "*"
                exclude: env
          endpoint:
            health:
              # 详细信息显示给所有用户。
              show-details: ALWAYS
          health:
            status:
              http-mapping:
                # 自定义健康检查返回状态码对应的 http 状态码
                FATAL:  503
    

    JMX实现参考原文中EnvironmentChangeListener的实现思路,基于Spring的JMX注解实现即可。

       
       @Component
       @ManagedResource(objectName = "com.ArthasAgentManageMbean:name=ArthasMbean", description = "Arthas远程管理Mbean")
       public class ArthasMbeanImpl {
       
           @Autowired
           private Map<String, String> arthasConfigMap;
       
           @Autowired
           private ArthasProperties arthasProperties;
       
           @Autowired
           private ApplicationContext applicationContext;
       
           /**
            * 初始化
            *
            * @return
            */
           private ArthasAgent arthasAgentInit() {
               arthasConfigMap = StringUtils.removeDashKey(arthasConfigMap);
               // 给配置全加上前缀
               Map<String, String> mapWithPrefix = new HashMap<String, String>(arthasConfigMap.size());
               for (Map.Entry<String, String> entry : arthasConfigMap.entrySet()) {
                   mapWithPrefix.put("arthas." + entry.getKey(), entry.getValue());
               }
               final ArthasAgent arthasAgent = new ArthasAgent(mapWithPrefix, arthasProperties.getHome(),
                       arthasProperties.isSlientInit(), null);
               arthasAgent.init();
               return arthasAgent;
           }
       
           @ManagedOperation(description = "获取配置Arthas Tunnel Server地址")
           public String getArthasTunnelServerUrl() {
               return arthasProperties.getTunnelServer();
           }
       
           @ManagedOperation(description = "设置Arthas Tunnel Server地址,重新attach后生效")
           @ManagedOperationParameter(name = "tunnelServer", description = "example:ws://127.0.0.1:7777/ws")
           public Boolean setArthasTunnelServerUrl(String tunnelServer) {
               if (tunnelServer == null || tunnelServer.trim().equals("") || tunnelServer.indexOf("ws://") < 0) {
                   return false;
               }
               arthasProperties.setTunnelServer(tunnelServer);
               return true;
           }
       
           @ManagedOperation(description = "获取AgentID")
           public String getAgentId() {
               return arthasProperties.getAgentId();
           }
       
           @ManagedOperation(description = "获取应用名称")
           public String getAppName() {
               return arthasProperties.getAppName();
           }
       
           @ManagedOperation(description = "获取ArthasConfigMap")
           public HashMap<String, String> getArthasConfigMap() {
               return (HashMap) arthasConfigMap;
           }
       
           @ManagedOperation(description = "返回是否已经加载Arthas agent")
           public Boolean isArthasAttched() {
               DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
               String bean = "arthasAgent";
               if (defaultListableBeanFactory.containsBean(bean)) {
                   return true;
               }
               return false;
           }
       
           @ManagedOperation(description = "启动Arthas agent")
           public Boolean startArthasAgent() {
               DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
               String bean = "arthasAgent";
               if (defaultListableBeanFactory.containsBean(bean)) {
                   ((ArthasAgent) defaultListableBeanFactory.getBean(bean)).init();
                   return true;
               }
               defaultListableBeanFactory.registerSingleton(bean, arthasAgentInit());
               return true;
           }
       
           @ManagedOperation(description = "关闭Arthas agent,暂未实现")
           public Boolean stopArthasAgent() {
               // TODO 无法获取自定义tmp文件夹加载的classLoader,因此无法获取到com.taobao.arthas.core.server.ArthasBootstrap类并调用destroy方法
               DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
               String bean = "arthasAgent";
               if (defaultListableBeanFactory.containsBean(bean)) {
                   defaultListableBeanFactory.destroySingleton(bean);
                   return true;
               } else {
                   return false;
               }
           }
       }

实际使用

管理工程投产后,多次在生产环境用于问题排查和代码热修复。性能问题主要用于性能流控组件以及灰度发布相关配置参数的在线验证和debug。
代码热加载相关初期通过jad+mc的方式进行操作,后续发现jad在部分代码上因环境配置以及jvm问题产生反编译代码不一致的情况,后续通过maven打包部署应用程序source压缩包的方式解决,直接使用和应用jar同版本构建的source进行修改更加可靠。
整体方案在管理较为严格的生产环境提供了有效的性能分析以及热修复的能力。

遗留问题:

  • 现有官方提供的com.taobao.arthas.agent.attach.ArthasAgent 中启动arthas agent的客户端使用的arthasClassLoader和bootstrapClass均为方法内的临时变量,外部无法获取相关句柄实现通过bootstrapClass关闭arthas agent的功能;
    临时解决方案为通过JMX启动后,在web console连接使用后,使用stop命令实现目标进程中 arthas agent的关闭。
  • 现有字节码加载工具可以很好的实现内部类,私有类的在线热部署替换,同时经测试可以兼容SkyWalk8.x版本的javaagent插件,但是在测试环境因为配置有jacoco覆盖度采集插件与Arthas字节码产生了不兼容的情况,在部分环境使用时需要先关闭对应的agent后才能正常使用arthas的相关功能。
@ShayLau
Copy link

ShayLau commented Apr 28, 2021

@password36
external-views:这块好像没找到对应的 properties

@password36
Copy link
Author

@password36
external-views:这块好像没找到对应的 properties

external-views 具体参考sba的示例工程,在spring的application.yml里配置

@biqiufeng
Copy link

代码。地址哪里有?

@Xlinlin
Copy link

Xlinlin commented Aug 22, 2021

external-views:
怎么配置的,配置不对,我的配置:
spring:
boot:
admin:
ui:
# 外链或扩展页面
external-views:
- label: "Arthas Console"
url: /arthas/index.html
order: 1900
通过Maven Resource的指定配置打入指定目录

@password36
Copy link
Author

password36 commented Aug 30, 2021

external-views:
怎么配置的,配置不对,我的配置:
spring:
boot:
admin:
ui:

外链或扩展页面

external-views:

  • label: "Arthas Console"
    url: /arthas/index.html
    order: 1900
    通过Maven Resource的指定配置打入指定目录

properties里如果配置:/arthas/index.html的话,对应maven构建包中,index.html的路径如下:
xxxxx-run.jar\META-INF\spring-boot-admin-server-ui\extensions\arthas\index.html
参考maven build路径,这个和正文截图是可以对上的:

       <resource>
            <directory>static</directory>
            <targetPath>${project.build.directory}/classes/META-INF/spring-boot-admin-server-ui/extensions/arthas
            </targetPath>
            <filtering>false</filtering>
        </resource>

@Xlinlin
Copy link

Xlinlin commented Aug 30, 2021

/arthas/index.html

我也是按照楼主的配置,但是还是没有生效,访问效果图:
image

  1. pom配置
    image

  2. properties配置:
    image

  3. 打包后的路径:
    image

请楼主指点,感谢

@password36
Copy link
Author

/arthas/index.html

我也是按照楼主的配置,但是还是没有生效,访问效果图:
image

  1. pom配置
    image
  2. properties配置:
    image
  3. 打包后的路径:
    image

请楼主指点,感谢

可以确认下jar内最终的目录结构,看target的结构是没有问题的,最后的访问路径如下:
截屏2021-08-30 16 19 02

@Xlinlin
Copy link

Xlinlin commented Aug 30, 2021

/arthas/index.html

我也是按照楼主的配置,但是还是没有生效,访问效果图:
image

  1. pom配置
    image
  2. properties配置:
    image
  3. 打包后的路径:
    image

请楼主指点,感谢

可以确认下jar内最终的目录结构,看target的结构是没有问题的,最后的访问路径如下:
截屏2021-08-30 16 19 02

jar包正确:
image
直接访问这个链接是没问题的:
image
个人感觉就是这个外链感觉在sc admin 首页没生效,所以比较迷茫,sc admin的版本:2.2.4 arthas的版本:3.4.5

@password36
Copy link
Author

/arthas/index.html

我也是按照楼主的配置,但是还是没有生效,访问效果图:
image

  1. pom配置
    image
  2. properties配置:
    image
  3. 打包后的路径:
    image

请楼主指点,感谢

可以确认下jar内最终的目录结构,看target的结构是没有问题的,最后的访问路径如下:
截屏2021-08-30 16 19 02

jar包正确:
image
直接访问这个链接是没问题的:
image
个人感觉就是这个外链感觉在sc admin 首页没生效,所以比较迷茫,sc admin的版本:2.2.4 arthas的版本:3.4.5

参考下我的配置,看看. 这个行不行哈
截屏2021-08-30 16 37 13

@Xlinlin
Copy link

Xlinlin commented Aug 30, 2021

/arthas/index.html

我也是按照楼主的配置,但是还是没有生效,访问效果图:
image

  1. pom配置
    image
  2. properties配置:
    image
  3. 打包后的路径:
    image

请楼主指点,感谢

可以确认下jar内最终的目录结构,看target的结构是没有问题的,最后的访问路径如下:
截屏2021-08-30 16 19 02

还是不行,外链的配置感觉都不影响首页的这个标签的值:
image

@password36
Copy link
Author

/arthas/index.html

我也是按照楼主的配置,但是还是没有生效,访问效果图:
image

  1. pom配置
    image
  2. properties配置:
    image
  3. 打包后的路径:
    image

请楼主指点,感谢

可以确认下jar内最终的目录结构,看target的结构是没有问题的,最后的访问路径如下:
截屏2021-08-30 16 19 02

还是不行,外链的配置感觉都不影响首页的这个标签的值:
image

./ 加点也不行么,我的生效元素如下图。
截屏2021-08-30 16 54 40

@Xlinlin
Copy link

Xlinlin commented Aug 30, 2021

/arthas/index.html

我也是按照楼主的配置,但是还是没有生效,访问效果图:
image

  1. pom配置
    image
  2. properties配置:
    image
  3. 打包后的路径:
    image

请楼主指点,感谢

可以确认下jar内最终的目录结构,看target的结构是没有问题的,最后的访问路径如下:
截屏2021-08-30 16 19 02

还是不行,外链的配置感觉都不影响首页的这个标签的值:
image

./ 加点也不行么,我的生效元素如下图。
截屏2021-08-30 16 54 40

是的,还是不生效,这就很惆怅,你那里的代码能共享出来么?

@password36
Copy link
Author

/arthas/index.html

我也是按照楼主的配置,但是还是没有生效,访问效果图:
image

  1. pom配置
    image
  2. properties配置:
    image
  3. 打包后的路径:
    image

请楼主指点,感谢

可以确认下jar内最终的目录结构,看target的结构是没有问题的,最后的访问路径如下:
截屏2021-08-30 16 19 02

还是不行,外链的配置感觉都不影响首页的这个标签的值:
image

./ 加点也不行么,我的生效元素如下图。
截屏2021-08-30 16 54 40

是的,还是不生效,这就很惆怅,你那里的代码能共享出来么?

就是我理解你不论配什么首页显示了Arthas Console这个自定义的菜单,但是链接的都是 / ?比如直接填一个完整地http://www.baidu.com 可以生效么?另外就是index.html是不是和sba自带的index.html冲突了,换成arthas.html试试,这个和源代码已经没啥关系了,如果地址直接可以访问,证明打包加载是ok的,首页的标签就是走external-views的配置,我原来测试时也是比如配置的和实际生效的不一致就是404,这个首页的代码是sba自己根据spring application配置自动生成的和你自定医的资源并没有实际的关联关系。

@Xlinlin
Copy link

Xlinlin commented Sep 1, 2021

/arthas/index.html

我也是按照楼主的配置,但是还是没有生效,访问效果图:
image

  1. pom配置
    image
  2. properties配置:
    image
  3. 打包后的路径:
    image

请楼主指点,感谢

可以确认下jar内最终的目录结构,看target的结构是没有问题的,最后的访问路径如下:
截屏2021-08-30 16 19 02

还是不行,外链的配置感觉都不影响首页的这个标签的值:
image

./ 加点也不行么,我的生效元素如下图。
截屏2021-08-30 16 54 40

是的,还是不生效,这就很惆怅,你那里的代码能共享出来么?

就是我理解你不论配什么首页显示了Arthas Console这个自定义的菜单,但是链接的都是 / ?比如直接填一个完整地http://www.baidu.com 可以生效么?另外就是index.html是不是和sba自带的index.html冲突了,换成arthas.html试试,这个和源代码已经没啥关系了,如果地址直接可以访问,证明打包加载是ok的,首页的标签就是走external-views的配置,我原来测试时也是比如配置的和实际生效的不一致就是404,这个首页的代码是sba自己根据spring application配置自动生成的和你自定医的资源并没有实际的关联关系。

可以了,sc-admin-server更换成2.3.1 就好了,另外有快配置在nacos的配置中心没注意,本地更改一直没生效困惑了好久,突然想到了曾经在配置中心配置这一块。
另外,能分享下你的static目录下的静态文件吗?按照前文的分享有些css文件缺失,呈现不了效果

@Xlinlin
Copy link

Xlinlin commented Sep 7, 2021

docker 容器启动,无法获取到mben,怎么整,楼主?

@Xlinlin
Copy link

Xlinlin commented Sep 8, 2021

docker 容器启动,无法获取到mben,怎么整,楼主?

https://stackoverflow.com/questions/61958642/spring-boot-managedresource-component-is-not-visible-in-visualvm-when-launched

@password36
Copy link
Author

docker 容器启动,无法获取到mben,怎么整,楼主?

https://stackoverflow.com/questions/61958642/spring-boot-managedresource-component-is-not-visible-in-visualvm-when-launched

个人感觉和这个stackoverflow没什么关系,这个jmx是在docker环境下暴露jmx端口和ip访问的问题,sba和springboot的management是通过org.jolokia.jologia-core这个依赖包把本地mbean转换成spring actuator通过http暴露接口的,SBA也是通过这个spring actuator包括的操作endpoit实现远程调用操作的。

@password36
Copy link
Author

/arthas/index.html

我也是按照楼主的配置,但是还是没有生效,访问效果图:
image

  1. pom配置
    image
  2. properties配置:
    image
  3. 打包后的路径:
    image

请楼主指点,感谢

可以确认下jar内最终的目录结构,看target的结构是没有问题的,最后的访问路径如下:
截屏2021-08-30 16 19 02

还是不行,外链的配置感觉都不影响首页的这个标签的值:
image

./ 加点也不行么,我的生效元素如下图。
截屏2021-08-30 16 54 40

是的,还是不生效,这就很惆怅,你那里的代码能共享出来么?

就是我理解你不论配什么首页显示了Arthas Console这个自定义的菜单,但是链接的都是 / ?比如直接填一个完整地http://www.baidu.com 可以生效么?另外就是index.html是不是和sba自带的index.html冲突了,换成arthas.html试试,这个和源代码已经没啥关系了,如果地址直接可以访问,证明打包加载是ok的,首页的标签就是走external-views的配置,我原来测试时也是比如配置的和实际生效的不一致就是404,这个首页的代码是sba自己根据spring application配置自动生成的和你自定医的资源并没有实际的关联关系。

可以了,sc-admin-server更换成2.3.1 就好了,另外有快配置在nacos的配置中心没注意,本地更改一直没生效困惑了好久,突然想到了曾经在配置中心配置这一块。
另外,能分享下你的static目录下的静态文件吗?按照前文的分享有些css文件缺失,呈现不了效果

截屏2021-09-08 11 07 38
请做参考~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants