虚拟机工作负载部署概览

本指南介绍了使用虚拟机运行时将基于虚拟机 (VM) 的工作负载部署到 Google Distributed Cloud (GDC) 网闸隔离裸机集群所需的概念背景。本指南中的工作负载是一个可在本地硬件上运行的示例票务系统平台。

架构

资源层次结构

在 GDC 中,您可以在运维团队的专用租户组织中部署构成工单系统的组件,该组织与任何客户组织完全相同。 组织是集中管理的一系列集群、基础架构资源和应用工作负载。GDC 实例中的每个组织都使用一组专用服务器,从而在租户之间提供强大的隔离。如需详细了解基础架构,请参阅设计访问边界。

GDC 网闸隔离配置资源层次结构

此外,您还可以在一个项目中集中部署和管理票务系统资源,从而通过软件政策和强制执行机制在组织内实现逻辑隔离。项目中的资源旨在将必须在生命周期内保持在一起的组件耦合在一起。

该工单系统采用三层架构,依靠负载均衡器将流量定向到应用服务器,这些服务器连接到存储永久性数据的数据库服务器。

三层式架构示意图

这种架构具有可伸缩性和可维护性,因为每个层级都可以独立开发和维护。它还提供了清晰的关注点分离,从而简化了调试和问题排查。将这些层封装在 GDC 项目中,您可以统一地部署和管理组件,例如应用和数据库服务器。

网络

在生产环境中运行票务系统需要部署两个或更多应用服务器,以便在节点发生故障时实现高可用性。结合使用负载均衡器,此拓扑还允许在多台机器之间分配负载,以横向扩缩应用。GDC 的 Kubernetes 原生平台利用 Cloud Service Mesh 将流量安全地路由到构成票务系统的应用服务器。

Cloud Service Mesh 是 Google 基于开源项目的实现,用于管理、观察和保护服务。在 GDC 上托管票务系统时,会用到 Cloud Service Mesh 的以下功能:

  • 负载均衡:Cloud Service Mesh 将流量与基础架构伸缩分离开来,从而实现许多流量管理功能,包括动态请求路由。票务系统需要持久的客户端连接,因此我们使用 DestinationRules 启用粘性会话,以配置流量路由行为。
  • TLS 终结:Cloud Service Mesh 使用 TLS 证书公开入站网关,并通过 mTLS(双向传输层安全协议)在集群内提供传输身份验证,而无需更改任何应用代码。

  • 故障恢复:Cloud Service Mesh 提供了多项关键故障恢复功能,包括超时、断路器、主动健康检查和重试次数限制。

在 Kubernetes 集群中,我们使用标准 Service 对象作为一种抽象方式,将应用和数据库服务器公开到网络。服务提供了一种便捷的方式,可使用选择器定位实例,并使用集群感知 DNS 服务器在集群内提供域名解析。

apiVersion: v1
kind: Service
metadata:
  name: http-ingress
spec:
  selector:
    app.kubernetes.io/component: application-server
  ports:
  - name: http
    port: 80
---
apiVersion: v1
kind: Service
metadata:
  name: database-ingress
spec:
  selector:
    app.kubernetes.io/component: database-server
  ports:
  - name: mysql
    port: 3306

计算

该工单系统建议使用裸金属或虚拟机来托管本地安装,因此我们利用 GDC Virtual 虚拟机 (VM) 管理功能,将应用服务器和数据库服务器都部署为虚拟机工作负载。通过定义 Kubernetes 资源,我们可以同时指定 VirtualMachine 和 VirtualMachineDisk,以便根据不同类型服务器的需求量身定制资源。VirtualMachineExternalAccess 允许我们为虚拟机配置数据传入和数据传出。

apiVersion: virtualmachine.gdc.goog/v1
kind: VirtualMachineDisk
metadata:
  name: vm1-boot-disk
spec:
  size: 100G
  source:
    image:
      name: ts-ticketing-system-app-server-2023-08-18-203258
      namespace: vm-system
---
apiVersion: virtualmachine.gdc.goog/v1
kind: VirtualMachine
metadata:
  labels:
    app.kubernetes.io/component: application-server
  name: vm1
  namespace: support
spec:
  compute:
    vcpus: 8
    memory: 12G
  disks:
  - boot: true
    virtualMachineDiskRef:
      name: vm1-boot-disk

---
apiVersion: virtualmachine.gdc.goog/v1
kind: VirtualMachineExternalAccess
metadata:
  name: vm1
  namespace: support
spec:
  enabled: true
  ports:
   - name: ssh
     protocol: TCP
     port: 22

