1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
## 建立專案
dotnet new mvc -n MyMvcApp

## or 指定版本
dotnet new mvc -n MyMvcApp --framework net8.0

cd .\MyMvcApp\

## 觀察結構
tree /F


## 測試
dotnet run

一開始怎麼知道要這樣寫?

首先,我們要從這個問題開始:我的程式要跑起來,需要哪些步驟?

1
2
3
4
dotnet restore
dotnet build
dotnet publish -c Release -o ./publish
dotnet ./publish/MyMvcApp.dll

f

而其實 Dockerfile 只是把這些步驟搬進 container 裡,並且我們可以一段一段作

  1. 本機 dotnet run 成功
  2. 本機 dotnet publish 成功
  3. 本機 dotnet ./publish/MyMvcApp.dll 成功
  4. 寫 SDK-only Dockerfile
  5. docker build 成功
  6. docker run 成功
  7. 改成 multi-stage Dockerfile
  8. 再 docker build / docker run

f

撰寫 dockerfile

1
2
3
4
5
6
7
8
9
10
11
## 在 csproj 層
nvim Dockerfile

## 進入編輯模式
i

Esc

## 寫入存檔離開
:wq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 建立 build 環境 (用 SDK)
## build 階段:用 .NET SDK 還原套件、編譯、發布程式
#### 使用 Microsoft 官方提供的 .NET 8 SDK 映像檔,建立一個叫做 build 的階段
#### 這個 Image 裡面有完整的 .NET 開發工具 所以它可以拿來「編譯專案」
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build

WORKDIR /src
#### COPY 來源 目的地
#### 所以第二個 . 代表 Container 裡面的 /src。
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o /app/publish

# 2. 建立執行環境 (用 runtime)
## runtime 階段:用比較小的 .NET ASP.NET Runtime 只負責執行程式
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyMvcApp.dll"]

SDK only

f

假設想先測非 multi-stage 版本也可以先測試

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM mcr.microsoft.com/dotnet/sdk:9.0

WORKDIR /src

COPY . .

RUN dotnet restore

RUN dotnet publish -c Release -o /app/publish

WORKDIR /app/publish

ENTRYPOINT ["dotnet", "MyMvcApp.dll"]

雖然它只有一個 stage,但它同時做了 build + runtime,只是 runtime 也繼續用 SDK image,假設現在在 MyMvcApp 專案資料夾,也就是有 .csproj 的那一層

1
docker build -t mymvcapp-sdk-only .

成功後執行

1
docker run -it -p 5000:8080 mymvcapp-sdk-only

然後打開瀏覽器 http://localhost:5000,如果畫面出現 MVC 首頁,就代表成功

因為 一個 stage 可以跑起來,但 multi-stage 是為了讓正式部署的 image 更乾淨、更小、更安全、更像正式環境

一個 stage 的問題是這種 SDK-only 版本正式部署時,container 裡會包含很多其實不需要的東西

  • .NET SDK
  • 原始碼
  • NuGet restore/build 工具
  • 編譯工具
  • 可能還有測試檔案、Dockerfile、README、.git 內容

可是網站真正執行時,其實只需要

  • .NET Runtime
  • publish 出來的 dll
  • appsettings
  • wwwroot
  • 相關相依檔案

Multi-stage 把「建置」和「執行」分開

build stage:負責編譯

1
2
3
4
5
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o /app/publish

runtime stage:負責執行

1
2
3
4
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyMvcApp.dll"]

意思是第一段 SDK image 用來 restore / build / publis,而第二段 ASP.NET Runtime image 只拿 publish 結果來跑網站,最後真的留下來的 image 只有第二段,第一段 build stage 不會被帶進正式 image 裡

f

f

1
2
## -t 是 --tag 的縮寫
docker build -t mymvcapp .

執行容器

1
2
## 電腦的 5000 port 轉到 container 裡面的 8080
docker run -it -p 5000:8080 mymvcapp

推到 Docker Hub

1
2
3
docker login
docker tag mymvcapp yourdockerhubid/mymvcapp:v1
docker push yourdockerhubid/mymvcapp:v1

PO 執行

1
docker run -it -p 5000:8080 yourdockerhubid/mymvcapp:v1

f