项目

本文档有多个版本。请选择最适合您的选项。

UI
Database
Tiered

使用 Docker Compose 进行 Docker 部署

本文档假设您倾向于使用 Blazor Server 作为 UI 框架,使用 Entity Framework Core 作为数据库提供程序。对于其他选项,请更改本文档顶部的偏好设置。

本指南将引导您了解如何为您的应用程序构建 Docker 镜像并使用 docker compose 在本地运行。您将详细了解提供的构建脚本和 docker compose 文件,并学习如何为生产环境修改它们。

构建 Docker 镜像

每个应用程序都包含一个名为 Dockerfile.local 的 dockerfile 用于构建 Docker 镜像。顾名思义,这些 Dockerfile 不是多阶段 Dockerfile,需要以 Release 模式构建项目才能创建镜像。目前,如果您使用 CI & CD 流水线构建镜像,则需要在构建镜像之前将 SDK 包含到流水线中,或者添加您自己的多阶段 dockerfile

由于它们不是多阶段 Dockerfile,如果您想单独构建镜像,可以导航到相关要托管的应用程序文件夹并运行以下命令:

dotnet publish -c Release

首先填充 Release 文件夹,该文件夹将用于构建 Docker 镜像。之后,您可以运行以下命令:

docker build -f Dockerfile.local -t mycompanyname/myappname:version .

来手动构建您的应用程序镜像。

为了简化流程,应用程序模板在 etc/docker-compose 文件夹下提供了一个构建脚本 build-images-locally.ps1,用于一键构建所有镜像。 根据您的应用程序名称、UI 和类型,将生成一个构建镜像脚本。

param ($version='latest')

$currentFolder = $PSScriptRoot
$slnFolder = Join-Path $currentFolder "../../"

Write-Host "********* 正在构建 DbMigrator *********" -ForegroundColor Green
$dbMigratorFolder = Join-Path $slnFolder "src/Acme.BookStore.DbMigrator"
Set-Location $dbMigratorFolder
dotnet publish -c Release
docker build -f Dockerfile.local -t acme/bookstore-db-migrator:$version .

Write-Host "********* 正在构建 Blazor 应用程序 *********" -ForegroundColor Green
$blazorFolder = Join-Path $slnFolder "src/Acme.BookStore.Blazor"
Set-Location $blazorFolder
dotnet publish -c Release
docker build -f Dockerfile.local -t acme/bookstore-blazor:$version .

 
 $authServerAppFolder = Join-Path $slnFolder "src/Acme.BookStore.AuthServer"
 Set-Location $authServerAppFolder
 dotnet publish -c Release
 docker build -f Dockerfile.local -t acme/bookstore-authserver:$version .
 
 Write-Host "********* 正在构建 Api.Host 应用程序 *********" -ForegroundColor Green
 $hostFolder = Join-Path $slnFolder "src/Acme.BookStore.HttpApi.Host"
 Set-Location $hostFolder
 dotnet publish -c Release
 docker build -f Dockerfile.local -t acme/bookstore-api:$version .
  
### 全部完成
Write-Host "完成" -ForegroundColor Green
Set-Location $currentFolder

默认情况下,镜像标签设置为 latest。您可以在第一行更新 param $version 以将其设置为镜像的标签。

您可以在下面查看发布应用程序所需的所有 Dockerfile:

DBMigrator

DbMigrator 是一个控制台应用程序,用于迁移应用程序数据库并填充运行应用程序所需的重要初始数据。例如预定义的语言、管理员用户和角色、OpenIddict 应用程序和作用域。

此项目下提供的 Dockerfile.local 如下:

FROM mcr.microsoft.com/dotnet/aspnet:10.0
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app
ENTRYPOINT ["dotnet", "BookStore.DbMigrator.dll"]

如果您不想使用 build-images-locally.ps1 来构建镜像,或者想单独手动构建此镜像,请导航到 DbMigrator 文件夹并运行:

