如何使用 Docker 打造 .NET Core 開發環境 ?
雖然可以在 Windows 或 macOS 開發 .NET Core,但畢竟 production 環境是 Linux,因此在 Windows 或 macOS 測試成功,也不代表真正上 Linux 沒有問題。
透過 Docker,我們可以在開發階段就測試 Linux 環境,及早發現可能的問題。
Version
macOS High Sierra 10.13.4
Docker for Mac 18.03-ce-mac65 (24312)
.NET Core 2.0.7
VS Code 1.23.1
建立 .NET Core App
1 | $ dotnet new console -o MyConsole |
使用 dotnet new
建立 .NET Core App。
- new : 建立 project
- console : 建立 console 類型 project
- -o : 以
MyConsole
為專案名稱並建立目錄
執行 .NET Core App
1 | $ cd MyConsole |
進入 MyConsole
目錄,並在 Host OS 執行 .NET Core App。
目前 .NET Core App 是跑在 Host OS,也就是 macOS,但並不保證在 Linux 也正常,因此我們必須進一步在 Linux 環境下測試。
實務上有兩種測試方式:
- 在 Container 內測試
- 在 Container 外測試
在 Container 內測試
我們先將 container 執行起來,然後連進 container 內部,以 Linux 環境執行 app。
建立 Docker Compose
docker-compose.yml
1 | version: "3" services: net-core: image: microsoft/dotnet container_name: MyCore volumes: - "${HOST_DIR}:/home/" tty: true |
第 5 行
1 | image: microsoft/dotnet |
其基底的 image 為 microsoft/dotnet
,此為 Micrsoft 所發布的 official .NET Core docker image。
實務上 Docker 建議盡量使用 official image,避免被安裝木馬程式而不自知
第 6 行
1 | container_name: MyCore |
建立名為 MyCore
的 container。
container_name
相當於docker run
的--name
第 7 行
1 | volumes: - "${HOST_DIR}:/home/" |
設定 Host OS 與 container 所共享的目錄,將來 Host OS 所分享的目錄,相當於 container 內的 /home
目錄。
由於每個人 Host OS 要分享的目錄都不一樣,因此設定成 HOST_DIR
變數,稍後自行在 .env
設定。
volumns
相當於docker run
的-v
第 9 行
1 | tty: true |
Docker 為了節省硬體資源,app 執行完就會釋放 container,但處於開發階段,我們需要的是類似 service 跑在背景,隨時可以進入 container 測試 Linux app。
tty: true
將令 Docker 分配一個 TTY (T
ele TY
pewriter),綁定到 container 的 stdin
與 stdout
上,將來我們才能透過 Bash 對 container 下指令,由於 tty
在背景持續執行,因此 container 就不會被釋放。
tty
相當於docker run
的-t
.env
1 | HOST_DIR=~/Code/CSharp/MyConsole |
設定 Host OS 要與 container 共享的目錄。
注意 =
前後都沒有空格,否則 docker-compose
會執行錯誤。
設計
docker-compose.yml
時,要盡量保持.yml
不要修改,也就是開放封閉原則
,而將要修改客製化的部分搬到.env
,將來docker-compose.yml
會進 git,但.env
則不進 git
執行 Docker Compose
1 | $ docker-compose up -d |
進入 docker-compose.yml
所在的目錄,執行 docker-compose up
執行 container。
- -d:
d
etatch,表示docker-compose
執行後將離開 container 的 process,讓 container 在背景執行
1 | $ docker ps |
可以發現 MyCore
container 正在背景執行中,沒有被釋放。
連進 Container 內
1 | ~/MyConsole $ docker exec -it MyCore bash |
使用 docker exec -it MyCore bash
進入 container。
- exec:執行 container 內的 Linux 指令
- -i:
i
nteractive,可對 terminal 輸入資料 - -t:
t
erminal,可對 terminal 顯示資料 - MyCore:container 名稱
- bash:在 container 執行
bash
,讓我們可以對 Linux 下指令
進入 /home
,此為剛剛我們在 .env
所設定的共享目錄。
使用 dotnet run
執行 .NET Core App。
目前是同一份 code 真正跑在 Linux,因此可藉由 Docker 在開發階段,測試 app 是否真正能跑在 Linux。
結束 Docker Compose
1 | $ docker-compose down |
進入 docker-compose.yml
所在的目錄,執行 docker-compose down
結束 container。
1 | $ docker ps |
Container 已經結束且被釋放。
1 | $ docker ps -a |
Container 不僅被釋放,且在硬碟的 container 也被刪除了。
若使用
docker run
,儘管使用docker stop
停止 container,但 container 仍然存在於硬碟,必須手動使用docker rm
刪除 container,但若使用docker-compose down
,會一併從硬碟刪除 container
在 Container 外測試
直接在 container 外部使用 docker run
執行 container,並在 container 內執行 dotnet run
。
建立 Bash
RunDocker.sh
1 | #!/bin/bash |
第 2 行
1 | HOST_DIR=~/Code/CSharp/MyConsole |
設定 Host OS 與 container 所共享的目錄,將來 Host OS 所分享的目錄,相當於 container 內的 /home
目錄。
由於每個人 Host OS 要分享的目錄都不一樣,因此設定成 HOST_DIR
變數,可自行設定。
第 4 行
1 | docker run --rm -v $HOST_DIR:/home -w /home microsoft/dotnet dotnet run |
使用 docker run
直接執行 container。
- –rm:執行完 app 之後立即刪除 container,避免執行完的 container 留在硬碟內,除了佔空間,日後還必須手動刪除
- -v:
v
olumn,設定 Host OS 與 container 所共享的目錄,:
左側為 Host OS 目錄,右側為 container 內目錄 - -w:
w
orking directory,避免自己下cd
指令切換目錄 - microsoft/dotnet:Microsoft 官方最新的 .NET Core Docker image
- dotnet run:container 建立後,要在 container 內執行的指令就是
dotnet run
設定執行權限
1 | ~MyConsole $ chmod +x RunDocker.sh |
使用 chmod
讓 RunDocker.sh
能夠有 被執行
權限。
執行 Bash
1 | ~/MyConsole $ ./RunDocker.sh |
執行 RunDocker.sh
,建立 container,並在 container 內執行 app。
目前是同一份 code 在 Docker 內執行,由於 Docker 內就是 Linux,因此可藉由 Docker 在開發階段,測試 app 是否真正能跑在 Linux。
Conclusion
- .NET Core 雖然號稱跨平台,但也不保證真正執行在 Linux 沒問題,透過 Docker,我們在開發階段就能很簡單測試 Linux 環境是否正常
- 第一種方式比較接近傳統 VM,直接進 container 內測試 app
- 第二種方式比較接近正統 Docker,是直接使用
docker run
執行 container 內的 app - 以上兩種方式都只適合開發階段測試用,若要上 production,則要將 .NET Core App 整個包進 Docker image,將以專文另外討論之
Sample Code
完整的範例可以在我的 GitHub 上找到