系列導航及源代碼
需求
.NET 6 Web API應用使用最多的場景是作為后端微服務應用,在實際的項目中,我們一般都是通過將應用程序打包成docker鏡像進行發布,以便更好地進行部署,包括基於Kubernetes平台的微服務項目部署。
一般來說作為微服務部署的應用程序,都是位於某個虛擬子網下的,也就是說它們不直接暴露給外部用戶,請求都是走的內部網絡,所以很少會有HTTPS的需求,但是作為演示,在本文中我們還是會介紹如何實現HTTPS訪問docker中的應用程序。
目標
實現應用程序的docker鏡像打包運行,包括實現基於HTTPS的訪問。
原理與思路
應用程序docker鏡像打包的實現思路很簡單,准備一個正確的dockerfile,再根據需要進行HTTPS配置,最后正確構建鏡像就可以了。
實現
實現Docker鏡像打包
在Api
項目中新建dockerfile文件,一般我們構建應用程序都是通過兩步構建:第一步進行編譯發布,第二步將發布的文件拷貝到運行時環境中,這樣可以減少鏡像的大小。
如果你是使用Visual Studio或者Rider開發項目,可以在創建項目的時候就將是否使用Docker支持
選上,選擇容器環境為Linux即可,項目模版會為我們自動生成正確的Dockerfile。或者我們也可以在項目上右擊,選擇添加Docker支持
。
下面是我們手寫的dockerfile的文件內容,對於編寫dockerfile經驗不多的小伙伴來說,最容易出錯的地方就是路徑的問題。因為我們將dockefile文件生成在了Api
項目中了,所以單從文件內容里的路徑來看,是有問題的,但是不要緊,我們在打包鏡像的時候可以指定dockefile文件執行的上下文,只要在解決方案目錄下執行docker build就沒問題了。
ARG NET_IMAGE=6.0-bullseye-slim
FROM mcr.microsoft.com/dotnet/aspnet:${NET_IMAGE} AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
ENV ASPNETCORE_ENVIRONMENT=Development
FROM mcr.microsoft.com/dotnet/sdk:${NET_IMAGE} AS build
WORKDIR /src
COPY ["src/TodoList.Api/TodoList.Api.csproj", "TodoList.Api/"]
COPY ["src/TodoList.Application/TodoList.Application.csproj", "TodoList.Application/"]
COPY ["src/TodoList.Domain/TodoList.Domain.csproj", "TodoList.Domain/"]
COPY ["src/TodoList.Infrastructure/TodoList.Infrastructure.csproj", "TodoList.Infrastructure/"]
RUN dotnet restore "TodoList.Api/TodoList.Api.csproj"
COPY ./src .
WORKDIR "/src/TodoList.Api"
RUN dotnet build "TodoList.Api.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish --no-restore "TodoList.Api.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "TodoList.Api.dll"]
在構建鏡像之前,有幾個小坑需要注意一下:
- 暫時刪除
Program
中的UseHttpsRedirection
中間件,因為我們還沒有配置HTTPS證書; - 在
Api
項目的csproj文件中,將TodoList.Api.xml
文件在Debug模式下的配置復制到Release中,否則會報錯Could not find file '/app/TodoList.Api.xml
; - 修改對應appsettings.{env}.json中的數據庫連接字符串,使用docker網絡模型獲取其他容器的方式,將localhost改為mssql,這個名字是在本地運行sql server docker的容器名稱,我們將使用docker網絡允許應用程序連接到數據庫docker容器。
下面我們就來構建一下這個鏡像,確保位於解決方案目錄下,注意最后那.
指明了當前選擇的dockerfile文件執行的上下文路徑,即解決方案目錄:
$ docker build -t todo-list -f src/TodoList.Api/Dockerfile .
生成的鏡像:
運行起來,把80端口暴露出來,使用--link
參數指出需要將當前應用容器連接到數據庫容器所在的網絡,並使用API客戶端去驗證登陸請求:
$ docker run -p 80:80 --name=todo_list_in_docker --link=mssql -d todo-list
4733f35c2c9558b78e3c7b9281536d8891f19bf87b18fa0ad953e94f7b984184
請求認證:
實現HTTPS訪問
接下來我們為容器添加HTTPS支持,為了實現這一點,我們當然還是需要繼續使用UseHttpsRedirection
中間件,然后需要添加一個證書,並在啟動容器的時候添加這個證書。
dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p Test@Password
dotnet dev-certs https --trust
重新build並運行容器,這次我們使用HTTPS的5001端口去訪問容器中的API,需要將HTTPS容器內的443端口暴露到host上的端口(我選擇的是5001端口)並制定相關的HTTPS的環境變量,證書的指定並將host上保存證書的路徑掛載到容器內可以訪問到。
docker run \
-p 80:80 \
-p 5001:443 \
--name=todo_list_in_docker \
--link=mssql \
-e ASPNETCORE_URLS="https://+;http://+" \
-e ASPNETCORE_HTTPS_PORT=5001 \
-e ASPNETCORE_Kestrel__Certificates__Default__Password="Test@Password" \
-e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx \
-v ${HOME}/.aspnet/https:/https/ \
-d \
todo-list
增加docker-compose功能
到這里我們發現了一個比較麻煩的地方在於我們需要記住這些配置,並且每次需要手動分別啟動數據庫容器和應用容器,我們完全可以通過docker-compose來完成,所以我們先把應用容器和數據庫容器都停止並刪除掉,開始在解決方案目錄下新建docker-compose文件:
version: '3.4'
services:
todo-list:
image: todo-list
# 配置端口轉發
ports:
- "80:80"
- "5001:443"
# 配置容器環境變量
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=https://+;http://+
- ASPNETCORE_HTTPS_PORT=5001
- ASPNETCORE_Kestrel__Certificates__Default__Password=Test@Password
- ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx
# 掛載證書路徑
volumes:
- ~/.aspnet/https:/https:ro
# 需要先啟動數據庫容器
depends_on:
- mssql
# todo-list通過public網絡響應請求,通過private網絡連接數據庫容器
networks:
- private
- public
mssql:
image: mcr.microsoft.com/mssql/server:2019-latest
# 配置端口轉發,這是為從主機直接訪問數據庫需要的,如果沒有從主機直接訪問數據庫的需求,只需要聲明容器端口1433不做轉發即可
ports:
- "1433:1433"
environment:
- SA_PASSWORD=StrongPwd123
- ACCEPT_EULA=Y
# 掛載數據目錄實現持久化
volumes:
- mssqldata:/var/opt/mssql
networks:
- private
- public
# 因為mssqldata路徑之前已經創建了,所以需要在這里聲明使用已有的
volumes:
mssqldata:
networks:
private:
public:
運行起來以后繼續請求認證:
$ docker-compose up --build
Creating network "todolist_private" with the default driver
Creating network "todolist_public" with the default driver
Recreating todolist_mssql_1 ... done
Recreating todolist_todo-list_1 ... done
Attaching to todolist_mssql_1, todolist_todo-list_1
// 省略后面的日志....
請求結果:
到此為止如何使用容器去進行應用程序打包和部署的演示就結束了,關於如何在Kubernetes和CI/CD中應用這些步驟,會在后面將微服務的系列中再次涉及到。
總結
docker打包應用程序比較容易出錯的地方在於dockerfile路徑,除此之外如果在容器中還需要有其他操作比如安裝一些第三方的agent(比如splunk agent),也需要仔細操作,關於如何進行Docker Build的Debug,可以參考其他人寫的文章,例如這篇:Debugging Docker builds。