虚拟机工作负载部署概览

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

架构

资源层次结构

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

GDC 网闸隔离资源层次结构

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

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

三层式架构的示意图

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

网络

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

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

  • 负载均衡: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 虚拟机 (VM) 管理将应用服务器和数据库服务器都部署为 VM 工作负载。通过定义 Kubernetes 资源,我们可以同时指定 VirtualMachineVirtualMachineDisk,从而根据不同类型服务器的需求量身定制资源。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 资源的 Project。

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

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

根据日志记录和监控处理器中的可用数据,我们使用 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: <a href="/distributed-cloud/hosted/docs/latest/gdch/gdch-io/service-manual/ts/runbooks/ts-r0001">TS-R0001</a>
      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 webhook,以接收来自应用的提醒,并使用通过 Cloud Service Mesh 公开的工单系统 API 端点管理突发事件的生命周期。

API 密钥使用 GDC Secret 存储区进行存储,该存储区由 Kubernetes Secret 提供支持,并通过基于角色的访问权限控制 (RBAC) 进行控制。其他配置(例如 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 数据传输的方式。