报错背景

使用docker在服务器上部署springboot项目时服务器上日志出现报错如下
failed to start thread
“Gc Thread#0”
failed(EPERM)pthread createfor attributes:
stacksize:1024k,guardsize: 4k,detached.
There is insufficient memory for the Java Runtime Environment to continue.
Cannot create worker Gc thread. Out of system resources.
容器内的日志报错如下:
docker容器的日志报错futexwakeup addr=0xfc0030 returned -1
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x1006 pc=0x42d18b]
runtime stack:
runtime.throw(0xadfe47, 0x2a)
runtime/panic.go:774 +0x72
runtime.sigpanic()
runtime/signal_unix.go:378 +0x47c
runtime.futexwakeup(0xfc0030, 0x7f7c00000001)
runtime/os_linux.go:68 +0x8b
runtime.notewakeup(0xfc0030)
runtime/lock_futex.go:136 +0x42
runtime.exitsyscallfast_pidle(0x7f7cac42e0c8)
runtime/proc.go:3106 +0xce
runtime.exitsyscallfast.func1()
runtime/proc.go:3059 +0x41
runtime.systemstack(0x45ae84)
runtime/asm_amd64.s:370 +0x66
runtime.mstart()

原因和解释

这个报错大概意思就是,启动线程失败,Java 运行环境内存不足,无法继续。无法创建垃圾回收工作线程。系统资源不足。
出现这个内存不足的原因其实是docker的默认的 seccomp(安全计算模式)策略会限制容器内进程可调用的系统调用 ,导致jvm不能正常创建线程所以我们的java项目就跑不起来。

解决方案

方案一,临时解决方案,添加参数-security-opt seccomp=unconfined有安全风险,不推荐生产环境使用。

只需要在docker的启动命令添加参数-security-opt seccomp=unconfined禁用seccomp即可

# 比如
docker run --security-opt seccomp=unconfined -d -p 8080:8080 my-java-app

方案二,使用自定义的seccomp 策略,生产环境推荐,比较安全

原理

基于 Docker 官方默认模板,仅放开 Java 所需的系统调用(如 pthread_create),而非完全禁用 seccomp

1.下载官方默认 seccomp 模板

终端执行以下命令

wget https://raw.githubusercontent.com/moby/moby/master/profiles/seccomp/default.json -O java-seccomp.json

若从官网无法获取 java-seccomp.json可以私聊我把这个java-seccomp.json文件发给你们,或者评论一下,这个文件字符太多就不放出来了

2.编辑生成的java-seccomp.json在 “syscalls” 数组中新增以下内容(找同级别 syscall 节点插入)

{
  "names": ["pthread_create"],
  "action": "SCMP_ACT_ALLOW"
}

3.重新启动docker容器,添加参数–security-opt seccomp

#比如
docker run --security-opt seccomp=刚才下载json的绝对路径/java-seccomp.json -d -p 8080:8080 my-java-app

扩展

通过上面两个方案可以解决docker运行springboot项目时出现的报错

Java Runtime Environment to continue.
Cannot create worker Gc thread. Out of system resources

下列措施可以帮助更好的排查这些问题

限制物理机 / 虚拟机线程数限制

# 临时生效(重启失效)添加docker参数
ulimit -u 65535
# 永久生效(修改配置文件)
echo "* soft nproc 65535" >> /etc/security/limits.conf
echo "* hard nproc 65535" >> /etc/security/limits.conf

Java 启动参数优化

# 添加环境变量参数参数和启动选项
docker run -e "JAVA_OPTS=-XX:ParallelGCThreads=8 -XX:ConcGCThreads=2" \
  镜像名字 sh -c 'java $JAVA_OPTS -jar app.jar'

进入docker容器

# 进入容器执行相关命令,
docker exec -it 容器名 bash 或者 sh

进入docker容器后查看java启动命令

ps -ef | grep java

进入docker容器后查看启动容器时的环境变量是否生效

比如
docker exec -it 容器名 env | grep -E "变量名1|变量名2"

检查镜像架构是否与宿主机兼容

docker inspect 镜像名 | grep Architecture

seccomp 策略验证

#查看被拒绝的 syscall
strace -f java -jar app.jar 2>&1 | grep -iE 'denied|EPERM'

查看docker配置

docker inspect 镜像名

检查系统端口

netstat -tulpn | grep 想要查看的端口
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