对于客机操作系统映像,我们创建了一个自定义映像,以满足我们的合规性和安全性要求。您可以使用 VirtualMachineAccessRequest 通过 SSH 连接到正在运行的虚拟机实例,这样我们就可以通过 Kubernetes RBAC 限制连接到虚拟机的能力,并避免在自定义映像中创建本地用户账号。访问权限请求还定义了存留时间 (TTL),允许基于时间的访问权限请求管理自动过期的虚拟机。

自动化

作为该项目的一项关键交付成果,我们设计了一种以可重复的方式安装票务系统实例的方法,该方法可以支持广泛的自动化并减少部署之间的配置偏差。

发布流水线

自定义应用和数据库服务器映像时,首先要从基础操作系统映像开始,根据需要修改基础映像,以安装每个服务器映像所需的依赖项。我们选择了一个基础操作系统映像,因为它在本地安装中广泛用于托管票务系统。此基础操作系统映像还提供满足安全技术实施指南 (STIG) 所需的安全强化功能,以提供符合 NIST-800-53 控制措施的合规映像。

我们的持续集成 (CI) 流水线使用以下工作流来自定义应用和数据库服务器映像:

持续集成工作流

当开发者更改自定义脚本或映像依赖项时,我们会在 CI 工具中触发自动化工作流,以生成一组新映像,这些映像会与 GDC 版本捆绑在一起。在映像构建过程中,我们还会刷新操作系统依赖项 (yum update),并使用压缩技术对映像进行稀疏化处理,以减小将映像传输到客户环境所需的映像大小。

软件开发生命周期

软件开发生命周期 (SDLC) 是一个帮助组织规划、创建、测试和部署软件的过程。通过遵循明确定义的 SDLC,组织可以确保以一致且可重复的方式开发软件,并尽早发现潜在问题。除了在持续集成 (CI) 流水线中构建映像之外,我们还定义了用于开发和预演票务系统预发布版本的环境,以便进行测试和质量保证。

通过为每个 GDC 项目部署单独的工单系统实例,我们能够单独测试更改,而不会影响同一 GDC 实例上的现有实例。我们使用 ResourceManager API,通过 Kubernetes 资源以声明方式创建和销毁项目。

apiVersion: resourcemanager.gdc.goog/v1
kind: Project
metadata:
  name: ticketing-system-dev
---
apiVersion: resourcemanager.gdc.goog/v1
kind: Project
metadata:
  name: ticketing-system-qa
---
apiVersion: resourcemanager.gdc.goog/v1
kind: Project
metadata:
  name: ticketing-system-staging

结合图表打包和管理基础设施(如虚拟机)作为代码,开发者可以快速迭代,在生产实例旁边进行更改和测试新功能。声明式 API 还使自动化测试执行框架能够执行常规回归测试并验证现有功能。

可操作性

可操作性是指系统易于操作和维护。这是设计任何软件应用时都需要考虑的重要因素。有效的监控有助于提高可操作性,因为它可以在问题对系统产生重大影响之前发现并解决问题。监控还可用于确定改进机会,并为服务等级目标 (SLO) 建立基准。

监控

我们将工单系统与现有的 GDC 可观测性基础设施(包括日志记录和指标)集成。对于指标,我们从每个虚拟机公开 HTTP 端点,使应用能够抓取应用和数据库服务器生成的数据点。这些端点包括使用应用节点导出器收集的系统指标和应用专用指标。

在每个虚拟机中公开端点后,我们使用 MonitoringTarget 自定义资源配置了应用轮询行为,以定义抓取间隔并为指标添加注解。

apiVersion: monitoring.gdc.goog/v1
kind: MonitoringTarget
metadata:
  name: database-monitor
spec:
  podMetricsEndpoints:
    path:
      value: /metrics
    port:
      annotation: application.io/dbMetrics
    scrapeInterval: 60s

对于日志记录,我们在每个虚拟机中安装并配置了日志和指标处理器,以跟踪相关日志并将日志数据发送到日志记录工具,在其中通过监控实例对数据进行索引和查询。审核日志会转发到配置了延长保留期限的特殊端点,以确保合规。基于容器的应用可以使用 LoggingTarget 和 AuditLoggingTarget 自定义资源,指示日志记录流水线从项目中的特定服务收集日志。

根据日志记录和监控处理器中提供的数据,我们使用 MonitoringRule 自定义资源创建了提醒,这使我们能够将此配置作为代码在图表打包中进行管理。使用声明式 API 定义提醒和信息中心,还可让我们将此配置存储在代码库中,并遵循我们依赖于任何其他代码更改的相同代码审核和持续集成流程。

故障模式

