项目

微服务解决方案:添加新的应用程序

您必须拥有 ABP Business 或更高级别的许可证,才能创建微服务解决方案。

您可以将新的应用程序添加到微服务解决方案模板中。本文档解释了如何向解决方案中添加新的 Web 应用程序。在解决方案模板中,根目录下有一个名为 apps 的文件夹,其中包含解决方案中的所有应用程序。您可以出于不同目的创建单独的应用程序,例如 Web 应用程序、用于落地页的公共网站或管理面板。

此外,根目录下还有一个名为 _templates 的文件夹。此文件夹包含可用于创建新微服务、API 网关和应用程序的模板。这些模板可以根据您的需要进行定制。

添加新的 Web 应用程序

要向解决方案中添加新的 Web 应用程序,您可以使用 web 模板。此模板会创建一个带有必要配置和依赖项的新 ASP.NET Core 应用程序。请按照以下步骤添加新的 Web 应用程序:

在 ABP Studio 的 解决方案资源管理器 中,右键单击 apps 文件夹,然后选择 添加 -> 新建模块 -> Web

new-web-application

这将打开 创建新模块 对话框。输入新应用程序的名称,如果需要则指定输出目录,然后单击 下一步 按钮。有一个命名约定:模块名称 应包含解决方案名称作为前缀,并且模块名称中不允许使用点 (.) 字符。

create-new-web-app-module

选择 UI 框架,然后单击 下一步 按钮。

create-new-module-ui-framework

选择 UI 主题,然后单击 创建 按钮。

create-new-module-ui-theme

新应用程序已创建并添加到解决方案中。您可以在 apps 文件夹中看到新应用程序。

public-web-app

配置 appsettings.json

新应用程序已创建了必要的配置和依赖项。我们应该配置 appsettings.json 文件,为身份验证设置 Authority 部分,为 API 网关设置 Gateway 部分。您可以从现有的 Web 应用程序复制配置,并根据新应用程序进行修改。以下是 Public Web 应用程序的 appsettings.json 文件示例。

{
  "App": {
    "SelfUrl": "http://localhost:44344",
    "EnablePII": false
  },
  "AuthServer": {
    "Authority": "http://localhost:44387",
    "RequireHttpsMetadata": "false",
    "ClientId": "WebPublic",
    "ClientSecret": "1q2w3e*",
    "IsOnK8s": "false",
    "MetaAddress": "http://localhost:44387"
  },
  "RemoteServices": {
    "Default": {
      "BaseUrl": "http://localhost:44333"
    }
  },
  ...
}

配置 OpenId 选项

您可以使用现有的 OpenIddict 应用程序 或为新 Web 应用程序创建一个新的。在我们的示例中,我们使用 WebPublic 作为 ClientId,使用 1q2w3e* 作为 ClientSecret。根据 appsettings.json 文件中的配置,我们应该配置 Identity 服务中的 OpenIddictDataSeeder 类。您可以从现有的 Web 应用程序复制配置,并根据新应用程序进行修改。以下是 Public Web 应用程序的 OpenIddictDataSeeder 配置示例。

private async Task CreateClientsAsync()
{
    ...

    //Web 公共客户端
    var webPublicClientRootUrl = _configuration["OpenIddict:Applications:WebPublic:RootUrl"]!.EnsureEndsWith('/');
    await CreateOrUpdateApplicationAsync(
        name: "WebPublic",
        type: OpenIddictConstants.ClientTypes.Confidential,
        consentType: OpenIddictConstants.ConsentTypes.Implicit,
        displayName: "Web 公共客户端",
        secret: "1q2w3e*",
        grantTypes: new List<string> //混合流程
        {
            OpenIddictConstants.GrantTypes.AuthorizationCode,
            OpenIddictConstants.GrantTypes.Implicit
        },
        scopes: commonScopes.Union(new[]
        {
            "AuthServer", 
            "IdentityService",
            "SaasService",
            "AuditLoggingService",
            "AdministrationService"
        }).ToList(),
        redirectUris: new List<string> { $"{webPublicClientRootUrl}signin-oidc" },
        postLogoutRedirectUris: new List<string>() { $"{webPublicClientRootUrl}signout-callback-oidc" },
        clientUri: webPublicClientRootUrl,
        logoUri: "/images/clients/aspnetcore.svg"
    );
}

将新应用程序的 URL 添加到 Identity 服务的 appsettings.json 文件中。

{
  "OpenIddict": {
    "Applications": {
      ...
      "WebPublic": {
        "RootUrl": "http://localhost:44344"
      }
    }
  }
}

Prometheus 的 Docker 配置

如果您想在调试解决方案时使用 Prometheus 监控新应用程序,则应将新应用程序添加到 etc/docker/prometheus 文件夹中的 prometheus.yml 文件中。您可以从现有应用程序复制配置,并根据新应用程序进行修改。以下是 WebPublic 应用程序的 prometheus.yml 文件示例。

  - job_name: 'webpublic'
    scheme: http
    metrics_path: 'metrics'
    static_configs:
    - targets: ['host.docker.internal:44344']

为新应用程序创建 Helm Chart

如果您想将新应用程序部署到 Kubernetes,您应该为新应用程序创建一个 Helm chart。

首先,将新应用程序添加到 etc/helm 文件夹中的 build-all-images.ps1 脚本中。您可以从现有应用程序复制配置,并根据新应用程序进行修改。以下是 WebPublic 应用程序的 build-all-images.ps1 脚本示例。

./build-image.ps1 -ProjectPath "../../apps/web-public/Acme.Bookstore.WebPublic/Acme.Bookstore.WebPublic.csproj" -ImageName bookstore/webpublic

