乍一看这是一句废话,你限制了内存最大值,超出的时候当然要OOM
其实不然,很多常见的应用程序使用的内存最大值,并不能简单的通过 Limits 来限制
在Docker容器中,容器无法感知到自己处在一种 被限制的,资源隔离 的环境中,它会认为自己像正常的非容器应用一样,可以使用全部的宿主机资源
Linux 环境中
即使 -c 分配了 1000 个微核,200M 内存,但是容器仍然认为自己在使用宿主机 的 6个内核 和 8GB 内存
docker run -i -t --name ubuntu -c 1000 -m 200M ubuntu /bin/bash
这种情况会对传统程序,比如Java造成一定的困扰
内存方面
如果不设置 -Xms ,java默认会使用 (它认为的)物理内存的 1/64 作为堆容量的最小值。
如果不设置 -Xmx,java默认会使用(它认为的)物理内存的 1/4 作为堆容量的最大值。
initial heap size Larger of 1/64th of the machine's physical memory on the machine or some reasonable minimum. Before Java SE 5.0, the default initial heap size was a reasonable minimum, which varies by platform. You can override this default using the -Xms command-line option. maximum heap size Smaller of 1/4th of the physical memory or 1GB. Before Java SE 5.0, the default maximum heap size was 64MB. You can override this default using the -Xmx command-line option. Note: The boundaries and fractions given for the heap size are correct for Java SE 5.0. They are likely to be different in subsequent releases as computers get more powerful.
因为 获取了错误的 资源信息,容器内应用有可能分配超出自己 能够使用的 资源范围,触发 OOM 异常
那么,仅仅是简单的设置-Xmx 为 200M 就可以避免这个问题么?并不是
容器 -m 参数会限制整个容器的内存使用上限,但是 -Xms 和 -Xmx 限制的仅仅是堆的大小,一个完整的Java程序实际会使用
- java程序的堆内存,最大就是
-Xmx
设置的这个值 - Garbage collection在垃圾回收的时候使用的内存
- JIT optimization使用的内存
- java程序的Off-heap所使用的内存
- java程序的Metaspace所使用的内存
- JNI Code所占用的内存
- jvm启动的时候所占用的内存。
这还不包括Ubuntu 容器本身可能消耗的一些资源(虽然小到可以忽略不计),所以,将 -Xmx 设置为 -m 的 80% 到 90% 可能会是一个稳妥的选择
具体数值需要根据具体项目实际测试
CPU 方面,因为 cpu 可以超卖,所以不会出现 OOM 的情况,
但是,容器内程序认为自己可以使用宿主机的全部 6个内核,真的没有问题么?
docker run --name cputest -it --rm -c 512 progrium/stress --cpu 1
由于 -c 只是标注了容器的 cpu 竞争分配,最多 512 个微核,所以这里它仍然使用了 100% 的cpu, –cpu 强制锁住它在一个内核上,所以,它使用了全部内核的 1个内核
docker run --name cputest -it --rm -c 512 progrium/stress --cpu 6
这次使用了全部的宿主机 cpu
实际上 -c 参数并不会影响程序使用足够的cpu资源,即使是 -c 512,也不会影响线程在多个内核上的调度(不会局限在一个内核的 0.5 上),
Docker 上 -c 只限制竞争
Kubernetes 上 requests 限制竞争,limits 限制 cpu 获得的 cpu片段时间,所以它们都不会影响cpu调度
不会出现6个内核,120个线程被过度频繁调度的情况