在第一部分,我们介绍了 N26 团队为达成 “在 Day 1 实现轻松部署” 的目标而设定的战略规划和开发人员体验图,在这一部分,我们将带您了解该团队如何构建最简可行平台以及该平台如何运作。
01 计划构建最简可行平台
我们通过采用 Story Workshop,将功能分解为三个层级,以洞察其基本特征和范围大小,并更好地确定优先级。
在研讨会中,我们深刻认识到软件工程的复杂性。我们讨论了一些简单问题,比如数据库迁移和日志记录,也讨论了更高级的问题,比如在软件交付生命周期(SSDLC)中确保安全性,再比如在分期付款计算和信贷融资等操作中采用 Money 模式来避免四舍五入错误。这些问题的代价不容忽视,而且发生的频率比我们想象的更高。
软件开发的残酷事实:永远没有足够的人力、时间和资金来构建你想要的一切。因此,关键是确定重点和优先顺序。而这正是 Story Workshop 的优势所在,因为它能帮助我们定义 “最简可行平台”(TVP)。
在某些情况下,TVP 可能是作为软件开发构件的组件或服务文档。尽管如此,定义 TVP 还是很重要的,因为软件工程师喜欢构建技术性的东西,如果没有重点,就有可能交付一个忽视开发人员体验的复杂平台。这里我们将着重关注在 TVP 中实现哪些功能,目标是设计一个能够在一周内运行的配置流程,初始功能集将为我们构造平台的基础。
因此,我们优先安装了 Kubernetes、编排自动化(我们将其命名为 “项目引导器”)和服务架构。但是在没有任何数据库支持的情况下,如何交付呢?我们告诉团队,他们可以使用内存存储库来实现这些目的。TVP 发布后不久,平台团队发布了数据库配置功能。自此产品团队可以在生产中部署完全可运行的服务。
对于从事平台工程的人来说,这是一堂重要的产品管理课。通过发布产品,可以部分解决问题。要计划减少构建量,并为用户和利益相关者提供替代方案。在解决临时问题时,协调和沟通非常重要。
从那时起,我们的平台发生了翻天覆地的变化。但每一个进步都离不开平台团队的辛勤工作,他们始终计划以迭代的方式推进平台的功能。这一过程的核心是了解业务目标、产品工程师的需求和监管要求,优先考虑能为公司带来价值的功能。
02 我们的平台如何运行
转眼到了2023年7月。我们平台日益壮大,不断优化协同工作,使每个人的工作都更加便捷。开发一项全新的服务时,产品工程师需要复制和管理基础架构的资源库,并运行make
命令:
$ git checkout -b overnight
$ make create-team spaces
该命令提供了一个目录,产品工程师团队将在该目录中编辑一个配置文件来启动新服务。该文件允许配置服务级别目标(SLO)和数据库集群大小等内容:
locals {projects = {overnight = {description = "Calculate the balances' CDI-indexed yield."repository = {bootstrap = truelanguage = "kotlin"}service = {bootstrap = true}monitoring = {bootstrap = trueerror_rate = "0.5"read_latency = "0.4"write_latency = "4"apdex = "0.94"}database = {bootstrap = trueinstance = "db.r6g.large"replicas = 4engine = "aurora-postgresql"}queues = {bootstrap = truenames = ["invoice-paid", "yield-calculated"]}logs = {archive = true}cdn = {bootstrap = false}}}
}
然后,产品工程师提交文件并创建拉取请求,由平台团队进行审核。我们制定了一项政策来防止微服务失败。作为领域驱动设计(Domain-Driven Design)的实践者,我们更倾向于围绕有边界的上下文设计服务,只有经过深思熟虑后才能拆分服务,平台团队才会审查是否符合政策。
拉取请求获得批准后,就会触发编排。编排:
- 在 AWS IAM 中为应用程序创建服务用户。
- 创建启用水平和垂直 pod 自动分级器的 Kubernetes 服务。
- 将API 网关与 Kubernetes 入口集成,以公开服务的 API。
- 在 AWS RDS 中创建 Aurora集群,并启用水平自动扩展。
- 创建消息队列/主题。
- 创建与内容分发网络(CDN)集成的 S3 目录。
- 在机密管理系统中存储凭证(API 密钥、数据库密码等)。
- 使用 service chassis 引导新服务代码库。
- 在BitBucket 中创建新的服务存储库。
- 配置 BitBucket 资源库变量。
- 创建 CI/CD 流水线。
- 配置质量和安全网关。
- 运行服务的首次部署。
- 在 Datadog 中创建服务级别目标 (SLO)。
- 在 Datadog中配置服务警报。
- 运行健康检查以确保供应工作。
这里的 service chassis 是一个基于 Ktor 的框架,其封装了细致的开源库和内部开发的组件,以减少对持久性和日志记录等问题的认知负荷。
配置完成后,产品工程师会克隆存储库并开始开发新服务。service chassis 会自动生成 Docker Compose 文件,来支持本地开发。所有项目共享相同的引导结构,方便产品工程师在项目间切换。此外,更少的可变性意味着平台工程师在增强工程团队使用的底层工具时,可以加快优化周期。
产品工程师可以在早期监控应用性能,并排除查询速度慢和 Apdex 分数低等问题。警报会自动配置并发送到标准化的 Slack 频道(每个团队都有一套标准的 Slack 频道,其中一个专门用于可观察性警报)。
为了微调可用算力,产品工程师可以更新配置文件,增加运行服务所需的 Kubernetes pod 数量。从产品工程师的角度来看,建立生产环境的唯一方法就是通过这个抽象了底层云基础设施的配置文件。这就保证了对生产环境的所有更改都会受到全面的审计。
同时,我们还有一系列需要人工操作但必须遵循准则或标准的工作。有些指南是在同行评审过程中产生的,比如 API 设计评审。我们努力从语义上设计 API。目前我们团队遵循设计文档中的规则。
强调文档的重要性也是我们一直在做的事。文档记录了产品工程师工作中最关键的部分,包括整个软件开发生命周期(SDLC)。例如,产品工程师可以通过阅读文档了解如何创建新凭证并将其机密存储在我们的云环境中,或者如何调整警报阈值。自动化流程和文档是自助服务平台的基础。
当新人加入我们的团队时,identity provider 会发送一封欢迎电子邮件,告知如何激活笔记本电脑。当然笔记本电脑是已完全配置好的,并更新到最新版本的操作系统和开发工具。软件工程师需要做的唯一工作就是克隆资源库,并运行 README 文件中提供的启动脚本(通常是 Docker Compose 命令)。
为了让我们基于网络的工具(如 Rancher、Datadog、Jira、Confluence、1Password、Slack)随时可用,我们使用 Okta 来提供身份管理。只需一个密码,用户就能解锁笔记本电脑并访问所有工具。将我们的平台范围扩大到访问管理和笔记本电脑设置等问题,有助于我们实施工程范围内的变更,如滚动工具更新和增强,而无需中断人员手动运行设置向导。
03 总结
N26 的平台工程之路漫长而曲折。从定制的协调工具到手动流程的标准化,我们致力于让开发人员在 Day1 就能够轻松部署。在此过程中,我们消除了对第三方基础设施的依赖,从而使应用程序接口延迟大大降低。最终开发人员生产率大幅提高。
通过构建内部平台,我们深刻体会到战略性地管理平台的重要性。除了充足的人力、时间和资金,还需要建立许多其他要素。其中,最重要的是产品思维,可以利用现有工具,特别是开源工具,来缩小开发范围;要关注开发者体验以及公司的目标成果。