- A+
系列导航及源代码
需求
.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。