学习使用多个容器来运行完整的ASP.NET解决方案。
到目前为止,在本教程中,我们已经创建了一个.NET控制台应用程序和一个ASP.NET Core Web应用程序。这是我们码头之旅的一个很好的开始,但集装箱化是关于分布式系统的,对吗?让我们向ASP.NET Core Web应用程序添加一个数据库,并使用Docker Compose创建一个逻辑应用程序。
使用我们在上一节中创建的应用程序,让我们修改我们的项目。我们将从添加Dapper和System.Data.SqlClient两个NuGet包开始,这将允许我们查询最终的Microsoft SQL Server实例。
<Project Sdk="Microsoft.NET.Sdk.Web"><PropertyGroup><TargetFramework>net5.0</TargetFramework><DockerDefaultTargetOS>Linux</DockerDefaultTargetOS></PropertyGroup><ItemGroup><PackageReference Include="Dapper" Version="2.0.78" /><PackageReference Include="System.Data.SqlClient" Version="4.8.2" /></ItemGroup></Project>
现在,让我们修改ASP.NET Core应用程序以注册SqlConnection实例,并使用它来查询我们的数据库。
using System.Data;using System.Data.SqlClient;using Microsoft.AspNetCore.Builder;using Microsoft.AspNetCore.Hosting;using Microsoft.AspNetCore.Http;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Hosting;using Dapper;namespace HelloDockerWeb{public class Startup{private IConfiguration Configuration { get; }public Startup(IConfiguration configuration){Configuration = configuration;}public void ConfigureServices(IServiceCollection services){services.AddTransient<IDbConnection>(_ => {var connectionString = Configuration.GetConnectionString("mssql");var connection = new SqlConnection(connectionString);connection.Open();return connection;});}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseRouting();app.UseEndpoints(endpoints =>{endpoints.MapGet("/", async context =>{var connection = context.RequestServices.GetRequiredService<IDbConnection>();var result = await connection.QueryFirstAsync<string>("Select @@version");await context.Response.WriteAsync(result!);});});}}}
最后,让我们向尚未创建的数据库实例添加一个连接字符串。在appsettings.json文件中,我们需要添加一个新的ConnectionStrings部分。
{"Logging": {"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}},"AllowedHosts": "*","ConnectionStrings": {"mssql" : "Server=mssql;Database=master;User Id=sa;Password=Pass123!;"}}
我们的下一步是在我们的项目中创建一个docker-compose.yml文件。如前所述,Docker Compose是一个允许我们定义应用程序拓扑的工具。我们可以配置任意数量的服务并描述它们之间的关系。一旦我们在项目中创建了一个空的docker-compose.yml文件,我们就需要粘贴以下描述。
version: "3.3"services:web:container_name: webbuild:context: ..dockerfile: ./HelloDockerWeb/Dockerfiledepends_on: [ mssql ]ports:- "8080:80"mssql:image: "mcr.microsoft.com/mssql/server"container_name: mssqlhostname: mssqlenvironment:SA_PASSWORD: "Pass123!"ACCEPT_EULA: "Y"restart: unless-stoppedports:# So we can access the database# From a tool like JetBrains Rider# Optional for this demo- "11433:1433"
正如我们所看到的,我们的组合定义有两个服务。
Web应用程序使用我们在上一个示例中定义的Dockerfile。我们可以设置文件上下文,以及此容器需要的其他服务。最后,我们将容器上的端口80映射到主机端口8080。
Microsoft SQL Server容器使用流行数据库的Linux变体。阅读映像文档时,我们必须设置管理员密码并接受最终用户许可协议。我们还将hostname设置为mssql,这是我们在定义连接字符串时使用的。
当我们粘贴撰写定义时,我们可能已经注意到编辑器中出现了几个运行标记。Compose允许我们开始完整的描述或选择应用程序的元素。在本例中,我们将选择最顶部的运行标记,它将执行docker-compose up命令。我们的Deploy Log应该包含来自两个容器的消息。
请注意描述网络创建的消息。我们的容器现在可以通过这个虚拟网络相互通信。
/usr/local/bin/docker-compose -f /Users/khalidabuhakmeh/Projects/Dotnet/HelloDockerWeb/HelloDockerWeb/docker-compose.yaml upCreating network "hellodockerweb_default" with the default driverCreating mssql ... doneCreating web ... doneAttaching to mssql, webmssql | SQL Server 2019 will run as non-root by default.mssql | This container is running as user mssql.mssql | To learn more visit https://go.microsoft.com/fwlink/?linkid=2099216.web | info: Microsoft.Hosting.Lifetime[0]web | Now listening on: http://[::]:80web | info: Microsoft.Hosting.Lifetime[0]web | Application started. Press Ctrl+C to shut down.web | info: Microsoft.Hosting.Lifetime[0]web | Hosting environment: Productionweb | info: Microsoft.Hosting.Lifetime[0]web | Content root path: /app
使用Web浏览器,我们可以导航到URLhttp://localhost:8080并查看应用程序的结果。
现在,这是把它带到了另一个水平!我们可以创建的应用程序拓扑是无限的,启动和停止就像单个命令一样简单。
Development Caveats and Workarounds 开发警告和解决方法
在本例中,我们看到我们将应用程序直接构建到一个映像中,并运行了一个容器实例。这些容器实例寿命很长,不会自动检索对C#代码的最新更改。对于此特定问题,有几个建议的解决方法。
Develop Outside Of Docker 在Docker之外开发
组合定义对部署很有帮助,可以帮助我们在本地开发的同时运行第三方服务。我们提到,我们可以有选择地启动服务,并且可以选择停止ASP.NET核心容器。我们可以在访问应用程序拓扑服务的同时,继续在主机上工作和运行应用程序。
这种方法将实现更快的开发循环,尽管我们的应用程序将不再是虚拟网络的一部分。我们需要确保我们的配置使用主机上的映射端口,而不是内部暴露的端口。对我们来说幸运的是,ASP.NET Core的配置覆盖使这一点变得简单而方便。
Mount The Development Directory 挂载开发目录
我们在前面的小节中提到了卷,这是一种无需重新启动容器就可以创建更快的开发循环的方法。我们将在我们的开发环境中有效地进行开发和构建,同时将项目文件夹装载为卷。
以下服务将使用DotNet/SDK映像来运行我们的应用程序并挂载我们的本地开发目录。我们需要SDK来利用监视工具。
web_develop:container_name: web_developmentimage: "mcr.microsoft.com/dotnet/sdk:5.0"depends_on: [ mssql ]volumes:- ./:/appcommand: dotnet watch --project ./app run --urls "http://0.0.0.0:80"ports:- "8081:80"
当我们更改主机操作系统上的任何文件时,我们的容器将停止正在运行的Web进程,并使用更新的程序集重新启动。
Deleting and Rebuilding Containers
如果我们想在Docker环境中运行所有内容,可以删除容器并让Docker重新构建它们。要实现这一点,我们需要编辑web服务的运行配置。添加build: always的docker-compose选项将确保我们在重新启动容器时重新构建映像。
虽然此技术是有效的,但它可能会有点慢,因为我们需要在部署时重新构建容器。
