Kubernetes之路 1 - Java应用资源限制的迷思

摘要: 随着容器技术的成熟,越来越多的企业客户在企业中选择Docker和Kubernetes作为应用平台的基础。然而在实践过程中,还会遇到很多具体问题。本文分析并解决了Java应用在容器使用过程中关于Heap大小设置的一个常见问题。

图片描述

随着容器技术的成熟,越来越多的企业客户在企业中选择Docker和Kubernetes作为应用平台的基础。然而在实践过程中,还会遇到很多具体问题。本系列文章会记录阿里云容器服务团队在支持客户中的一些心得体会和最佳实践。我们也欢迎您通过邮件和钉钉群和我们联系,分享您的思路和遇到的问题。

问题

有些同学反映:自己设置了容器的资源限制,但是Java应用容器在运行中还是会莫名奇妙地被OOM Killer干掉。

这背后一个非常常见的原因是:没有正确设置容器的资源限制以及对应的JVM的堆空间大小。

我们拿一个tomcat应用为例,其实例代码和Kubernetes部署文件可以从Github中获得。

git clone https://github.com/denverdino/system-info
cd system-info`

下面是一个Kubernetes的Pod的定义描述:

1.Pod中的app是一个初始化容器,负责把一个JSP应用拷贝到 tomcat 容器的 “webapps”目录下。注: 镜像中JSP应用index.jsp用于显示JVM和系统资源信息。
2.tomcat 容器会保持运行,而且我们限制了容器最大的内存用量为256MB内存。

apiVersion: v1
kind: Pod
metadata:name: test
spec:initContainers:- image: registry.cn-hangzhou.aliyuncs.com/denverdino/system-info
    name: appimagePullPolicy: IfNotPresentcommand:- "cp"
      - "-r"
      - "/system-info"
      - "/app"
    volumeMounts:- mountPath: /app
      name: app-volumecontainers:- image: tomcat:9-jre8
    name: tomcatimagePullPolicy: IfNotPresentvolumeMounts:- mountPath: /usr/local/tomcat/webapps
      name: app-volumeports:- containerPort: 8080
    resources:requests:memory: "256Mi"cpu: "500m"limits:memory: "256Mi"cpu: "500m"volumes:- name: app-volume
    emptyDir: {}

我们执行如下命令来部署、测试应用

$ kubectl create -f test.yaml
pod "test" created
$ kubectl get pods test
NAME      READY     STATUS    RESTARTS   AGE
test      1/1       Running   0          28s
$ kubectl exec test curl http://localhost:8080/system-info/
...

我们可以看到HTML格式的系统CPU/Memory等信息,我们也可以用 html2text 命令将其转化成为文本格式。

注意:本文是在一个 2C 4G的节点上进行的测试,在不同环境中测试输出的结果会有所不同

$ kubectl exec test curl http://localhost:8080/system-info/ | html2textJava version     Oracle Corporation 1.8.0_162
Operating system Linux 4.9.64
Server           Apache Tomcat/9.0.6
Memory           Used 29 of 57 MB, Max 878 MB
Physica Memory   3951 MB
CPU Cores        2**** Memory MXBean ****
Heap Memory Usage     init = 65011712(63488K) used = 19873704(19407K) committed= 65536000(64000K) max = 921174016(899584K)
Non-Heap Memory Usage init = 2555904(2496K) used = 32944912(32172K) committed =33882112(33088K) max = -1(-1K)

我们可以发现,容器中看到的系统内存是 3951MB,而JVM Heap Size最大是 878MB。纳尼?!我们不是设置容器资源的容量为256MB了吗?如果这样,当应用内存的用量超出了256MB,JVM还没对其进行GC,而JVM进程就会被系统直接OOM干掉了。

问题的根源在于:

  • 对于JVM而言,如果没有设置Heap Size,就会按照宿主机环境的内存大小缺省设置自己的最大堆大小。
  • Docker容器利用CGroup对进程使用的资源进行限制,而在容器中的JVM依然会利用宿主机环境的内存大小和CPU核数进行缺省设置,这导致了JVM
    Heap的错误计算。

类似,JVM缺省的GC、JIT编译线程数量取决于宿主机CPU核数。如果我们在一个节点上运行多个Java应用,即使我们设置了CPU的限制,应用之间依然有可能因为GC线程抢占切换,导致应用性能收到影响。

了解了问题的根源,我们就可以非常简单地解决问题了

解决思路

开启CGroup资源感知

Java社区也关注到这个问题,并在JavaSE8u131+和JDK9 支持了对容器资源限制的自动感知能力 https://blogs.oracle.com/java-platform-group/java-se-support-for-docker-cpu-and-memory-limits

其用法就是添加如下参数

java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap

我们在上文示例的tomcat容器添加环境变量 “JAVA_OPTS”参数

apiVersion: v1
kind: Pod
metadata:name: cgrouptest
spec:initContainers:- image: registry.cn-hangzhou.aliyuncs.com/denverdino/system-info
    name: appimagePullPolicy: IfNotPresentcommand:- "cp"
      - "-r"
      - "/system-info"
      - "/app"
    volumeMounts:- mountPath: /app
      name: app-volumecontainers:- image: tomcat:9-jre8
    name: tomcatimagePullPolicy: IfNotPresentenv:- name: JAVA_OPTS
      value: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"volumeMounts:- mountPath: /usr/local/tomcat/webapps
      name: app-volumeports:- containerPort: 8080
    resources:requests:memory: "256Mi"cpu: "500m"limits:memory: "256Mi"cpu: "500m"volumes:- name: app-volume
    emptyDir: {}

我们部署一个新的Pod,并重复相应的测试

$ kubectl create -f cgroup_test.yaml
pod "cgrouptest" created$ kubectl exec cgrouptest curl http://localhost:8080/system-info/ | html2txt
Java version     Oracle Corporation 1.8.0_162
Operating system Linux 4.9.64
Server           Apache Tomcat/9.0.6
Memory           Used 23 of 44 MB, Max 112 MB
Physica Memory   3951 MB
CPU Cores        2**** Memory MXBean ****
Heap Memory Usage     init = 8388608(8192K) used = 25280928(24688K) committed =46661632(45568K) max = 117440512(114688K)
Non-Heap Memory Usage init = 2555904(2496K) used = 31970840(31221K) committed =32768000(32000K) max = -1(-1K)

我们看到JVM最大的Heap大小变成了112MB,这很不错,这样就能保证我们的应用不会轻易被OOM了。随后问题又来了,为什么我们设置了容器最大内存限制是256MB,而JVM只给Heap设置了112MB的最大值呢?

这就涉及到JVM的内存管理的细节了,JVM中的内存消耗包含Heap和Non-Heap两类;类似Class的元信息,JIT编译过的代码,线程堆栈(thread stack),GC需要的内存空间等都属于Non-Heap内存,所以JVM还会根据CGroup的资源限制预留出部分内存给Non Heap,来保障系统的稳定。(在上面的示例中我们可以看到,tomcat启动后Non Heap占用了近32MB的内存)

在最新的JDK 10中,又对JVM在容器中运行做了进一步的优化和增强。

容器内部感知CGroup资源限制

如果无法利用JDK 8/9的新特性,比如还在使用JDK6的老应用,我们还可以在容器内部利用脚本来获取容器的CGroup资源限制,并通过设置JVM的Heap大小。

Docker1.7开始将容器cgroup信息挂载到容器中,所以应用可以从 /sys/fs/cgroup/memory/memory.limit_in_bytes 等文件获取内存、 CPU等设置,在容器的应用启动命令中根据Cgroup配置正确的资源设置 -Xmx, -XX:ParallelGCThreads等参数

在 https://yq.aliyun.com/articles/18037 一文中已经有相应的示例和代码,本文不再赘述

总结

本文分析了Java应用在容器使用中一个常见Heap设置的问题。容器与虚拟机不同,其资源限制通过CGroup来实现。而容器内部进程如果不感知CGroup的限制,就进行内存、CPU分配可能导致资源冲突和问题。

我们可以非常简单地利用JVM的新特性和自定义脚本来正确设置资源限制。这个可以解决绝大多数资源限制的问题。

关于容器应用中资源限制还有一类问题是,一些比较老的监控工具或者free/top等系统命令,在容器中运行时依然会获取到宿主机的CPU和内存,这导致了一些监控工具在容器中运行时无法正常计算资源消耗。社区中常见的做法是利用 lxcfs 来让容器在资源可见性的行为和虚机保持一致,后续文章会介绍其在Kubernetes上的使用方案。

阿里云Kubernetes服务 全球首批通过Kubernetes一致性认证,简化了Kubernetes集群生命周期管理,内置了与阿里云产品集成,也将进一步简化Kubernetes的开发者体验,帮助用户关注云端应用价值创新。

原文链接
干货好文,请关注扫描以下二维码:
图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/522229.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

你家的饮水机,到底可以有多脏?

戳蓝字“CSDN云计算”关注我们哦!作者 | 胡巍巍出品 | CSDN(ID:CSDNnews)几年前,笔者在一所培训学校工作。因为学生多、学校大,老师也多,自然办公室也多。这种情况下,学校为了省事&a…

ajax前台传json到后台解析的方法以及注意事项

首先&#xff0c;如果是maven工程的话&#xff0c;需要在pom.xml文件中添加下方依赖<dependency><groupId>net.sf.json-lib</groupId><artifactId>json-lib</artifactId><version>2.4</version><classifier>jdk15</classif…

java http get_我是如何进入阿里巴巴的-面向春招应届生Java面试指南(九)

基础篇基本功面向对象的特征1.final, finally, finalize 的区别 final—修饰符&#xff08;关键字&#xff09;如果一个类被声明为final&#xff0c;意味着它不能再派生出新的子类&#xff0c;不能作为父类被继承。因此一个类不能既被声明为 abstract的&#xff0c;又被声明为f…

链表的快慢指针思想的解决

看接下来的一道题目 1、给定一个头结点为 head 的非空单链表&#xff0c;返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 例子&#xff1a;[1,2,3,4,5] 返回中间节点3 [1,2,3,4,5,6] 返回中间节点4 我们该如何做呢&#xff0c;首先这里用到了快慢…

使用Helm 在容器服务k8s集群一键部署wordpress

摘要&#xff1a; Helm 是啥? 微服务和容器化给复杂应用部署与管理带来了极大的挑战。Helm是目前Kubernetes服务编排领域的唯一开源子项目&#xff0c;做为Kubernetes应用的一个包管理工具&#xff0c;可理解为Kubernetes的apt-get / yum&#xff0c;由Deis 公司发起&#xff…

保镖机器人作文_关于机器人作文400字

【第1篇】“爱闯祸”的机器人世纪科技发达&#xff0c;人类发明出了机器人&#xff0c;我也赶时髦&#xff0c;买了一个。可是&#xff0c;这是一个“爱闯祸”的机器人。我的学校正在举行科技展览会&#xff0c;科学老师建议我把机器人带去&#xff0c;让同学们开开眼界。第二天…

Jupyter Notebook数据科学高效技巧

摘要&#xff1a; 本文有一些关于Jupyter Notebook的干货&#xff0c;希望看完文章可以给你带来收获当我学习有关深度学习的优秀的fast.ai课程时&#xff0c;我学到了很多适用于通用软件工程的干货。我写这篇文章是为了总结这些技巧并与你分享。1.Jupyter Notebook拓展标准的Ju…

格罗方德起诉台积电侵犯16项专利、影响巨大;中兴通讯与印尼Smartfren展开合作;网传FB开发新通讯应用Threads……...

关注并标星星CSDN云计算极客头条&#xff1a;速递、最新、绝对有料。这里有企业新动、这里有业界要闻&#xff0c;打起十二分精神&#xff0c;紧跟fashion你可以的&#xff01;每周三次&#xff0c;打卡即read更快、更全了解泛云圈精彩newsgo go go 索尼将于9月5日在IFA 2019上…

JS之前台参数提交到后台,双引号转义为解决办法

问题描述 var param $("#searchForm").serializeJson(); 前台封装好了键值对形式的字符串&#xff0c;使用了EasyUI的treeGrid控件&#xff0c;传到后台后&#xff0c;双引号转义为" 解决办法 apache工具包common-lang中有一个很有用的处理字符串的工具类&am…

MaxCompute理解数据、运算和用户的大脑:基于代价的优化器

摘要&#xff1a; 回顾大数据技术领域大事件&#xff0c;最早可追溯到06年Hadoop的正式启动&#xff0c;而环顾四下&#xff0c;围绕着数据库及数据处理引擎&#xff0c;业内充斥着各种各样的大数据技术。在云栖社区2017在线技术峰会大数据技术峰会上&#xff0c;阿里云大数据计…

python高阶函数闭包装饰器_Python自学从入门到就业之高阶函数、嵌套函数、闭包、装饰器...

高阶函数 在Python中&#xff0c;函数其实也是一种数据类型。 def test(): return hello world print(type(test)) # 函数对应的数据类型是 function,可以把它当做是一种复杂的数据类型。 既然同样都是一种数据类型&#xff0c;我们就可以把它当做数字或者字符串来处理。 定义…

MaxCompute与OSS非结构化数据读写互通(及图像处理实例)

摘要&#xff1a; MaxCompute作为阿里巴巴集团内部绝大多数大数据处理需求的核心计算组件&#xff0c;拥有强大的计算能力&#xff0c;随着集团内外大数据业务的不断扩展&#xff0c;新的数据使用场景也在不断产生。在这样的背景下&#xff0c;MaxCompute&#xff08;ODPS&…

装mysql最后一步没响应_每天14点遭遇惊魂时刻,如何一步一步揪出真凶?

“ 笔者所在的公司有一款大 DAU(日活)的休闲游戏。这款游戏的后端架构很简单&#xff0c;可以简单理解为通讯-逻辑-存储三层结构。其中存储层大量使用了 Redis 和 MySQL。图片来自 Pexels随着存量用户的增加&#xff0c;Redis 就隔三差五的出现问题。所以笔者打算把遇到的一系列…

form表单提交,后台实体类接收转义问题

问题&#xff1a;前台表单用ajax提交&#xff0c;data为validateForm.serializeArray()&#xff0c;后台用实体类接收参数&#xff0c;&符号被转义为&但是从request中直接取值是没问题的&#xff0c;请问如何解决实体类接收到的参数的转义问题。 代码如下&#xff1a;…

AI落地谁最强?AI Top 30+案例评选等你来秀

人工智能历经百年发展&#xff0c;如今迎来发展的黄金时期。目前&#xff0c;AI 技术已涵盖自然语言处理、模式识别、图像识别、数据挖掘、机器学习等领域的研究&#xff0c;在汽车、金融、教育、医疗、安防、零售、家居、文娱、工业等行业获得了令人印象深刻的成果。在各行业宣…

NAT网关之SNAT进阶使用(二)构建ECS级别SNAT出网方式

摘要&#xff1a; NAT网关是云上VPC ECS访问Internet的出入口。阿里云NAT网关控制台创建SNAT条目默认只支持交换机粒度。如何设置ECS粒度的SNAT规则呢&#xff0c;本文将为您揭晓。 背景 NAT网关是云上VPC ECS访问Internet的出入口。阿里云NAT网关控制台创建SNAT条目默认只支持…

java下载json需要的包_jenkins 获取插件,下载插件提速,配置国内镜像

jenkins 是目前比较流行的自动化运维工具&#xff0c;由于 jenkins 的镜像源是国外的所以在下载插件的时候会非常慢&#xff0c;甚至会超时,所以本文就重点介绍如何解决这个问题。正确的做法自然是修改为国内的镜像源&#xff0c;本文使用的是清华大学的镜像源站 清华大学开源软…

使用NAT网关轻松为单台云服务器设置多个公网IP

摘要&#xff1a; 背景 在应用中&#xff0c;有时会遇到用户询问如何使单台云服务器具备多个公网IP的问题。 具体如何操作呢&#xff0c;有了NAT网关这个也不是难题。 配置单云主机多公网IP功能流程 1、为ECS实例配置多块网卡。 2、创建NAT网关。 背景 在应用中&#xff0c;有时…

如何把手变成手控_手把手教您如何在生产环境直接web级设计图形报表

相信很多从事数据分析的兄弟姐妹&#xff0c;对于数据报表的设计&#xff0c;图形化的设计等等&#xff0c;都是停留在后台进行模板设计&#xff0c;设计好模板后&#xff0c;再上传到前台&#xff0c;发布进行显示&#xff0c;这也是目前绝大多数工具的模式。也难怪&#xff0…

form表单提交,后台实体类接收转义问题 解决方案

Ajax传一个实体给后台&#xff0c;以RequestBody来接收&#xff1a; https://blog.csdn.net/zhou_pp/article/details/94738434 前台提交整个表单数据&#xff0c;后台实体类接收&#xff1a;https://blog.csdn.net/dong001687/article/details/89294256 form表单ajax提交&…