快速搞懂.NET 5/.NET Core應用程式的釋出部署

Eric zhou發表於2021-02-16

.NET Framework時代,.NET 應用程式大多直接部署執行在Windows伺服器上,當然也可以通過Mono部署執行在Linux上。無論部署exe,還是IIS站點、或是Windows Service,編譯後的程式直接copy、簡單配置部署上即可。

有了.NET Core之後,.NET應用程式完美支援跨平臺部署,.NET 5 Release後,沿襲了.NET Core應用程式釋出模式。

支援跨平臺部署執行,.NET 5/.NET Core的應用程式面臨著多平臺,多場景的部署需求。比如說:部署在Windows、Linux、MaxOS...,OS層面是否需要部署.NET Runtime執行時,.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?WT.mc_id=DT-MVP-5003918

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的Console應用程式,同時引用Newtonsoft.Json Nuget包。

    

    Main函式的程式碼:

using System;

namespace NET5PublishExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var msg = Newtonsoft.Json.JsonConvert.SerializeObject("Hello .NET 5!");
            Console.WriteLine(msg);
            Console.ReadKey();
        }
    }
}

  首先,編譯一下這個工程dotnet build,這一步很重要。然後,使用命令列執行dotnet publish指令:

   

   我們看一下F:\GitHub\Source\Repos\NET5PublishExample\bin\Debug\net5.0\publish\目錄下生成的檔案:

   

    正如上面所說,輸出包括以下內容

  • 具有dll副檔名的程式集中的中間語言(IL)程式碼:NET5PublishExample.dll
  • 一個.deps.json檔案,其中包含專案的所有依賴項NET5PublishExample.deps.json
  • 一個.runtimeconfig.json檔案,它指定應用程式期望的共享執行時,以及該執行時的其他配置選項(例如,垃圾收集型別)。NET5PublishExample.runtimeconfig.json
  • 應用程式依賴的應用程式,從NuGet快取目錄複製到輸出資料夾中。Newtonsoft.Json.dll

     同時,還生成了一個Windows平臺的可執行檔案:NET5PublishExample.exe,雙擊可以執行:

     

     另外,使用dotnet NET5PublishExample.dll,也可以直接執行:

      

     然後有幾個疑問:

     NET5PublishExample.dll是不是可以跨平臺執行?

     在Linux、MacOS平臺下有沒有對應的可執行檔案?

     帶著這2個問題,我們繼續往下研究?

三、dotnet publish生成的可執行檔案和跨平臺二進位制檔案

     自包含的方式釋出應用程式,依賴於框架的方式釋出應用程式。這兩種釋出模式預設情況下都會生成特定於平臺的可執行檔案和跨平臺二進位制檔案。

     1. 可執行檔案

     可執行檔案不是跨平臺的。它們特定於作業系統和CPU體系結構。因為Windows和linux下的可執行檔案的結構和內容是不同的,所以可執行檔案是分作業系統的。

     建立可執行檔案時,同時可以選擇將應用程式釋出為自包含或依賴於框架。

     以自包含的方式釋出應用程式包括該應用程式的.NET執行時,並且該應用程式的使用者不必擔心在執行該應用程式之前安裝.NET。可以直接執行!

     釋出為與框架相關的應用程式不包括.NET執行時和庫。僅包括應用程式和第三方依賴項。需要安裝.NET 執行時。

     這裡我們示例2個平臺的可執行檔案:

     ① windows-x64平臺

     

      ② Linux-x64平臺

       使用的dotnet publish指令 dotnet publish -r linux-x64 --self-contained false

       

       生成的可執行檔案:

       

     2. 跨平臺的二進位制檔案

     將應用程式釋出為依賴於框架的dll檔案形式時,就會建立跨平臺的二進位制檔案。該dll檔案以專案命名。例如,如果您有一個名為應用程式NET5PublishExample,檔名為NET5PublishExample.dll建立。

     以這種方式釋出的應用程式dotnet <filename.dll>可以通過命令執行,可以在任何平臺上執行。

