指南/Docker占用内存过多
Mac上Docker占用内存过多?如何找出容器并限制内存
简要回答
在Mac上,Docker把所有容器都放在一个Linux虚拟机里运行,所以活动监视器显示的是一个很大的进程(通常是Virtual Machine Service for Docker),而不是各个容器。要查看是哪个容器在占用内存,运行docker stats。要限制总量,在Docker Desktop的Settings → Resources → Advanced中调低Memory limit;默认值是Mac内存的一半。同一处的Resource Saver会在Docker Desktop闲置时关闭虚拟机。Vitals是一款Mac系统监控工具,会在“容器”标签页中显示每个容器的内存、CPU、网络和端口,还能从菜单栏停止容器。
如何查找并限制Docker的内存
- 01 打开活动监视器,点按“内存”,在搜索栏中输入Docker。最大的那一行就是运行容器的虚拟机;使用Apple的Virtualization框架时,它名为Virtual Machine Service for Docker。com.docker.backend是Docker Desktop本身。
- 02 在“终端”中运行
docker stats --no-stream。每一行是一个正在运行的容器,MEM USAGE / LIMIT显示它用了多少以及最多能用多少。 - 03 停止不用的容器:
docker stop加容器名称,或在Compose项目的文件夹中运行docker compose down。 - 04 打开Docker Desktop的Settings → Resources → Advanced,把Memory limit调低到比
docker stats在最忙时显示的数值稍高一点。应用更改后,Docker Desktop会重启虚拟机。 - 05 在同一部分打开Resource Saver,这样没有容器运行时虚拟机就会关闭。
OrbStack、Colima、Rancher Desktop和Podman也在虚拟机中运行容器,各有各的内存设置。对于Colima,colima start --memory 4会给虚拟机分配4 GB。
容器是Linux的功能,所以在Mac上,每种引擎都在一个小型Linux虚拟机中运行容器。macOS把这台虚拟机视为一个进程,完全不知道里面哪个容器用了什么。这就是为什么活动监视器显示一个占了好几GB的进程,却一个容器都看不到,也是为什么答案要在Docker自己的工具里找。下表把你看到的现象与它是什么以及该怎么做对应起来。
Docker的内存都去哪儿了
| 你看到的现象 | 它是什么 | 怎么做 |
|---|---|---|
| Virtual Machine Service for Docker,占好几GB | 运行所有容器的Linux虚拟机 | 在Settings → Resources → Advanced中调低Memory limit |
| com.docker.backend | Docker Desktop自身的进程 | 通常很小;如果一直增长,就重启Docker Desktop |
| 没有容器运行,内存仍然很高 | 虚拟机仍占着它用过的内存 | 打开Resource Saver,或退出Docker Desktop |
| 某个容器接近上限 | 它需要更多内存,或者存在泄漏 | docker stats,并为单个容器设置上限 |
| 构建镜像时内存上升 | 构建在同一台虚拟机中运行 | 正常;构建结束后会降下来 |
活动监视器为什么只显示一个很大的Docker进程
虚拟机像任何App一样从macOS获取内存,最多到你设定的Memory limit。在虚拟机内部,Linux把这些内存分给你的容器,剩下的用来缓存文件,就像macOS一样。从外面看,这些都一样:一个进程占着很多内存。
只有引擎知道怎么分的,而docker stats就是向它询问这个。那里的CPU %列是相对于虚拟机CPU的占比,MEM %则是相对于容器上限的占比;如果容器没有设上限,就是相对于整台虚拟机的占比。
该给Docker分配多少内存
Docker Desktop默认占用Mac内存的一半:16 GB的Mac上就是8 GB。一个典型的开发栈(一个应用服务器、一个数据库和一个缓存)通常需要的要少得多。像平时一样运行你的开发栈,观察docker stats在最忙时的总量,把Memory limit设得比它稍高一点。
设得太低,虚拟机里的Linux就会停止耗尽内存的容器。运行docker inspect --format '{{.State.OOMKilled}}'加容器名称:如果发生过这种情况,它会输出true。调高上限,或者给那个容器单独设一个上限,免得它把其他容器也拖垮。
为单个容器设置内存上限
给单个容器设上限,可以防止存在泄漏的服务占满虚拟机。使用docker run时,加上--memory 512m。在Compose文件中,给服务设置mem_limit: 512m。容器一旦超出上限就会被停止,docker stats会在它的用量旁边显示上限。
Docker为什么不归还内存
停止容器会释放虚拟机内部的内存,但虚拟机并不总是马上把它还给macOS,所以它在活动监视器中的数值可能一直很高。Resource Saver会在Docker Desktop闲置时关闭虚拟机,从而归还全部内存;有容器需要时,几秒钟就能重新启动。退出Docker Desktop也有同样的效果。
Docker与磁盘空间
镜像、已停止的容器、卷和构建缓存都存放在一个磁盘映像中,它会随着你的工作不断变大,在Mac上计入系统数据。docker system df会显示每一类占了多少。docker system prune会移除已停止的容器、未使用的网络、悬空镜像和构建缓存;只有确定没有哪个卷存着你需要的数据时,才加上--volumes。Docker Desktop设置中Memory limit旁边的Disk usage limit可以限制它的大小。
Vitals能多做什么
Vitals能显示虚拟机不会告诉macOS的内容:每个容器的内存。
- “容器”标签页显示每个容器的内存、CPU、网络、磁盘和已发布的端口,Compose项目归在一起
- 显示占用最多的容器和最接近上限的容器的卡片,以及按容器划分的内存图表
- Docker Desktop在App列表中占一行,已计入它的虚拟机;点按它即可看到里面的容器
- 菜单栏下拉面板中也有这些容器,询问后一键即可停止某个容器。请参阅如何停止Docker容器
- 支持Docker Desktop、OrbStack、Colima、Rancher Desktop和Podman
须知
- Vitals从引擎读取容器数据,从不启动或移除容器。“停止”是它唯一会做的更改,而且只在你要求时才做。
- 这个标签页在有引擎运行时显示。
常见问题
Mac上的Docker为什么占用这么多内存?
Docker在Linux虚拟机中运行容器,虚拟机最多可占用到它的Memory limit,默认是Mac内存的一半。在Settings → Resources → Advanced中调低Memory limit,并运行docker stats查看哪个容器占用最多。
Virtual Machine Service for Docker是什么?
它是Docker Desktop使用Apple的Virtualization框架时运行容器的虚拟机。它的内存就是容器和其中的Linux所用的全部内存,最多到Docker Desktop的Memory limit。
如何在Mac上限制Docker的内存?
在Docker Desktop中打开Settings → Resources → Advanced,调低Memory limit。对于单个容器,使用docker run --memory 512m,或在Compose文件中使用mem_limit: 512m。
停止容器后,Docker为什么不归还内存?
即使容器停止了,虚拟机仍会保留一部分用过的内存。打开Resource Saver,让虚拟机在Docker Desktop闲置时关闭,或者退出Docker Desktop。
Virtual Machine Service for Docker为什么占用这么多CPU?
通常是某个容器在忙,docker stats会显示是哪一个。如果没有容器运行CPU仍然很高,就退出并重新打开Docker Desktop,然后更新它:什么都没运行时CPU也很高,在某些Docker Desktop版本中已被报告为bug。
Mac上该给Docker分配多少内存?
比你的容器在最忙时的用量稍多一点,docker stats会显示这个用量。对于典型的开发栈,这通常是2到4 GB,远低于默认的Mac内存一半。
有没有能显示Mac上Docker容器内存的App?
Vitals有一个“容器”标签页,显示每个容器的内存、CPU、网络、磁盘和端口,支持Docker Desktop、OrbStack、Colima、Rancher Desktop和Podman。它还能在询问后从菜单栏停止容器。