搜尋此網誌

2025年3月11日 星期二

Dotnet ASP 專案與 docker-compose

緣起:


    上週有思考工作專案的大架構,主幹大概長這樣


    打算都用 docker 來實作,部屬跟搬遷會方便很多,只是要學不少東西,而且在實作時也踩了不少的雷。這篇文章主要是紀錄 dotnet 專案的 Dockerfile ,還有搭配 docker-compose 的使用。


Dotnet Web 專案與 Docker:


    電腦要先安裝 Docker,然後啟動。先建立一個 dotnet web 專案



    如果專案一開始沒有選 "啟用容器支援" 的話,後面還是可以在方案總管那裡,右鍵專案 -> 加入 -> Docker 支援。

    有 Docker 支援的專案,它會給你預設的 Dockerfile 跟 .dockerignore,整個方案的架構長這樣

DockerTest
├── DockerTest
|  ├── appsettings.Development.json
|  ├── appsettings.json
|  ├── bin
|  ├── Dockerfile
|  ├── DockerTest.csproj
|  ├── DockerTest.csproj.user
|  ├── obj
|  ├── Pages
|  ├── Program.cs
|  ├── Properties
|  └── wwwroot
└── DockerTest.sln

    預設的 Dockerfile 長這樣

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER $APP_UID
WORKDIR /app
EXPOSE 8080


# 此階段是用來組建服務專案
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["DockerTest/DockerTest.csproj", "DockerTest/"]
RUN dotnet restore "./DockerTest/DockerTest.csproj"
COPY . .
WORKDIR "/src/DockerTest"
RUN dotnet build "./DockerTest.csproj" -c $BUILD_CONFIGURATION -o /app/build

# 此階段可用來發佈要複製到最終階段的服務專案
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./DockerTest.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false

# 此階段用於生產環境,或以一般模式從 VS 執行時 (未使用偵錯設定時的預設值)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "DockerTest.dll"]

    第一個碰到的問題是 build image,我那時直接跑到有 Dockerfile 的目錄去下 docker build 指令,然後收到錯誤


    正確的指令格式是這樣,在方案目錄下

docker build -t docker-test -f .\DockerTest\Dockerfile .

    用 -t 來指定 build 好的 image 的 tag name, 可以用 -f  來指定 Dockerfile 的位置,最後是 build context,那個 . 代表當前目錄。


加入 docker-compose.yml:


    之後在測試跟部屬專案時,會需要同時開啟 redis 跟 postgresql,所以就去學習 docker-compose 來管理 image 的 build 跟 container 的啟動。

    方案總管那邊,右鍵方案 -> 新增項目 -> 輸入 docker-compose.yml -> 新增。第一次寫 yaml 檔,不知道它是用用空格來識別區塊,而且不接受 tab,那時是用 tab,想說,怎麼 Visual Studio 一直報錯 ...


    之後在 build image 時,會需要各自的 Dockerfile,postgreSQL 需要設定帳密、執行初始化用的 sql script 來建構資料庫跟資料表,nginx 也會需要做其它的設定之類的,所以我需要新增其它的 Dockerfile 來給每個 app 使用。有看到一個解決方案是,把檔名叫 Dockerfile.{app名稱},但這樣的話,在用 visual studio 編輯時,會無法得到 markdown 支援。

    我在這邊看到滿意的解答,他在專案目錄下新增一個 docker 資料夾,然後依 app 名稱在裡面新增資料夾,把對應的 Dockerfile 放進來。我跟著操作,然後把 DockerTest 專案下的 Dockerfile 複製過去

DockerTest
├── docker
|  └── DockerTest
|     └── Dockerfile
├── docker-compose.yml
├── DockerTest
(以下略)

    再來編輯 docker-compose.yml 檔
