Dotnet Core Docker容器在Linux上泄漏内存并导致OOM。

63

我正在Docker中的Linux容器中运行Dotnet Core 2.2。

我尝试了许多不同的配置/环境选项,但我一直遇到内存不足的问题('docker events'报告了OOM)。

在生产环境中,我使用Ubuntu进行托管。对于开发环境,我在Windows上使用Docker的Linux容器(MobyLinux)。

我已经回到运行Web API模板项目,而不是我的实际应用程序。我只是简单地返回一个字符串,什么也没做。如果我从curl调用它约1,000次,容器将死亡。垃圾收集器似乎根本没有工作。

尝试在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

(重复约1,000次)

预期:对于一个非常基本的请求,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文件

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

=>

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左右。


8
我尝试了重现问题,但未看到该问题。您能否提供详细的重现步骤,包括Dockerfile、compose文件和其他命令?对于我的步骤,我使用了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)。我运行了这个循环多次,容器从未死掉。 - Matt Thalman
3
我曾经遇到同样的问题,增加内存有助于解决,但我认为他们在3.0版本上做了一些修复工作https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0,因此可以尝试更新。 - Filip Cordas
2
请注意,在 Windows 10 上的 Docker Desktop 中,默认情况下为运行您的 Docker 镜像分配了仅 2GB 的内存。如果您的应用程序需要更多内存,则需要为实例提供更多内存。 - Thorbjørn Ravn Andersen
1
你确定泄漏与Docker VM有关吗?您是否尝试在其他主机(IIS,其他)下运行Api以查看泄漏是否也发生在这些条件下?您的中间件中是否有可能泄漏的代码? - Jonathan Larouche
曾经遇到过类似的问题,是因为我没有在一个IDisposable对象上调用Dispose()方法。当时我很惊讶内存使用量如此之快(相对于分配的资源和等待垃圾回收的对象数量来说),但我很高兴解决了我的问题,所以无法提供更多细节。建议:尝试减慢调用速率,看看问题是否消失或者在OOM之前是否有更多的调用(这不是解决方案,但可以通过指向GC来缩小搜索范围)。 - AlanK
显示剩余16条评论
1个回答

2
这可能是因为垃圾回收(GC)没有执行。

看着这个未解决的问题,它看起来非常相似:

https://github.com/dotnet/runtime/issues/851

在虚拟机上使 Ubuntu 18.04.4 正常工作的一种解决方案是使用 Workstation 垃圾收集(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://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/workstation-server-gc

这是另一个发现:
经过进一步调查,我注意到我的服务器在可用逻辑 CPU 数量方面存在很大差异(80 vs 16)。在一些谷歌搜索后,我遇到了这个主题dotnet/runtime#622,这引导我进行了有关 CPU/GC/Threads 设置的实验。
我在堆栈文件中使用了--cpus constraint;在runtimeconfig.template.json文件中明确设置了System.GC.Concurrent=true, System.GC.HeapCount=8, System.GC.NoAffinitize=true, System.Threading.ThreadPool.MaxThreads=16;将镜像更新为3.1.301-bionic sdk3.1.5-bionic asp.net runtime - 我以各种组合做了所有这些事情,但是所有这些都没有效果。应用程序会一直挂起,直到被 OOMKilled 终止。
唯一使其与 Server GC 正常工作的是 --cpuset-cpus 约束。当然,显式设置可用处理器不是 Docker Swarm 模式的选项。但是我正在尝试使用可用的 CPU 进行实验,以找到任何规律。这里我得到了一些有趣的事实。
有趣的是,之前我已将其他 3 个后端服务迁移到了新的服务器集群,并且它们所有的默认设置都很好。它们的内存限制设置为 600 Mb,但实际上只需要约 400 Mb 运行。只有在消耗内存的应用程序(我有两个这样的应用程序)中才会出现问题,它需要 3 Gb 来构建内存结构,并带有 6 Gb 的限制运行。
它在可用 CPU 数量[1, 35]范围内一直工作,并在 CPU 数量为36时挂起。

https://github.com/dotnet/runtime/issues/851#issuecomment-645237830


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接