在 Docker 中容器化 C# 应用程序:高级水平

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么是高级水平?

\n

基于 .NET 的 C# 应用程序容器化已不再仅仅是一种趋势——它已成为行业标准。在入门阶段,一个基本的 Dockerfile 加上 FROM mcr.microsoft.com/dotnet/aspnet 就足够了。然而,在生产环境中,会出现需要更深入理解的任务:最小化镜像大小、安全性、多容器架构、有效的配置管理和调试。

\n

本文面向已经熟悉 Docker 和 C# 基础知识,但希望提升到下一水平的开发者。我们将深入探讨高级技术,帮助您为 .NET 应用程序创建轻量、安全且易于扩展的镜像。

\n\n

1. 多阶段构建:轻量镜像的艺术

\n

一个经典问题是 .NET SDK 镜像重量超过 1.5 GB。将其带入生产环境既无意义又危险。多阶段构建通过分离构建阶段和运行阶段来解决这个问题。

\n\n

带多阶段构建的高级 Dockerfile

\n

我们不再简单地复制二进制文件,而是使用官方 SDK 镜像进行编译,然后将结果复制到最小的 aspnet 镜像中。

\n
# 阶段 1:构建\nFROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nWORKDIR /app\n\n# 仅复制项目文件(缓存优化)\nCOPY src/MyApp.csproj .\nRUN dotnet restore\n\n# 复制其余代码\nCOPY src/ .\nRUN dotnet publish -c Release -o /app/publish\n\n# 阶段 2:运行时\nFROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime\nWORKDIR /app\n\n# 添加非特权用户\nRUN adduser --disabled-password --gecos '' appuser\nUSER appuser\n\nCOPY --from=build /app/publish .\n\nEXPOSE 8080\nENV ASPNETCORE_URLS=http://+:8080\nENTRYPOINT ["dotnet", "MyApp.dll"]
\n

这里的关键点:

\n
    \n
  • 层缓存:首先仅复制 .csproj 并执行 dotnet restore。只要依赖项不变,Docker 就会对该层使用缓存。
  • \n
  • 非特权用户: appuser — 对生产环境的安全性至关重要。
  • \n
  • 端口 8080:在 .NET 8 中默认使用 8080,符合现代云标准。
  • \n
\n\n

技巧:裁剪和 AOT 构建

\n

为了进一步减小镜像大小,请使用裁剪(移除未使用的代码)或原生 AOT。这可以生成大小为 10-30 MB 的镜像。

\n
# 用于 ReadyToRun (R2R) 和裁剪\nRUN dotnet publish -c Release -r linux-x64 --self-contained true \\\n    -p:PublishTrimmed=true \\\n    -p:PublishSingleFile=true \\\n    -o /app/publish
\n

重要提示:AOT 对反射和动态加载程序集有限制。使用前请确保您的代码兼容。

\n\n

2. 配置与密钥管理

\n

将连接字符串和 API 密钥存储在镜像内部是一个严重错误。在高级水平,我们使用Docker Secrets、环境变量以及与 appsettings.json 的结合。

\n\n

通过 Docker 覆盖 appsettings

\n

.NET 内置了配置优先级机制。我们可以通过环境变量传递设置,这些变量会自动映射到配置部分。

\n
// 在 C# 代码中\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Configuration\n    .AddJsonFile("appsettings.json", optional: false)\n    .AddEnvironmentVariables(); // 覆盖 JSON 中的值
\n

启动容器:

\n
docker run -d -p 8080:8080 \\\n  -e "ConnectionStrings__DefaultConnection=Server=db;Database=myapp;User Id=sa;Password=MyPass123!" \\\n  -e "Logging__LogLevel__Default=Warning" \\\n                

博客

书籍推荐