本指南介绍了使用虚拟机运行时将基于虚拟机 (VM) 的工作负载部署到 Google Distributed Cloud (GDC) 裸金属气隙集群所需的概念背景知识。本指南中的工作负载是可在本地硬件上使用的示例 ServiceNow 平台。
架构
资源层次结构
在 GDC 中,您可以在运营团队的专用租户组织中部署构成 ServiceNow 的组件,这与任何客户组织相同。 组织是集中管理的一系列集群、基础架构资源和应用工作负载。GDC 实例中的每个组织都使用一组专用服务器,可在租户之间实现强隔离。如需详细了解基础设施,请参阅设计访问权限边界。

此外,您可以在项目中一起部署和管理 ServiceNow 资源,从而通过软件政策和强制执行在组织内提供逻辑隔离。项目中的资源旨在耦合在整个生命周期内必须保持在一起的组件。
ServiceNow 采用三层架构,依靠负载均衡器在连接到存储持久性数据的数据库服务器的应用服务器之间引导流量。
这种架构可实现可伸缩性和可维护性,因为每个层级都可以独立开发和维护。它还可清晰分离关注点,从而简化调试和问题排查。通过将这些层级封装在 GDC 项目中,您可以一起部署和管理组件,例如应用和数据库服务器。
网络
在生产环境中运行 ServiceNow 需要部署两个或更多应用服务器,以在节点发生故障时实现高可用性。结合使用负载均衡器,此拓扑还可将负载分配到多台机器上,以横向扩缩应用。GDC 的 Kubernetes 原生平台利用 Cloud Service Mesh 将流量安全地路由到构成 ServiceNow 的应用服务器。
Cloud Service Mesh 是基于开源 Istio 项目的 Google 实现,可用于管理、观察和保护服务。以下 Cloud Service Mesh 功能用于在 GDC 上托管 ServiceNow:
- 负载均衡:Cloud Service Mesh 将流量与基础架构伸缩分隔开来,并提供了许多流量管理功能,包括动态请求路由。ServiceNow 需要持久的客户端连接,因此我们使用
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
计算
ServiceNow 建议使用裸机或虚拟机来托管本地安装,而我们利用 GDC 虚拟机 (VM) 管理功能将应用服务器和数据库服务器都部署为 VM 工作负载。通过定义 Kubernetes 资源,我们可以同时指定 VirtualMachine 和 VirtualMachineDisk,从而根据不同类型服务器的需求量身定制资源。VirtualMachineExternalAccess 允许我们配置虚拟机的传入数据转移和传出数据转移。
apiVersion: virtualmachine.gdc.goog/v1
kind: VirtualMachineDisk
metadata:
name: vm1-boot-disk
spec:
size: 100G
source:
image:
name: ts-service-now-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
对于客机操作系统映像,我们基于 Red Hat Enterprise Linux 创建了一个自定义映像,以满足我们在合规性和安全性方面的要求。可以使用 VirtualMachineAccessRequest 通过 SSH 连接到正在运行的虚拟机实例,这样一来,我们就可以通过 Kubernetes RBAC 限制连接到虚拟机的能力,并避免在自定义映像中创建本地用户账号。访问权限请求还定义了存留时间 (TTL),以便基于时间的访问权限请求能够管理自动过期的虚拟机。
自动化
作为此项目的关键交付项,我们设计了一种以可重复的方式安装 ServiceNow 实例的方法,该方法可支持广泛的自动化,并减少部署之间的配置偏差。
Helm 软件包
Helm 是 Kubernetes 的软件包管理器,用于管理应用的生命周期,包括组成应用的所有组件的安装和升级。Helm 还提供了一种管理依赖项的方法,例如与其他系统(如可观测性和合规性)集成所需的自定义资源。通过创建 Helm 图表,我们可以封装这些资源,从而简化安装和管理 ServiceNow 的流程。
发布流水线
自定义应用和数据库服务器映像从基础操作系统映像开始,根据需要修改基础映像以安装每个服务器映像所需的依赖项。我们之所以选择 Red Hat Enterprise Linux (RHEL) 作为基础操作系统映像,是因为它被广泛用于在本地安装中托管 ServiceNow。Red Hat Enterprise Linux 还提供安全强化功能,可满足安全技术实施指南 (STIG) 的要求,从而提供符合 NIST-800-53 控制措施的合规映像。
我们的持续集成 (CI) 流水线使用以下工作流程来自定义应用和数据库服务器映像:

