.NET 5/.NET Core应用程序发布部署完全指南
在.NET Framework时代,.NET应用程序主要部署在Windows服务器环境,当然也可以借助Mono框架运行于Linux系统。无论是部署可执行程序、IIS站点还是Windows Service,编译后的产物直接复制并简单配置即可完成部署。
进入.NET Core时代后,.NET应用程序实现了真正的跨平台支持。.NET 5发布后,沿用了.NET Core的应用程序发布模式。
由于支持跨平台部署运行,.NET 5/.NET Core应用程序面临着多平台、多场景的部署需求,包括:部署目标涵盖Windows、Linux、macOS等操作系统;目标主机是否需要安装.NET Runtime运行时;运行时版本的选择等问题。
本文将详细探讨.NET 5/.NET Core应用程序的发布部署方式。
一、两种应用程序发布模式
1. 自包含发布模式
自包含发布方式将.NET运行时与应用程序及其所有依赖项打包在一起。应用程序可以在未安装.NET运行时的操作系统上独立运行。
简而言之:把.NET Runtime运行时打包到程序运行目录中,部署目标主机无需预先安装.NET Runtime运行时。
2. 框架依赖发布模式
框架依赖发布方式仅生成应用程序本身及其依赖项。应用程序的运行需要目标主机单独安装.NET运行时。
简而言之:不包含.NET Runtime运行时,仅包含应用程序及其依赖项。部署目标主机需要单独安装相应版本的.NET Runtime运行时。
二、NET 5/.NET Core应用程序的发布命令:dotnet publish
dotnet publish命令用于将应用程序及其依赖项发布到指定文件夹,便于后续部署到目标托管系统。
dotnet publish命令的详细用法可参考官方文档:https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish
dotnet publish [<PROJECT>|<SOLUTION>] [-c|--configuration <CONFIGURATION>]
[-f|--framework <FRAMEWORK>] [--force] [--interactive]
[--manifest <PATH_TO_MANIFEST_FILE>] [--no-build] [--no-dependencies]
[--no-restore] [--nologo] [-o|--output <OUTPUT_DIRECTORY>]
[-p:PublishReadyToRun=true] [-p:PublishSingleFile=true] [-p:PublishTrimmed=true]
[-r|--runtime <RUNTIME_IDENTIFIER>] [--self-contained [true|false]]
[--no-self-contained] [-v|--verbosity <LEVEL>]
[--version-suffix <VERSION_SUFFIX>]
dotnet publish -h|--help
dotnet publish会执行以下操作:编译应用程序,读取项目文件中指定的依赖项,然后将结果文件集发布到指定目录。输出包括:
- 扩展名为dll的程序集中的中间语言(IL)代码
- 一个deps.json文件,包含项目的所有依赖项信息
- 一个runtimeconfig.json文件,指定应用程序期望的共享运行时及相关配置选项(如垃圾回收类型)
- 应用程序依赖的NuGet包,从缓存目录复制到输出文件夹
通过dotnet publish命令,我们可以编译应用程序并生成适用于指定运行环境的部署包。
接下来创建一个.NET 5的控制台应用程序,并引用Newtonsoft.Json NuGet包。

Main函数代码如下:
using System;
namespace DemoApp
{
class EntryPoint
{
static void Main(string[] args)
{
var message = Newtonsoft.Json.JsonConvert.SerializeObject("Hello .NET 5!");
Console.WriteLine(message);
Console.ReadKey();
}
}
}
首先执行dotnet build命令编译项目,这一步至关重要。随后使用命令行执行dotnet publish指令:

查看生成目录下的文件:

如前所述,输出包含以下内容:
- 扩展名为dll的程序集中的中间语言代码:DemoApp.dll
- 包含项目所有依赖项的deps.json文件:DemoApp.deps.json
- 指定共享运行时及配置的runtimeconfig.json文件:DemoApp.runtimeconfig.json
- 应用程序依赖的NuGet包:Newtonsoft.Json.dll
同时还生成了Windows平台的可执行文件DemoApp.exe,双击可直接运行:

此外,使用dotnet DemoApp.dll命令也可以直接执行:

这里产生两个疑问:
DLL文件是否可以跨平台运行?
在Linux、macOS平台下是否有对应的可执行文件?
带着这两个问题,继续往下研究。
三、生成的可执行文件和跨平台二进制文件
无论采用自包含方式还是框架依赖方式发布,默认情况下都会生成平台特定的可执行文件和跨平台二进制文件。
1. 可执行文件
可执行文件不具备跨平台特性。它们与特定的操作系统和CPU架构绑定。由于Windows和Linux下可执行文件的结构和内容不同,可执行文件按操作系统区分。
创建可执行文件时,可以选择发布为自包含或框架依赖模式。
自包含发布包含应用程序的.NET运行时,用户在运行前无需安装.NET,可直接执行。
框架依赖发布不包含.NET运行时和库,仅包含应用程序和第三方依赖项,需要目标主机安装.NET运行时。
以下演示两个平台的可执行文件生成:
① Windows x64平台

