使用.NET 6开发TodoList应用(30)——实现Docker打包和部署

  • A+
所属分类:.NET技术
摘要

.NET 6 Web API应用使用最多的场景是作为后端微服务应用,在实际的项目中,我们一般都是通过将应用程序打包成docker镜像进行发布,以便更好地进行部署,包括基于Kubernetes平台的微服务项目部署。


系列导航及源代码

需求

.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 . 

生成的镜像:

使用.NET 6开发TodoList应用(30)——实现Docker打包和部署

运行起来,把80端口暴露出来,使用--link参数指出需要将当前应用容器连接到数据库容器所在的网络,并使用API客户端去验证登陆请求:

$ docker run -p 80:80 --name=todo_list_in_docker --link=mssql -d todo-list 4733f35c2c9558b78e3c7b9281536d8891f19bf87b18fa0ad953e94f7b984184 

使用.NET 6开发TodoList应用(30)——实现Docker打包和部署

请求认证:
使用.NET 6开发TodoList应用(30)——实现Docker打包和部署

实现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 

使用.NET 6开发TodoList应用(30)——实现Docker打包和部署

增加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 // 省略后面的日志.... 

请求结果:
使用.NET 6开发TodoList应用(30)——实现Docker打包和部署

到此为止如何使用容器去进行应用程序打包和部署的演示就结束了,关于如何在Kubernetes和CI/CD中应用这些步骤,会在后面将微服务的系列中再次涉及到。

总结

docker打包应用程序比较容易出错的地方在于dockerfile路径,除此之外如果在容器中还需要有其他操作比如安装一些第三方的agent(比如splunk agent),也需要仔细操作,关于如何进行Docker Build的Debug,可以参考其他人写的文章,例如这篇:Debugging Docker builds

参考资料

  1. Hosting ASP.NET Core images with Docker over HTTPS
  2. Hosting ASP.NET Core images with Docker Compose over HTTPS
  3. Debugging Docker builds