如何设置docker 容器内存大小容器中Java应用的内存限制

实例解读:如何减少Docker中的Java内存消耗
实例解读:如何减少Docker中的Java内存消耗
【IT168 编译】最近,我所在的团队面临着部署微服务(Java+SpringMVC in Docker on AWS)的问题。主要问题是,很多非常轻巧的应用程序消耗了太多的内存。因此,我们经过多方尝试找到了在Docker中关于Java内存消耗的问题,并通过重构和迁移到Spring Boot实现了减少消耗的方法。本文,我将和大家分享这整个过程,希望能够对大家有所帮助。在部署之前,我们要估计应用程序消耗多少内存。为此,我们制定了一个清晰简单的方程来找到RSS:RSS = Heap size + MetaSpace + OffHeap size这里OffHeap由线程堆栈,缓冲区,库(* .jars)和JVM代码组成。Resident Set Size是当前分配给进程使用的RAM的数量。它包括代码、数据和共享库。让我们根据本地Java VisualVM值找到它:RSS = 253(Heap) + 100(Metaspace) + 170(OffHeap) + 52*1(Threads) = 600Mb (max avarage)所以,我们得出结果:大概600Mb就够了。我们选择了一个t2.micro AWS实例(具有1Gb RAM)进行部署,并开始部署应用程序。首先,通过JVM选项提供有关内存配置的一些信息:此外,作为应用程序的基础图像,我们选择了 ,因为我们发现它是 Jetty Java *.wars中最轻量级的图像之一。正如前文所提到的600Mb就足够了,所以启动了一个容器,其容量如下:docker run -m 600m那么你觉得这样会发生什么?我们的集装箱会由于内存不足被DD(Docker daemon)杀死。这个容器是在本地启动的,具有完全相同的参数,通过逐步增加容器的内存限制,我们达到了700&我开玩笑的,我们有850mb。经过观察和阅读有用的文章,我们决定进行测量。结果是非常奇怪和有争议的。Heap Size与我们本地相同:但Docker显示了一些疯狂的统计数据:发生了什么事?情况变得非常混乱...我们花了很多时间去搞清楚这些数据为什么会有争议,在搜索过程中发现我们并不是个例。在查找了很多资料,分析了 应用之后,我们几乎找到了答案。大部分额外的内存都是被用于存储编译的类及其元数据。那么有关JavaVM / Docker统计数据的争议数字呢?事实证明,Java VisualVM不了解OffHeap的内容,因此,使用此工具调查Java应用程序的内存消耗会非常棘手。此外,了解您使用的JVM选项也是至关重要。在我看来,指定-Xmx=512m告诉JVN分配512mb Heap,但是它并不告诉JVM整个内存使用要限制在512mb,所以就会出现代码缓存和其它非Heap数据占用内存直至超过。所以,要指定总内存需要使用XX:MaxRAM参数。请注意,使用MaxRam = 512m,你的Heap约为250mb,这时就要注意应用程序JVM选项。星期一早上,寻找灵丹妙药...NMT和Java VisualVM Memory Sampler 可以帮助我们发现内部核心框架在内存中多次重写的依赖,并且重复的数量等于微服务中子模块的数量。如果想要更好的掌握这一点,首先要说明一下我们的&微服务&结构:这是NMT(在本地机器上)的一个模块快照(具有73MB加载的类元数据,42MB线程和37MB的代码,包括lib):据我所知,构建这样的应用程序是一个很大的错误。首先,每个* .war已经作为一个单独的应用程序部署在Jetty servlet容器中,这是非常奇怪的。因为根据定义,微服务应该是一个部署应用程序(部署单元)。其次,Jetty在内存中分别保存每个* .war的所有必需库,即使所有这些库具有相同的版本。因此,DB连接、核心框架的各种基本功能等都会在内存中重复。一般的解决方案就是重构,让应用成为真正的微服务器。此外,我们可能还需要一整套的Jetty,但是一般人听到它的报价就能望而却步。圈内有句著名的评论&不要在Jetty中部署应用程序,而是要在应用程序中部署Jetty。我们决定尝试使用嵌入式Jetty的Spring Boot,因为它是独立应用程序中最常用的工具,尤其是在我们的案例中。配置很少,没有XML,每个Spring框架都有优势,还有很多插件,自动配置。除此之外,网上还有大量的实用教程和文章,这些都让Spring Boot看起来是个不错的选择。由于我们不再需要单独的Jetty应用程序服务器,所以我们将基础Docker的图像更改为轻量级的openjdk。openjdk:8-jre-alpine根据新的要求重构了应用程序之后,我们得到了类似下图的东西:现在一切都准备好了,我们来测试一下。先从Java VirtualVM进行测试:从数据中可以看出,虽然新版本有了一些改进,但是与之前的应用程序相比变化并没有那么大:然后,我们再来看看Docker的统计数据:结果很明显,我们已经减少了内存消耗。结论对我们团队来说,这是一个很有意思的挑战。找到导致错误或者某件事情的根本原因,会让你在特定领域中看得更深更广。网上的资源非常丰富,如果你下定决心去查找答案,那么就一定会找得到。另外,从这次事件中也认识到,不要完全信任Java VisualVM的内存消耗预计。对于技术的学习和性能的提高,我有三个更多和大家分享,阅读更多,改进更多,调查更多。另外,避免常规,尽可能自动化,这样你可以更有效的进行工作。本文对你是否有帮助?欢迎在下方留言评论或者反馈您遇到的问题。
本文仅代表作者观点,不代表百度立场。系作者授权百家号发表,未经许可不得转载。
百家号 最近更新:
简介: IT垂直媒体
作者最新文章关注社区微信公众号: PMvideo
在 Docker 里跑 Java,你必须知道的那些事儿!
背景:众所周知,当我们执行没有任何调优参数(如“java-jar mypplication-fat.jar”)的 Java 应用程序时,JVM 会自动调整几个参数,以便在执行环境中具有最佳性能。
但是许多开发者发现,如果让 JVM ergonomics (即JVM人体工程学,用于自动选择和行为调整)对垃圾收集器、堆大小和运行编译器使用默认设置值,运行在 Linux 容器(docker,rkt,runC,lxcfs 等)中的 Java 进程会与我们的预期表现严重不符。
本篇文章采用简单的方法来向开发人员展示在 Linux 容器中打包 Java 应用程序时应该知道什么。
懒人超精简阅读版:
a.JVM 做不了内存限制,一旦超出资源限制,容器就会出错
b.即使你多给些内存资源,也没什么卵用,只会错上加错
c.解决方案:用 Dockfile 中的环境变量来定义 JVM 的额外参数
d.更进一步:使用由 Fabric8 社区提供的基础 Docker 镜像来定义 Java 应用程序,将始终根据容器调整堆大小
详细全文:
我们往往把容器当虚拟机,让它定义一些虚拟 CPU 和虚拟内存。其实容器更像是一种隔离机制:它可以让一个进程中的资源(CPU,内存,文件系统,网络等)与另一个进程中的资源完全隔离。Linux 内核中的 cgroups 功能用于实现这种隔离。
然而,一些从执行环境收集信息的应用程序已经在 cgroups 存在之前就被执行了。“top”,“free”,“ps”,甚至 JVM 等工具都没有针对在容器内执行高度受限的 Linux 进程进行优化。
1.存在的问题
为了演示,我用“docker-machine create -d virtualbox –virtualbox-memory ‘1024’ docker1024”在1GB RAM的虚拟机中创建了 docker daemon。接下来,在一个虚拟内存为100MB的容器里面跑三个不同的Linux distribution,执行 “free -h”命令,结果是:它们都显示了995MB的总内存。
即使在 Kubernetes / OpenShift 集群中,结果也类似。
我在一个15GB内存的集群中跑一个 Kubernetes Pod ,并将 Pod 的内存限制为512M (通过“kubectl run mycentos –image=centos -it –limits=’memory=512Mi'”命令实现),最后显示的总内存却是14GB。
如果想知道为什么会发生这种情况,建议您阅读博客(//memory-inside-linux-containers/)
docker switches(-m,-memory和-memory-swap)和kubernetes switch(–limits)在进程超过限制的情况下,会指示Linux内核杀死该进程;但JVM是完全不知道限制,所以在进程超过限制的时候,糟糕的事情就发生了!
为了模拟在超过指定的内存限制后被杀死的进程,我们可以通过“docker run -it –name mywildfly -m=50m jboss/wildfly”命令在50MB内存限制的容器中跑WildFly应用server,用“dockerstats”命令来检查容器限制。
但是在几秒钟之后,Wildfly的容器执行将被中断并显示:*** JBossAS process (55) received KILL signal ***
“docker inspect mywildfly -f ‘{{json.State}}'”命令显示由于OOM(内存不足),该容器已被杀死。注意容器“state”中的OOMKilled = true。
2.JAVA的应用程序是如何被影响的?
在docker daemon里用Dockerfile中定义的参数-XX:+ PrintFlagsFinal和-XX:+ PrintGCDetails起一个java应用。
其中 machine:1GB RAM容器内存:限制为150M(对于这个Spring Boot应用,似乎够用)
这些参数允许我们读取初始JVM人机工程学参数,并了解有关垃圾收集(GC)执行的详细信息。
动手试一下:
我已经在“/ api / memory /”上准备了一个端点,它使用String对象加载JVM内存来模拟消耗大量内存的操作。我们来调用一次:
此端点将回复“分配超过80%(219.8 MiB)的最大允许JVM内存大小(241.7 MiB)”
在这里我们可以提至少两个问题:
为什么JVM最大允许内存241.7 MiB?
如果这个容器将内存限制为150MB,那为什么它允许Java分配近220MB?
首先,我们需要回顾一下JVM人机工程学页面上关于“最大堆大小”的内容:是物理内存的1/4。由于JVM不知道它在一个容器内执行,所以允许最大堆大小将接近260MB。鉴于我们在容器初始化期间添加了-XX:+ PrintFlagsFinal标志,我们可以检查这个值:
其次,我们需要了解,当我们在docker命令行中使用参数“-m 150M”时,docker daemon将在RAM中限制150M,在Swap中限制为150M。因此,该过程可以分配300M。这就解释了为什么我们的进程没有被杀死。
docker命令行中的内存限制(-memory)和swap(-memory-swap)之间的更多组合可以在这里(/engine/reference/run/#example-run-htop-inside-a-container)找到。
3.提供更多内存是否靠谱?
不了解问题的开发者往往认为环境不能为执行JVM提供足够的内存。所以通常的解决办法是提供更多内存,这实际上会使事情变得更糟。
我们假设将daemon从1GB更改为8GB(使用“docker-machinecreate -d virtualbox –virtualbox-memory ‘8192’ docker8192”创建),并将容器内存从150M更改为800M:
请注意这次,“curl http://`docker-machine ipdocker/api/memory”命令甚至没有执行完,因为在8GB环境中计算的JVM的MaxHeapSize为字节(?2GB)。检查“docker logs mycontainer|grep -i MaxHeapSize”
该应用将尝试分配超过1.6GB的内存,这超出了此容器的限制(RAM中的800MB + Swap中的800MB),并且该进程将被杀掉。
很显然,用增加内存且让JVM自定义参数的方式在容器里跑Java,不是什么好主意。在容器内部运行Java应用程序时,我们应该根据应用程序需求和容器限制设置最大堆大小(-Xmx参数)。
4.解决方案
Dockerfile的一个细微变化允许用户指定一个环境变量来定义JVM的额外参数。检查以下行:
现在我们可以使用JAVA_OPTIONS环境变量来通知JVM堆的大小。对于这个应用程序,300M就够了。稍后可以检查日志并获取字节(300MBi)的值
对于docker,您可以使用“-e”switch指定环境变量。
在Kubernetes中,您可以使用switch“-env = [key = value]”设置环境变量:
如果可以根据容器限制自动计算堆的值,该怎么做?
使用由Fabric8社区提供的基础Docker镜像,就可以搞定。这个镜像fabric8 / java-jboss-openjdk8-jdk使用一个脚本来计算容器限制,并使用50%的可用内存作为上限。请注意,这个50%的内存比可以被复写。您还可以使用此镜像来启用/禁用调试,诊断等。
下面一起看看Dockerfile是如何作用于这个Spring Boot应用程序:
搞定!现在,无论容器内存限制是多少,我们的Java应用程序将始终根据容器调整堆大小,而不是根据daemon调整堆大小。
直到现在,Java JVM依然没有提供什么支持,让大家可以理解它在容器内是如何运行的,而且它有一些资源是内存和CPU限制的。因此,您不能让JVM人体工程学本身决定最大堆大小。
解决此问题的一种方法是使用能够理解它在受限容器内运行的Fabric8 Base镜像。
在JVM中有一个实验支持,已经包含在JDK9中以支持容器(即Docker)环境中的cgroup内存限制。可以参考:
原文评论:更好的方法是以exec表单定义您的CMD指令,这将确保java是PID 1进程-这对于允许Java在容器停止时正常关闭至关重要。
Exec表单不支持环境变量替换,但您可以通过设置JAVA_TOOL_OPTIONS环境变量来传递其他命令行标志(请参阅)
笔记社区是一个面向中高端IT开发者、程序员的知识共享社区,通过网络抓取与文章分类总结,由专家为用户提供高质量的专题文章系列。
原文链接:/p/
声明:所有文章资源均从网络抓取,如果侵犯到您的著作权,请联系删除文章。联系方式请关注微信公众号PMvideo【锤子视频-程序员喜欢的短视频】,笔记社区开发者交流群 。
今日签到7人
关注微信公众号:PMvideoDocker使用之Java web应用部署 - 崛起中的邹鹰 - ITeye博客
博客分类:
此篇博客一部分内容有赖于上一篇博客中已经陈述过的信息,如需了解,请移步:
/blog/2365651
切入正题,上一篇中介绍了如何在Docker中部署Tomcat,接下来将详细说明如何在Docker的Tomcat容器中部署war包。
通常情况下,一个web应用都脱不开数据库操作,这里我们以MySQL为例来说明一个web应用Docker、一个MySQL数据库Docker构成的最基本的应用实例
首先获取MySQL的最新Docker:
docker pull daocloud.io/mysql:latest
下载后得到的image通过以下命令查看:
root@danlley-VirtualBox:/home/danlley# docker images
REPOSITORY
a32bca93f476
About an hour ago
2 hours ago
abbbfb1f232d
12 hours ago
daocloud.io/mysql
7666f75adb6b
8 weeks ago
daocloud.io/ubuntu
f49eec89601e
9 weeks ago
接下来直接用镜像生成MySQL容器,并启动:
docker run -d -p
-e MYSQL_ROOT_PASSWORD=6f75adb6b
注意:这里默认的docker是没有设定root用户的密码的,为方便后面对数据库docker进行管理,我们对root的密码进行了初始化,同时,如果你是计划将已经有的web应用迁移到docker的话,这里的端口号依然使用3306可以为你后面的工作省一些力气。
数据库docker启动成功以后,就需要对该数据库进行管理,根据自身的应用,导入数据库表及结构,给相应的数据库实例开管理账号等,这些都是和平时的做法没什么差别了。
至此,数据库的docker准备工作结束,接下来开始准备部署war包到Tomcat容器并进行应用调试。
docker的war包部署相对简单,将war包放在Dockerfile相应的同一个目录下(其中Tomcat、jdk等都放该路径),编写Dockerfile文件:
daocloud.io/ubuntu:latest
MAINTAINER
# now add java and tomcat support in the container
ADD jdk-8u121-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.0.M18.tar.gz /usr/local/
# configuration of java and tomcat ENV
ENV JAVA_HOME /usr/local/jdk1.8.0_121
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.0.M18
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.0.M18
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
# add war in webapps
ADD ./myteay-web.war /usr/local/apache-tomcat-9.0.0.M18/webapps
# container listener port
EXPOSE 8080
# startup wev application services by self
CMD /usr/local/apache-tomcat-9.0.0.M18/bin/catalina.sh run
接下来通过Dockerfile构建webapp镜像
1、生成镜像:
root@danlley-VirtualBox:/home/danlley/dockerfiles# docker build .
2、为生成的镜像打tag:
root@danlley-VirtualBox:/home/danlley/dockerfiles# docker tag abbbfb1f232d webapp
3、启动镜像:
docker run -d -p
4、测试镜像部署结果,本地浏览器访问地址: http://192.168.56.102:8090
至此,一个由docker构成的简单web应用搭建成功。
各位看官,原创不易啊,转载请注明出处:
看在打字不易的份上,打赏一个吧
参考资料:
浏览: 275400 次
来自: 西安
最好这样:java -Xms3700M -Xmx3700M - ...
学习一哈……
System.out.println(&开始注册Js ...
你的头像看起来很像我们宿舍老四。。。随笔 - 110&
文章 - 0&评论 - 7&trackbacks - 0
&默认情况下,容器没有资源的限制,它可以使用整个主机的所有资源。Dcoker提供了控制资源的方法,
&多少内存,CPU,IO,都可以在docker run使用标志符来设置。
Docker可以强制执行硬内存限制,允许容器使用不超过给定数量的用户或系统内存,
或软限制,允许容器使用所需的内存,除非满足某些条件,例如 内核检测到主机上的低内存或争用。
当单独使用或设置多个选项时,这些选项中的一些具有不同的效果。
-m&or&--memory=
容器能使用的最大的内存。
如果你设置了这个选项,最小允许使用的值为4M。
--memory-swap*
容器允许swap的内存大小。
更多细节:.
--memory-swappiness
默认情况下,不需要设置。
主机内核可以让容器使用一定比例的匿名内存页。你可以通过这个参数来设置这个百分比。更多细节:&.
--memory-reservation
允许你指定软限制,比--memory 参数值要小。这软限制在Docker检测到主机上的连接或内存较少时激活。
--kernel-memory
容器可以使用的最大的内核内存。最小值是4M。&因为内核内存不能被交换出去,一个缺少内核内存的容器可能会阻碍主机的资源,这会对主机和其他容器产生负面影响。
更多细节:.
--oom-kill-disable
默认情况下:out-of-memory(oom)内存溢出的错误出现,内核杀死容器中的进程。使用--oom-kill-disable 可以改变这种行为。 仅仅在已经设置了-m 的容器中关闭 OOM 杀死。&如果-m 标志没有设置,主机可能耗尽内存,内核可能需要杀死主机系统进程来释放内存。
--memory-swap 细节
如果没有设置,而--me内核内存限制以分配给容器的总内存来表示。mory设置如300m了,则swap默认为300*2=600M
如果--memory,--memory-swap都设置了
--memory-swap 代表了可以使用的总共内存,--memory 代表不使用swap的内存
如:--memory-swap=1g,--memory=300M,
docker可以使用300M内存,1g-300m=700m swap.
如果设置为-1,则容器不限制使用swap 内存。
OptionDescription
--cpu-shares
Set this flag to a value greater or less than the default of 1024 to increase or reduce the container’s weight, and give it access to a greater or lesser proportion of the host machine’s CPU cycles. This is only enforced when CPU cycles are constrained. When plenty of CPU cycles are available, all containers use as much CPU as they need. In that way, this is a soft limit.&--cpu-sharesdoes not prevent containers from being scheduled in swarm mode. It prioritizes container CPU resources for the available CPU cycles. It does not guarantee or reserve any specific CPU access.
--cpu-period
容器上一个逻辑CPU的调度周期.&--cpu-periodmor默认值是ms)
--cpu-quota
在由--cpu-period设置的时间段内容器可以调度的最大时间量。
--cpuset-cpus
使用这个选项指定CPU使用一个或者多个CPU核,并用逗号分隔。
--cpu-period 和 --cpu-qota
例子:如果你有1核的CPU系统,容器run使用--cpu-period=100000
--cpu-quota=50000,容器可以消耗高达50%的1个CPU
$ docker run -ti --cpu-period=<span style="color: #000 --cpu-quota=<span style="color: #000 busybox
如果你有4核的CPU系统,容器运行--cpu-period=100000
--cpu-quota=200000,容器可以使用最多2个逻辑CPU(--cpu-period的200%)
$ docker run -ti --cpu-period=<span style="color: #0000 --cpu-quota=<span style="color: #0000
--cpu-set-cpus
给容器实际4核的CPU
$ docker run -ti --cpuset-cpus=<span style="color: # busybox
有两个选项可用于调整给定容器对直接块IO设备的访问。
您还可以按照每秒的字节数或每秒的IO操作来指定带宽限制。
OptionDescription
blkio-weight
By default, each container can use the same proportion of block IO bandwidth (blkio). The default weight is 500. To raise or lower the proportion of blkio used by a given container, set the&--blkio-weight&flag to a value between 10 and 1000. This setting affects all block IO devices equally.
blkio-weight-device
The same as&--blkio-weight, but you can set a weight per device, using the syntax&--blkio-weight-device="DEVICE_NAME:WEIGHT"&The DEVICE_NAME:WEIGHT is a string containing a colon-separated device name and weight.
--device-read-bps&and--device-write-bps
Limits the read or write rate to or from a device by size, using a suffix of&kb,&mb, or&gb.
--device-read-iops&or--device-write-iops
Limits the read or write rate to or from a device by IO operations per second.
如果指定--blkio-weight和-blkio-weight-device,Docker使用--blkio-weight作为默认权重,
并使用--blkio-weight-device重写命名设备上的默认值。
要将/ dev / sda的容器的设备权重设置为200,而不指定默认blkio-weight:
$ docker run -it \
--blkio-weight-device "/dev/sda:200" \
此示例将ubuntu容器限制为从/ dev / sda到每秒1000次IO操作的最大读取速率:
$ docker run -ti --device-read-iops /dev/sda:<span style="color: #00 ubuntu
默认容器没有资源的限制,Docker提供了控制方法;
内存,CPU,IO
docker run + 标志符来设置
-m 容器能使用的最大内存
--memory--reservation&
比-m的值要小,在docker检测到主机的内存较少时激活
--cpu-period, --cpu-quota
允许容器使用50%的CPU
--cpu-period = 10 ,--cpu-quota=5&
允许容器使用主机4核中的两核
--cpu-period = 10 , --cpu-quota=20
控制每秒的字节数或每秒的IO操作来限制带宽
限制从/dev/sda 每秒1000次的IO读取操作
docker run -ti --device-read-iops /dev/sda:1000 ubuntu
阅读(...) 评论()

我要回帖

更多关于 docker 容器内存大小 的文章

 

随机推荐