Java程序运行在Docker等容器环境有哪些新问题
基本回答
一. 对于Java来说,Docker毕竟是一个较新的环境,其内存、CPU等资源限制是通过ControlGroup实现的。早期的JDK版本并不能识别这些限制,进而会导致一些基础问题。
1.如果未配置合适的JVM堆和元数据区、直接内存等参数,Java就有可能试图使用超过容器限制的内存,最终被容器OOM kill,或者自身发生OOM。
2.错误判断了可获取的CPU资源,例如,Docker限制了CPU的核数,JVM就可能设置不合适的GC并行线程数等。
二. 从应用打包、发布等角度出发JDK自身就比较大,生成的镜像就更为臃肿,当我们的镜像非常多的时候,镜像的存储等开销就比较明显了。
三. 如果考虑到微服务、Serverless等新的架构和场景,Java自身的大小、内存占用、启动速度,都存在一定局限性,因为Java早期的优化大多是针对长时间运行的大型服务器端应用。
如果真的没做过,可以从操作系统、容器原理、JVM内部机制、软件开发实践等角度展示系统性分心新问题、新场景的能力,毕竟变化才是世界永恒的主题,能够在新变化中找出共性与关键,是优秀工程师的必备能力。
扩展
Java在容器环境的局限性来源,Docker到底有什么特别?
虽然看起来Docker之类容器和虚拟机非常相似,例如,有自己的shell,能独立安装软件包,运行时与其他容器互不干扰。但是深入分析会发现,Docker并不是一种完全的虚拟化技术,而更是一种轻量级的隔离技术。
1.从技术角度讲,基于namespace,Docker为每个容器提供了单独的命名空间,对网络,PID,用户,IPC通信,文件系统挂载点等实现了隔离。对于CPU\内存,磁盘IO等资源,则是通过ControlGroup等进行管理。
2.Docker仅在类似Linux内核之上实现了有限的 隔离和虚拟化,并不是像传统虚拟化软件那样,独立运行与一个新的操作系统。如果是虚拟化的操作系统,不管是Java还是其他程序,只要调用的是同一个系统API,都可以透明的获取所需的信息,基本不需要额外的兼容性改变。
容器虽然省略了虚拟操作系统的开销,实现了轻量级的目标,但也带来了额外复杂性,它限制对于应用是不透明的,需要用户理解Docker的新行为。所以有人曾说过,“幸运的是Docker没有完全隐藏底层信息,但是不幸的也是Docker没有隐藏底层信息”。
对于Java平台来说,这些未隐藏的底层信息带来很多意外的困难,只要体现在以下几个方面:
1). 容器环境对计算资源的管理方式是全新的,ControlGroup作为相对比较新的技术,历史版本的Java显然并不能自然的理解相应的资源机制。
2). namespace对于容器内的应用细节增加了一些微妙的差异,比如jcmd、jstack等工具会依赖于"/proc"下面提供的部分信息,但是Docker的设计改变了这部分信息的原有结构,我需要对原有工具进行修改以适应这种变化。
3.从JVM运行机制的角度,为什么这些沟通障碍会导致OOM问题?
这个问题实际反映了JVM如何根据系统资源(内存,CPU等)情况,在启动时设置默认参数。
这就是所谓的Ergonomics机制,例如:
1)JVM会根据检测到的内存大小,设置最初启动时的堆大小为系统内存的1/64;并将堆最大值,设置为系统内存的1/4
2)而JVM检测到系统的CPU核数,则直接影响到了Parallel GC的并行线程数目和JIT complier线程数目,甚至是我们应用中ForkJoinPool等机制的并行等级。
这些默认参数,是根据通用场景选择的初始值。但是由于容器环境的差异,Java的判断很可能是基于错误信息而做出的,这就类似我以为我住的是整栋别墅,实际上却只有一个房间是给我住的。
更加严重的是,JVM的一些原有诊断或备用机制也受到影响,为保证服务的可用性,一种常见的选择是依赖 -XX:OnOutOfMemoryError 功能,通过调用处理脚本的形式来做一些补救措施,比如自动重启服务等,但是这种机制是基于fork实现的,当Java进程已经过度提交内存时,fork新的进程往往已经不可能正常运行了。
该如何解决这些问题呢?
1))首先,如果能升级到最新的JDK版本,就一切ok了
针对这种情况,JDK9中引入了一些实验性的参数,以方便Docker和Java沟通,通过针对内存限制,可以使用下面的参数设置:
-XX:+UnlockExperimentaVMOptions
-XX:+UserCGroupMemoryLimitForHeap
这两个参数是顺序敏感的,并且只支持Linux环境。而对于CPU核数限定,Java已经可以正确理解-cpuset-cpus等设置,,无需单独设置参数。
2))如果可以切换到JDK10或者更新的版本,问题就更加简单了。Java对容器Docker的支持已经比较完善,默认就会自适应各种资源限制和实现差异,前面提到的实验性参数已经被标记为废弃。于此同时,新增了参数用以明确指定CPU核心的数目。
-xx:ActiveProcessorCount=N
如果实践中发现有问题,也可以使用-XX:UseContainerSupport,关闭Java容器的支持特性,这可作为一种防御型机制,避免新特性破坏原有功能。
幸运的是,JKD9中的实验性改进已经被移植到Oracle JDK 8u131中了,可以直接下载镜像,并配置UseCGroupMemoryLimitForHeap
3))如果暂时只能用JDK老版本怎么办?
第一:明确设置堆、元数据区等内存区域大小,保证Java进程的总大小可控,
限制容器内存: $ docker run -it --rm --name your container -p 8080:8080 -m 800M repo/your -java-container:openjdk
这样就可以额外配置下面的环境变量,直接指定JVM堆大小 -e JAVA_OPTIONS='xMX300m'
第二:明确GC和JIT并行线程数目,以避免二者占用过多资源。
-XX:ParallelGCTreads
-XX:CICompilerCount
第三:Java在Docker环境中会意外Swap。
当内存消耗达到一定门限,操作系统会试图将不活跃的进程唤出
Java程序运行在Docker等容器环境有哪些新问题的更多相关文章
- 把AspDotNetCoreMvc程序运行在Docker上-part3:使用独立的存储容器
接上一篇博文<把AspDotNetCoreMvc程序运行在Docker上-part2:修改容器以及发布镜像>,这次我们看看如何使用docker存储数据. 背景 之前的示例都只有一个网站应用 ...
- linux(ubuntu) 搭建java程序运行环境
一:简介 ubuntu 系统的和linux差不多,我们需要在系统上搭建java程序运行环境,需要安装jdk,mysql这两个软件,tomcat是绿色版,直接通过taz -zxvf tomcat 就可以 ...
- 把AspDotNetCoreMvc程序运行在Docker上-part2:修改容器以及发布镜像
在上一个part<把AspDotNetCoreMvc程序运行在Docker上-part1>,已经将成功将aspdotnetcore程序运行在两个不同的容器中,目前两个容器的内容完全相同,只 ...
- 1、Java语言概述与开发环境——Java程序运行机制
Java语言是一种特殊的高级语言,它既有解释型语言的特性,也具有编译型语言的特征,因为Java要经过先编译后解释两个步骤. 一.高级语言的运行机制 计算机高级语言按程序的执行方式可以分为编译型和解释型 ...
- 把AspDotNetCoreMvc程序运行在Docker上-part4:实现负载均衡
在上一part<把AspDotNetCoreMvc程序运行在Docker上-part3:使用独立的存储容器>,我们利用MySql容器和Volume实现了真正意义上的数据存储.整个结构非常简 ...
- 把AspDotNetCoreMvc程序运行在Docker上-part1
接<基于ASP.Net Core学习Docker技术第一步:在CentOS7安装Docker平台>这个博文,在搭建完成Docker平台之后,可以开始让aspdotnetcore程序运行在d ...
- 把AspDotNetCoreMvc程序运行在Docker上-part5:使用docker-compose
在上一part<把AspDotNetCoreMvc程序运行在Docker上-part4:实现负载均衡>中,我们通过几个比较复杂的步骤在docker平台上实现了对网站程序的负载均衡,配置步骤 ...
- java程序运行时内存分配详解
java程序运行时内存分配详解 这篇文章主要介绍了java程序运行时内存分配详解 ,需要的朋友可以参考下 一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个 ...
- 02 基础 卸载JDK 安装JDK Java程序运行机制
基础 JDK:Java Development Kit(Java开发者工具 包含JRE和JVM) JRE:Java Runtime Environment(java运行时环境,包含JVM) JVM:J ...
随机推荐
- 盘点:七大.NET著名开源项目
尽管过了相当长的时间,花费了不少资源,微软和.NET社区还是在最近几年加入到了开源运动的阵营中来了,这令人相当惊讶,因为两大阵营一直都是经常对立的.然而,事实是依靠开源,微软社区中的开源开发工具日益发 ...
- CSS解决图片缩小不变形
我会在图片上加: <img style="max-width:80px;max-height:80px;"> 限制其最大宽度和高度
- egret.Capabilities 在pc和移动端输出值
egret.log("Device:", egret.Capabilities.os, App.DeviceUtils.IsWeb, App.DeviceUtils.isMobil ...
- 树形结构数据存储方案的选择和java list转tree
树形结构数据存储方案 Adjacency List:每一条记录存parent_idPath Enumerations:每一条记录存整个tree path经过的node枚举Nested Sets:每一条 ...
- 【教程】AI画放射图
第一步:画矩形作图宇宙键shift 第二步:分为网格 第三步:直接选择工具 第四步:填充交叉色,这步不再敖述: 第五步:视图--轮廓:快捷键ctrl+y; 第六步:直接选择工具选择除边框以外的所有节点 ...
- 从0到1实现SourceTree连接Gitlab
见下面的链接 http://note.youdao.com/noteshare?id=3622d02a38464c524222ede1b4fb06d2 SourceTree下载地址:Windows V ...
- eclipse中设置在编译运行项目之前自动保存修改的文件
Window -> Preferences -> General -> Workspace -> “Save automatically before build” Windo ...
- python AI(numpy,matplotlib)
http://blog.csdn.net/ywjun0919/article/details/8692018 apt-cache policy python-numpy sudo apt-get in ...
- Spring Data 查询方法的规则定义(五)
有句话这样说 欲练神功 挥刀自宫 请亲们先回到第一个 从Spring data 介绍 开始看 搭好环境 跟着步伐一块走 Spring Data 的方法必须严格按照它的规范进行编写,如果写错了 ...
- 剑指Offer——复杂链表的复制
题目描述: 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head.(注意,输出结果中请不要返回参数中的节点引用, ...