如何设置Docker容器中Java应用的内存限制

简介: 最近在和阿里的一些同事谈起使用Docker部署Java应用的场景,其中一个大家普遍关心的问题就是如何设置容器中JVM的内存限制。本文介绍了一种方法能够让Docker容器中的Java应用自动适配容器的内存限制。

最近在和阿里的一些同事谈起使用Docker部署Java应用的场景,其中一个大家普遍关心的问题就是如何设置容器中JVM的内存限制。

14596085983505

如果使用官方的Java镜像,或者基于Java镜像构建的Docker镜像,都可以通过传递 JAVA_OPTS 环境变量来轻松地设置JVM的内存参数。比如,对于官方Tomcat 镜像,我们可以执行下面命令来启动一个最大内存为512M的tomcat实例

docker run --rm -e JAVA_OPTS='-Xmx512m' tomcat:8

在日志中,我们可以清楚地发现设置已经生效 “Command line argument: -Xmx512m”

02-Apr-2016 12:46:26.970 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version:        Apache Tomcat/8.0.32
02-Apr-2016 12:46:26.974 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built:          Feb 2 2016 19:34:53 UTC
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server number:         8.0.32.0
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name:               Linux
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version:            4.1.19-boot2docker
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture:          amd64
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home:             /usr/lib/jvm/java-7-openjdk-amd64/jre
02-Apr-2016 12:46:26.976 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version:           1.7.0_95-b00
02-Apr-2016 12:46:26.976 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor:            Oracle Corporation
02-Apr-2016 12:46:26.977 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE:         /usr/local/tomcat
02-Apr-2016 12:46:26.977 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME:         /usr/local/tomcat
02-Apr-2016 12:46:26.978 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
02-Apr-2016 12:46:26.978 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
02-Apr-2016 12:46:26.978 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx512m
...

然而在Docker集群上部署运行Java容器应用的时候,仅仅对JVM的heap参数设置是不够的,我们还需要对Docker容器的内存资源进行限制:

1. 限制容器使用的内存的最大量,防止对系统或其他应用造成伤害
2. 能够将Docker容器调度到拥有足够空余的内存的节点,从而保证应用的所需运行资源

关于容器的资源分配约束,Docker提供了相应的启动参数

对内存而言,最基本的就是通过 -m参数来约束容器使用内存的大小

-m, --memory=""
Memory limit (format: <number>[<unit>]). Number is a positive integer. Unit can be one of b, k, m, or g. Minimum is 4M.

那么问题就来了,为了正确设置Docker容器内存的大小,难道我们需要同时传递容器的内存限制和JAVA_OPTS环境变量吗? 如下所示:

docker run --rm -m 512m -e JAVA_OPTS='-Xmx512m' tomcat:8

这个方法有两个问题

  1. 需要管理员保证容器内存和JVM内存设置匹配,否则可能引发错误
  2. 当对容器内存限制调整时,环境变量也需要重新设定,这就需要重建一个新的容器

是否有一个方法,可以让容器内部的JVM自动适配容器的内存限制?这样可以采用更加统一的方法来进行资源管理,简化配置工作。

大家知道Docker是通过CGroup来实现资源约束的,自从1.7版本之后,Docker把容器的local cgroups以只读方式挂载到容器内部的文件系统上,这样我们就可以在容器内部,通过cgroups信息来获取系统对当前容器的资源限制了。

我创建了一个示例镜像 registry.aliyuncs.com/denverdino/tomcat:8-autoheap
,其源代码可以从Github 获得。它基于Docker官方Tomcat镜像创建,它的启动脚本会检查CGroup中内存限置,并计算JVM最大Heap size来传递给Tomcat。其代码如下

#!/bin/bash
limit_in_bytes=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes)

# If not default limit_in_bytes in cgroup
if [ "$limit_in_bytes" -ne "9223372036854771712" ]
then
    limit_in_megabytes=$(expr $limit_in_bytes \/ 1048576)
    heap_size=$(expr $limit_in_megabytes - $RESERVED_MEGABYTES)
    export JAVA_OPTS="-Xmx${heap_size}m $JAVA_OPTS"
    echo JAVA_OPTS=$JAVA_OPTS
fi

exec catalina.sh run

说明:

  • 为了JVM自身的Non-Heap内存,以及监控,故障排查等场景,我们预留了部分内存(缺省256M),其余容器内存我们都分配给JVM的堆。
  • 这里没有对边界情况做进一步处理。在生产系统中需要根据情况做相应的设定,比如最大的堆大小等等。

现在我们启动一个tomcat运行在512兆的容器中

docker run -d --name test -m 512m registry.aliyuncs.com/denverdino/tomcat:8-autoheap

通过下列命令,从日志中我们可以检测到相应的JVM参数已经被设置成 256MB (512-256)

docker logs test

...
02-Apr-2016 14:18:09.870 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx256m
...

我们也可以方便的调整Java应用的内存.

Docker 1.10提供了对容器资源限制的动态修改能力。但是由于JVM无法感知容器资源修改,我们依然需要重启tomcat来变更JVM的内存设置,例如,我们可以通过下面命令把容器内存限制调整到1GB

docker update -m 1024m test
docker restart test

再次检查日志,相应的JVM Heap Size最大值已被设置为768MB

docker logs test

...
02-Apr-2016 14:21:07.644 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx768MB
...
目录
相关文章
|
5月前
|
Kubernetes Docker Python
Docker 与 Kubernetes 容器化部署核心技术及企业级应用实践全方案解析
本文详解Docker与Kubernetes容器化技术,涵盖概念原理、环境搭建、镜像构建、应用部署及监控扩展,助你掌握企业级容器化方案,提升应用开发与运维效率。
941 108
|
3月前
|
Java 虚拟化 容器
(Java)Java里JFrame窗体的基本操作(容器布局篇-1)
容器 容器,我的理解是可以包容其他东西的玩意。它可以是一个盒子,可以是一个虚拟化的物品,可只要能包裹住其他存在质体的东西,那么都可以称作是容器。例如:JPanel组件和JScollPane组件两者都是容器也是组件。 既然有容器,那么容器中的布局就必不可少了。不然不规矩的摆放物品,人类看不习惯,我也看不习惯 ???? 本篇内容,将说明java JFrame窗体里容器中几类布局。 说明:所有在JFrame窗体里的容器布局都会使用setLayout()方法,采用的布局参数都将放进这个方法里 绝对布局 调用窗体容器
147 1
|
3月前
|
监控 Kubernetes 安全
还没搞懂Docker? Docker容器技术实战指南 ! 从入门到企业级应用 !
蒋星熠Jaxonic,技术探索者,以代码为笔,在二进制星河中书写极客诗篇。专注Docker与容器化实践,分享从入门到企业级应用的深度经验,助力开发者乘风破浪,驶向云原生新世界。
还没搞懂Docker? Docker容器技术实战指南 ! 从入门到企业级应用 !
|
4月前
|
前端开发 JavaScript 应用服务中间件
在Docker部署的前端应用中使用动态环境变量
以上步骤展示了如何在 Docker 配置过程中处理并注入环墨遁形成可执行操作流程,并确保最终用户能够无缝地与之交互而无须关心背后复杂性。
242 13
|
6月前
|
存储 监控 Java
如何对迁移到Docker容器中的应用进行性能优化?
如何对迁移到Docker容器中的应用进行性能优化?
453 59
|
9月前
|
监控 Java Go
无感改造,完美监控:Docker 多阶段构建 Go 应用无侵入观测
本文将介绍一种基于 Docker 多阶段构建的无侵入 Golang 应用观测方法,通过此方法用户无需对 Golang 应用源代码或者编译指令做任何改造,即可零成本为 Golang 应用注入可观测能力。
460 85
|
6月前
|
缓存 Java Docker
如何对应用代码进行优化以提高在Docker容器中的性能?
如何对应用代码进行优化以提高在Docker容器中的性能?
344 1
|
5月前
|
运维 Cloud Native Docker
Docker:重塑现代应用交付的基石
Docker:重塑现代应用交付的基石
|
10月前
|
存储 Docker 容器
Docker Volumes卷详解及时区、时间同步设置技巧
总的来说,Docker卷是一种强大的数据持久化和数据共享工具,而Docker的时区和时间同步设置则可以帮助你更好地管理和使用你的容器。希望这些信息能够帮助你更好地理解和使用Docker。
452 75
|
7月前
|
安全 Java Docker
Docker 部署 Java 应用实战指南与长尾优化方案
本文详细介绍了Docker容器化部署Java应用的最佳实践。首先阐述了采用多阶段构建和精简JRE的镜像优化技术,可将镜像体积减少60%。其次讲解了资源配置、健康检查、启动优化等容器化关键配置,并演示了Spring Boot微服务的多模块构建与Docker Compose编排方案。最后深入探讨了Kubernetes生产部署、监控日志集成、灰度发布策略以及性能调优和安全加固措施,为Java应用的容器化部署提供了完整的解决方案指南。文章还包含大量可落地的代码示例,涵盖从基础到高级的生产环境实践。
424 3