前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kubernetes Demystified:Java应用程序资源的限制

Kubernetes Demystified:Java应用程序资源的限制

作者头像
February
修改2018-11-20 17:23:07
1.3K0
修改2018-11-20 17:23:07
举报
文章被收录于专栏:技术翻译技术翻译

本系列文章探讨了企业客户在使用Kubernetes时遇到的一些常见问题。

随着容器技术变得越来越复杂,越来越多的企业客户选择Docker和Kubernetes作为其应用平台的基础。但是,这些客户在实践中遇到许多问题。本系列文章介绍了阿里云集装箱服务团队帮助客户完成此流程的经验中的一些见解和最佳实践。

关于Java应用程序的容器化部署,一些用户报告说,尽管他们设置了容器资源限制,但是他们的活动Java应用程序容器被OOM Killer莫名其妙地杀死了。

此问题是一个非常常见的错误的结果:无法正确设置容器资源限制和相应的JVM堆大小。

在这里,我们使用Tomcat应用程序作为示例。您可以从GitHub获取其实例代码和Kubernetes部署文件。

代码语言:javascript
复制
git clone https://github.com/denverdino/system-info
cd system-info`

我们使用以下Kubernetes pod定义:

  1. pod中的应用程序是一个初始化容器,负责将一个JSP应用程序复制到Tomcat容器的“webapps”目录。注意:在映像中,JSP应用程序index.jsp用于显示JVM和系统资源信息。
  2. Tomcat容器保持活跃,我们限制了最大内存使用量为256 MB。
代码语言:javascript
复制
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: {}

我们执行以下命令来部署和测试应用程序:

代码语言:javascript
复制
$ 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,内存和其他信息。我们可以使用html2text命令将信息转换为文本格式。

注意:在这里,我们在2C 4G节点上测试应用程序。在不同环境中测试可能会产生略微不同的结果:

代码语言:javascript
复制
$ 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)

我们可以看到,容器中的系统内存为3,951 MB,但最大JVM堆大小为878 MB。为什么会这样?我们没有将容器资源容量设置为256 MB吗?在这种情况下,应用程序内存使用量超过256 MB,但JVM尚未实现垃圾收集(GC)。相反,JVM进程直接被系统的OOM杀手杀死。

问题的根本原因:

  1. 如果我们不设置JVM堆大小,则默认情况下会根据主机环境的内存大小设置最大堆大小。
  2. Docker容器使用cgroup来限制进程使用的资源。因此,如果容器中的JVM仍使用基于主机环境内存和CPU内核的默认设置,则会导致不正确的JVM堆计算。

同样,默认的JVM GC和JIT编译器线程计数由主机CPU核心数决定。如果我们在单个节点上运行多个Java应用程序,即使我们设置了CPU限制,GC线程仍有可能抢占应用程序之间的切换,从而影响应用程序性能。

现在我们了解了问题的根本原因,很容易解决它。

解决方案

启用cgroup资源感知

Java社区也意识到了这个问题,现在支持在Java SE 8u131 +和JDK 9中自动感知容器资源限制。要使用此方法,请添加以下参数:

代码语言:javascript
复制
java -XX:+ UnlockExperimentalVMOptions -XX:+ UseCGroupMemoryLimitForHeap⋯

继续前面的Tomcat容器示例,我们添加环境变量“JAVA_OPTS”:

代码语言:javascript
复制
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并重复测试:

代码语言:javascript
复制
$ 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堆大小已更改为112 MB,确保应用程序不会被OOM杀手杀死。但这引出了另一个问题:如果我们将最大容器内存限制设置为256 MB,为什么我们只将最大JVM堆内存设置为112 MB?

答案涉及JVM内存管理的细节。JVM中的内存消耗包括堆内存和非堆内存。类元数据,JIT编译代码,线程堆栈,GC和其他此类进程所需的内存取自非堆内存。因此,基于cgroup资源限制,JVM会为非堆使用保留一部分内存,以确保系统稳定性。(在前面的示例中,我们可以看到,在启动Tomcat之后,非堆内存占用了近32 MB。)

在最新版本JDK 10中,对容器中的JVM操作进行了进一步的优化和增强。

容器中cgroup资源限制的感知

如果我们不能使用JDK 8和9的新功能(例如,如果我们仍在使用旧的JDK 6应用程序),我们可以使用容器中的脚本来获取容器的cgroup资源限制并使用它来设置JVM堆尺寸。

从Docker 1.7开始,容器cgroup信息被安装在容器中,允许应用程序从/sys/fs/cgroup/memory/memory.limit_in_bytes和其他文件中获取内存,CPU和其他设置。因此,容器中应用程序的启动命令包含-Xmx,-XX:ParallelGCThreads的正确资源设置,以及基于cgroup配置的其他参数。

结论

本文着眼于在容器中运行Java应用程序时出现的常见堆设置问题。容器与虚拟机的不同之处在于,它们的资源限制是使用cgroup实现的。此外,如果内部容器进程不了解cgroup限制,则内存和CPU分配可能会产生资源冲突和问题。

通过使用新的JVM功能或自定义脚本来正确设置资源限制,可以很容易地解决此问题。这些解决方案解决了绝大多数资源限制问题。

但是,这些解决方案留下了未解决的影响容器应用程序的资源限制问题。某些较旧的监视工具和系统命令(如“free”和“top”)在容器中运行时仍会获取主机的CPU和内存设置。这意味着某些监视工具在容器中运行时无法准确计算资源消耗。社区中提出的此问题的常见解决方案是使用LXCFS来维护容器的资源可见性行为与虚拟机之间的一致性。随后的文章将讨论这种方法在Kubernetes上的使用。

原文标题《Kubernetes Demystified: Restrictions on Java Application Resources》

作者:Leona Zhang

译者:February

不代表云加社区观点,更多详情请查看原文链接

本文系外文翻译,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系外文翻译前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 解决方案
    • 启用cgroup资源感知
      • 容器中cgroup资源限制的感知
      • 结论
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档