dotnet publish -c Release # 以 Release 模式构建项目
docker build -f Dockerfile.local -t acme/bookstore-db-migrator:latest . # 使用 "latest" 标签构建镜像

Blazor Server

​ Blazor Server 应用程序是一个服务器端渲染应用程序,默认使用 Cookie 身份验证作为默认方案,使用 OpenIdConnect 作为默认挑战方案。

BlazorModule 的身份验证配置下,有一个针对容器化环境支持的额外配置:

if (Convert.ToBoolean(configuration["AuthServer:IsOnK8s"]))
{
    context.Services.Configure<OpenIdConnectOptions>("oidc", options =>
    {
        options.TokenValidationParameters.ValidIssuers = new[]
        {
            configuration["AuthServer:MetaAddress"].EnsureEndsWith('/'), 
            configuration["AuthServer:Authority"].EnsureEndsWith('/')
        };

        options.MetadataAddress = configuration["AuthServer:MetaAddress"].EnsureEndsWith('/') +
                                ".well-known/openid-configuration";

        var previousOnRedirectToIdentityProvider = options.Events.OnRedirectToIdentityProvider;
        options.Events.OnRedirectToIdentityProvider = async ctx =>
        {
            // 拦截重定向,使浏览器导航到您主机中的正确 URL
            ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"].EnsureEndsWith('/') + "connect/authorize";

            if (previousOnRedirectToIdentityProvider != null)
            {
                await previousOnRedirectToIdentityProvider(ctx);
            }
        };
        var previousOnRedirectToIdentityProviderForSignOut = options.Events.OnRedirectToIdentityProviderForSignOut;
        options.Events.OnRedirectToIdentityProviderForSignOut = async ctx =>
        {
            // 为注销拦截重定向,使浏览器导航到您主机中的正确 URL
            ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"].EnsureEndsWith('/') + "connect/logout";

            if (previousOnRedirectToIdentityProviderForSignOut != null)
            {
                await previousOnRedirectToIdentityProviderForSignOut(ctx);
            }
        };
    });

}

AuthServer 运行在 Docker 容器(或 Pod)中时,用于为内部网络和 Web 配置重定向 URL。浏览器上的 /authorize/logout 请求必须重定向到真实的 DNS(本例中为 localhost),但在隔离网络内部处理令牌验证,而无需连接到互联网。"AuthServer:MetaAddress" 应用程序设置应指示容器/Pod 服务名称,而 AuthServer:Authority 应指向真实的 DNS 以供浏览器重定向。

appsettings.json 文件不包含 AuthServer:IsOnK8sAuthServer:MetaAddress 设置,因为它们用于编排部署场景,您可以看到这些设置被 docker-compose.yml 文件覆盖。

此项目下提供的 Dockerfile.local 如下:

FROM mcr.microsoft.com/dotnet/aspnet:10.0
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app
ENTRYPOINT ["dotnet", "Acme.BookStore.Blazor.dll"]

如果您不想使用 build-images-locally.ps1 来构建镜像,或者想单独手动构建此镜像,请导航到 Blazor 文件夹并运行:

dotnet publish -c Release # 以 Release 模式构建项目
docker build -f Dockerfile.local -t acme/bookstore-blazor:latest . # 使用 "latest" 标签构建镜像

AuthServer

这是 openid 提供程序应用程序,即身份验证服务器,与非分层应用程序模板相比,应单独托管。dockerfile.local 位于 AuthServer 项目下,如下所示:

FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
RUN dotnet dev-certs https -v -ep authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED

FROM base AS final
WORKDIR /app
COPY --from=build /src .

ENTRYPOINT ["dotnet", "Acme.BookStore.AuthServer.dll"]

构建镜像时,您可能会遇到错误。这是因为 dotnet dev-certs 命令试图列出容器内的现有证书但无法访问。这不是一个重要错误,因为我们的目标是生成 authserver.pfx 文件并丢弃构建它的容器。

auth-server-pfx-generation-error