早期测试发现了多种与资源相关的故障模式,这有助于我们确定应优先添加哪些指标和提醒。我们首先监控高内存和磁盘用量,因为数据库配置错误最初导致缓冲区表消耗了所有可用内存,并且过度日志记录填满了挂接的永久性卷磁盘。在调整存储缓冲区空间大小并实施日志轮换策略后,我们引入了在虚拟机接近高内存或磁盘用量时运行的提醒。

apiVersion: monitoring.gdc.goog/v1
kind: MonitoringRule
metadata:
  name: monitoring-rule
spec:
  interval: 60s
  limit: 0
  alertRules:
  - alert: vm1_disk_usage
    expr:
      (node_filesystem_size_bytes{container_name="compute"} -
      node_filesystem_avail_bytes{container_name="compute"}) * 100 /
      node_filesystem_size_bytes{container_name="compute"} > 90
    labels:
      severity: error
      code: TS-R0001
      resource: vm1
    annotations:
      message: "vm1 disk usage above 90% utilization"

在验证系统稳定性后,我们将重点转向了工单系统中的应用故障模式。由于调试应用内的问题通常需要我们使用安全 shell (SSH) 进入每个应用服务器虚拟机来检查票务系统日志,因此我们配置了一个应用,将这些日志转发到日志记录工具,以在 GDC 可观测性堆栈的基础上构建,并在监控实例中查询所有票务系统操作日志。

中心化日志记录还使我们能够同时查询多个虚拟机的日志,从而整合我们对系统每个组件的视图。

备份

备份对于软件系统的可操作性非常重要,因为它们可以在发生故障时恢复系统。GDC 通过 Kubernetes 资源提供虚拟机备份和恢复功能。通过使用 VirtualMachineBackupPlanTemplate 自定义资源创建 VirtualMachineBackupRequest 自定义资源,我们可以将挂接到每个虚拟机的永久性卷备份到对象存储区,并根据设置的保留政策保留备份。

apiVersion: virtualmachine.gdc.goog/v1
kind: VirtualMachineBackupPlanTemplate
metadata:
  name: vm-backup-plan
spec:
  backupRepository: "backup-repository"
---
apiVersion: virtualmachine.gdc.goog/v1
kind: VirtualMachineBackupRequest
metadata:
  name: "db-vm-backup"
spec:
  virtualMachineBackupPlanTemplate: vm-backup-plan
  virtualMachine: db1
  virtualMachineBackupName: db-vm-backup

同样,从备份恢复虚拟机状态涉及创建 VirtualMachineRestoreRequest 自定义资源,以恢复应用和数据库服务器,而无需修改任一服务的代码或配置。

apiVersion: virtualmachine.gdc.goog/v1
kind: VirtualMachineRestoreRequest
metadata:
  name: vm-restore-1
spec:
  virtualMachineBackup: db-vm-backup
  restoreName: restore1
  restoredResourceName: db1

升级

为了支持票务系统及其依赖项的软件生命周期,我们确定了三种类型的软件升级,每种升级都单独处理,以最大限度地减少停机时间和业务中断:

  1. 操作系统升级。
  2. 平台升级,例如补丁和主要版本。
  3. 配置更新。

对于操作系统升级,我们会持续为应用服务器和数据库服务器构建和发布新的虚拟机映像,这些映像会随每个 GDC 版本一起分发。这些映像包含安全漏洞修复和底层操作系统更新。

票务系统平台升级需要对现有虚拟机映像应用更新,因此我们无法依靠不可变的基础设施来执行修补和主要发布版本更新。对于平台升级,我们会在开发和预演环境中测试和验证补丁或主要发布版本,然后再发布独立的升级软件包以及 GDC 版本。

最后,通过工单系统 API 应用配置更新,无需停机,即可更新集和其他用户数据。我们在开发和预演环境中开发和测试配置更新,然后在 GDC 发布过程中将多个更新集打包在一起。

集成

身份提供商

为了向客户提供顺畅的体验,并使组织能够为其用户提供服务,我们将工单系统与 GDC 中提供的多个身份提供方集成。通常,企业和公共部门客户拥有自己的管理良好的身份提供方,用于向其员工授予和撤消授权。由于合规性要求以及为了便于进行身份和访问权限治理,这些客户希望使用其现有身份提供方作为可靠来源,来管理其员工对工单系统的访问权限。

该票务系统通过其多身份提供方模块支持 SAML 2.0 和 OIDC 提供方,我们在应用服务器虚拟机映像中预先启用了该模块。客户通过其组织身份提供方进行身份验证,该提供方会自动在工单系统中创建用户并分配角色。

允许通过 ProjectNetworkPolicy 自定义资源出站到身份提供方服务器,这会限制是否可以从 GDC 中的组织访问外部服务。这些政策使我们能够以声明方式控制票务系统可以访问网络上的哪些端点。

提醒注入

