java与容器
之前用rancher部署就遇到过没限制容器内存大小导致K8S集群挂掉的问题,但是仅仅限制容器资源是不够的,jdk1.8中java程序是不能自动识别docker设置的内存,cpu限制的,这就会导致当java程序运行时占的内存过大时,容器会杀掉JVM进程,而健康检查又会拉起一个新的pod,进而导致服务一天重启很多次。
jdk11默认支持了容器,由参数 UseContainerSupport = true 来设置,在之前的jdk中,Java 8u131 以上版本支持 Docker 的 cpu 和memory 限制,使用参数 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
在容器中对JVM调优
默认情况下,JVM的Max Heap Size是系统内存的1/4,实际中,这点heap是不够的,会导致受限制的容器启动失败,只有加大容器的资源才能正常启动,除此之外还有一种方法,就要对容器里面的 jvm 调优。
但是这里遇到了一个问题,dockerfile 配置
FROM openjdk:8-jdk-alpine
...
ENTRYPOINT java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=docker -jar /$name
这样启动后 JVM 的 pid 为1,使用 jmap,jinfo,jstack等命令时会报出错误:
Unable to get pid of LinuxThreads manager thread
docker容器默认会把容器内部第一个进程,也就是pid=1的程序作为docker容器是否正在运行的依据,所以这里可以用一个容器 init 解决:
https://github.com/krallin/tini#tini---a-tiny-but-valid-init-for-containers
修改 dockerfile 配置,使用 tini 来启动 jvm
...
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=docker -jar /$name
...
rancher 控制台中限制容器内存
image.png
进入容器查看jvm,可以看到pid=1的程序已经是tini了,接下来就可以使用jvm 命令查看各个参数,可以看到 MaxHeapSize=411041792 ,差不多392mb,正好是内存限制1568mb的1/4
查看jvm详细信息
Full GC 以后 Old Generation 大概占了254mb
网友评论