AuthServer Docker 镜像构建过程包含多阶段以生成 authserver.pfx 文件,该文件被 OpenIddict 用作签名和加密证书。此配置位于 AuthServerModulePreConfigureServices 方法中:

if (!hostingEnvironment.IsDevelopment())
{
    PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
    {
        options.AddDevelopmentEncryptionAndSigningCertificate = false;
    });

    PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
    {
        serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", configuration["AuthServer:CertificatePassPhrase"]!);
        serverBuilder.SetIssuer(new Uri(configuration["AuthServer:Authority"]!));
    });
}

此配置禁用 DevelopmentEncryptionAndSigningCertificate,并使用名为 authserver.pfx 的自签名证书来签名和加密令牌。该证书是在构建 Docker 镜像时使用 dotnet dev-certs 工具创建的。它是一个示例生成的证书,并且建议为生产环境更新它。您可以查看 OpenIddict 加密和签名凭据文档 以了解不同的选项和自定义。

您始终可以使用 Dockerfile 之外的任何其他工具创建任何自签名证书。您需要记住将它们设置为嵌入式资源

如果您不想使用 build-images-locally.ps1 来构建镜像,或者想单独手动构建此镜像,请导航到 AuthServer 文件夹并运行:

dotnet publish -c Release # 以 Release 模式构建项目
docker build -f Dockerfile.local -t acme/bookstore-authserver:latest . # 使用 "latest" 标签构建镜像

Http.Api.Host

这是暴露端点和 Swagger UI 的后端应用程序。它不是一个多阶段 dockerfile;因此您需要已经以 Release 模式构建了此应用程序才能使用此 dockerfile。dockerfile.local 位于 Http.Api.Host 项目下,如下所示:

FROM mcr.microsoft.com/dotnet/aspnet:10.0
COPY bin/Release/net10.0/publish/ app/
WORKDIR /app
ENTRYPOINT ["dotnet", "Acme.BookStore.HttpApi.Host.dll"]

如果您不想使用 build-images-locally.ps1 来构建镜像,或者想单独手动构建此镜像,请导航到 Http.Api.Host 文件夹并运行:

dotnet publish -c Release # 以 Release 模式构建项目
docker build -f Dockerfile.local -t acme/bookstore-api:latest . # 使用 "latest" 标签构建镜像

在 Localhost 上运行 Docker-Compose

etc/docker-compose 文件夹下,您可以找到用于运行应用程序的 docker-compose.yml。为了简化运行过程,模板提供了 run-docker.ps1(和 run-docker.sh)脚本,用于处理环境变量中使用的 HTTPS 证书的创建;

$currentFolder = $PSScriptRoot

$slnFolder = Join-Path $currentFolder "../"
$certsFolder = Join-Path $currentFolder "certs"

If(!(Test-Path -Path $certsFolder))
{
    New-Item -ItemType Directory -Force -Path $certsFolder
    if(!(Test-Path -Path (Join-Path $certsFolder "localhost.pfx") -PathType Leaf)){
        Set-Location $certsFolder
        dotnet dev-certs https -v -ep localhost.pfx -p 91f91912-5ab0-49df-8166-23377efaf3cc -t        
    }
}

Set-Location $currentFolder
docker-compose up -d

run-docker.ps1(或 run-docker.sh)脚本将检查 etc/certs 文件夹下是否已存在开发证书,如果不存在,则生成 localhost.pfx 文件。Kestrel 将使用此文件作为 HTTPS 证书

您也可以使用 dotnet dev-certs https -v -ep myCert.pfx -p YOUR_PASSWORD_FOR_HTTPS_CERT -t 或使用任何其他自签名证书生成工具,在不同路径手动创建 localhost.pfx 文件,使用不同的名称和密码。

您需要在 docker-compose.yml 文件中更新服务环境变量 Kestrel__Certificates__Default__Path 为您创建的路径和文件名,以及 Kestrel__Certificates__Default__Password 为您的新密码。

现在让我们分解 docker-compose.yml 文件中的每个 Docker compose 服务:

bookstore-blazor

