Dotnet Core Docker 容器在 Linux 上泄漏 RAM 并导致 OOM
Posted
技术标签:
【中文标题】Dotnet Core Docker 容器在 Linux 上泄漏 RAM 并导致 OOM【英文标题】:Dotnet Core Docker Container Leaks RAM on Linux and causes OOM 【发布时间】:2019-09-28 10:35:41 【问题描述】:我在 Docker 的 Linux 容器中运行 Dotnet Core 2.2。
我尝试了许多不同的配置/环境选项 - 但我总是遇到内存不足的问题('docker events' 报告 OOM)。
在生产中,我在 Ubuntu 上托管。对于开发,我在 Windows 的 Docker 上使用 Linux 容器 (MobyLinux)。
我已经返回运行 Web API 模板项目,而不是我的实际应用程序。我实际上是在返回一个字符串并且什么都不做。如果我从 curl 调用它大约 1000 次,容器就会死掉。垃圾收集器似乎根本没有工作。
尝试在 docker-compose 中设置以下环境变量:
DOTNET_RUNNING_IN_CONTAINER=true
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true
ASPNETCORE_preventHostingStartup=true
还在 docker-compose 中尝试了以下操作:
mem_reservation: 128m
mem_limit: 256m
memswap_limit: 256m
(这些只会让它死得更快)
尝试将以下设置为true或false,没有区别:
ServerGarbageCollection
我尝试过作为 Windows 容器运行,这不会 OOM - 但它似乎也不尊重内存限制。
我已经排除了使用 HttpClient 和 EF Core - 因为在我的示例中我什至没有使用它们。我已经读过一些关于在端口 443 上监听的问题 - 因为我可以让容器整天闲置,如果我在一天结束时检查 - 它用掉了更多的内存(不是大量,但它增长)。
我的 API 中的示例:
// GET api/values/5
[HttpGet("id")]
public ActionResult<string> Get(int id)
return "You said: " + id;
使用 Curl 调用示例:
curl -X GET "https://localhost:44329/api/values/7" -H "accept: text/plain" --insecure
(重复 1000 次左右)
预期:对于一个非常原始的请求,RAM 使用率仍然很低
实际:RAM 使用量持续增长,直到出现故障
完整的 Dockerfile:
FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /src
COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"]
RUN dotnet restore "WebApplication1/WebApplication1.csproj"
COPY . .
WORKDIR "/src/WebApplication1"
RUN dotnet build "WebApplication1.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "WebApplication1.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "WebApplication1.dll"]
docker-compose.yml
version: '2.3'
services:
webapplication1:
image: $DOCKER_REGISTRY-webapplication1
mem_reservation: 128m
mem_limit: 256m
memswap_limit: 256m
cpu_percent: 25
build:
context: .
dockerfile: WebApplication1/Dockerfile
docker-compose.override.yml
version: '2.3'
services:
webapplication1:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=https://+:443;http://+:80
- ASPNETCORE_HTTPS_PORT=44329
- DOTNET_RUNNING_IN_CONTAINER=true
- DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true
- ASPNETCORE_preventHostingStartup=true
ports:
- "50996:80"
- "44329:443"
volumes:
- $APPDATA/ASP.NET/Https:/root/.aspnet/https:ro
- $APPDATA/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
我在 Windows 上运行 Docker CE Engine 18.0.9.1,在 Ubuntu 上运行 18.06.1。确认一下 - 我也尝试过 Dotnet Core 2.1。
我还在 IIS Express 中尝试过 - 进程达到大约 55MB,这实际上是在使用多个线程等向它发送垃圾邮件。
当它们都完成后,它会下降到大约 29-35MB。
【问题讨论】:
应该收集更多数据,因为结论是什么原因,docs.microsoft.com/en-us/aspnet/core/performance/… 我尝试重新制作,但没有发现问题。您能否提供详细的重现步骤,包括 Dockerfile、撰写文件和其他命令?对于我的步骤,我将 VS 中的默认 Dockerfile 用于 Web API 项目。构建后,我运行docker run --name test --memory-reservation 128m -m 256m --memory-swap 256m -p 8000:80 webapp
。然后我从命令提示符运行我的循环:for /L %G in (1, 1, 1000) do (curl -X GET "http://localhost:8000/api/values/7" -H "accept: text/plain" --insecure)
。我多次运行该循环,容器从未死亡。
在我的情况下添加更多内存有同样的问题,但我认为他们在 3.0 上做了一些工作来解决这个问题docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0 所以也许尝试更新。
请注意,对于 Windows 10 下的 Docker Desktop,实际运行 docker 映像的虚拟机默认仅分配 2GB 内存。如果您的应用程序需要更多内存,则需要为实例提供更多内存。
您确定泄漏与 Docker VM 有关吗?您是否尝试在其他主机(IIS、其他)下运行您的 Api,以查看在这些情况下是否也发生泄漏?中间件中是否有任何可能泄漏的代码?
【参考方案1】:
这可能是因为没有执行垃圾回收 (GC)。
看看这个未解决的问题,它看起来非常相似:
https://github.com/dotnet/runtime/issues/851
使Ubuntu 18.04.4
在虚拟机上工作的一个解决方案是使用工作站垃圾收集 (GC):
<PropertyGroup>
<ServerGarbageCollection>false</ServerGarbageCollection>
</PropertyGroup>
https://github.com/dotnet/runtime/issues/851#issuecomment-644648315
https://github.com/dotnet/runtime/issues/851#issuecomment-438474207
https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/workstation-server-gc
这是另一个发现:
经过进一步调查,我注意到存在很大差异 我的服务器之间的可用逻辑 CPU 数量(80 vs 16)。经过一番谷歌搜索后,我遇到了这个话题dotnet/runtime#622 这让我对 CPU/GC/Threads 设置进行了实验。
我在堆栈文件中使用--cpus constraint;显式设置 System.GC.Concurrent=true, System.GC.HeapCount=8, System.GC.NoAffinitize=true, System.Threading.ThreadPool.MaxThreads=16 在
runtimeconfig.template.json
文件中;将图像更新为 3.1.301-bionic sdk 和 3.1.5-bionic asp.net runtime — 我在一个 各种组合,所有这些都没有效果。只是申请 挂起直到被 OOMKilled。使它与服务器 GC 一起工作的唯一因素是
--cpuset-cpus
约束。当然,可用处理器的显式设置不是 docker swarm 模式的一个选项。但我正在尝试 可用cpu 来查找任何规律性。在这里我得到了一些 有趣的事实。有趣的是,之前我已经迁移了 3 个其他后端 为新服务器集群提供服务,并且默认情况下它们都运行良好 设置。他们的内存限制设置为
600 Mb
但实际上他们需要 关于400 Mb
运行。只有消耗内存才会出错 应用程序(我有两个),它需要3 Gb
来构建 内存结构并使用6 Gb
约束运行。它在
[1, 35]
可用cpu和gets之间的任何范围内保持工作 当 cpu 计数为36
时挂起。
https://github.com/dotnet/runtime/issues/851#issuecomment-645237830
【讨论】:
以上是关于Dotnet Core Docker 容器在 Linux 上泄漏 RAM 并导致 OOM的主要内容,如果未能解决你的问题,请参考以下文章
dotnet core docker web api没有连接
.NET Core 2.1 容器镜像将从 Docker Hub 中删除
Asp.Net Core WebAPI [dotnet publish --self-contained -r linux-64] 放入Docker容器后是不是真的自包含