当开发者更改自定义脚本或映像依赖项时,我们会触发 CI 工具中的自动化工作流,以生成一组与 GDC 版本捆绑的新映像。在映像构建过程中,我们还会刷新操作系统依赖项 (yum update),并通过压缩来稀疏化映像,以减少将映像传输到客户环境所需的映像大小。
软件开发生命周期
软件开发生命周期 (SDLC) 是一种可帮助组织规划、创建、测试和部署软件的流程。通过遵循明确定义的 SDLC,组织可以确保以一致且可重复的方式开发软件,并尽早发现潜在问题。除了在持续集成 (CI) 流水线中构建映像之外,我们还定义了用于开发和预演 ServiceNow 预发布版本的环境,以便进行测试和质量保证。
为每个 GDC 项目部署单独的 ServiceNow 实例,使我们能够单独测试更改,而不会影响同一 GDC 实例上的现有实例。我们使用 ResourceManager API 通过 Kubernetes 资源以声明方式创建和拆解项目。
apiVersion: resourcemanager.gdc.goog/v1
kind: Project
metadata:
name: service-now-dev
---
apiVersion: resourcemanager.gdc.goog/v1
kind: Project
metadata:
name: service-now-qa
---
apiVersion: resourcemanager.gdc.goog/v1
kind: Project
metadata:
name: service-now-staging
结合 Helm 图表打包和将虚拟机等基础设施作为代码进行管理,开发者可以快速迭代,在生产实例旁边更改和测试新功能。借助声明性 API,自动化测试执行框架还可以执行常规回归测试并验证现有功能。
可操作性
可操作性是指系统易于操作和维护的程度。在设计任何软件应用时,这都是一项重要的考虑因素。有效的监控有助于提高可操作性,因为它可以让您在问题对系统产生重大影响之前发现并解决问题。监控还可以用于发现改进机会,并为服务等级目标 (SLO) 建立基准。
监控
我们将 ServiceNow 与现有的 GDC 可观测性基础架构(包括日志记录和指标)集成在一起。对于指标,我们会从每个虚拟机公开 HTTP 端点,以便 Prometheus 抓取应用和数据库服务器生成的数据点。这些端点包括使用 Prometheus 节点导出器收集的系统指标和应用特有的指标,例如使用 MySQL 数据库导出器收集的指标。
在每个虚拟机中公开端点后,我们使用 MonitoringTarget 自定义资源配置了 Prometheus 轮询行为,以定义抓取间隔并注释指标。
apiVersion: monitoring.gdc.goog/v1
kind: MonitoringTarget
metadata:
name: database-monitor
spec:
podMetricsEndpoints:
path:
value: /metrics
port:
annotation: application.io/dbMetrics
scrapeInterval: 60s
对于日志记录,我们在每个虚拟机中安装并配置了 FluentBit,以跟踪相关日志并将日志数据发送到 Loki,然后在 Loki 中通过 Grafana 对数据进行索引和查询。审核日志会转发到配置了延长保留期限的特殊端点,以满足合规性要求。基于容器的应用可以使用 LoggingTarget 和 AuditLoggingTarget 自定义资源来指示日志记录流水线从项目中的特定服务收集日志。
根据 Prometheus 和 Loki 中提供的数据,我们使用 MonitoringRule 自定义资源创建了提醒,这使我们能够以代码形式在 Helm 图表中管理此配置。使用声明式 API 定义提醒和信息中心,还可让我们将此配置存储在 Git 代码库中,并遵循我们用于任何其他代码更改的相同代码审核和持续集成流程。
故障模式
早期测试发现了多种与资源相关的故障模式,这有助于我们确定优先添加哪些指标和提醒。我们首先监控了高内存和磁盘使用率,因为数据库错误配置最初导致缓冲区表消耗了所有可用内存,而过度日志记录填满了附加的永久性卷磁盘。在调整 InnoDB 缓冲区空间并实施日志轮换策略后,我们引入了在虚拟机接近高内存或磁盘使用率时运行的提醒。我们还编写了操作手册并分配了错误代码,以便操作员在收到提醒时快速查看有关如何诊断和缓解问题的文档。
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"
在验证系统稳定性后,我们将重点转移到 ServiceNow 中的应用故障模式。由于调试应用中的问题通常需要我们使用安全 Shell (SSH) 进入每个应用服务器虚拟机来检查 ServiceNow 系统日志,因此我们配置了 FluentBit 将这些日志转发到 Loki,以便在 GDC 可观测性堆栈的基础上构建,并在 Grafana 中查询所有 ServiceNow 运营日志。
集中式日志记录还使我们能够同时查询多个虚拟机的日志,从而整合对系统各个组件的视图。然后,我们将日志搜索查询纳入了运行手册,使操作员能够将错误情况与已知的解决方法和解决方案相匹配。
备份
备份对于软件系统的可操作性至关重要,因为它们允许在发生故障时恢复系统。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
升级
为了支持 ServiceNow 及其依赖项的软件生命周期,我们确定了三种类型的软件升级,每种升级的处理方式略有不同,以最大限度地减少停机时间和中断服务:
- 操作系统升级。
- 平台升级,例如补丁和主要版本。
- 配置更新。
对于操作系统升级,我们会持续为应用服务器和数据库服务器构建并发布新的虚拟机映像,这些映像会随每个 GDC 版本一起分发。这些映像包含针对安全漏洞的修复和对底层 Red Hat 操作系统的更新。为了部署新映像,运营商会遵循 runbook,其中详细说明了上传和启动新虚拟机、验证配置以及停用旧实例所需的步骤。
ServiceNow 平台升级需要对现有虚拟机映像应用更新,因此我们无法依靠不可变的基础设施来执行修补和主要版本更新。对于平台升级,我们会先在开发和预演环境中测试并验证补丁或主要版本,然后再发布独立升级软件包以及 GDC 版本。为了应用补丁和主要版本升级,操作员会按照运行手册连接到现有的虚拟机实例,上传升级软件包,并从每个正在运行的虚拟机启动升级。
最后,使用 ServiceNow API 更新集和其他用户数据,可在不中断服务的情况下应用配置更新。在 GDC 发布过程中,我们会先在开发和预演环境中开发并测试配置更新,然后再将多个更新集打包在一起。然后,运维人员按照运行手册调用我们开发的命令行工具,该工具会调用相应的 ServiceNow API 来上传和应用更新集。
集成
身份提供商
为了向客户提供顺畅的体验,并帮助组织顺利完成用户入门,我们将 ServiceNow 与 GDC 中提供的多个身份提供方集成在一起。通常,企业和公共部门客户拥有自己的身份提供方(例如 Active Directory 或其他此类系统),用于向员工授予和撤消授权。出于合规性要求以及身份和访问权限管理的便利性考虑,这些客户希望使用其现有身份提供商作为事实来源,以管理员工对 ServiceNow 的访问权限。