bookstore-blazor:
    image: acme/bookstore-blazor:latest
    container_name: bookstore-blazor
    build:
      context: ../../
      dockerfile: src/Acme.BookStore.Blazor/Dockerfile.local
    environment:
      - ASPNETCORE_URLS=https://+:443;http://+:80;
      - Kestrel__Certificates__Default__Path=/root/certificate/localhost.pfx
      - Kestrel__Certificates__Default__Password=91f91912-5ab0-49df-8166-23377efaf3cc
      - App__SelfUrl=https://localhost:44314
      - AuthServer__RequireHttpsMetadata=false  
     - AuthServer__IsOnK8s=true
   - AuthServer__Authority=https://localhost:44334/
      - AuthServer__MetaAddress=http://bookstore-authserver
      - RemoteServices__Default__BaseUrl=http://bookstore-api
      - RemoteServices__AbpAccountPublic__BaseUrl=http://bookstore-authserver       
      - ConnectionStrings__Default=Data Source=sql-server;Initial Catalog=BookStore;User Id=sa;Password=myPassw0rd;MultipleActiveResultSets=true;TrustServerCertificate=True;   
      - Redis__Configuration=redis   
    ports:
      - "44353:443"    
    depends_on:
      - bookstore-api   
    restart: on-failure
    volumes:
      - ./certs:/root/certificate
    networks:
      - abp-network

这是 Blazor Server 应用程序 Docker 服务,使用我们通过 build-images-locally.ps1 脚本构建的 acme/bookstore-blazor:latest 镜像。它默认运行在 https://localhost:44314,通过挂载我们在 etc/certs 文件夹下生成的自签名证书来实现。

Blazor Server 是一个服务器端渲染应用程序,使用混合流程。此流程使用浏览器登录/注销到 OpenID 提供程序,但从后端通道(服务器端)颁发 access_token。为了实现此功能,模块类具有额外的 OpenIdConnectOptions 来覆盖某些事件:

if (Convert.ToBoolean(configuration["AuthServer:IsOnK8s"]))
{
    context.Services.Configure<OpenIdConnectOptions>("oidc", options =>
    {
        options.TokenValidationParameters.ValidIssuers = new[]
        {
            configuration["AuthServer:MetaAddress"].EnsureEndsWith('/'), 
            configuration["AuthServer:Authority"].EnsureEndsWith('/')
        };

        options.MetadataAddress = configuration["AuthServer:MetaAddress"].EnsureEndsWith('/') +
                                ".well-known/openid-configuration";

        var previousOnRedirectToIdentityProvider = options.Events.OnRedirectToIdentityProvider;
        options.Events.OnRedirectToIdentityProvider = async ctx =>
        {
            // 拦截重定向,使浏览器导航到您主机中的正确 URL
            ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"].EnsureEndsWith('/') + "connect/authorize";

            if (previousOnRedirectToIdentityProvider != null)
            {
                await previousOnRedirectToIdentityProvider(ctx);
            }
        };
        var previousOnRedirectToIdentityProviderForSignOut = options.Events.OnRedirectToIdentityProviderForSignOut;
        options.Events.OnRedirectToIdentityProviderForSignOut = async ctx =>
        {
            // 为注销拦截重定向,使浏览器导航到您主机中的正确 URL
            ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"].EnsureEndsWith('/') + "connect/logout";

            if (previousOnRedirectToIdentityProviderForSignOut != null)
            {
                await previousOnRedirectToIdentityProviderForSignOut(ctx);
            }
        };
    });
}
  • App__SelfUrl 指向我们暴露端口的 localhost https://localhost:44314部署到生产环境时,必须指向真实的 DNS

  • AuthServer__RequireHttpsMetadataopenid 提供程序强制使用 HTTPS 的选项。由于我们使用隔离的内部 Docker 网络。我们希望在没有 SSL 开销的内部网络通信中使用 HTTP,因此,默认情况下设置为 false

    • AuthServer__IsOnK8s 是用于启用 OpenIdConnectOptions 的配置,为 OpenID 提供程序的 MetaAddress 提供不同的端点,并拦截 authorizationlogout 端点的 URLS。

    • AuthServer__MetaAddress 是用于颁发 access_token 和内部令牌验证的 .well-known/openid-configuration 端点。默认情况下是容器化的 http://bookstore-authserver

    • AuthServer__Authority 是颁发者 URL。https://localhost:44334/ 是应用程序在 authorizationlogout 过程中重定向到的颁发者。部署到生产环境时,必须指向真实的 DNS

    • RemoteServices__Default__BaseUrl 是后端;API 端点应用程序使用 access_token 获取资源。默认情况下是容器化的 http://bookstore-api

    • RemoteServices__AbpAccountPublic__BaseUrl 是用于获取用户个人资料图片的帐户 URL。由于帐户相关信息位于 authserver 中,因此默认情况下是容器化的 http://bookstore-authserver

  • ConnectionStrings__Default 是被覆盖的默认连接字符串。它默认使用 容器化的 sql-server 以及 sa 用户 。

    • Redis__Configuration 是被覆盖的 Redis 配置。它使用容器化的 redis 服务。如果您不使用容器化的 Redis,请更新您的 Redis URL。

