groovy program 运行 as kubernetes job dumping threads during execution 的可能原因
Possible reasons for groovy program running as kubernetes job dumping threads during execution
我有一个简单的 groovy 脚本,它利用 GPars
库的 withPool
功能向两个内部 API 端点并行发起 HTTP GET 请求。
脚本 运行 在本地和 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()
}
Dockerfile
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)
的调用,就会有一个巨大的线程转储,但执行会继续。
我正在尝试找出 什么 可能导致此行为。有什么想法♂️吗?
为了后代,在这里回答我自己的问题。
问题原来是 this log4j2 JVM hot-patch 我们目前正在利用它来修复最近的 log4j2 漏洞。此代理(运行 作为 DaemonSet
)修补我们所有 k8s 集群中的所有 运行 JVM。
不知何故,这导致我基于 OpenJDK 17
的应用程序出现线程转储。我在 ElasticSearch 8.1.0
部署中也发现了同样的问题(也使用 pre-packaged OpenJDK 17
)。这是一项服务,所以我几乎每半小时就会看到一次线程转储!有趣的是,还有其他 JVM 服务(和一些 SOLR 8
部署)没有 有这个问题♂️。
无论如何,我与我们的 devops 团队合作暂时排除 部署 运行 的节点,你瞧,线程转储消失了!
宇宙的平衡已经恢复♂️。
我有一个简单的 groovy 脚本,它利用 GPars
库的 withPool
功能向两个内部 API 端点并行发起 HTTP GET 请求。
脚本 运行 在本地和 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()
}
Dockerfile
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)
的调用,就会有一个巨大的线程转储,但执行会继续。
我正在尝试找出 什么 可能导致此行为。有什么想法♂️吗?
为了后代,在这里回答我自己的问题。
问题原来是 this log4j2 JVM hot-patch 我们目前正在利用它来修复最近的 log4j2 漏洞。此代理(运行 作为 DaemonSet
)修补我们所有 k8s 集群中的所有 运行 JVM。
不知何故,这导致我基于 OpenJDK 17
的应用程序出现线程转储。我在 ElasticSearch 8.1.0
部署中也发现了同样的问题(也使用 pre-packaged OpenJDK 17
)。这是一项服务,所以我几乎每半小时就会看到一次线程转储!有趣的是,还有其他 JVM 服务(和一些 SOLR 8
部署)没有 有这个问题♂️。
无论如何,我与我们的 devops 团队合作暂时排除 部署 运行 的节点,你瞧,线程转储消失了!
宇宙的平衡已经恢复♂️。