项目

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

UI
Database
Tiered

使用 Docker Compose 进行 Docker 部署

本文档假设您倾向于使用 MVC / Razor Pages 作为 UI 框架,使用 MongoDB 作为数据库提供程序。对于其他选项,请更改本文档顶部的偏好设置。

本指南将引导您了解如何为您的应用程序构建 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 "********* 正在构建 Web 应用程序 *********" -ForegroundColor Green
$webFolder = Join-Path $slnFolder "src/Acme.BookStore.Web"
Set-Location $webFolder
dotnet publish -c Release
docker build -f Dockerfile.local -t acme/bookstore-web:$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 "********* 正在构建 AuthServer 应用程序 *********" -ForegroundColor Green
 $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 "完成" -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" 标签构建镜像

MVC/Razor Pages

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

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

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.Web.dll"]

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

dotnet publish -c Release # 以 Release 模式构建项目
docker build -f Dockerfile.local -t acme/bookstore-web: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-web

bookstore-web:
    image: acme/bookstore-web:latest
    container_name: bookstore-web
    hostname: bookstore-web
    build:
      context: ../../
      dockerfile: src/Acme.BookStore.Web/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:44353
      - AuthServer__RequireHttpsMetadata=false        
     - AuthServer__IsOnK8s=true
   - AuthServer__Authority=https://localhost:44334/
      - RemoteServices__Default__BaseUrl=http://bookstore-api
      - RemoteServices__AbpAccountPublic__BaseUrl=http://bookstore-authserver
      - AuthServer__MetaAddress=http://bookstore-authserver    
      - App__HealthCheckUrl=http://bookstore-web/health-status        
      - ConnectionStrings__Default=mongodb://mongodb/BookStore   
      - Redis__Configuration=redis   
    ports:
      - "44353:443"
    restart: on-failure
    volumes:
      - ./certs:/root/certificate
    networks:
      - abp-network

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

MVC/Razor Page 是一个服务器端渲染应用程序,使用混合流程。此流程使用浏览器登录/注销到 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:44353部署到生产环境时,必须指向真实的 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 是被覆盖的默认连接字符串。它默认使用 容器化的 mongodb 服务 。

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

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

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=mongodb://mongodb/BookStore   
      - Redis__Configuration=redis
    ports:
      - "44354:443"
    depends_on:        
      mongo-db:
        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 是被覆盖的默认连接字符串。它默认使用 容器化的 mongodb 服务 。

  • 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
      - App__CorsOrigins=https://localhost:44353,https://localhost:44354
      - AuthServer__Authority=http://bookstore-authserver
      - AuthServer__RequireHttpsMetadata=false            
      - ConnectionStrings__Default=mongodb://mongodb/BookStore       
      - Redis__Configuration=redis
    ports:
      - "44334:443"
    depends_on:               
      mongo-db:
        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
  • App__CorsOrigins 是 CORS 的覆盖配置。默认情况下为 https://localhost:44353,https://localhost:44354部署到生产环境时,必须指向真实的 DNS
  • AuthServer__Authority 是颁发者 URL。http://bookstore-authserver 默认是 authserver 的端点。
  • AuthServer__RequireHttpsMetadataopenid 提供程序强制使用 HTTPS 的选项。Docker-compose 使用一个称为 abp-network 的隔离内部 Docker 网络。默认情况下设置为 false
  • ConnectionStrings__Default 是被覆盖的默认连接字符串。它默认使用 容器化的 mongodb 服务 。
  • 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_Web__RootUrl=https://localhost:44353      
      - OpenIddict__Applications__BookStore_Swagger__RootUrl=https://localhost:44354                  
      - ConnectionStrings__Default=mongodb://mongodb/BookStore       
    depends_on:               
      mongo-db:
        condition: service_healthy        
    networks:
      - abp-network

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

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

下一步是什么?

在本文档中