查看原文
其他

金山世游的 HashiStack 实践

大可不加冰 大可不加冰 2022-10-07

这是《HashiStack 在中国》系列的第一次访谈,也非常巧是这个公众号的第 100 篇原创,原本想要线下交流的,很可惜国内最近防疫政策收紧,所以未能成行,于是在线上与金山世游的 @zhiguangwang 进行了线上交流,非常有收获,以下是我根据记忆做的一些回顾,以飨读者。如果读者觉得意犹未尽,请一定要积极留言、转发和点赞,我将根据数据来决定是否会对大家所关心的问题进行后续更深入的访谈,为大家挖掘更多宝贵的知识和经验。

下文中如果提到“团队”,一般情况下指代金山世游团队。

金山世游成立于 2016 年,是金山软件集团旗下专注于网络游戏研发、全球发行运营、游戏投资等的全资子公司。总部位于北京,在武汉、大连、珠海设有研发中心。根据金山软件集团业务发展需求,2020 年 3 月起由“西山居世游”正式更名为“金山世游”,以全新的理念与姿态展开战略规划与产品布局。

说起来西山居可是少年时的回忆了,中国大陆地区第一款电脑游戏就是西山居士求伯君开发的《中国民航》,以前还玩过哈哈哈。

中国民航

金山世游使用了哪些 HashiCorp 工具?

  • Terraform
  • Packer
  • Consul
  • Nomad

西山多云

团队目前采用了多云战略,同时使用了金山云、腾讯云以及 AWS。采用多云战略的主要原因是因为金山世游戏有出海业务,需要在全球部署服务集群,这方面目前国内公有云厂商在全球的部署和服务与御三家(AWS、Azure、GCP)相比仍然有不小的差距。目前团队在国内主要使用金山云,而在海外则主力使用 AWS。

团队最早尝试过使用 AWS CloudFormation 作为 IaC 工具,但很快就遇到以下两个问题:

  1. CloudFormation 当时只支持 Json 格式,而 Json 代码无论是在书写还是维护上都差强人意
  2. CloudFormation 仅支持编排 AWS 资源,国内业务金山云资源被迫要采用不同的体系运维

在调研后,团队于 2019 年开始使用 Terraform 作为基础设施编排方案。

他们是如何使用 Terraform 管理基础设施的?

在一开始金山世游和很多团队一样,都采用我们称为 “Vanilla Terraform” 的方式,也就是平铺直叙地把所有需要用到的资源都用 Terraform Resource 定义在一个大的 Root Module 里。很快团队就发现这么做的问题:代码复用性差,云基础设施复杂度高,团队为了创建一些简单的业务功能,需要反复编写大量雷同的代码,并且这个 Root Module 会以极快的速度变成一个无法维护的大泥球。

所以团队很快就开始转向模块化组装基础设施。经过调研后,团队选择使用 Terragrunt 作为日常使用的工具。

Terragrunt

金山世游的业务场景是典型的网游场景,不同的游戏就是不同的产品线,在每个游戏都会部署在多个地域,其后台服务由一组相互协同的服务组成。游戏的部署又分为测试、预演以及生产等不同的环境。所以团队组织基础设施的模式是:

.
└── 产品
├── 测试
│   ├── ap-northeast-1
│   │   ├── app
│   │   ├── db
│   │   └── network
│   └── us-west-1
│   ├── app
│   ├── db
│   └── network
├── 生产
│   ├── ap-northeast-1
│   │   ├── app
│   │   ├── db
│   │   └── network
│   └── us-west-1
│   ├── app
│   ├── db
│   └── network
└── 预演
├── ap-northeast-1
│   ├── app
│   ├── db
│   └── network
└── us-west-1
├── app
├── db
└── network

请注意这个结构只是我根据访谈推想的一个大概结构,应该会与实际情况有出入。每一个叶子文件夹都是一个独立的 Root Module,包含了 Terragrunt 的配置文件,以 HCL 格式编写,描述了该模块调用了哪些 Terraform Module 组装出这一层需要的资源,此外也会通过 dependency 块来描述本模块所依赖的其他前置 Root Module,比如这样:

