都莱纳杰瑞 發表於 2022-1-15 23:46:00

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

<h2 id="系列导航及源代码">系列导航及源代码</h2>
<ul>
<li>使用.NET 6开发TodoList应用文章索引</li>
</ul>
<h2 id="需求">需求</h2>
<p>.NET 6 Web API应用使用最多的场景是作为后端微服务应用,在实际的项目中,我们一般都是通过将应用程序打包成docker镜像进行发布,以便更好地进行部署,包括基于Kubernetes平台的微服务项目部署。</p>
<p>一般来说作为微服务部署的应用程序,都是位于某个虚拟子网下的,也就是说它们不直接暴露给外部用户,请求都是走的内部网络,所以很少会有HTTPS的需求,但是作为演示,在本文中我们还是会介绍如何实现HTTPS访问docker中的应用程序。</p>
<h2 id="目标">目标</h2>
<p>实现应用程序的docker镜像打包运行,包括实现基于HTTPS的访问。</p>
<h2 id="原理与思路">原理与思路</h2>
<p>应用程序docker镜像打包的实现思路很简单,准备一个正确的dockerfile,再根据需要进行HTTPS配置,最后正确构建镜像就可以了。</p>
<h2 id="实现">实现</h2>
<h3 id="实现docker镜像打包">实现Docker镜像打包</h3>
<p>在<code>Api</code>项目中新建dockerfile文件,一般我们构建应用程序都是通过两步构建:第一步进行编译发布,第二步将发布的文件拷贝到运行时环境中,这样可以减少镜像的大小。</p>
<p>如果你是使用Visual Studio或者Rider开发项目,可以在创建项目的时候就将<code>是否使用Docker支持</code>选上,选择容器环境为Linux即可,项目模版会为我们自动生成正确的Dockerfile。或者我们也可以在项目上右击,选择<code>添加Docker支持</code>。</p>
<p>下面是我们手写的dockerfile的文件内容,对于编写dockerfile经验不多的小伙伴来说,最容易出错的地方就是路径的问题。因为我们将dockefile文件生成在了<code>Api</code>项目中了,所以单从文件内容里的路径来看,是有问题的,但是不要紧,我们在打包镜像的时候可以指定dockefile文件执行的上下文,只要在解决方案目录下执行docker build就没问题了。</p>
<pre><code class="language-dockerfile">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"]
</code></pre>
<p>在构建镜像之前,有几个小坑需要注意一下:</p>
<ul>
<li>暂时删除<code>Program</code>中的<code>UseHttpsRedirection</code>中间件,因为我们还没有配置HTTPS证书;</li>
<li>在<code>Api</code>项目的csproj文件中,将<code>TodoList.Api.xml</code>文件在Debug模式下的配置复制到Release中,否则会报错<code>Could not find file '/app/TodoList.Api.xml</code>;</li>
<li>修改对应appsettings.{env}.json中的数据库连接字符串,使用docker网络模型获取其他容器的方式,将localhost改为mssql,这个名字是在本地运行sql server docker的容器名称,我们将使用docker网络允许应用程序连接到数据库docker容器。</li>
</ul>
<p>下面我们就来构建一下这个镜像,确保位于解决方案目录下,注意最后那<code>.</code>指明了当前选择的dockerfile文件执行的上下文路径,即解决方案目录:</p>
<pre><code class="language-bash">$ docker build -t todo-list -f src/TodoList.Api/Dockerfile .
</code></pre>
<p>生成的镜像:</p>
<p><img src="https://img2020.cnblogs.com/blog/2487237/202201/2487237-20220114230400383-1946635868.png" alt="image" loading="lazy"></p>
<p>运行起来,把80端口暴露出来,使用<code>--link</code>参数指出需要将当前应用容器连接到数据库容器所在的网络,并使用API客户端去验证登陆请求:</p>
<pre><code class="language-bash">$ docker run -p 80:80 --name=todo_list_in_docker --link=mssql -d todo-list
4733f35c2c9558b78e3c7b9281536d8891f19bf87b18fa0ad953e94f7b984184
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/2487237/202201/2487237-20220115133131009-512419646.png" alt="image" loading="lazy"></p>
<p>请求认证:<br>
<img src="https://img2020.cnblogs.com/blog/2487237/202201/2487237-20220115133208338-1424326794.png" alt="image" loading="lazy"></p>
<h3 id="实现https访问">实现HTTPS访问</h3>
<p>接下来我们为容器添加HTTPS支持,为了实现这一点,我们当然还是需要继续使用<code>UseHttpsRedirection</code>中间件,然后需要添加一个证书,并在启动容器的时候添加这个证书。</p>
<pre><code class="language-bash">dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p Test@Password
dotnet dev-certs https --trust
</code></pre>
<p>重新build并运行容器,这次我们使用HTTPS的5001端口去访问容器中的API,需要将HTTPS容器内的443端口暴露到host上的端口(我选择的是5001端口)并制定相关的HTTPS的环境变量,证书的指定并将host上保存证书的路径挂载到容器内可以访问到。</p>
<pre><code class="language-bash">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
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/2487237/202201/2487237-20220115134103762-1859446765.png" alt="image" loading="lazy"></p>
<h3 id="增加docker-compose功能">增加docker-compose功能</h3>
<p>到这里我们发现了一个比较麻烦的地方在于我们需要记住这些配置,并且每次需要手动分别启动数据库容器和应用容器,我们完全可以通过docker-compose来完成,所以我们先把应用容器和数据库容器都停止并删除掉,开始在解决方案目录下新建docker-compose文件:</p>
<pre><code class="language-yml">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:
</code></pre>
<p>运行起来以后继续请求认证:</p>
<pre><code class="language-bash">$ 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
// 省略后面的日志....
</code></pre>
<p>请求结果:<br>
<img src="https://img2020.cnblogs.com/blog/2487237/202201/2487237-20220115135855069-518433.png" alt="image" loading="lazy"></p>
<p>到此为止如何使用容器去进行应用程序打包和部署的演示就结束了,关于如何在Kubernetes和CI/CD中应用这些步骤,会在后面将微服务的系列中再次涉及到。</p>
<h2 id="总结">总结</h2>
<p>docker打包应用程序比较容易出错的地方在于dockerfile路径,除此之外如果在容器中还需要有其他操作比如安装一些第三方的agent(比如splunk agent),也需要仔细操作,关于如何进行Docker Build的Debug,可以参考其他人写的文章,例如这篇:Debugging Docker builds。</p>
<h2 id="参考资料">参考资料</h2>
<ol>
<li>Hosting ASP.NET Core images with Docker over HTTPS</li>
<li>Hosting ASP.NET Core images with Docker Compose over HTTPS</li>
<li>Debugging Docker builds</li>
</ol>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:CODE4NOTHING,转载请注明原文链接:https://www.cnblogs.com/code4nothing/p/build-todolist-30.html</p><br><br>
来源:https://www.cnblogs.com/code4nothing/p/build-todolist-30.html
頁: [1]
查看完整版本: 使用.NET 6开发TodoList应用(30)——实现Docker打包和部署