此服务在名为 abp-network 的 Docker 网络中运行。它等待 Redis 服务和 bookstore-api 容器启动。 它还会在失败时重新启动。您可以根据自己的偏好自定义这些编排行为。

bookstore-api

bookstore-api:
    image: acme/bookstore-api:latest
    container_name: bookstore-api
    hostname: bookstore-api
    build:
      context: ../../
      dockerfile: src/Acme.BookStore.HttpApi.Host/Dockerfile.local
    environment:
      - ASPNETCORE_URLS=https://+:443;http://+:80;
      - Kestrel__Certificates__Default__Path=/root/certificate/localhost.pfx
      - Kestrel__Certificates__Default__Password=91f91912-5ab0-49df-8166-23377efaf3cc
      - App__SelfUrl=https://localhost:44354
      - App__HealthCheckUrl=http://bookstore-api/health-status
   - AuthServer__Authority=http://bookstore-authserver
      - AuthServer__RequireHttpsMetadata=false  
      - ConnectionStrings__Default=Data Source=sql-server;Initial Catalog=BookStore;User Id=sa;Password=myPassw0rd;MultipleActiveResultSets=true;TrustServerCertificate=True;    
      - Redis__Configuration=redis
    ports:
      - "44354:443"
    depends_on: 
      sql-server:
        condition: service_healthy        
      redis:
        condition: service_healthy
    restart: on-failure
    volumes:
      - ./certs:/root/certificate
    networks:
      - abp-network

此服务是 MVC/Razor Page 应用程序的后端应用程序,使用我们通过 build-images-locally.ps1 脚本构建的 acme/bookstore-api:latest 镜像。它默认运行在 https://localhost:44354,通过挂载我们在 etc/certs 文件夹下生成的自签名证书来实现。

  • App__SelfUrl 指向我们暴露端口的 localhost https://localhost:44354部署到生产环境时,必须指向真实的 DNS

  • App__HealthCheckUrl 是健康检查 URL。由于此请求将在内部完成,因此它指向容器化环境中的服务名称 http://bookstore-api/health-status

  • AuthServer__Authority 是颁发者 URL。http://bookstore-authserver 是容器化的颁发者。部署到生产环境时,必须指向真实的 DNS

  • AuthServer__RequireHttpsMetadataopenid 提供程序强制使用 HTTPS 的选项。由于我们使用隔离的内部 Docker 网络。我们希望在没有 SSL 开销的内部网络通信中使用 HTTP,因此,默认情况下设置为 false

  • ConnectionStrings__Default 是被覆盖的默认连接字符串。它默认使用 容器化的 sql-server 以及 sa 用户 。

  • Redis__Configuration 是被覆盖的 Redis 配置。它使用容器化的 redis 服务。如果您不使用容器化的 Redis,请更新您的 Redis URL。

