目录
1. 背景
2. 问题
3. 解决方案
3.1. 注意
4. 参考
1. 背景
在使用Kubernetes部署业务应用的实际操作中,由于k8s集群的资源是有限的,为了防止部分应用无节制地使用资源,我们会在Deployment.yaml文件中设置request,limit的资源限制大小。
在 Kubernetes 中,你可以通过设置 pod 的 spec.containers[].resources.requests 和 spec.containers[].resources.limits 来配置容器的资源需求和限制。以下是一个样例:
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可用内存占容器可用内存的最大和最小百分比。
-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如下:
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程序的运行性能。文章来源:https://www.toymoban.com/news/detail-851624.html
4. 参考
ChatGPT文章来源地址https://www.toymoban.com/news/detail-851624.html
到了这里,关于在Pod设置limit 的情况下,如何让JDK容器适配的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!