services:
 docker-test:
  build:
   context: .
   dockerfile: ./docker/DockerTest/Dockerfile
  image: docker-test
  container_name: docker-test
  ports:
  - "8080:8080"

    然後在專案目錄下 docker-compose build,等 image build 出來後,下 docker-compose up 來啟動所有 service,可以加 -d 的參數來背景執行。

    接著可以去 Docker Desktop 的 Containers 查看啟動的 container


    它看起來會有點像是階層的架構,還沒仔細去研究,我是直接把它當成一個群組來看,可以加個 httpd service,然後再 docker-compose up 跑看看

services:
 docker-test:
  build:
   context: .
   dockerfile: ./docker/DockerTest/Dockerfile
  image: docker-test
  container_name: docker-test
  ports:
  - "8080:8080"
 httpd-server:
  image: httpd:latest
  container_name: httpd-serve
  ports:
  - "8081:80"


    要結束所有 service 的話可以下 docker-compose down。如果只想跑單一個 service,可以用 docker-compose run,不過要注意的是,跑單一個 service 時,它預設是不會依你 docker-compose.yml 檔的設定來做 port forward

來源

    有這個需求的話,可以加上 --service-ports 參數。



加入 library 專案:


    我需要自己寫個 library 來處理 SQL 相關的功能,在加入 library 專案後如果沒修改 Dockerfile,會出問題 (廢話)。

    visual studio 2019 後的版本,在新增專案時,預設不會顯示 library 的那些樣板,你需要手動去開啟 (參考資料)。工具 -> 選項 -> 環境 -> 預覽功能 -> 勾選 "使用 .NET SDK 的預覧",然後重新啟動 visual studio,之後在新增專案時就有類別庫的樣板能選擇




    MyLib 就是個空殼子,不用去動程式。再來讓 DockerTest 專案參考 MyLib 專案



    會特別講這個是因為,我自己在測試時,有修改過原本 Docker 
8~15 行那區的 COPY,原本是 COPY . .,我改成只有複製單一專案資料夾裡的內容,以下是錯誤示範(我忘了我為什麼那時要這麼做)

COPY ["DockerTest/*", "DockerTest/"]

    最要命的部份是,我多了那個 * 號,我用 bash 的思維來寫 Docker 檔,以為要加 * 才能複製整個資料夾下的東西,但其實官方的教學就有說,COPY 的 Source 指定資料夾,它就會把資料夾內的所有東西 (不包含資料夾本身) 複製過去


    我畫蛇添足地加了一個 *,導致 build 完的 image 有問題,container 跑起來後,用瀏覽器打開會發現 RazorPage 都 404,我一開始以為是 Program.cs 裡的 app.MapRazorPages() 出問題,找了老半天都不知道問題出在哪,後來是到 container 裡翻資料時,注意到 wwwroot 裡都沒有 js 跟 css 資料夾,才意識到是 docker COPY 那部份出了問題。

    總之,要 COPY 源始碼到 image 裡 build 時,沒什麼特別需求的話就直接寫 COPY . .就好。那時好像還有碰到 build 時沒辦法參考到自己寫的 Lib 的問題,後來在這邊找到解答,把原本 Dockerfile 8~15 行那區改成

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["DockerTest/DockerTest.csproj", "DockerTest/"]
COPY ["MyLib/MyLib.csproj", "MyLib/"]
RUN dotnet restore "./DockerTest/DockerTest.csproj"
COPY . .
WORKDIR "/src/DockerTest"
RUN dotnet build "./DockerTest.csproj" -c $BUILD_CONFIGURATION -o /app/build

    這樣,就能正常 build image 了。


補充:


時區:


    我進 container 裡下 date 指令時,有發現那個時間怪怪的,後來去查才知道,需要設定環境變數 "TZ" 來指定時區 (TZ List),在 docker-compose 裡,可以在 app service 下加 environment 來指定 container 的環境變數

services:
 docker-test:
  build:
   context: .
   dockerfile: ./docker/DockerTest/Dockerfile
  image: docker-test
  container_name: docker-test
  environment:
   - ASPNETCORE_ENVIRONMENT=Development
   - TZ=Asia/Taipei
  ports:
  - "8080:8080"

沒有留言:

張貼留言