此服务在名为 abp-network 的 Docker 网络中运行,等待 Redis 服务和数据库容器启动,并在失败时重新启动。您可以根据自己的偏好自定义这些编排行为。

bookstore-authserver

bookstore-authserver:
    image: acme/bookstore-authserver:latest
    container_name: bookstore-authserver
    build:
      context: ../../
      dockerfile: src/Acme.BookStore.AuthServer/Dockerfile.local
    environment:
      - ASPNETCORE_URLS=https://+:443;http://+:80;      
      - Kestrel__Certificates__Default__Path=/root/certificate/localhost.pfx
      - Kestrel__Certificates__Default__Password=91f91912-5ab0-49df-8166-23377efaf3cc
      - App__SelfUrl=https://localhost:44334
      - AuthServer__Authority=http://bookstore-authserver
      - AuthServer__RequireHttpsMetadata=false      
      - ConnectionStrings__Default=Data Source=sql-server;Initial Catalog=BookStore;User Id=sa;Password=myPassw0rd;MultipleActiveResultSets=true;TrustServerCertificate=True;            
      - Redis__Configuration=redis
    ports:
      - "44334:443"
    depends_on:     
      sql-server:
        condition: service_healthy              
      redis:
        condition: service_healthy
    restart: on-failure
    volumes:
      - ./certs:/root/certificate
    networks:
      - abp-network

这是使用 OpenIddict 库处理应用程序间身份验证的身份验证服务器应用程序。它使用我们通过 build-images-locally.ps1 脚本构建的 acme/bookstore-authserver:latest 镜像。它默认运行在 https://localhost:44334,通过挂载我们在 etc/certs 文件夹下生成的自签名证书来实现。

  • App__SelfUrl 指向我们暴露端口的 localhost https://localhost:44334部署到生产环境时,必须指向真实的 DNS
  • AuthServer__Authority 是颁发者 URL。http://bookstore-authserver 默认是 authserver 的端点。
  • AuthServer__RequireHttpsMetadataopenid 提供程序强制使用 HTTPS 的选项。Docker-compose 使用一个称为 abp-network 的隔离内部 Docker 网络。默认情况下设置为 false
  • ConnectionStrings__Default 是被覆盖的默认连接字符串。它默认使用 容器化的 sql-server 以及 sa 用户 。
  • Redis__Configuration 是被覆盖的 Redis 配置。它使用容器化的 redis 服务。如果您不使用容器化的 Redis,请更新您的 Redis URL。

此服务在名为 abp-network 的 Docker 网络中运行,等待 Redis 服务和数据库容器启动,并在失败时重新启动。您可以根据自己的偏好自定义这些编排行为。

db-migrator

db-migrator:
    image: acme/bookstore-db-migrator:latest
    container_name: db-migrator
    build:
      context: ../../
      dockerfile: src/BookStore.DbMigrator/Dockerfile.local
    environment:
      - OpenIddict__Applications__BookStore_BlazorServerTiered__RootUrl=https://localhost:44314      
      - OpenIddict__Applications__BookStore_Swagger__RootUrl=https://localhost:44354            
      - ConnectionStrings__Default=Data Source=sql-server;Initial Catalog=BookStore;User Id=sa;Password=myPassw0rd;MultipleActiveResultSets=true;TrustServerCertificate=True;            
    depends_on:     
      sql-server:
        condition: service_healthy                  
    networks:
      - abp-network

数据库迁移器服务迁移数据库并填充初始数据。OpenIddict 数据是您的应用程序最重要的填充数据之一。在生产环境中,您需要覆盖应用程序的根 URL (https://localhost:44353) 和 swagger-ui 客户端 URL (<https://localhost:44354),以便身份验证正常工作。>

此服务在名为 abp-network 的 Docker 网络中运行,等待数据库容器启动,并在失败时重新启动。您可以根据自己的偏好自定义这些编排行为。

下一步是什么?

在本文档中