我有一个简单的groovy脚本,它利用GPars
库的withPool
功能向两个内部API端点并行地发出HTTPGET请求。
该脚本在本地运行良好,无论是直接运行还是作为docker容器运行。
当我将其部署为Kubernetes Job
时(在我们的内部EKS集群中:1.20
),它也在那里运行,但在它遇到第一个withPool
调用时,我看到了一个巨大的线程转储,但执行继续,并成功完成。
注意:集群中的容器使用以下Pod安全上下文运行:
securityContext:
fsGroup: 2000
runAsNonRoot: true
runAsUser: 1000
环境
# From the k8s job container
groovy@app-271df1d7-15848624-mzhhj:/app$ groovy --version
WARNING: Using incubator modules: jdk.incubator.foreign, jdk.incubator.vector
Groovy Version: 4.0.0 JVM: 17.0.2 Vendor: Eclipse Adoptium OS: Linux
groovy@app-271df1d7-15848624-mzhhj:/app$ ps -ef
UID PID PPID C STIME TTY TIME CMD
groovy 1 0 0 21:04 ? 00:00:00 /bin/bash bin/run-script.sh
groovy 12 1 42 21:04 ? 00:00:17 /opt/java/openjdk/bin/java -Xms3g -Xmx3g --add-modules=ALL-SYSTEM -classpath /opt/groovy/lib/groovy-4.0.0.jar -Dscript.name=/usr/bin/groovy -Dprogram.name=groovy -Dgroovy.starter.conf=/opt/groovy/conf/groovy-starter.conf -Dgroovy.home=/opt/groovy -Dtools.jar=/opt/java/openjdk/lib/tools.jar org.codehaus.groovy.tools.GroovyStarter --main groovy.ui.GroovyMain --conf /opt/groovy/conf/groovy-starter.conf --classpath . /tmp/script.groovy
groovy 116 0 0 21:05 pts/0 00:00:00 bash
groovy 160 116 0 21:05 pts/0 00:00:00 ps -ef
脚本(相关部分)
@Grab('org.codehaus.gpars:gpars:1.2.1')
import static groovyx.gpars.GParsPool.withPool
import groovy.json.JsonSlurper
final def jsl = new JsonSlurper()
//...
while (!(nextBatch = getBatch(batchSize)).isEmpty()) {
def devThread = Thread.start {
withPool(poolSize) {
nextBatch.eachParallel { kw ->
String url = dev + "&" + "query=$kw"
try {
def response = jsl.parseText(url.toURL().getText(connectTimeout: 10.seconds, readTimeout: 10.seconds,
useCaches: true, allowUserInteraction: false))
devResponses[kw] = response
} catch (e) {
println("\tFailed to fetch: $url | error: $e")
}
}
}
}
def stgThread = Thread.start {
withPool(poolSize) {
nextBatch.eachParallel { kw ->
String url = stg + "&" + "query=$kw"
try {
def response = jsl.parseText(url.toURL().getText(connectTimeout: 10.seconds, readTimeout: 10.seconds,
useCaches: true, allowUserInteraction: false))
stgResponses[kw] = response
} catch (e) {
println("\tFailed to fetch: $url | error: $e")
}
}
}
}
devThread.join()
stgThread.join()
}
停靠文件
FROM groovy:4.0.0-jdk17 as builder
USER root
RUN apt-get update && apt-get install -yq bash curl wget jq
WORKDIR /app
COPY bin /app/bin
RUN chmod +x /app/bin/*
USER groovy
ENTRYPOINT ["/bin/bash"]
CMD ["bin/run-script.sh"]
bin/run-script.sh
只是在运行时下载上面的groovy脚本并执行它。
wget "$GROOVY_SCRIPT" -O "$LOCAL_FILE"
...
groovy "$LOCAL_FILE"
一旦执行命中对withPool(poolSize)
的 * 第一个 * 调用,就会有一个巨大的线程转储,但执行仍在继续。
我想弄清楚 * 什么 * 可能导致这种行为。任何想法🤷🏽♂️?
Thread dump
1条答案
按热度按时间wn9m85ua1#
为了子孙后代,在这里回答我自己的问题。
这个问题原来是this log4j2 JVM hot-patch的问题,我们目前正在利用它来修复最近的log4j 2漏洞。这个代理(作为
DaemonSet
运行)修补我们所有k8s集群中所有正在运行的JVM。不知何故,这会导致我的基于OpenJDK
17
的应用程序发生线程转储。(也使用了一个预打包的OpenJDK17
)。这是一个服务,所以我可以看到几乎每半个小时发生一次线程转储!有趣的是,有其他JVM服务(和一些SOLR8
部署)* 没有 * 这个问题🤷🏽♂️.无论如何,我和我们的devops团队一起工作,临时 * 排除 * 了正在运行部署的节点,你瞧,线程转储消失了!
宇宙的平衡已经恢复🧘🏻♂️。