为公司新的架构的技术选型为,springCloud 架构搭建微服务,在ECS以docker形式,部署每个微服务。并为每个docker容器,设置内存限制。在服务部署上线后,发现经常有微服务,莫名的停止。日志上却没有任何error错误。很让人捉急。 具体配置如下: 1、docker 容器的创建: 为容器配置 640M的内存限制,但不限制 虚拟内存的使用。 2、jar 启动命令如下(start.sh): 重点如下: 设置 jvm 的最大内存和初始内存均为512M,项目能顺利启动。 但是,经常不知所云的停止。 第一能想到的是,内存溢出,造成服务停止,但是查看日志,又没有任何相关信息。 无奈,只能先解决当前的问题,即,让服务停止后,能自动重启。 尽量保证服务的不可用时长,最小化。 因此,写了个脚本,每隔1秒,检查一次服务线程。如果停止了,就重新启动,如下: 虽然,能将服务的停用时间,缩短到1秒内(不含启动时间)。【但不能根本找出问题,就无法根本解决,还得继续寻找问题的根源】。 我们知道,Docker使用控制组(cgroups)来限制资源。在容器中运行应用程序时限制内存和CPU绝对是个好主意――它可以阻止应用程序占用整个可用内存及/或CPU,这会导致在同一个系统上运行的其他容器毫无反应。限制资源可提高应用程序的可靠性和稳定性。它还允许为硬件容量作好规划。在Kubernetes或DC/OS之类的编排系统上运行容器时尤为重要。 首先Docker容器本质是是宿主机上的一个进程,它与宿主机共享一个/proc目录,也就是说我们在容器内看到的/proc/meminfo,/proc/cpuinfo 与直接在宿主机上看到的一致,如下。 Host 容器 那么Java是如何获取到Host的内存信息的呢?没错就是通过/proc/meminfo来获取到的。 默认情况下,JVM的Max Heap Size是系统内存的1/4,假如我们系统是8G,那么JVM将的默认Heap≈2G。 Docker通过CGroups完成的是对内存的限制,而/proc目录是已只读形式挂载到容器中的,由于默认情况下Java 压根就看不见CGroups的限制的内存大小,而默认使用/proc/meminfo中的信息作为内存信息进行启动, 这种不兼容情况会导致,如果容器分配的内存小于JVM的内存,JVM进程会被直接杀死。 划重点:如果容器分配的内存小于JVM的内存,JVM进程会被直接杀死。 我们来回顾一下,上面创建容器的限制内存上限为640M。 java项目设置的JVM 堆内存为最大为512M,注意是堆内存 。我们知道,JVM 除了堆区,还有非堆区(Metaspace等本地内存区)以及还有其他内存使用。 而 640-512 剩下128M。 因此,在这种情况下,如果我们的程序,出现了大量的内存溢出,GC还没来得及回收的情况下,就已经因为内存达到容器限制,而线程被docker直接杀死了。 那也就不会存在,报错信息的出现。 而是项目直接down掉了。 到此,问题的大概,已经明白的差不多了。那就开始解决问题吧: 1、能不能不让docker容器,杀死我的项目。就算内存爆了 ,也不直接kill。 开始查找相关资料,幸运找到: docker内存限制相关的参数 执行 OOM killer 默认情况下,在出现 out-of-memory(OOM) 错误时,系统会杀死容器内的进程来获取更多空闲内存。这个杀死进程来节省内存的进程,我们姑且叫它 OOM killer。我们可以通过设置 下面的例子限制了容器的内存为 100M 并禁止了 OOM killer: 是正确的使用方法。 而下面这个容器没设置内存限制,却禁用了 OOM killer 是非常危险的: 容器没用内存限制,可能或导致系统无内存可用,并尝试时杀死系统进程来获取更多可用内存。 一般一个容器只有一个进程,这个唯一进程被杀死,容器也就被杀死了。我们可以通过 接下来,删除容器。 加上这个 –oom-kill-disable 重新运行容器。 然后新的问题出现了。 重启容器后, 项目虽然不会被kill掉, 但因为内存爆满,达到容器限制,却没有出现 out-of-memory(OOM)错误,而是请求,一直卡在那里。 原因,前面也说过了。 就是 容器剩余的 内存,没有来得及GC线程启用,就瞬间被 堆占用了。 解决方法: 再次重启容器, 通过 java jvisualvm工具,可以看到 堆内存已经占满了,并且日志中,也出现了 java.lang.OutOfMemoryError: Java heap space 异常信息了。 至此,问题是基本解决了。 但又有一个需要优化的地方。 那就是 在 启动命令中, 我是设置了 -Xms -Xmx 的固定值。 原因,不用多说, 因为jvm 默认对容器的资源限制是无感的。 而是以宿主机的资源为主。那就会因为 JVM内存达到容器限制,而被kill掉。 但这意味着需要控制内存两次,一次在Docker中,一次在JVM中。每当想要做出改变时,必须做两次,不理想。 能不能,有个办法,可以让JVM 感知 docker 容器的资源限制。 这样,我可以不设置 JVM 堆的固定配置。还是开始找资料,发现 在JDK 8u131及以后版本开始支持了Docker的cpu和memory限制。 很幸运的是,本项目使用的是 JDK8u241版本。 在java8u131+及java9,需要加上-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap 才能使得Xmx感知docker的memory limit。 查看参数默认值 部分输出 可以看到在java9,UseCGroupMemoryLimitForHeap参数还是实验性的,默认关闭。 其他参数: 这三个参数是JDK8U191为适配Docker容器新增的几个参数(单位 百分比),类比Xmx、Xms。 这里提供如下建议: 注意:复制的时候,可能有空格格式问题,造成一些其他的报错信息。 前提说明:
docker run -dit -m 640M --memory-swap -1 --net docker-network-dev --ip 192.168.0.100 --restart=always --privileged=true --name=keda6-dev-information-main --hostname=slave_informationr_main -v /home/docker/springCloud/project/keda6-information-main/:/var/local/project/ -v /home/springCloud/project/keda6-information-main/:/home/springCloud/ -v /etc/localtime:/etc/localtime -e TZ='Asia/Shanghai' -e LANG="en_US.UTF-8" -p 30009:30009 -p 20209:20209 -p 20009:9820 -p 2199:22 seowen/jdk8u241-project:latest /usr/sbin/init
#!/bin/bash ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime echo "Asia/Shanghai" > /etc/timezone sh stop.sh #sh jmx.sh JARR=$(ls -lt /var/local/project/ | grep 'keda-' | head -n 1 |awk '{print $9}') nohup java -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=192.168.1.126 -Dcom.sun.management.jmxremote.port=30009 -Dcom.sun.management.jmxremote.rmi.port=30009 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.access.file=/usr/local/jmx/jmxremote.access -Dcom.sun.management.jmxremote.password.file=/usr/local/jmx/jmxremote.password -Dcom.sun.management.jmxremote.ssl=false -Xms512m -Xmx512m -jar $JARR --server.port=9820 --eureka.instance.non-secure-port=20009 --management.server.port=20209 --eureka.instance.ip-address=192.168.1.126 --eureka.instance.hostname=192.168.1.126 --spring.profiles.active=dev &
-Xms512m -Xmx512m -jar $JARR
always.sh
#!/bin/bash while : do PID=$(ps -ef | grep keda | grep -v grep | awk '{ print $2 }') if [ -z "$PID" ] then sh start.sh fi sleep 1 done &
docker run
命令时能使用的和内存限制相关的所有选项如下。
选项
描述
-m
,--memory
内存限制,格式是数字加单位,单位可以为 b,k,m,g。最小为 4M
--memory-swap
内存+交换分区大小总限制。格式同上。必须必
-m
设置的大
--memory-reservation
内存的软性限制。格式同上
--oom-kill-disable
是否阻止 OOM killer 杀死容器,默认没设置
--oom-score-adj
容器被 OOM killer 杀死的优先级,范围是[-1000, 1000],默认为 0
--memory-swappiness
用于设置容器的虚拟内存控制行为。值为 0~100 之间的整数
--kernel-memory
核心内存限制。格式同上,最小为 4M
--oom-kill-disable
选项来禁止 OOM killer 杀死容器内进程。但请确保只有在使用了-m/--memory
选项时才使用--oom-kill-disable
禁用 OOM killer。如果没有设置-m
选项,却禁用了 OOM-killer,可能会造成出现 out-of-memory 错误时,系统通过杀死宿主机进程或获取更改内存。
$ docker run -it -m 100M --oom-kill-disable ubuntu:16.04 /bin/bash
$ docker run -it --oom-kill-disable ubuntu:16.04 /bin/bash
--oom-score-adj
选项来设置在系统内存不够时,容器被杀死的优先级。负值更教不可能被杀死,而正值更有可能被杀死。
docker run -dit -m 640M --memory-swap -1 --oom-kill-disable --net docker-network-dev --ip 192.168.0.100 --restart=always --privileged=true --name=keda6-dev-information-main --hostname=slave_informationr_main -v /home/docker/springCloud/project/keda6-information-main/:/var/local/project/ -v /home/springCloud/project/keda6-information-main/:/home/springCloud/ -v /etc/localtime:/etc/localtime -e TZ='Asia/Shanghai' -e LANG="en_US.UTF-8" -p 30009:30009 -p 20209:20209 -p 20009:9820 -p 2199:22 seowen/jdk8u241-project:latest /usr/sbin/init
减少 jvm 堆内存,腾出更多空闲内存,或得增加容器的 内存限制。 (现在能体会到JVM 最大堆默认为 系统的 1/4的原因了)
memory limit
java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal
bool UseCGroupMemoryLimitForHeap = false {experimental} {default}
举例说明:假如docker容器内存限制是6G,那么:
-XX:MaxRAMPercentage=75 -XX:InitialRAMPercentage=75 -XX:MinRAMPercentage=75 -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m 等价于: -Xmx4608m -Xms4608m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m
最终启动命令如下:
nohup java -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=192.168.1.126 -Dcom.sun.management.jmxremote.port=30009 -Dcom.sun.management.jmxremote.rmi.port=30009 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.access.file=/usr/local/jmx/jmxremote.access -Dcom.sun.management.jmxremote.password.file=/usr/local/jmx/jmxremote.password -Dcom.sun.management.jmxremote.ssl=false -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMPercentage=50.0 -XX:InitialRAMPercentage=50.0 -XX:MinRAMPercentage=50.0 -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -XshowSettings:vm -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/springCloud/heapLogs -jar $JARR --server.port=9820 --eureka.instance.non-secure-port=20009 --management.server.port=20209 --eureka.instance.ip-address=192.168.1.126 --eureka.instance.hostname=192.168.1.126 --spring.profiles.active=dev &
建议:
如果需要排查问题时,最好在 JVM 参数中加上
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution
让 GC log 更加详细,方便定位问题。
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算