② Linux x64平台
使用命令 dotnet publish -r linux-x64 --self-contained false

生成的可执行文件:

2. 跨平台二进制文件
以框架依赖的DLL形式发布应用程序时,会创建跨平台二进制文件。DLL文件以项目命名,例如项目名为DemoApp,则生成DemoApp.dll。
以此方式发布的应用程序可通过dotnet <filename.dll>命令运行,可在任何平台上执行。
四、自包含发布选项详解
自包含发布会生成平台特定的可执行文件。
输出发布文件夹包含应用程序的所有组件,包括.NET库和目标运行时。应用程序与其他.NET应用隔离,不使用本地安装的.NET运行时。因此无需下载安装.NET运行时。
可执行二进制文件针对指定的目标平台生成。例如,应用程序名为DemoApp,发布Windows自包含可执行文件时会创建DemoApp.exe;发布Linux或macOS版本则创建名为DemoApp的可执行文件。目标平台和架构通过-r <RID>参数指定。
若应用程序包含平台特定的依赖项(如某些NuGet包),这些依赖项会随应用程序一起复制到发布文件夹。
此模式的优势:
- **可控.NET版本:**可自主控制随应用程序部署的.NET版本。
- **指定运行平台:**由于需为每个平台单独发布应用程序,可确定应用程序的运行环境。若.NET支持新平台,需先发布针对该平台的版本才能运行。
此模式的不足:
- **部署包较大:**应用程序包含.NET运行时和所有依赖项,下载大小和硬盘占用空间大于框架依赖版本。
- **.NET版本升级复杂:**运行时(随应用分发)只能通过发布新版本应用程序来升级。但.NET会根据运行机器上的框架库需要更新关键安全补丁。
示例演示:
示例1:发布独立的macOS 64位可执行文件,包含.NET运行时
dotnet publish -r osx-x64
生成文件列表:包含macOS 64可执行文件DemoApp及对应macOS 64平台的.NET运行时

示例2:发布独立的Windows 64位可执行文件,包含.NET运行时
dotnet publish -r win-x64
生成文件列表:包含Windows 64可执行文件DemoApp.exe及对应Windows 64平台的.NET运行时

五、框架依赖发布选项详解
框架依赖发布的应用是跨平台的,不包含.NET运行时。应用程序运行需要目标主机单独安装指定版本的.NET运行时。
跨平台二进制文件可使用dotnet <filename.dll>命令运行,适用于任何平台。若应用程序使用具有平台特定实现的NuGet包,所有平台的依赖项都会随应用复制到发布文件夹。
可通过传递-r <RID> --self-contained false参数为特定平台创建可执行文件。省略-r参数则为当前平台创建可执行文件。具有平台特定依赖的NuGet包会复制到发布文件夹。
此模式的优势:
- **部署包较小:**仅分发应用程序及其依赖项。.NET运行时和库由用户安装,所有应用共享。
- **跨平台:**应用程序和基于.NET的库可在其他操作系统上运行。
- **使用最新运行时:**应用程序使用目标系统上安装的最新运行时(在同一主次版本范围内)。这意味着应用会自动使用最新的运行时补丁版本。
此模式的不足:
- **需要预装.NET运行时:**仅当主机系统已安装应用目标版本的.NET运行时时,应用才能运行。
- **.NET可能变更:**运行时和库可能在运行应用的计算机上更新。
示例演示:
示例1:发布当前平台的框架依赖跨平台应用,不包含.NET运行时,会生成当前平台的可执行文件
dotnet publish

使用dotnet DemoApp.dll可直接运行(本机已安装.NET运行时,DemoApp.dll是跨平台二进制文件)
示例2:发布框架依赖的跨平台应用(Linux 64位),不包含.NET运行时,生成Linux 64位可执行文件和DLL文件
dotnet publish -r linux-x64 --self-contained false

使用dotnet DemoApp.dll可直接运行(本机已安装.NET运行时,DemoApp.dll是跨平台二进制文件)

六、ReadyToRun编译选项
通过将应用程序程序集编译为ReadyToRun(R2R)格式,可以改善.NET应用程序的启动时间和延迟。R2R是一种提前(AOT)编译形式。
R2R二进制文件通过减少应用程序加载时JIT编译器需要完成的工作量来提升启动性能。与JIT生成的代码相比,二进制文件包含相似的本机代码。
但R2R二进制文件较大,因为它们既包含IL代码(某些情况下仍需要)和同一代码的本机版本。仅当发布针对特定运行时环境(如Linux x64或Windows x64)的应用程序时,R2R才可用。
总结:**通过R2R方式可将代码直接编译为原生代码,减少.NET程序首次加载时JIT编译的性能消耗,提升应用首次加载性能。**类似于ngen的程序集预加载。
对应的发布命令:dotnet publish -c Release -r win-x64 -p:PublishReadyToRun=true
以上是.NET 5/.NET Core应用程序发布部署的完整介绍。
推荐参考链接:https://docs.microsoft.com/en-us/dotnet/core/deploying/#publish-framework-dependent