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 ...
随机推荐
- EF简单查询
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- 13个非常实用的JavaScript小技巧
使用!!操作符转换布尔值 有时候我们需要对一个变量查检其是否存在或者检查值是否有一个有效值,如果存在就返回true值.为了做这样的验证,我们可以使用!!操作符来实现是非常的方便与简单.对于变量可以使用 ...
- IOS7开发~新UI学起(一)
本文转载至:http://blog.csdn.net/lizhongfu2013/article/details/9124893 IOS7在UI方面发生了很大改变,所以感觉有必要重新审视的学习一下(新 ...
- poj_2479 动态规划
题目大意 给定一列数,从中选择两个不相交的连续子段,求这两个连续子段和的最大值. 题目分析 典型的M子段和的问题,使用动态规划的方法来解决. f[i][j] 表示将A[1...i] 划分为j个不相交连 ...
- HDU1536 S-Nim
S-Nim Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submi ...
- ios 如何改变UISegmentedControl文本的字体大小?
UIFont *Boldfont = [UIFont boldSystemFontOfSize:16.0f]; NSDictionary *attributes = [NSDictionary dic ...
- IE edge是怎么了??
IE edge 怎么不能通过$.getJSON(url,function(data){ alert(''); });获取数据呢?,其他浏览器和IE的10以下版本都没问题获取到了,这是什么情况!本来是想 ...
- java常用队列分析
一.ArrayBlockingQueue 首先看一段源码: public class ArrayBlockingQueue<E> extends AbstractQueue<E> ...
- Exchange Management Console Error
Exchange 2010 error solved : The WS-Management service cannot process the request. The system load q ...
- easyui datagrid行内编辑
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...