ServiceNow 通过其多身份提供方模块同时支持 SAML 2.0 和 OIDC 提供方,我们在应用服务器虚拟机映像中预先启用了该模块。基础设施运营商 (IO) 和客户都通过其组织身份提供方进行身份验证,该身份提供方会在 ServiceNow 中自动创建用户并分配角色。
通过 ProjectNetworkPolicy 自定义资源允许出站到身份提供方服务器,该资源限制了是否可以从 GDC 中的组织访问外部服务。借助这些政策,我们可以声明式地控制 ServiceNow 可以访问网络上的哪些端点。
提醒提取
除了让用户登录以手动创建支持请求外,我们还与 Prometheus AlertManager 集成,以创建 ServiceNow 事件来响应系统提醒。通过将我们的可观测性和工单系统与集中式入口点相关联,此集成可让运营商在 GDC 环境中对软件和硬件问题进行分诊和解决。
为了实现这种集成,我们自定义了一个开源 Kubernetes webhook,以接收来自 Prometheus 的提醒,并使用通过 Cloud Service Mesh 公开的 ServiceNow API 端点管理突发事件的生命周期。在 ServiceNow 中创建突发事件后,运营商可以启动突发事件工作流,以便在突发事件发生时对其进行分类和确定优先级,还可以点击链接在 Grafana 中查看触发的提醒的实时状态。
API 密钥使用 GDC Secret 存储区进行存储,该存储区由 Kubernetes Secret 提供支持,并通过基于角色的访问权限控制 (RBAC) 进行控制。其他配置(例如 API 端点和突发事件自定义字段)通过 Kubernetes ConfigMap 键值对存储进行管理。
电子邮件
ServiceNow 提供邮件服务器集成,以便客户通过电子邮件获得支持。自动化工作流可将客户来信转换为支持请求,并向客户发送包含支持请求链接的自动回复,从而帮助我们的支持团队更好地管理收件箱、系统地跟踪和解决电子邮件请求,并提供更好的客户服务。
为了与 GDC 中公开的 SMTP 和 POP3 服务集成,我们使用网络政策来控制项目之间的访问权限。将电子邮件组件托管在单独的项目中,可让我们将电子邮件作为一项可供其他应用重复使用的服务进行解耦。
apiVersion: networking.gdc.goog/v1
kind: ProjectNetworkPolicy
metadata:
name: allow-ingress-traffic-from-service-now
spec:
subject:
subjectType: UserWorkload
ingress:
- from:
- projects:
matchNames:
- service-now
将电子邮件公开为 Kubernetes 服务还可提供服务发现和域名解析,并进一步将电子邮件服务器后端与 ServiceNow 等客户端分离。
合规性
审核日志记录
审核日志可用于跟踪和监控软件使用情况,并提供系统活动记录,从而有助于提升合规性。审核日志会记录未经授权的用户尝试访问的情况、跟踪 API 使用情况,并识别潜在的安全风险。审核日志符合合规性要求,例如《健康保险流通与责任法案》(HIPAA)、支付卡行业数据安全标准 (PCI DSS) 和《萨班斯-奥克斯利法案》(SOX) 规定的要求。
GDC 提供了一个系统来记录平台内的管理活动和访问情况,并可将这些日志保留一段可配置的时间。部署 AuditLoggingTarget 自定义资源会将日志记录流水线配置为从我们的应用中收集审核日志。
对于 ServiceNow,我们配置了审核日志记录目标,以用于从 Red Hat Enterprise Linux 上的 auditd 守护程序收集的系统审核事件,以及由 ServiceNow 安全审核日志生成的特定于应用的事件。这两种类型的日志都会发送到集中的 Loki 实例,我们可以在其中编写查询并在 Grafana 中查看信息中心。
访问权限控制
访问权限控制是指根据请求访问的用户或进程的身份授予或拒绝资源访问权限的过程。这有助于保护数据免遭未经授权的访问,并确保只有获得授权的用户才能更改系统。在 GDC 中,我们依靠 Kubernetes RBAC 来声明政策,并强制执行对由 ServiceNow 应用组成的系统资源的授权。
在 GDC 中定义 ProjectRole 可让我们使用预设的授权角色授予对 Kubernetes 资源的精细访问权限。与虚拟机管理员 (vm-admin) 角色等其他预先存在的角色相结合,操作员可以绑定到一个或多个角色,以便调试问题和执行日常维护。
在 GDC 中定义 ProjectRole 可让我们使用预设的授权角色授予对 Kubernetes 资源的精细访问权限。
apiVersion: resourcemanager.gdc.goog/v1
kind: ProjectRole
metadata:
name: service-now-admin
labels:
resourcemanager.gdc.goog/rbac-selector: system
spec:
rules:
- apiGroups:
- ""
resources:
- configmaps
- events
- pods/log
- services
verbs:
- get
- list
通过 Gitlab 等基础架构即代码 (IaC) 系统和 Config Management 等同步服务管理角色和角色绑定,运维人员可以通过审批流程临时获得对角色的提升访问权限。这些系统还提供对系统的限时访问权限,同时维护系统更改的审核日志。
Runbook
对于维护合规的软件应用,运行手册非常重要,因为它们提供了完成任务的分步指南。这有助于确保所有任务均正确完成,并符合所有适用的法律法规。运行手册还有助于确保所有团队成员都了解自己的责任以及如何完成这些责任。我们为 ServiceNow 编写了 runbook,以执行轮换密码、重启服务器等管理任务和其他标准操作规程 (SOP)。
灾难恢复
数据库复制
为了满足灾难恢复 (DR) 要求,我们在多个 GDC 实例中以主实例-辅助实例配置部署 ServiceNow。在此模式下,对 ServiceNow 的请求通常会路由到主站点,而辅助站点会持续复制 MariaDB 数据库的二进制日志。如果发生故障切换事件,辅助网站会升级为新的主网站,然后请求会路由到新的主网站。

