Docker 容器达到 Memory Limit 后的行为

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Docker 容器达到 Memory Limit 后的行为相关的知识,希望对你有一定的参考价值。

参考技术A

当一个容器申请使用多于整个主机可用的内存时, 内核可能会杀掉容器或者是Docker daemon(守护进程)来释放内存, 这可能会导致所有服务不可用, 为了避免这个错误, 我们应该给每个容器限制合适的内存.

我们可以在Docker-Compose或者Docker Stack环境中使用以下配置来限制容器的内存使用:

接下来我们来理解上面的配置

limits.memory

容器允许的内存最大使用量, 最小值为4M.

当容器使用了大于限制的内存时, 会发生什么, 触发程序GC还是Kill?

不幸的时, 官方文档好像没有对内存限制说明得很详细, 不过Google可以帮忙, 在下面的文章中能找到一点蛛丝马迹:

再经过试验证明当程序使用超过limits.memory限制的内存时, 容器会被Kill (cgroup干的 resource_management_guide/sec-memory ).

简单的, 可以使用redis容器来进行这个实验: 限制内存为10M, 再添加大量数据给redis, 然后查看容器的状态.

实际上我们不想让容器直接被Kill, 而是让Redis触发清理逻辑, 直接Kill会导致服务在一段时间内不可用(虽然会重启).

怎么办?

各种调研后发现官方提供的其他参数都不能解决这个问题, 包括memory-reservation, kernel-memory, oom-kill-disable.

看来并不能傻瓜化的解决这个问题, 现在如果我们只想触发程序的GC, 应该怎么做?

一般来说, 程序当判定到内存不足时会有自己的GC机制, 但正如这篇文章 Understanding Docker Container Memory Limit Behavior 里所说, 运行在docker容器里的程序对内存限制是不可见的, 程序还是会申请大于docker limit的内存最终引起OOM Kill.

这就需要我们额外对程序进行配置, 如 redis的maxmemory配置, java的JVM配置, 不幸的是并不是所有程序都有自带的内存限制配置, 如mysql, 这种情况下建议调低程序性能 和 保证留够的程序需要的内存.

如果你的服务器开启了Swap, 有可能还会遇到一个问题: 当容器将要达到内存限制时会变得特别慢并且磁盘IO很高(达到顶峰).

这是因为我们还忽略了一个参数: memory-swap, 当没有设置memory-swap时它的值会是memory-limit的两倍, 假如设置了limit-memory=300M, 没有设置memory-swap, 这意味着容器可以使用300M内存和300M Swap. https://docs.docker.com/config/containers/resource_constraints/#--memory-swap-details

值得注意的是Swap并不是无损的, 相反的, 它十分慢(使用磁盘代替内存), 我们应该禁用它.

不过compose file v3并不支持memory-swap limit 的设置, 唉.

无奈, 那就关闭主机的swap吧.

总结 当容器达到内存限制时会发送的事情:

docker 笔记 限制容器

  • 内存
    • -m 或 --memory:设置内存的使用限额,例如 100M, 2G。--memory-swap:设置 内存+swap 的使用限额。--vm 1:启动 1 个内存工作线程。--vm-bytes 280M:每个线程分配 280M 内存
    • docker run -m 200M --memory-swap=300M ubuntu  

 

  • CPU
    • docker run --name "container_A" -c 1024 ubuntu

      docker run --name "container_B" -c 512 ubuntu


      container_A 的 cpu share 1024,是 container_B 的两倍。当两个容器都需要 CPU 资源时,container_A 可以得到的 CPU 是 container_B 的两倍。

      需要特别注意的是,这种按权重分配 CPU 只会发生在 CPU 资源紧张的情况下。如果 container_A 处于空闲状态,这时,为了充分利用 CPU 资源,container_B 也可以分配到全部可用的 CPU。

  • I/O
    • block IO 权重

      默认情况下,所有容器能平等地读写磁盘,可以通过设置 --blkio-weight 参数来改变容器 block IO 的优先级。

      --blkio-weight 与 --cpu-shares 类似,设置的是相对权重值,默认为 500。在下面的例子中,container_A 读写磁盘的带宽是 container_B 的两倍。

      docker run -it --name container_A --blkio-weight 600 ubuntu   

      docker run -it --name container_B --blkio-weight 300 ubuntu

      限制 bps 和 iops

      bps 是 byte per second,每秒读写的数据量。
      iops 是 io per second,每秒 IO 的次数。

      可通过以下参数控制容器的 bps 和 iops:
      --device-read-bps,限制读某个设备的 bps。
      --device-write-bps,限制写某个设备的 bps。
      --device-read-iops,限制读某个设备的 iops。
      --device-write-iops,限制写某个设备的 iops。

      下面这个例子限制容器写 /dev/sda 的速率为 30 MB/s

      docker run -it --device-write-bps /dev/sda:30MB ubuntu

以上是关于Docker 容器达到 Memory Limit 后的行为的主要内容,如果未能解决你的问题,请参考以下文章

容器内部设置JVM的Heap大小

无法更新 Docker 容器中的 php.ini 文件

PHP修改memory_limit的三种办法

增加 PHP memory_limit。啥时候会变得疯狂?

docker限制容器的cpu内存使用率

云原生|Docker04-docker的资源限制