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

为使用Alpine Linux作为Docker基础镜像的Java应用提供profiler命令支持 #2481

Merged
merged 4 commits into from May 8, 2023
Merged

为使用Alpine Linux作为Docker基础镜像的Java应用提供profiler命令支持 #2481

merged 4 commits into from May 8, 2023

Conversation

tdy218
Copy link
Contributor

@tdy218 tdy218 commented Apr 21, 2023

问题背景

在 arthas cli 命令行,可使用 profiler 命令支持生成应用热点的火焰图,背后使用了 async-profiler 提供支持。在 Linux 环境下,async-profiler 默认只对 x86-64、aarch64 这两种操作系统架构提供了 glibc 版的动态链接库文件:

libasyncProfiler-linux-x64.so
libasyncProfiler-linux-arm64.so

但是在基于 musl libc 实现的 Alpine Linux 环境下,执行 profiler 命令会误加载到 libasyncProfiler-linux-x64.so 或者 libasyncProfiler-linux-arm64.so ,首先会提示缺少 libstdc++、libgcc 库文件,手工执行下面的命令安装后,再次执行 profiler 命令会造成应用 jvm 进程 crash :
hs_err_pid17.log

Stack: [0x0000ffff60bf0000,0x0000ffff60dffac8],  sp=0x0000ffff60dfe520,  free space=2105k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [libstdc++.so.6+0x1683dc]
C  [libstdc++.so.6+0xd9b9c]  std::locale::operator=(std::locale const&)+0x54
C  [libstdc++.so.6+0xd9094]  std::ios_base::_M_init()+0x4c
C  [libstdc++.so.6+0x1153c0]  std::basic_ios<char, std::char_traits<char> >::init(std::basic_streambuf<char, std::char_traits<char> >*)+0x18
C  [ArthasJniLibrary5656461273075369895.tmp+0x273ac]  Symbols::parseLibraries(NativeCodeCache**, int volatile&, int, bool)+0xf4
C  [ArthasJniLibrary5656461273075369895.tmp+0x20208]  VM::ready()+0x28
C  [ArthasJniLibrary5656461273075369895.tmp+0x20c48]  VM::init(JavaVM_*, bool)+0x430
C  [ArthasJniLibrary5656461273075369895.tmp+0x210e0]  JNI_OnLoad+0x14
C  [libjava.so+0xecc4]  Java_java_lang_ClassLoader_00024NativeLibrary_load+0xb4
j  java.lang.ClassLoader$NativeLibrary.load(Ljava/lang/String;Z)V+0
j  java.lang.ClassLoader.loadLibrary0(Ljava/lang/Class;Ljava/io/File;)Z+328
j  java.lang.ClassLoader.loadLibrary(Ljava/lang/Class;Ljava/lang/String;Z)V+48
j  java.lang.Runtime.load0(Ljava/lang/Class;Ljava/lang/String;)V+57
j  java.lang.System.load(Ljava/lang/String;)V+7
j  one.profiler.AsyncProfiler.getInstance(Ljava/lang/String;)Lone/profiler/AsyncProfiler;+23
j  com.taobao.arthas.core.command.monitor200.ProfilerCommand.profilerInstance()Lone/profiler/AsyncProfiler;+165
j  com.taobao.arthas.core.command.monitor200.ProfilerCommand.process(Lcom/taobao/arthas/core/shell/command/CommandProcess;)V+43
j  com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(Lcom/taobao/arthas/core/shell/command/CommandProcess;)V+34
j  com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(Lcom/taobao/arthas/core/shell/command/impl/AnnotatedCommandImpl;Lcom/taobao/arthas/core/shell/command/CommandProcess;)V+2
j  com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(Lcom/taobao/arthas/core/shell/command/CommandProcess;)V+5
j  com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(Ljava/lang/Object;)V+5
j  com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run()V+11
..........................
.............

然后在 Alpine Linux 环境下查看基于 Alpine Linux 环境手工编译的 libasyncProfiler 和原始的基于 glibc + xxx Linux 环境编译的 libasyncProfiler,可以看到如下区别:
image

我提了一个 issue #741给 async-profiler ,async-profiler 的开发者建议我根据需要自行编译 libasyncProfiler

考虑到在 K8s 集群环境下,使用 Alpine Linux 作为 Java 应用基础镜像的用户比较多,所以这里考虑增加对 Alpine Linux x86-64 和 aarch64 这两种操作系统环境下使用 profiler 进行支持。

主要改动代码

基于 async-profiler 源码和Alpine Linux x86-64 和 aarch64 这两种操作系统的 docker 镜像构建环境打包 libasyncProfiler so

FROM --platform=$TARGETPLATFORM base-registry.cn-hangzhou.cr.aliyuncs.com/cnstack/alpine-linux-gcc-builder-env:gcc-openjdk11

ARG TARGETOS
ARG TARGETARCH
ARG OSS_ENDPOINT
ARG ALIYUN_USER_AK
ARG ALIYUN_USER_SK

WORKDIR /root

