1. 程式人生 > 其它 >將 ASP.Net Core WebApi 應用打包至 Docker 映象

將 ASP.Net Core WebApi 應用打包至 Docker 映象

將 ASP.Net Core WebApi 應用打包至 Docker 映象

執行環境為 Windows 10 專業版 21H1, Docker Desktop 3.6.0(67351),Docker Engine 20.10.8

1. ASP.Net Core Runtime 還是 .Net Core Runtime

在這裡首先要區分一下 SDK 和 Runtime 的區別。SDK (Software Development Kit)主要是在開發過程中使用的,而 Runtime 是在實際執行的時候使用的(類似於 JDK 和 JRE 的關係)。所以對於我們這種釋出後執行的情況,Runtime 就足夠了。

一開始沒有分清楚 ASP.Net Core Runtime,和 .Net Core Runtime 的區別,導致自己的網站專案雖然拷貝進了映象但一直提示缺少執行時。ASP 的全稱是 Active Server Pages,顧名思義,是用於動態網頁的,所以網站應用要使用 ASP.Net Core Runtime;而 .NET Core Runtime 一般是用於控制檯應用的;還有一個類似的 .NET Desktop Runtime 一般是用於 Windows 桌面應用的。詳細可以參見微軟的 .NET 下載頁面

2. ASP.NET Core WebApi 應用的編譯

雖然前兩天 .NET 6.0 釋出了,也是 LTS,但此處還是使用 .NET Core 3.1 哈

新建一個 ASP.NET Core WebApi 應用,在 Controllers 資料夾裡面新增一個 HelloWorldController,並且在 appSettings.json 中新增一個配置項 WelcomeStr

HelloWorldController.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;

namespace HelloWorldWebApplication.Controllers
{
    [ApiController]
    [Route("[controller]/[action]")]
    public class HelloWorldController
    {
        private readonly IConfiguration Configuration;

        public HelloWorldController(IConfiguration configuration)
        {
            this.Configuration = configuration;
        }

        [HttpGet]
        public string Hello()
        {
            return Configuration.GetSection("WelcomeStr").Value;
        }
    }
}

appSettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "WelcomeStr": "Hello ASP.NET Core 3.1!"	# 新增部分
}

功能也比較簡單,返回一個在配置檔案中定義的歡迎語。控制器中的依賴注入相關內容在此處就不細講了。執行一下看一下本地的效果:

接下來就是本地釋出了,右鍵專案選擇釋出,選擇釋出到資料夾。配置項的 Debug 和 Release、目標框架 自不用多說。關於“部署模式”,框架依賴 的意思是,釋出的內容必須要在安裝了相應執行環境的機器上才能執行(正是我們打包至Docker想要的),而獨立 的意思是,即使機器沒有安裝相應的執行環境,也可以執行。對於目標執行時,如果我們要打包到 Docker 的 Linux x64 映象中,應當選擇 linux-x64。點選“釋出”,在專案的根目錄中,依次進入 bin\Release\netcoreapp3.1\publish,這裡面的檔案就是釋出後的檔案,我們要將它們打包進映象。

3. Dockerfile 的建立與打包

準備工作的最後一步了。注意檔名稱一定要是 Dockerfile,鄙人一開始使用 VS Code 新建了一個 .dockerfile 檔案,在使用 docker 構建命令時一直提示沒有構建檔案。

Dockerfile

FROM mcr.microsoft.com/dotnet/aspnet:3.1-focal
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
RUN mkdir /HelloWorldWebApplication
COPY ./ /HelloWorldWebApplication
EXPOSE 80
WORKDIR /HelloWorldWebApplication
CMD ["/bin/bash","-c","./HelloWorldWebApplication"]

這裡面的注意點就比較多了,我們逐個來說

第 1 行,使用 From 選擇基礎映象。所有的選項可以參見 ASP.NET Core Runtime 的 DockerHub 頁面。此處選用的是基於 Ubuntu、 執行時版本是 3.1 的映象。後面的 focal 其實對應的作業系統的代號,即 Ubuntu 20.04 Focal Fossa(類似的 Buster 是 Debian 10 的代號)。如果希望映象儘可能的小,可以使用 alpine 映象(這裡選用 Ubuntu 映象主要是為了個人的操作方便,比如按下別名 ll 就可以執行對應命令 ls -l,alpine 映象雖然小,但很多常用命令、功能都沒有)

第 2 ~ 3 行設定時區。如果使用的是 alpine 映象,需要手動拷貝時區檔案

第 4 行,在容器中建立目錄。一開始的時候沒寫這行,導致後面的 COPY 找不到檔案夾了。這可不像是 docker cp 可以自動在容器中建立資料夾啊。