四、關於自包含的釋出選項和示例

    以自包含的方式釋出應用會生成特定於平臺的可執行檔案

    輸出釋出資料夾包含應用程式的所有元件,包括.NET庫和目標執行時。該應用程式與其他.NET應用程式隔離,並且不使用本地安裝的.NET執行時。因此無需下載並安裝.NET 執行時。

    可執行二進位制檔案針對指定的目標平臺生成。例如,如果您有一個名為NET5PublishExample的應用程式,並且釋出了Windows的自包含可執行檔案,則會建立NET5PublishExample.exe檔案。對於Linux或macOS釋出,將建立一個NET5PublishExample檔案。目標平臺和體系結構-r <RID>由dotnet publish命令的引數指定。有關RID的更多資訊,請參見.NET RID目錄

    如果應用程式具有特定於平臺的依賴項,例如包含特定於平臺的依賴項的NuGet程式包,則這些依賴項將與應用程式一起復制到publish資料夾中。

    這種模式的優勢有哪些呢?

  1.     控制.NET版本:可以控制與應用程式一起部署的.NET版本。
  2.     指定執行的平臺:因為必須為每個平臺釋出應用程式,所以我們需要確定應用程式將在何處執行。如果.NET引入了新平臺,則必須先發布針對該平臺的版本,然後才能在該平臺上執行您的應用程式。

    同時也帶來了以下問題:

  1.     更大的部署內容:應用程式包括.NET執行時和所有應用程式依賴項,所以所需的下載大小和硬碟空間大於依賴於框架的版本。
  2.     難以更新.NET版本.NET Runtime:(隨應用程式分發)只能通過釋出新版本的應用程式進行升級。但是,.NET將根據應用程式執行的計算機上框架庫的需要更新關鍵的安全補丁。

    例如:

    示例1:釋出一個獨立的應用程式,建立macOS 64位可執行檔案,同時包含了.NET 執行時

dotnet publish -r osx-x64

    生成的檔案列表如下:包含macOS 64可執行檔案NET5PublishExample,以及包含了對應macOS 64平臺下的.NET 執行時

    

      示例2:釋出一個獨立的應用程式,建立Windows 64位可執行檔案,同時包含了.NET 執行時

 dotnet publish -r win-x64

    生成的檔案列表如下:包含Windows 64可執行檔案NET5PublishExample.exe,以及包含了對應Windows 64平臺下的.NET 執行時

       

五、關於依賴框架的釋出選項和示例

  釋出為依賴框架的應用程式是跨平臺的,並且不包含.NET執行時。應用程式的執行需要單獨安裝指定版本的.NET執行時

  應用程式的跨平臺二進位制檔案可以使用dotnet <filename.dll>命令執行,並且可以在任何平臺上執行。如果應用程式使用具有特定於平臺的實現的NuGet包,則所有平臺的依賴項都將與應用程式一起復制到publish資料夾中。

  可以通過將-r <RID> --self-contained false引數傳遞給dotnet publish命令來為特定平臺建立可執行檔案。當-r引數被省略,為當前平臺建立一個可執行檔案。具有目標平臺特定於平臺的依賴關係的任何NuGet軟體包都將複製到publish資料夾中。

  這種模式帶來的優勢有:

  1.   小型部署:僅分發應用程式及其依賴項。.NET執行時和庫由使用者安裝,所有應用共享執行時。
  2.   跨平臺:應用程式和任何基於.NET的庫都可以在其他作業系統上執行
  3.   使用最新的.NET執行時:該應用程式使用目標系統上安裝的最新執行時(在.NET的目標主要,次要家族中)。這意味著應用程式會自動使用最新的.NET執行時修補版本。

   同時也帶來了以下問題:

  1.    需要預安裝.NET 執行時僅在主機系統上已安裝應用目標的.NET執行時版本時,應用才能執行。
  2.    .NET可能會更改:.NET執行時和庫可能會在執行該應用程式的計算機上更新。

   例如:

   示例1:釋出一個當前平臺的依賴框架的跨平臺應用程式,不包含.NET 執行時,將與dll檔案一起建立一個針對當前平臺的可執行檔案。   

dotnet publish

   

   使用dotnet NET5PublishExample.dll,可以直接執行(本機已經安裝.NET執行時,NET5PublishExample.dll是跨平臺的二進位制檔案

   示例2:釋出一個依賴框架的跨平臺應用程式(Linux 64位),不包含.NET 執行時,將建立一個Linux 64位可執行檔案以及dll檔案。    

dotnet publish -r linux-x64 --self-contained false

      使用dotnet NET5PublishExample.dll,可以直接執行(本機已經安裝.NET執行時,NET5PublishExample.dll是跨平臺的二進位制檔案

      

  六、ReadyToRun編譯選項

   通過將應用程式程式集編譯為ReadyToRun(R2R)格式,可以改善.NET應用程式的啟動時間和延遲。R2R是一種提前(AOT)編譯的形式。

   R2R二進位制檔案通過減少應用程式載入時即時(JIT)編譯器需要完成的工作量來提高啟動效能。與JIT產生的程式碼相比,二進位制檔案包含相似的本機程式碼

   但是,R2R二進位制檔案較大,因為它們既包含中間語言(IL)程式碼(某些情況下仍然需要此程式碼)和同一程式碼的本機版本。僅當釋出針對特定執行時環境(RID)(例如Linux x64或Windows x64)的應用程式時,R2R才可用。

   總結一下:通過R2R方式,可以直接將程式碼編譯為Native Code,減少.NET 程式第一次載入時JIT編譯帶來的效能消耗,以提升.NET應用的首次載入效能。類似於ngen的程式集預載入。關於Ngen可以參考這個連結:ngen

   對應的dotnet publish指令選項:   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?WT.mc_id=DT-MVP-5003918

 

周國慶

2020/2/15

相關文章