dependencies {
  paths = ["../vpc""../mysql""../redis"]
}

Terragrunt 提供的能力允许我们在上层目录中递归地分析子目录的结构,用一个 terragrunt apply 命令把下层描述的基础设施都构建出来,同时所有的叶子目录中的 Root Module 都会被保存在独立的 Terraform State 中。可以推想出的是,不同环境使用的应该是不同的账号,权限、数据应该是相互隔离的。

对基础设施的变更完全使用 GitOps 的方式来管理。每一个产品都会有独立的一个代码仓库,并且团队开发了大量自研的 Terraform Module,这些自研的 Module 被存放在一个大的代码仓库中,根据应用场景和范围别组织到仓库里不同的目录层级中(Terraform 支持通过 Github 代码仓库地址来作为 Module 的 source,同时 source 也支持指定某个代码仓库中的子路径,例如 git::https://example.com/network.git//modules/vpc)。所有对基础设施变更,都需要通过 Pull Request 或是 Merge Request 的形式提交一个代码变更请求。他们使用了 Atlantis 来实现对 Pull Request 自动执行 terraform plan,然后将生成的变更计划通过评论的形式提交到 PR 中,审查通过后在评论中提交 atlantis apply 即可自动 Apply 变更,并关闭 PR。

Atlantis
一个典型的 Atlantis 工作流

歪个楼,这个功能我自己用 Github Action 也实现了,有兴趣以后可以专门写一篇。

Atlantis 打动团队的一点是对主流的 VCS 系统都有良好支持,因为团队同时使用 GitHub 和 GitLab(出海业务与国内业务),使用 Atlantis 能确保在两个 VCS 中的机制与体验完全一致,否则就需要用 GitHub Action 和 GitLab CI/CD 各实现一遍 Terraform Pull Request Automation。

自研的 Module 是主流

团队在一开始也使用了大量的开源社区提供的高质量 Module,这方面 AWS 社区拥有大量高质量的开源 Terraform Module,而且研发一个高质量的 Terraform Module 其实需要耗费不少的精力,但在一段时间以后团队决定自己开发 Module,这是因为需求多变,不少需求社区的 Module 无法很好地满足。目前团队使用的 Module 中有 90% 是自研的。

在从社区 Module 切换到自研 Module 的过程中,团队需要进行大量的手工 terraform import 操作,将旧 Module 管理的资源迁移到新 Module 中去。目前这种场景的确是 Terraform 的一大痛点。

这些自研 Module 都是为团队实际使用场景设计的,符合一定的规范,团队称其为 “Terragrunt Ready”,也就是它们的 variable output 设计都使它们特别适合被通过 Terragrunt 组合到一起。

如果两个产品之间需要互操作怎么办?

团队采用这种独立筒仓式的结构管理各个产品,那么如果筒仓之间需要互操作怎么办?一个常见的场景是需要把两个 Vpc 通过 Vpc Peering 打通,那么这种夹在两个产品之间的资源该如何管理?

团队的策略是,哪个产品发起 Peering,那么 Peering 的资源就声明在哪个产品的代码仓库里。团队最常见的 Peering 场景是后台的平台服务(Platform Engineering)要与各个产品线打通,这时所有的 Peering 资源就都声明在平台服务的代码仓库里(非常合理的设计,因为平台服务此时实际上扮演了一个 Hub 的角色)。

创建一个 Vpc Peering 需要双方共同声明一组资源

将代码化推进到极限

除了使用 Terraform 编排基础设施,团队在日常工作中还发现对 Jenkins 的管理不是很方便。Jenkins 虽然支持使用代码来定义 Pipeline,但你仍然需要通过 GUI 来提交这个代码。在使用了社区编写的 Terraform Jenkins Provider 后团队发现该插件已经疏于维护,团队提交的一些 Pull Request 也没有得到积极响应和处理,所以团队索性 Fork 了一个版本自己开发维护:https://github.com/kingsoftgames/terraform-provider-jenkins。

Packer For Kingsoft Cloud

首先团队使用了 Packer,用代码定义了使用的虚拟机镜像。除非你的团队完全使用了容器,否则只要使用了虚拟机,那么用 Terraform 但不用 Packer 都是一件非常奇怪的事,这意味着你无法以“不可变”的风格来变更虚拟机中的应用版本。为了统一国内国外的业务,团队还推动了金山云开发了他们的 Packer Builder 插件,地址是:https://github.com/kingsoftcloud/packer-plugin-ksyun

Nomad 挑大梁

与许多践行云原生的团队不同,金山世游在仔细评估后决定使用 HashiCorp Nomad 作为他们的应用编排平台。HashiCorp 对 Nomad 官方的描述是:

A simple and flexible scheduler and orchestrator to deploy and manage containers and non-containerized applications across on-prem and clouds at scale.

换句话简介,Nomad 可以让你用学习 K8s 20% 的精力,得到一个包含 K8s xx% 基础能力的应用编排与任务调度平台(这里 xx 我个人认为 > 50%)。

团队选用 Nomad 的原因有二:

  1. 足够简单
  2. 可以在不用将应用容器化的前提下就引入一个现代化的任务调度平台

事实上这两点就是 Nomad 最大的卖点。由于 K8s 有一个非常陡峭的学习曲线,它的运维和管理并不是那么容易的事情,所以如果团队规模不大,并且没有非常精通 K8s 的成员时,选择 Nomad 将是更加明智的决定。

对于大部分遗留应用来说,最为迫切的诉求是通过一个现代化的调度器来部署和管理,替换掉以前手动或半自动的部署运行方式。而容器化更多是一个加分项。

如果团队选择 Kubernetes 作为调度器,那么就必须要先做容器化改造,这在增加大量前置工作的同时,也显著增加了向云原生转型的耗时和风险。

团队先将一些遗留应用通过 raw_exec driver 运行在 Nomad 上,实现了最迫切的诉求,然后再逐步做容器化改造。容器化改造完毕后,再改为用 docker driver 运行在 Nomad 上,整个 Nomad Job 的主体结构变化不大。这就使得遗留应用的整个改造过程是平滑演进的,改造的风险是可控的。

目前团队已经将游戏业务直接部署在 Nomad 集群中,并且团队选择限制集群的规模,选择部署和运维数个独立的小型 Nomad 集群,而不是一个庞大的集群,对已经深度实践基于 Terraform 的 GitOps 的团队来说,这并不是什么难事。

小结

由于时间限制,今天的访谈有一种意犹未尽的味道,金山世游团队对 HashiStack 的使用同时具备广度和深度,有大量值得学习借鉴的地方,短短一小时的交流是无法穷尽其万一的,只能浮光掠影地为大家介绍一番。我个人的感受如下:

国际间交流已成为中国互联网行业提升自动化效率的最大动力

从前在优刻得工作时惊闻优刻得也成立了专门的团队来开发 UCloud Terraform Provider,后来打听下来也是因为有海外客户在评估进入中国后选用的公有云厂商,要求必须支持 Terraform 和 Packer。而更多的案例是像金山世游这样,发展出海业务时接触到了御三家高度发达的 DevOps 生态,一旦使用过 Terraform 或者 Pulumi 这样的 IaC 工具后就再也无法倒退回 ClickOps,于是在 Everything as Code 的道路上越走越远。这从另外一个角度印证了总书记同志的“中国开放的大门不会关闭,只会越开越大。... 我们将坚持对外开放的基本国策,坚持以开放促改革、促发展、促创新,持续推进更高水平的对外开放。”讲话的重要性。关门造车只会让中国落后,积极走出国门,与世界一流水平的生态协作才能提升自身的水平。总有人说“技术不重要”,说这种话的人就是鸦片战争前将西洋科技斥为奇技淫巧盲目自大的人的精神后裔,这种想法是要不得的。

从长周期看,经济的增长只有依靠生产效率的提升,这在现代社会等同于科技水平的提升

Terragrunt 就是 Terraform 最佳实践的工具版本

金山世游团队在实践中得出了和我一样的结论:大规模基础设施管理的最佳实践模式基本就是 Terragrunt 所总结,并在 Terragrunt 工具中实现的这些功能。

Terraform Module 很难做到适用于一切场景

从数个团队访谈的结果来看,我们极少见到完全使用社区维护的 Terraform Module 就能满足所有场景需求的团队,绝大多数的团队到最后都会选择开发和维护自研的 Terraform Module。

XXX as Code 是一条无法回头的单行道

一旦你开始使用 XXX as Code 以及 GitOps,你就再也无法忍受回到用鼠标点击,或是在命令行中用各种命令,以可变基础设施的风格来管理你的基础设施,因为见过 GitOps 的人是无法再回到过去了的。所以团队别无选择,只能把前进道路中所有无法代码化的部分,要么寻找或编写相应的 Provider,要么寻找支持代码化的替代品取代它。

IaC 防止基础设施架构的腐化

金山世游团队在成功地将对基础设施的维护、变更全部转为代码编写、审查、发布的同时,发现了这样一个现象:由于云服务不断推出各种新产品,云服务 API 以及 Terraform Provider 也在不停地演进,所以出现了《爱丽丝梦游仙境》中红皇后悖论,你必须尽力不停地奔跑,才能使你保持在原地。Terraform 代码如果长久不进行升级和测试,那么很快就将变得过时和不可靠。这反过来逼迫团队必须敏捷化,必须持续更新自身的基础设施,持续演进,但同时也保障了基础设施架构不至于腐化和过时,始终能够适应最新的变化。古语云:流水不腐,户枢不蠹,当你的基础设施架构像流水一样动态流动起来,你就不会落后和腐化。

逆全球化和互联网主权会影响 SaaS 行业

访谈中我询问团队,HashiCorp 这几年力推他们的 HCP (HashiCorp Cloud Platform)服务,团队会不会考虑使用例如 Terraform Cloud、Packer HCP 这样的托管服务?回答是评估下来不会。因为团队希望尽可能保持国内和出海业务在 DevOps 基础设施上的一致性,所以在国内没有部署的服务基本就不会考虑了。

这实际上是一个非常复杂的问题,例如中国法律规定数据不许出境,欧盟的 GDPR 也有类似的规定,对于 SaaS 来说,原先面对的可能是一个统一的全球市场,打开浏览器或是调用一个 API 就可以使用,而在这个逆全球化时代则必须考虑部署的地域,不同地域之间设立数据的防火墙,要在阻止数据流动的同时让服务能够流动,这实际上是非常高难度的挑战。例如中国虽然是一个大市场,但目前自动化程度不高,对于效率工具来说研究一个中国的特别部署版本需要花费不少的精力(即使是 AWS Azure 这样的云,其国内版本与国际版也有很多差异,更别说国内公有云与御三家之间的差异了),花费这样的精力但市场增长有限,这就导致很多优秀的工具会选择放弃中国市场,反过来又限制了中国互联网行业工具发展的水平。

Nomad 是一项被严重低估的产品

并不是所有团队都需要 K8s 的,事实上许多小团队无力自行维护,也没有必要自行维护一个 K8s 集群,更别说有团队会选择在公有云上用自建虚拟机来构建自己的 K8s 集群了,美其名曰“可控性好”。一个务实的小团队如果没有非常精通 K8s 的专家坐镇的话,其实在能满足业务需求的前提下,可能选用 Nomad 会是更加理性的选择,对于那些有着复杂遗留应用,很难容器化的团队来说更是如此,Nomad 支持容器、普通可执行程序、虚拟机等不同工作负载混布的特性非常完美地填补了这一块利基市场。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存