`
猫耳呀
  • 浏览: 170497 次
社区版块
存档分类
最新评论

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. 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: app
    imagePullPolicy: IfNotPresent
    command:
      - "cp"
      - "-r"
      - "/system-info"
      - "/app"
    volumeMounts:
    - mountPath: /app
      name: app-volume
  containers:
  - image: tomcat:9-jre8
    name: tomcat
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - mountPath: /usr/local/tomcat/webapps
      name: app-volume
    ports:
    - 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/ | html2text

Java 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: app
    imagePullPolicy: IfNotPresent
    command:
      - "cp"
      - "-r"
      - "/system-info"
      - "/app"
    volumeMounts:
    - mountPath: /app
      name: app-volume
  containers:
  - image: tomcat:9-jre8
    name: tomcat
    imagePullPolicy: IfNotPresent
    env:
    - name: JAVA_OPTS
      value: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
    volumeMounts:
    - mountPath: /usr/local/tomcat/webapps
      name: app-volume
    ports:
    - 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的开发者体验,帮助用户关注云端应用价值创新。

原文链接:https://yq.aliyun.com/articles/562440?spm=a2c41.11181499.0.0

分享到:
评论

相关推荐

    技术恐惧与技术拜物教--人工智能时代的迷思.pdf

    在探讨人工智能时代的迷思时,我们不可避免地会接触到两个核心议题:技术恐惧与技术拜物教。这两个概念不仅涉及人工智能技术本身的发展,也与社会、文化和伦理等因素紧密相关。以下将详细阐述这两个概念的内涵、成因...

    20201111-中银国际-美国低通胀迷思与中期展望:高核心通胀将挑战美联储宽松政策.rar

    1. **美国低通胀迷思**:在经济学中,通胀通常指一般价格水平的持续上涨。美国长期以来一直面临低通胀问题,这可能源于多种因素,如全球化的生产、技术进步导致的效率提升、人口老龄化以及需求疲软等。低通胀可能会...

    Java容器化部署架构方案.pptx

    在部署 Java 应用程序时,存在三个迷思困惑:单体 vs 微服务、容器镜像包和软件启动速度、组件间交互和效率迷思。 单体 vs 微服务是指在部署 Java 应用程序时,是否选择单体架构或微服务架构。微服务架构可以提高...

    精品资料2021-2022年六标准差之迷思.ppt

    然而,正确理解和应用六标准差,可以帮助任何规模的企业系统性地改进流程,提高效率,克服这些迷思。 总结来说,六标准差是一种强大的质量管理工具,通过系统性地减少缺陷,提高过程的稳定性和效率,助力企业实现...

    魔法与科学:人工智能的教育迷思及其祛魅.pdf

    人工智能(AI)作为一门划时代的伟大技术,在教育领域的应用和发展,往往会伴随着教育迷思的出现。这些迷思主要源于人们对AI技术的过高期望、缺乏科学认识、以及AI与教育结合的不确定性。文章中提到了几个关键点,...

    java教學課程-挑戰java程式語言

    - **应用软件**:如Microsoft Office、即时通讯软件(MSN Messenger、Yahoo Messenger、Skype)、企业资源规划系统(ERP)、客户关系管理系统(CRM)、供应链管理系统(SCM)等。 #### 1.3 程序和程序语言 - **...

    CEO的四大迷思(领导力经典讲义).ppt

    1. **CEO的四大迷思**: - **迷思一:事事亲力亲为**:许多CEO觉得必须亲自处理所有事务,但这种做法往往导致精力分散,无法专注于真正重要的事情。 - **迷思二:追求全面优势**:CEO们常常试图在产品、服务、人员...

    六标准差之迷思.pptx

    六标准差之迷思.pptx

    深度学习视域下的小学数学微课程设计——例谈“迷思”“错误”“问题”在线开发策略.pdf

    教学过程中,教师应善于捕捉和利用这些“迷思”,将它们转化为教学资源,通过设计合理的课程活动,引导学生主动探究和思考,从而帮助他们澄清疑惑,实现认知的深化。 同样,“错误”在学习过程中不应被忽视,错误...

    《教养的迷思》读后感.pdf

    《教养的迷思》读后感.pdf

    破除迷思:SOC 中的 AI

    ### 破除迷思:SOC中的AI 在当今高度数字化的世界中,网络安全已经成为企业和组织不可忽视的关键领域。随着威胁的不断演变和技术的进步,安全运营中心(SOC)面临着前所未有的挑战。本文旨在深入探讨SOC中引入人工...

    Java_API文档_中文详细版.rar

    1. **基础类库**:Java.lang包是每个Java程序的基础,包含基本数据类型包装类、运行时异常、字符串对象、Math类等。例如,System类提供系统级服务,如标准输入/输出流,而String类用于表示不可变的字符序列。 2. **...

    保险行业白皮书-破解迷思,探索高质量银保发展新模式.docx

    然而,行业在探索成长的道路上,也面临着一系列的迷思和挑战。本文将深入探讨保险行业的白皮书,专注于破解这些迷思,探索高质量银保发展新模式。 首先,银保业务已经成为全球保险市场增长的重要驱动力。尤其在寿险...

    科技品牌十大迷思

    科技品牌十大迷思

    CEO管理运营之道经典实用课件之二:CEO四大迷思.ppt

    CEO管理运营之道经典实用课件之二:CEO四大迷思.ppt

    七式「团队迷思」

    “七式团队迷思”是一个由心理学家欧文·珍尼斯提出的概念。这个概念描述的是一个团队陷入迷思的情况,从而导致团队作出一些劣质的决策。这种现象在团队管理中是非常普遍的,也是团队领导者需要特别注意的问题。 ...

    Java 容器化部署 从应⽤用服务器到云原⽣生1

    1. Java 应用程序的部署方式 2. 三个迷思困惑 3. 容器镜像包和软件启动速度 1. 哪个在先,哪个在后 2. 部署包的体积,占用的内存

    2021破解迷思探索高质量银保发展新模式.pdf

    为了破解这些迷思,麦肯锡等专业机构提出了“八大关键战略举措”,包括同客户经营、规模化精准营销、产品定制、整合客户旅程、数据洞见共享、一体化团队协同、生态场景共创等。通过这些举措的实施,可以全面推动银保...

Global site tag (gtag.js) - Google Analytics