在Pod设置limit 的情况下,如何让JDK容器适配

目录

[1. 背景](#1. 背景)

[2. 问题](#2. 问题)

[3. 解决方案](#3. 解决方案)

[3.1. 注意](#3.1. 注意)

[4. 参考](#4. 参考)


1. 背景

在使用Kubernetes部署业务应用的实际操作中,由于k8s集群的资源是有限的,为了防止部分应用无节制地使用资源,我们会在Deployment.yaml文件中设置request,limit的资源限制大小。

在 Kubernetes 中,你可以通过设置 pod 的 spec.containers[].resources.requests 和 spec.containers[].resources.limits 来配置容器的资源需求和限制。以下是一个样例:

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: my-springboot-jdk11-demo-image
    resources:
      requests:
        cpu: "0.5"
        memory: "250Mi"
      limits:
        cpu: "2"
        memory: "8192Mi"

在这个例子中:

  • requests:这部分设置了容器启动所需要的资源。在这个例子中,容器启动所需的 CPU 是 0.5 个核心,内存是 250 MiB。
  • limits:这部分设置了容器所能使用的资源的最大值。在这个例子中,容器所能使用的最多的 CPU 是 2 个核心,内存是 8192 MiB(8Gib)。

如果容器使用的资源超过了所设置的 limits,那么这个容器可能会被 Kubernetes 杀掉并重启。

2. 问题

在现实中,我们有很多应用使用java开发,那必然会使用JDK基础镜像,在这个实际操作中,我们可能会遇到明明设置了Limit,但是容器的最大Memory就是上不去,卡在2.1Gib左右,这个和使用的基础镜像有关系,部分JDK基础镜像有的存在默认的Xmx和Xms值。或者说,JDK运行在容器中,它无法确定Pod的limit是多少。

  • -Xmx<size>: 这个参数可以设置程序的最大内存分配。比如,如果你想要设置最大内存为1GB,你可以写成-Xmx1024m或者-Xmx1g。
  • -Xms<size>: 这个参数可以设置程序的初始化(最小)内存分配。比如,如果你想要设置最小内存为256MB,你可以写成-Xms256m。

3. 解决方案

对于运行在容器中的Java应用,于JDK 10及以后的版本,JVM可以自动识别并根据容器的内存限制动态设置堆大小,无需再额外手动设置JVM的-Xmx和-Xms参数。

JDK 10及以后的版本默认开启了-XX:+UseContainerSupport选项,默认使用容器的cgroup限制作为JVM的内存限制。如果你使用的是JDK 8,可以使用-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap选项来让JVM使用容器cgroup的内存限制。

此外,你还可以通过使用比例设置JVM的内存分配,例如,你可以使用-XX:MaxRAMPercentage和-XX:MinRAMPercentage参数来限制JVM使用的最大和最小内存占比。这两个参数设置的是JVM可用内存占容器可用内存的最大和最小百分比。

bash 复制代码
-XX:MaxRAMPercentage=80.0 -XX:MinRAMPercentage=5.0

# 注意
# -XX:+UseContainerSupport参数在JDK 10及以后的版本是默认开启的
# 完整命令:-XX:+UseContainerSupport -XX:MaxRAMPercentage=80.0 -XX:MinRAMPercentage=5.0

# JDK8完整命令:-XX:+UnlockExperimentalVMOptions -XX:+UseContainerSupport -XX:MaxRAMPercentage=80.0 -XX:MinRAMPercentage=5.0

上述命令就设置了JVM使用最大内存为容器内存的80%,最小内存为容器内存的20%。这样,无论你的pod限制如何变动,JVM都会按照这个比例动态调整。

在上述例子中,因为设置的容器内存上限是8GB,所以JVM会设置最大堆大小(-Xmx)为8GB的80%,即6.4GB。最小堆大小(-Xms)为8GB的5%,即400MB

完整的Dockerfile如下:

bash 复制代码
FROM openjdk-11

ADD target/my-app-name.jar /app/app.jar

WORKDIR /app

ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=80.0 -XX:MinRAMPercentage=5.0"

ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Dspring.profiles.active=$PROJECT_ENV -jar app.jar"]

EXPOSE 8080

3.1. 注意

在实际使用过程中,请注意,XX:MaxRAMPercentage=80.0是限制堆内存最大值。我遇到一个项目出现这个问题,当时就提出这个问题,此处也记录一下

:我的container limit 是3000M, 设置-XX:MaxRAMPercentage=80.0之后,在压力测试之后,从监控上看,内存还是能飚高到3000M,为何没有限制住?

这主要是因为Java程序的内存使用不仅仅包括堆内存(Heap),还包括了非堆内存(Non-Heap),其中包括MetaSpace(元数据区),JVM堆栈,JVM本身代码等等。 MaxRAMPercentage 这个参数只是设置了堆内存(Heap)的大小,但并没有限制非堆内存(Non-Heap)。

如果你看到的内存占用达到了3000M,可能的情况是,堆内存占用了你设置的比例(80%,即2400M),剩下的600M被非堆内存占用了。

另外一个需要注意的地方,3000M的limit是设置给了整个容器,容器里不仅仅有这一个Java进程。也可能是其他进程占用了一部分内存。

所以这意味着,如果你希望Java进程的最大内存使用不超过一个确定的值,那么你需要将MaxRAMPercentage设置的更小一些,比如50%或60%等,以便为非堆内存和其他进程留出足够的空间。

注意,一定要结合实际的应用情况去做这个调整和选择,避免设置过小影响了Java程序的运行性能。

4. 参考

ChatGPT

相关推荐
云上小朱3 分钟前
问题处理-k8s环境中,hadoop端口9000无法被访问
kubernetes
阿巴~阿巴~5 分钟前
Linux 第一个系统程序 - 进度条
linux·服务器·bash
?ccc?9 分钟前
容器技术技术入门与 Docker 环境部署
运维·docker·容器
时时刻刻看着自己的心18 分钟前
docker启动报错
运维·docker·容器
DIY机器人工房19 分钟前
代码详细注释:通过stat()和lstat()系统调用获取文件的详细属性信息
linux·嵌入式
匆匆那年9671 小时前
Docker容器中安装MongoDB,导入数据
运维·docker·容器
望获linux1 小时前
【Linux基础知识系列】第四十三篇 - 基础正则表达式与 grep/sed
linux·运维·服务器·开发语言·前端·操作系统·嵌入式软件
眠りたいです1 小时前
Mysql常用内置函数,复合查询及内外连接
linux·数据库·c++·mysql
我的泪换不回玫瑰1 小时前
Linux系统管理命令
linux
i小溪2 小时前
在使用 Docker 时,如果容器挂载的数据目录(如 `/var/moments`)位于数据盘,只要服务没有读写,数据盘是否就不会被唤醒?
人工智能·docker