將 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 埠),可以看到得到了與本地同樣的效果:

需要注意的是,此時使用的配置是 Production:

后記:
這一路操作下來,雖然主要的思路沒毛病,但細節的東西還是不少的,終于把之前的 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
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/356628.html
標籤:.NET技术