由于我们希望在集群外部公开我们的应用程序,因此应将主机 URL 添加到 etc/helm/projectname 文件夹中的 values.projectname-local.yaml 文件中。以下是 WebPublic 应用程序的 values.bookstore-local.yaml 文件示例。

global:
  ...
  hosts:
    ...
    webpublic: "[RELEASE_NAME]-webpublic"

出于开发目的,我们还应该为新应用程序创建 TLS 证书。您可以编辑 etc/helm 文件夹中的 create-tls-certificate.ps1 脚本,为新应用程序生成 TLS 证书。以下是 WebPublic 应用程序的 create-tls-certificate.ps1 脚本示例。

mkcert --cert-file bookstore-local.pem --key-file bookstore-local-key.pem "bookstore-local" ... "bookstore-local-webpublic"
kubectl create namespace bookstore-local
kubectl create secret tls -n bookstore-local bookstore-local-tls --cert=./bookstore-local.pem --key=./bookstore-local-key.pem

最后,我们应该在 etc/helm/projectname/templates 文件夹中的 _helpers.tpl 文件中定义新应用程序。您可以从现有应用程序复制配置,并根据新应用程序进行修改。以下是 WebPublic 应用程序的 _helpers.tpl 文件示例。

{{- define "bookstore.hosts.webpublic" -}}
{{- print "https://" (.Values.global.hosts.webpublic | replace "[RELEASE_NAME]" .Release.Name) -}}
{{- end -}}

之后,我们需要为新应用程序创建一个新的 Helm chart。您可以从现有应用程序复制配置,并根据新应用程序进行修改。以下是 WebPublic 应用程序的 webpublic Helm chart 示例。

# values.yaml
image:
  repository: "bookstore/webpublic"
  tag: "latest"
  pullPolicy: IfNotPresent
authServer:
  clientSecret: "1q2w3e*"

# Chart.yaml
apiVersion: v2
name: webpublic
appVersion: "1.0"
description: Bookstore 公共 Web 应用程序
version: 1.0.0
type: application

# webpublic.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: "{{ .Release.Name }}-{{ .Chart.Name }}"
spec:
  selector:
    matchLabels:
      app: "{{ .Release.Name }}-{{ .Chart.Name }}"
  template:
    metadata:
      labels:
        app: "{{ .Release.Name }}-{{ .Chart.Name }}"
    spec:
      containers:
      - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: "{{ .Values.image.pullPolicy }}"
        name: "{{ .Release.Name }}-{{ .Chart.Name }}"
        ports:
        - name: "http"
          containerPort: 80
        env:
        - name: "DOTNET_ENVIRONMENT"
          value: "{{ .Values.global.dotnetEnvironment }}"
        - name: "App__SelfUrl"
          value: "{{ include "bookstore.hosts.webpublic" . }}"
        - name: App__EnablePII
          value: "{{ .Values.global.enablePII }}"
        - name: "AuthServer__Authority"
          value: "{{ include "bookstore.hosts.authserver" . }}"
        - name: "AuthServer__ClientSecret"
          value: "{{ .Values.authServer.clientSecret }}"
        - name: "AuthServer__IsOnK8s"
          value: "true"
        - name: "AuthServer__MetaAddress"
          value: "http://{{ .Release.Name }}-authserver"
        - name: "RemoteServices__Default__BaseUrl"
          value: "http://{{ .Release.Name }}-webgateway"
          ...

# webpublic-service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    name: "{{ .Release.Name }}-{{ .Chart.Name }}"
  name: "{{ .Release.Name }}-{{ .Chart.Name }}"
spec:
  ports:
    - name: "80"
      port: 80
  selector:
    app: "{{ .Release.Name }}-{{ .Chart.Name }}"

# webpublic-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: "{{ .Release.Name }}-{{ .Chart.Name }}"
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: "/"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-buffer-size: "32k"
    nginx.ingress.kubernetes.io/proxy-buffers-number: "8"
    cert-manager.io/cluster-issuer: "letsencrypt"
spec:
  ingressClassName: "nginx"
  tls:
  - hosts:
      - "{{ (include "bookstore.hosts.webpublic" .) | trimPrefix "https://" }}"
    secretName: "{{ .Values.global.tlsSecret }}"
  rules:
  - host: "{{ (include "bookstore.hosts.webpublic" .) | trimPrefix "https://" }}"
    http:
      paths:
      - path: /
        pathType: "Prefix"
        backend:
          service:
            name: "{{ .Release.Name }}-{{ .Chart.Name }}"
            port:
              number: 80

创建 Helm chart 后,您可以在 ABP Studio 中刷新子图表

kubernetes-refresh-sub-charts

然后,更新元数据信息:右键单击应用程序 子图表,选择属性,这将打开图表属性窗口。您可以在元数据选项卡中进行编辑。

application-chart-properties

图表属性 -> Kubernetes 服务选项卡中,添加服务名称正则表达式模式 Kubernetes 服务

application-chart-properties-kubernetes-services

最后但同样重要的是,我们需要为 identity 微服务配置 helm chart 环境变量。

# identity.yaml 
# 在 "env:" 部分添加这行
- name: "OpenIddict__Applications__WebPublic__RootUrl"
  value: "{{ include "bookstore.hosts.webpublic" . }}"

自定义应用程序模板

您可以根据需要自定义应用程序模板。通过打开根目录中的 _templates 文件夹,然后打开 web 文件夹,向模板添加新的配置、依赖项或模块。根据需要修改 web 模板。命名约定规定,当创建应用程序时,microservicename 代表应用程序的名称。在模板文件中使用 microservicename 进行动态命名。在 web 文件夹中,有 3 个子文件夹:BlazorBlazorServerMVC。您可以根据创建新应用程序时选择的 UI 框架自定义模板。


在本文档中