除了让用户登录以手动创建支持请求之外,我们还会创建工单系统突发事件来响应系统提醒。

为了实现这种集成,我们自定义了一个开源 Kubernetes 网络钩子,用于接收来自应用的提醒,并使用通过 Cloud Service Mesh 公开的工单系统 API 端点管理突发事件的生命周期。

API 密钥使用 GDC 密钥存储区进行存储,并由通过基于角色的访问权限控制 (RBAC) 控制的 Kubernetes 密钥提供支持。其他配置(例如 API 端点和突发事件自定义字段)通过 Kubernetes ConfigMap 键值对存储进行管理。

电子邮件

工单系统提供邮件服务器集成,以便客户接收基于电子邮件的支持。自动化工作流会将客户发来的电子邮件转换为支持请求,并向客户发送包含支持请求链接的自动回复,从而使我们的支持团队能够更好地管理收件箱、系统地跟踪和解决电子邮件请求,并提供更好的客户服务。

apiVersion: networking.gdc.goog/v1
kind: ProjectNetworkPolicy
metadata:
  name: allow-ingress-traffic-from-ticketing-system
spec:
  subject:
    subjectType: UserWorkload
  ingress:
  - from:
    - projects:
        matchNames:
        - ticketing-system

将电子邮件公开为 Kubernetes 服务还可提供服务发现和域名解析功能,并进一步将电子邮件服务器后端与票务系统等客户端分离开来。

合规

审核日志记录

审核日志提供了一种跟踪和监控软件使用情况的方法,并提供系统活动记录,有助于提升合规状况。审核日志会记录未经授权的用户尝试访问的情况、跟踪 API 使用情况,并识别潜在的安全风险。审核日志符合合规性要求,例如《健康保险流通与责任法案》(HIPAA)、《支付卡行业数据安全标准》(PCI DSS) 和《萨班斯-奥克斯利法案》(SOX) 规定的要求。

GDC 提供了一个系统,用于记录平台内的管理活动和访问情况,并在可配置的时间段内保留这些日志。部署 AuditLoggingTarget 自定义资源会配置日志记录流水线,以从我们的应用收集审核日志。

对于票务系统,我们为收集的系统审核事件和票务系统安全审核日志生成的应用特定事件配置了审核日志记录目标。这两种类型的日志都会发送到集中式 Loki 实例,我们可以在监控实例中编写查询和查看信息中心。

访问权限控制

访问权限控制是根据请求访问的用户或进程的身份授予或拒绝资源访问权限的过程。这有助于保护数据免遭未经授权的访问,并确保只有获得授权的用户才能对系统进行更改。在 GDC 中,我们依靠 Kubernetes RBAC 来声明政策并强制执行对由票务系统应用组成的系统资源的授权。

在 GDC 中定义 ProjectRole 后,我们便可以使用预设的授权角色授予对 Kubernetes 资源的精细访问权限。

apiVersion: resourcemanager.gdc.goog/v1
kind: ProjectRole
metadata:
  name: ticketing-system-admin
  labels:
    resourcemanager.gdc.goog/rbac-selector: system
spec:
  rules:
  - apiGroups:
    - ""
    resources:
    - configmaps
    - events
    - pods/log
    - services
    verbs:
    - get
    - list

灾难恢复

数据库复制

为了满足灾难恢复 (DR) 要求,我们在多个 GDC 实例中以主从配置部署票务系统。在此模式下,对工单系统的请求通常会路由到主站点,而辅助站点会持续复制数据库的二进制日志。如果发生故障切换事件,辅助站点会提升为新的主站点,然后请求会路由到新的主站点。

我们利用数据库复制功能,根据设置的参数为每个 GDC 实例配置主数据库服务器和副本数据库服务器。

如需为运行时间超过二进制日志保留期限的现有实例启用复制,我们可以使用数据库备份来恢复副本数据库,以便从主数据库开始复制。

在主模式下,应用服务器和数据库的运行方式与现在相同,但主数据库配置为启用复制。例如:

  1. 启用二进制日志。

  2. 设置服务器 ID。

  3. 创建复制用户账号。

  4. 创建备份。

在副本模式下,应用服务器将停用票务 Web 服务,以避免直接连接到副本数据库。必须将副本数据库配置为从主数据库开始复制,例如:

  1. 设置服务器 ID。

  2. 配置复制用户凭据和主要连接详细信息,例如主机和端口。

  3. 从备份恢复会恢复二进制日志位置。

数据库复制需要网络连接,以便副本连接到主数据库以启动复制。为了公开主数据库端点以进行复制,我们使用 Cloud Service Mesh 创建一个支持在服务网格处 TLS 终结的 Ingress 服务网格,这与我们处理票务系统 Web 应用的 HTTPS 数据传输的方式类似。