项目

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

UI
Database
Tiered

使用 Docker Compose 进行 Docker 部署

本文档假设您倾向于使用 Angular 作为 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 "********* 正在构建 Angular 应用程序 *********" -ForegroundColor Green
$angularAppFolder = Join-Path $slnFolder "../angular"
Set-Location $angularAppFolder
yarn
npm run build:prod
docker build -f Dockerfile.local -t acme/bookstore-angular:$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 .

 
 $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" 标签构建镜像

Angular

Angular 应用程序使用 nginx:alpine-slim 基础镜像来托管 Angular 应用程序。您可以根据自己的偏好修改 Dockerfile.local 中的基础镜像,该文件位于解决方案的 angular 文件夹下,如下所示:

FROM nginx:alpine-slim
WORKDIR /app
COPY dist/BookStore /usr/share/nginx/html
COPY dynamic-env.json /usr/share/nginx/html
COPY /nginx.conf  /etc/nginx/conf.d/default.conf

您会注意到,除了构建好的 Angular 应用程序外,还有两个文件被复制到应用程序镜像中。

dynamic-env.json 文件是一个空的 JSON 文件,代表 Angular 应用程序的环境变量。镜像运行时的环境变量将覆盖此文件。如果您检查 angular/src/environments 文件夹下的 environment.prod.ts 文件,其中包含生产环境的远程环境配置

remoteEnv: {
    url: '/getEnvConfig',
    mergeStrategy: 'deepmerge'
  }

此配置用于从远程服务获取环境变量。此配置用于在不重建镜像的情况下覆盖环境变量。 URL /getEnvConfignginx.conf 文件中定义:

server {
    listen       80;
    listen  [::]:80;
    server_name  _;
    
    #listen              443 ssl;
    #server_name         www.myapp.com;
    #ssl_certificate     www.myapp.com.crt;
    #ssl_certificate_key www.myapp.com.key;
    
 location / {
        root   /usr/share/nginx/html;        
        index  index.html index.htm;
        try_files $uri $uri/ /index.html =404;  
 }
 
 location /getEnvConfig {
  default_type 'application/json';
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Content-Type' 'application/json';
  try_files $uri /dynamic-env.json;
    }
    
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

此配置允许将 dynamic-env.json 文件作为静态文件返回,ABP Angular 应用程序在渲染页面时的初始请求之一会使用它来获取环境变量。您需要覆盖的 dynamic-env.json 文件位于 aspnet-core/etc/docker-compose 文件夹下。

{
  "production": true,
  "application": {
    "baseUrl":"http://localhost:4200",  // https://myapp.com
    "name": "BookStore",
    "logoUrl": ""
  },
  "oAuthConfig": {
    "issuer": "https://localhost:44334/", // https://myauthserver.com/
    "redirectUri": "http://localhost:4200", // https://myapp.com
    "clientId": "BookStore_App",
    "responseType": "code",
    "scope": "offline_access openid profile email phone BookStore"
  },
  "apis": {
    "default": {
      "url": "https://localhost:44354",  // https://myapi.com
      "rootNamespace": "BookStore"
    },
    "AbpAccountPublic": {
      "url": "https://localhost:44334",  // https://myauthserver.com
      "rootNamespace": "AbpAccountPublic"
    }
  }
}

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

yarn     # 恢复项目
npm run build:prod # 在生产环境下构建。如果您安装了 angular CLI,也可以使用 "ng build  --prod"
docker build -f Dockerfile.local -t acme/bookstore-angular: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-angular

services:
  bookstore-angular:
    image: acme/bookstore-angular:latest
    container_name: bookstore-angular
    build:
      context: ../../../
      dockerfile: angular/Dockerfile.local
    ports:
      - "4200:80"
    depends_on:
      - bookstore-api
    volumes:
      - ./dynamic-env.json://usr/share/nginx/html/dynamic-env.json
    networks:
      - abp-network

这是我们在 http://localhost:4200 上默认部署的 angular 应用程序,使用我们通过 build-images-locally.ps1 脚本构建的镜像。它没有使用 localhost.pfx 运行 HTTPS,因为它运行在 Nginx 上,并且不接受 pfx 文件作为 SSL。您可以查看 Nginx 配置 HTTPS 服务器文档 了解更多信息,并将必要的配置应用到 angular 文件夹下的 nginx.conf 文件。

更新 angular 文件夹下的 nginx.conf 文件后,不要忘记重新构建 acme/bookstore-angular:latest 镜像。

bookstore-angular 服务挂载 etc/docker-compose/dynamic-env.json 文件,以更改镜像创建期间复制的现有 dynamic-env.json 文件,从而在部署时更改环境变量,而不是在每次环境变量更改后重新创建 Docker 镜像。不要忘记覆盖位于 aspnet-core/etc/docker-compose 文件夹下的 dynamic-env.json

如果您不使用带有 WSL 的 Docker,您可能会遇到卷挂载权限问题。您需要授予 Docker 能够使用本地文件系统的权限。有关更多信息,请参阅此 SO 答案

{
  "production": true,
  "application": {
    "baseUrl":"http://localhost:4200",  // https://myapp.com
    "name": "BookStore",
    "logoUrl": ""
  },
  "oAuthConfig": {
    "issuer": "https://localhost:44334/", // https://myauthserver.com/
    "redirectUri": "http://localhost:4200", // https://myapp.com
    "clientId": "BookStore_App",
    "responseType": "code",
    "scope": "offline_access openid profile email phone BookStore"
  },
  "apis": {
    "default": {
      "url": "https://localhost:44354",  // https://myapi.com
      "rootNamespace": "BookStore"
    },
    "AbpAccountPublic": {
      "url": "https://localhost:44334",  // https://myauthserver.com
      "rootNamespace": "AbpAccountPublic"
    }
  }
}

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__AngularUrl=http://localhost:4200
   - App__CorsOrigins=http://localhost:4200
      - 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

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

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

  • App__AngularUrl 是与帐户相关端点的 URL 覆盖配置。默认情况下为 http://localhost:4200部署到生产环境时,必须指向真实的 DNS

  • App__CorsOrigins 是 CORS 的覆盖配置。默认情况下为 http://localhost:4200部署到生产环境时,必须指向真实的 DNS

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

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

  • AuthServer__RequireHttpsMetadataopenid 提供程序强制使用 HTTPS 的选项。Docker-compose 使用一个称为 abp-network 的隔离内部 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=http://localhost:4200
      - App__RedirectAllowedUrls=http://localhost:4200
      - AuthServer__Authority=https://localhost:44334/
      - AuthServer__RequireHttpsMetadata=false            
      - ConnectionStrings__Default=mongodb://mongodb/BookStore       
      - Redis__Configuration=redis
    ports:
      - "44334:443"
    depends_on:               
      mongo-db:
        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 的覆盖配置。默认情况下为 http://localhost:4200部署到生产环境时,必须指向真实的 DNS
  • App__RedirectAllowedUrls 是 angular 应用程序的重定向 URL 覆盖配置。默认情况下为 http://localhost:4200部署到生产环境时,必须指向真实的 DNS
  • AuthServer__Authority 是颁发者 URL。https://localhost:44334/ 默认是 authserver 的端点。部署到生产环境时,必须指向真实的 DNS
  • 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_App__RootUrl=http://localhost:4200      
      - 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 (http://localhost:4200) 和 swagger-ui 客户端 URL (<https://localhost:44354),以便身份验证正常工作。>

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

下一步是什么?

在本文档中