RUN wget https://github.com/async-profiler/async-profiler/archive/refs/tags/v2.9.tar.gz -O async-profiler-2.9.tar.gz && \
    tar zxvf async-profiler-2.9.tar.gz && cd async-profiler-2.9 && \
    sed -i 's/CXXFLAGS=/CXXFLAGS=-static-libgcc -static-libstdc++ /' Makefile && make && \
    curl https://gosspublic.alicdn.com/ossutil/install.sh | bash && \
    ossutil -e ${OSS_ENDPOINT} -i ${ALIYUN_USER_AK} -k ${ALIYUN_USER_SK} \
    cp build/libasyncProfiler.so oss://async-profiler/alpine/libasyncProfiler-${TARGETOS}-musl-${TARGETARCH}.so -u

Note: 这里默认将 libstdc++ 和 libgcc 作为静态依赖打进 libasyncProfiler so 文件,使客户在 Alpine Linux 环境下为了使用 arthas 的 profiler 命令时,不再需要单独安装 libstdc++ 和 libgcc.

生成的两个 libasyncProfiler so 文件:
libasyncProfiler-linux-musl-amd64.so
libasyncProfiler-linux-musl-arm64.so

libasyncProfiler so 文件加载入口处代码:
core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java

if (OSUtils.isLinux()) {
    if (OSUtils.isX86_64() && OSUtils.isMuslLibc()) {
       profierSoPath = "async-profiler/libasyncProfiler-linux-musl-amd64.so";
    } else if(OSUtils.isX86_64()){
        profierSoPath = "async-profiler/libasyncProfiler-linux-x64.so";
    } else if (OSUtils.isArm64() && OSUtils.isMuslLibc()) {
       profierSoPath = "async-profiler/libasyncProfiler-linux-musl-arm64.so";
    } else if (OSUtils.isArm64()) {
       profierSoPath = "async-profiler/libasyncProfiler-linux-arm64.so";
    }
}

测试情况

image

image

image

image

@CLAassistant
Copy link

CLAassistant commented Apr 21, 2023

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ tdy218
❌ 昆泰


昆泰 seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@hengyunabc
Copy link
Collaborator

这样子提交的 so 不可持续更新。

可以考虑先 fork 一个 async-profiler,然后利用 github action + qemu 编译不同平台的 so 。

可以参考: https://github.com/alibaba/arthas/blob/arthas-all-3.6.8/.github/workflows/build-vmtool.yaml#L55

@tdy218
Copy link
Contributor Author

tdy218 commented Apr 26, 2023

通过 build async-profiler 工作流打包的 libasyncProfiler-linux-musl-x64.so 和 libasyncProfiler-linux-musl-arm64.so 测试情况:

Alpine Linux x86-64(=x64=amd64) docker:
image

Alpine Linux aarch64(=arm64) docker:
image

@tdy218
Copy link
Contributor Author

tdy218 commented May 8, 2023

1. 使用build-async-profiler.yml workflow 构建出的 async-profiler 2.10 版本动态链接库文件(.so)测试情况

image

2.使用build-async-profiler.yml workflow 构建时的已知问题

使用build-async-profiler.yml workflow 构建时,偶尔会遇到 invalid flag: --source 的错误:

.....
................
/usr/lib/jvm/java-11-openjdk/bin/javac --source 7 -target 7 -Xlint:-options -g:none src/helper/one/profiler/Server.java
error: invalid flag: --source
Usage: javac <options> <source files>
use --help for a list of possible options
make: *** [Makefile:145: src/helper/one/profiler/Server.class] Error 2
Error: Process completed with exit
................
.....

Action: 实际上是 -source 7,async-profiler 的作者已经在近期的一个 commit 中改掉了。实际上遇到这个问题时,终止当前构建任务,重试即可。

3.使用build-async-profiler.yml workflow 构建出的 libasyncProfiler-xxx.so 使用时已知问题

在 arthas 命令行执行 profiler start -d 60 命令时, 目标Java进程标准输出日志显示:

[WARN] Unknown argument: framebuf
[WARN] Unknown argument: framebuf

这是 async-profiler 2.7 版本引入的 change: - Do not accept unknown agent arguments

Action: 需要 arthas 侧修改 framebuf 参数,让 libasyncProfiler-xxx.so 能识别出来,当前不影响 profiler 命令的使用,可稍后修改 arthas profiler 发出的命令进行优化。

@hengyunabc hengyunabc added this to the 3.6.9 milestone May 8, 2023
@hengyunabc hengyunabc merged commit e3a5c3d into alibaba:master May 8, 2023
11 of 12 checks passed
@tdy218 tdy218 deleted the alpine-linux-profiler-support branch May 8, 2023 09:40
@tdy218 tdy218 restored the alpine-linux-profiler-support branch May 8, 2023 09:40
@tdy218 tdy218 deleted the alpine-linux-profiler-support branch May 8, 2023 09:40
hengyunabc pushed a commit that referenced this pull request May 9, 2023
* Support profiler command in Alpine Linux, support musl libc

* Add pipeline for building Alpine Linux async-profiler dynamic link library files

---------

Co-authored-by: 昆泰 <dongyun.tdy@alibaba-inc.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants