Java 10发布后,Docker容器管理能力得到显著增强


Apache Spark、Kafka等运行在JVM中的传统企业应用,实际上都可以运行在容器环境之内。然而,在容器内运行JVM的方案近期却遇到了麻烦——由于内存与CPU资源及利用率受限,致使其性能表现无法令人满意。究其原因,这是因为Java无法意识到自身正运行在容器当中。

随着Java 10的发布,JVM终于可以识别出由容器控制组(cgroups)提出的限制集合。这意味着内存与CPU限制条件皆可用于直接在容器内实现对Java应用的管理,具体包括:

  • 在容器内部设置内存限制
  • 在容器内部设置可用CPU个数
  • 在容器内设置CPU限制



Java 10的这些优化成果可在Docker for mac/windows和Docker企业版中正常起效。


容器内存限制

一直到Java 9版本,JVM仍然无法通过在容器中使用标识来识别内存或CPU限制。在Java 10中,内存限制可以被自动识别,而且这一特性将默认启用。

Java对服务器进行分级定义,如果一台服务器有双CPU加2 GB内存,那么默认的堆大小将为物理内存的1/4。这里假定有一台安装Docker企业版四CPU 加2 GB内存的服务器,下面我们来比较分别运行有Java 8和Java 10的容器的具体区别。先来看Java 8:

docker container run -it -m512 --entrypoint bash openjdk:latest

$ docker-java-home/bin/java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
uintx MaxHeapSize                              := 524288000                          {product}
openjdk version "1.8.0_162"


我们可以看到最大的堆大小是512 MB,刚好等于(1/4)x 2 GB,但这是通过Docker EE自动设置的,而而通过设置容器实现。作为比较,我们再来看Java 10环境下运行同样的命令是什么结果。

docker container run -it -m512M --entrypoint bash openjdk:10-jdk

$ docker-java-home/bin/java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
size_t MaxHeapSize                              = 134217728                                {product} {ergonomic}
openjdk version "10" 2018-03-20


上面的结果显示,容器里的内存限制非常接近我们期望的128 MB。


设置可用CPU个数

默认情况下,各容器所能获取的主机CPU时钟周期不受限制。通过额外设置,我们可以指定某一特定容器所能获得的主机CPU时钟周期。

Java 10能够识别这些限制:

docker container run -it --cpus 2 openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 2


所有分配给Docker EE的CPU均获得同样比例的CPU时钟周期。这一比例可以通过调整CPU共享比重(相对于其他所有运行的容器的比重)来进行修改。

这一比重只在运行CPU敏感型进程时才会生效。当某一容器中的任务处于空闲状态,那么其它容器可以使用剩余的全部CPU时钟周期。真实CPU时间量在很大程度上取决于运行在该系统之上的容器数量。这些在Java 10中都可以设置:

docker container run -it --cpu-shares 2048 openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 2


Java 10中也可以设置CPU集合限制(允许哪些CPU执行)。

docker run -it --cpuset-cpus="1,2,3" openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 3

分配内存与CPU

利用Java 10,我们可以使用容器设置来估算部署应用程序所需的内存和CPU配额。我们假设已经确定了容器中运行的每个进程的内存堆和CPU要求,并设置了JAVA_OPTS。例如,如果您有一个跨十个节点分布的应用程序,其中五个节点需要512 Mb的内存,且各自分配得1024个CPU比重;另外五个节点需要256 Mb,且各自分配得512个CPU比重。请注意,1块CPU的整体计算能力将被拆分为1024个比重单位。



对于内存,应用程序需要至少分配5 Gb容量。



512 Mb x 5 = 2.56 Gb




256Mb x 5 = 1.28 Gb

该应用程序需要8块CPU才能高效运行。

1024 x 5 = 5 CPU

512 x 5 = 3 CPU



最佳实践建议用户对应用程序进行分析,以确定运行在JVM每个进程的内存和CPU分配需求。但凭借着对容器需求的识别能力,Java 10使我们得以准确估算工作负载的CPU及内存资源占用情况,从而防止容器内运行的Java应用程序遭遇内存或CPU不足的问题。



原文链接:IMPROVED DOCKER CONTAINER INTEGRATION WITH JAVA 10(翻译:kelvinji)

0 个评论

要回复文章请先登录注册