我们基于 MariaDB 复制功能,根据在 Helm 部署期间设置的参数,为每个 GDC 实例配置主数据库服务器和副本数据库服务器。
若要为运行时间超过二进制日志保留期限的现有实例启用复制,我们可以使用 Mariabackup 恢复副本数据库,该工具还会记录全局事务 ID (GTID),以便从主数据库开始复制。
在主模式下,应用服务器和数据库的运行方式与现在相同,但主数据库配置为启用复制。例如:
启用二进制日志。
设置服务器 ID。
创建复制用户账号。
创建包含 GTID 信息的备份。
在副本模式下,应用服务器将停用 ServiceNow Web 服务,以避免直接连接到副本数据库。必须将副本数据库配置为从主数据库开始复制,例如:
设置服务器 ID。
配置复制用户凭据和主连接详细信息,例如主机和端口。
从备份恢复,从 GTID 恢复二进制日志位置。
数据库复制需要网络连接,以便副本连接到主数据库来启动复制。为了公开主数据库端点以进行复制,我们可以使用 Cloud Service Mesh 创建一个 Istio Ingress 网关,该网关支持在 Istio 网关处 TLS 终结,这与我们在 ServiceNow Web 应用中处理 HTTPS 数据传输的方式类似。
故障切换程序
如果发生故障切换,辅助网站会升级为新的主网站,然后请求会路由到新的主网站。此过程最初是手动操作,但可以通过额外努力逐步实现自动化。
在主网站上:
停止应用服务器上的 ServiceNow 服务。
将数据库服务器配置为副本模式。
如果应用服务器上使用了反向代理服务,请启动该服务。
在次要网站上:
停止数据库服务器上的复制。
停止在应用服务器上使用的反向代理服务。
将数据库服务器配置为主要模式。
在应用服务器上启动 ServiceNow 服务。
在运行手册中记录故障切换程序有助于确保在发生灾难时运营不会中断,并缩短从灾难中恢复所需的时间。