共计 1319 个字符,预计需要花费 4 分钟才能阅读完成。
背景
前段时间业务研发反馈说是他的利用内存使用率很高,导致频繁的重启,让我排查下是怎么回事;
在这之前我也没怎么在意过这个问题,正好这次排查剖析的过程做一个记录。
首先我查看了监控面板里的 Pod 监控:
发现的确是快满了,而此时去查看利用的 JVM 占用状况却只有 30% 左右;阐明并不是利用内存满了导致 JVM 的 OOM,而是 Pod 的内存满了,导致 Pod 的内存溢出,从而被 k8s 杀掉了。
而 k8s
为了维持利用的正本数量就得重启一个 Pod,所以看起来就是利用运行一段时间后就被重启。
而这个利用配置的是 JVM 8G,容器申请的内存是 16G,所以 Pod 的内存占用看起来也就 50% 左右。
容器的原理
在解决这个问题之前还是先简略理解下容器的运行原理,因为在 k8s 中所有的利用都是运行在容器中的,而容器实质上也是运行在宿主机上的一个个常常而已。
但咱们应用 Docker 的时候会感觉每个容器启动的利用之间互不烦扰,从文件系统、网络、CPU、内存这些都能齐全隔离开来,就像两个运行在不同的服务器中的利用。
其实这一点也不是啥黑科技,Linux 早就反对 2.6.x 的版本就曾经反对 namespace
隔离了,应用 namespace
能够将两个过程齐全隔离。
仅仅将资源隔离还不够,还须要限度对资源的应用,比方 CPU、内存、磁盘、带宽这些也得做限度;这点也能够应用 cgroups
进行配置。
它能够限度某个过程的资源,比方宿主机是 4 核 CPU,8G 内存,为了爱护其余容器,必须给这个容器配置应用下限:1 核 CPU,2G 内存。
这张图就很清晰的示意了 namespace
和 cgroups
在容器技术中的作用,简略来说就是:
- namespace 负责隔离
- cgroups 负责限度
在 k8s 中也有对应的提现:
resources:
requests:
memory: 1024Mi
cpu: 0.1
limits:
memory: 1024Mi
cpu: 4
这个资源清单示意该利用至多须要为一个容器调配一个 0.1 核和 1024M 的资源,CPU 的最高下限为 4 个外围。
不同的 OOM
回到本次的问题,能够确认是容器产生了 OOM 从而导致被 k8s 重启,这也是咱们配置 limits 的作用。
k8s 内存溢出导致容器退出会呈现 exit code 137 的一个 event 日志。
因为该利用的 JVM 内存配置和容器的配置大小是一样的,都是 8GB,但 Java 利用还有一些非 JVM 治理的内存,比方堆外内存之类的,这样很容易就导致容器内存大小超过了限度的 8G 了,也就导致了容器内存溢出。
云原生背景的优化
因为这个利用自身应用的内存不多,所以倡议将堆内存限度到 4GB,这样就防止了容器内存超限,从而解决了问题。
当然之后咱们也会在利用配置栏里加上倡议:举荐 JVM 的配置小于容器限度的 2/3,预留一些内存。
其实实质上还是开发模式没有转变过去,以传统的 Java 利用开发模式甚至都不会去理解容器的内存大小,因为以前大家的利用都是部署在一个内存较大的虚拟机上,所以感知不到容器内存的限度。
从而误以为将两者画了等号,这一点可能在 Java 利用中尤为显著,毕竟多了一个 JVM;甚至在老版本的 JDK 中如果没有设置堆内存大小,无奈感知到容器的内存限度,从而主动生成的 Xmx 大于了容器的内存大小,以致于 OOM。