好久没写文章了, 今天之所以突然心血来潮, 是因为昨天出现了这样一个情况:
我们公司的某个手机APP后端的用户(customer)微服务出现内存泄露, 导致OutOfMemoryError, 但是因为经过我们精心优化的openjdk容器参数, 这次故障对用户完全无感知. :muscle::muscle::muscle:
那么我们是如何做到的呢?
我们都知道, 在传统的虚拟机上部署的Java实例. 为了更好地分析问题, 一般都是要加上: -XX:+HeapDumpOnOutOfMemoryError
这个参数的. 加这个参数后, 如果遇到内存溢出, 就会自动生成HeapDump, 后面我们可以拿到这个HeapDump来更精确地分析问题.
但是, "大人, 时代变了!"
容器技术的发展, 给传统运维模式带来了巨大的挑战, 这个挑战是革命性的:
简单总结一下, 在使用容器平台后, 我们的工作倾向于:
所以, 针对Java应用容器, 我们也要优化以满足这种需求, 以OutOfMemoryError
故障为例:
-XX:+ExitOnOutOfMemoryError
就正好满足这种需求:
传递此参数时,抛出OutOfMemoryError时JVM将立即退出。 如果您想终止应用程序,则可以传递此参数。
让我们重新回顾故障: "我们公司的某个手机APP后端的用户(customer)微服务出现内存泄露, 导致OutOfMemoryError"
该customer应用概述如下:
完整的过程如下:
OutOfMomoryError
-XX:+ExitOnOutOfMemoryError
, 该实例的JVM(PID为1)立即退出.pid 1
进程退出, 此时pod立刻出于Terminating
状态, 并且变为:Terminated
在此过程中, 用户基本上是对后台故障"无感知"的.
当然, 要做到这些, 其实JVM参数以及启动脚本中, 还有很多细节和门道. 如: 启动脚本应该是: exec java ....$*
有机会再写文章分享.
上边一章, 我们解释了"为什么Java容器推荐使用ExitOnOutOfMemoryError而非HeapDumpOnOutOfMemoryError", 但是细心的小伙伴也会发现, 新的配置也会带来新的问题, 比如:
这些其实可以通过其他手段来解决:
Readiness Probe
, 只要Readiness Probe
探测失败, K8S就会自动将这个节点从SVC中摘除. 那么合理的Readiness Probe
在这里指的就是应用不可用时, Readiness Probe
探测必然是失败的. 所以一般不能是探测某个端口是否在监听, 而是应该是探测对应的api是否正常. 如下方.jcmd
等命令手动做heapdump.readinessProbe:
httpGet:
path: /actuator/info
port: 8088
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 3
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
复制代码
新的技术带来新的变革, 我们需要以发展的眼光看待"最佳实践, 最佳配置".