第 5 行,使用 COPY 命令複製。切記在 源路徑中不要使用 *,如 COPY * /HelloWorldWebApplication,因為這會忽略第一層的資料夾。假設我們當前目錄是這樣的(多了wwwroot):

│ appsettings.Development.json
│ appsettings.json
│ Dockerfile
│ HelloWorldWebApplication
│ HelloWorldWebApplication.deps.json
│ HelloWorldWebApplication.dll
│ HelloWorldWebApplication.pdb
│ HelloWorldWebApplication.runtimeconfig.json
│ web.config

└─wwwroot
index.html

複製進去後,wwwroot 資料夾已經沒了:

root@f659a7e407e1:/HelloWorldWebApplication# ll
total 264
drwxr-xr-x 1 root root 4096 Nov 12 02:54 ./
drwxr-xr-x 1 root root 4096 Nov 12 02:55 ../
-rwxr-xr-x 1 root root 216 Nov 12 02:54 Dockerfile*
-rwxr-xr-x 1 root root 90680 Nov 12 02:24 HelloWorldWebApplication*
-rwxr-xr-x 1 root root 114406 Nov 12 02:24 HelloWorldWebApplication.deps.json*
-rwxr-xr-x 1 root root 8704 Nov 12 02:24 HelloWorldWebApplication.dll*
-rwxr-xr-x 1 root root 19932 Nov 12 02:24 HelloWorldWebApplication.pdb*
-rwxr-xr-x 1 root root 311 Nov 12 02:24 HelloWorldWebApplication.runtimeconfig.json*
-rwxr-xr-x 1 root root 162 Nov 12 01:49 appsettings.Development.json*
-rwxr-xr-x 1 root root 236 Nov 12 01:56 appsettings.json*
-rwxr-xr-x 1 root root 0 Nov 12 02:47 index.html*
-rwxr-xr-x 1 root root 545 Nov 12 02:24 web.config*

第 6 行,使用 EXPOSE 指明應當對映的埠。為什麼這個埠一定是 80 呢,我就是想使用 8001 呢,這是在 ASP.NET Core 3.1 Runtime 映象中定義的環境變數。可以進入容器,使用 env 命令檢視,可以發現:

所以如果想修改埠,在 Dockerfile 新增如下指令即可

ENV ASPNETCORE_URLS=http://+:5000

第 7 行,使用 WORKDIR 切換工作路徑。如果不切換工作路徑,則會找不到配置檔案。如修改 Dockerfile,刪除該行,並修改最後一行(需要指定執行檔案的路徑)為:

CMD ["/bin/bash","-c","./HelloWorldWebApplication/HelloWorldWebApplication"]

再次訪問,可以看到沒有獲取到配置檔案的內容:

第 8 行,使用 CMD 執行命令。我們可以直接執行 ./HelloWorldWebApplication,可別忘了是在 shell 中,所以要指定 /bin/bash 或 /bin/sh(和使用的具體作業系統映象有關,在 Ubuntu 中是 /bin/bash)。也嘗試過使用 alpine,似乎直接執行並不奏效,需要執行的命令為 /bin/sh dotnet ./HelloWorldWebApplication.dll。

4. 執行

終於到最後的執行步驟了,在 Windows 的終端中切換目錄到專案的 publish 資料夾中,執行 docker build -t helloworld_web:v1 . 生成最終的映象。使用 docker run -itd -p 5002:80 helloworld_web:v1 建立並執行容器(對映到 5002 埠),可以看到得到了與本地同樣的效果:

後記:

這一路操作下來,雖然主要的思路沒毛病,但細節的東西還是不少的,終於把之前的 Docker 內容實際運用上了。

Docker Desktop 的功能也越來越完備了,現在可以檢視構建 image 的 Dockerfile 檔案:

另外無意間看到了 Visual Studio 2019 中可以直接使用釋出到 Docker 的功能, 有空嘗試下。

參考:

.NET Core 官方下載
https://dotnet.microsoft.com/download/dotnet/3.1

Asp.net core在docker容器中的埠問題
https://www.cnblogs.com/RandyField/p/13059985.html

Linux 下執行本目錄的可執行檔案(命令)為什麼需要在檔名前加“./”
https://www.cnblogs.com/fortunel/p/8663669.html

Docker COPY 複製資料夾的詭異行為
https://www.jianshu.com/p/9b7da9aacd8a

Dockerfile複製時如何保留子目錄的結構
https://www.pkslow.com/archives/dockerfile-copy-keep-subdirectory-structure

linux的顯示全部環境變數
https://blog.csdn.net/weixin_39880318/article/details/116864978

ASP.NET Core 2.1 使用Docker執行
https://www.cnblogs.com/stulzq/p/9201830.html