【问题标题】:Docker does not free memory after creating and deleting files with PHP使用 PHP 创建和删除文件后 Docker 不释放内存
【发布时间】:2018-03-07 18:36:58
【问题描述】:

我有一个 PHP 守护程序脚本下载远程图像并在上传到对象存储之前将它们临时存储在本地。

PHP 内部内存使用量保持稳定,但 Docker/Kubernetes 报告的内存使用量不断增加。

我不确定这是否与 PHP、Docker 或预期的 Linux 行为有关。

重现问题的示例:

Docker 镜像:php:7.2.2-apache

<?php
for ($i = 0; $i < 100000; $i++) {
    $fp = fopen('/tmp/' . $i, 'w+');
    fclose($fp);

    unlink('/tmp/' . $i);

    unset($fp);
}

在执行上述脚本之前在容器内调用free -m

          total        used        free      shared  buff/cache   available
Mem:           3929        2276         139          38        1513        1311
Swap:          1023         167         856

执行脚本后:

          total        used        free      shared  buff/cache   available
Mem:           3929        2277         155          38        1496        1310
Swap:          1023         167         856

显然内存已释放,但从主机调用docker stats php-apache 表明其他情况:

CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
ccc19719078f        php-apache          0.00%               222.1MiB / 3.837GiB   5.65%               1.21kB / 0B         1.02MB / 4.1kB      7

docker stats php-apache 报告的初始内存使用量为 16.04MiB。

解释是什么?如何释放内存?

让这个容器在资源受限的 Kubernetes 集群中运行会导致 pod 失败并反复重启。

【问题讨论】:

  • 内存被进程和内核使用。如果不是 PHP,则使用所有内存查找进程。

标签: php docker memory debian kubernetes


【解决方案1】:

是的,here 报告了类似的问题。

coolljt0725 的Here's the answer,其中一位贡献者,回答了为什么top 输出中的RES 列显示的内容与docker stats 不同(我只是照原样引用他的话):

如果我理解正确,docker stats 中的内存使用情况完全是从容器的内存 cgroup 中读取的,您可以看到该值与您从 cat /sys/fs/cgroup/memory/docker/665e99f8b760c0300f10d3d9b35b1a5e5fdcf1b7e4a0e27c1b6ff10098/1d9a96 读取的值相同memory.usage_in_bytes,这个限制也是创建容器时-m设置的内存cgroup限制。 RES 和 memory cgroup 的统计数据不同,RES 没有考虑缓存,但是 memory cgroup 有,这就是为什么 docker stats 中的 MEM USAGE 比 top 中的 RES 多

用户建议 here 可能实际上可以帮助您查看实际的内存消耗:

尝试设置docker run --memory的参数,然后检查你的 /sys/fs/cgroup/memory/docker/&lt;container_id&gt;/memory.usage_in_bytes 应该是对的。

--memory-m 被描述为here

-m, --memory="" - 内存限制(格式:&lt;number&gt;[&lt;unit&gt;])。数字是一个正整数。单位可以是bkmg 之一。最小值为4M

现在如何避免不必要的内存消耗。正如您发布的那样,在 PHP 中取消链接文件不需要立即删除内存缓存。相反,在特权模式下运行 Docker 容器(带有--privileged 标志)然后可以定期调用echo 3 &gt; /proc/sys/vm/drop_cachessync &amp;&amp; sysctl -w vm.drop_caches=3 来清除内存页面缓存。

另外,使用fopen('php://temp', 'w+') 并将文件临时存储在内存中可以避免整个问题。

【讨论】:

  • 感谢您的回答和参考!这解释了内存使用的差异。不幸的是,cgroup 在这种情况下是最精确的。 PHP 脚本中的文件操作增加了缓冲区/缓存,并且容器无法释放它。只有主持人可以拨打echo 3 &gt; /proc/sys/vm/drop_caches。你知道从容器访问文件系统时如何避免OOM吗?
  • 那么,为什么fopen('php://temp', 'w+') 的作用就像一个魅力,但fopen('/tmp/' . $i, 'w+') 不释放内存?是什么原因? @mpskovvang @Alex Karshin
  • @Q-bart 你可以在文档中阅读它:php://temp will use a temporary file once the amount of data stored hits a predefined limit (the default is 2 MB)
  • 知道了,谢谢。实际上我还有另一个问题:docker容器不会从将大量数据写入文件(记录器)的进程中释放内存,所以我很感兴趣为什么它可能以及如何在我的情况下修复它
  • @Q-bart 你应该打开一个单独的问题然后
【解决方案2】:

Alex 提到的问题解释了容器内部的free -m 和主机的docker stats 之间的内存使用差异。缓冲区/缓存包含在后者中。

在 PHP 中取消链接文件不需要立即删除内存缓存。

相反,在特权模式下运行 Docker 容器,我可以定期调用 echo 3 &gt; /proc/sys/vm/drop_caches 来清除内存页面缓存。

【讨论】:

  • 我应该将您的答案合并到我的答案中以组合完整吗?
  • 是的,请提供详细而完整的答案。我还发现了一个更漂亮的命令sync &amp;&amp; sysctl -w vm.drop_caches=3。作为奖励信息,我发现使用fopen('php://temp', 'w+') 并将文件临时存储在内存中可以避免整个问题。没有未释放的记忆挂起。
  • 我编辑了我的答案。如果我错过了什么,请随时编辑它
猜你喜欢
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多