持续交付:云原生应用的“十二要素”

2012 年年初,PaaS 先驱 Heroku 发布了 Twelve-Factor App,这是一组帮助开发人员构建 PaaS 应用程序的规则和指南 :
  • 使用声明式格式来建立自动化,最大限度减少新人加入项目的时间和成本。
  • 与底层操作系统形成一个干净的合约,为在不同执行环境之间移植提供最大的可能。
  • 适于部署在现代的云平台上,最大限度减少对服务器和系统管理的需要。
  • 最大限度地减少开发环境和生产环境之间的差异,使持续部署实现最大的灵活性。
  • 可以水平扩展,但不需要对工具、架构或者开发方式进行重大改变。

独立系统的架构原则

“独立系统的架构原则”(https://isa-principles.org/)与“十二要素应用”密切相关,但前者更注重架构方面。这些原则基于微服务,尤其是自包含系统(SCS)的经验,是一组最佳实践的集合。

现在让我们简要介绍一下这些要素,看看它们如何用来持续部署 Java 应用程序。

  1. 代码库 :在版本控制系统中跟踪的一个代码库,会被多次部署 应该将每个 Java 应用程序(或者服务)的代码,存储在一个共享的代码仓库中。部署所使用的配置文件(例如,脚本、Dockerfile 和 Jenkinsfile)也应当与应用程序代码存储在一起。
  2. 依赖关系 :显式声明并隔离依赖项 我们通常会使用构建工具(例如,Maven 或 Gradle)来管理 Java 应用程序中的依赖关系,并且应该在虚拟机(VM)镜像清单、Dockerfile 或者无服务器架构的配置文件中,明确指出操作系统方面的依赖项。
  3. 配置 :在环境中存储配置 Twelve-Factor App 建议通过环境变量将配置信息注入应用程序。实际上,许多 Java 开发人员更喜欢使用配置文件来管理这些变量,而且在构建包含密码的 VM 或者容器时,通过环境变量来指定密码可能会存在潜在的安全问题。 将非敏感的配置数据存储在 Spring Cloud Config(基于 Git 或 Consul)等远程服务中,并且将密码存储在 HashiCorp 的 Vault 服务中,既可以满足 Twelve-Factor 的建议,也符合当前的最佳实践。
  4. 支持服务 :将支持服务视为一种附加资源(一般通过网络进行调用) 在构建管道的组件测试中,Java 开发人员习惯用这种方式来代替数据存储和中间件,例如,使用内存数据库(例如,HSQLDB、Apache Qpid 和 Stubbed Cassandra)或者服务虚拟化(例如,Hoverfly 和 WireMock)框架。
  5. 构建、发布、运行 :严格分离构建和运行阶段 对于 Java 等编译语言,这条规范显而易见(并且几乎没有其他方式可选择)。值得一提的是,VM 和容器技术的灵活性意味着,你可以使用合理配置的单独构件来构建、测试和运行应用程序。例如,你可以使用一个完整的 OS、JDK 以及诊断工具,创建一个用于构建和测试的部署构件,也可以在生产环境中仅用 OS 和 JRE 创建一个运行应用程序的构件。 但是,我们将此视为一种反模式,因为只应该有一个构件进入构建管道,即“单一事实来源”。使用多个构件很容易导致开发环境和生产环境的配置不同,当发生问题时难以调试。
  6. 进程 :将应用程序作为一个或多个无状态的进程执行 借助 VM 镜像、容器镜像或者无服务器函数等技术,可以让构建和运行 Java 微服务应用程序变得更加容易。
  7. 端口绑定 :通过端口绑定暴露服务 Java 开发人员习惯通过端口暴露应用程序的服务(例如,在 Jetty 或 Apache Tomcat上运行一个应用程序)。
  8. 并发 :通过进程模型进行伸缩 传统的 Java 应用程序通常会采用相反的方式,因为运行中的 JVM 就像是一个巨大的“超级进程”,通常会通过添加更多堆内存来实现垂直伸缩,或者通过镜像和负载均衡来实现多个实例的水平伸缩。不过,将 Java 应用程序分解为微服务,然后在VM、容器或无服务器函数中运行这些服务,也可以实现可伸缩性。无论采用何种方法来实现可伸缩性,都应该在构建管道中进行测试。
  9. 可处置性 :通过快速启动和优雅关闭保证最大的稳定性 对于习惯于创建传统的、长期运行的 Java 应用程序的开发人员而言,这可能需要转变观念,因为大部分应用程序的配置和初始化,都是在 JVM 或者应用程序启动过程中预先加载的。但是现代化的、容器化的应用程序会使用更多的即时(JIT)配置,确保应用程序在关闭期间尽最大努力清理资源和状态。
  10. 开发 / 生产环境差异 :尽可能保持开发、预发布和生产环境一致 与传统的物理机部署相比,将 VM 或容器技术与 VMware、Kubernetes 和 Mesos 等编排技术结合使用,可以降低开发环境和生产环境之间的不同,因为在物理机器环境中,开发或测试机器的底层硬件和操作系统配置可能会有显著差异。 随着构件在构建管道中的不断构建,它会越来越接近真实的环境(例如,单元测试可以在一个内存的沙箱中运行)。但是,端到端测试应该尽可能在类似生产环境的环境中进行。
  11. 日志 :将日志视为事件流 Java 与日志框架之间存在着长期、重要的关系,但是像 Logback 和 Log4j2 等现代的日志框架,已经可以将日志流式传输到标准输出或磁盘上。
  12. 管理进程 :一次性运行管理任务 因为容器和无服务器函数可以非常简单地运行 Java 应用程序,所以管理任务可以一次性运行。但是,它们也必须在构建管道内(或者作为其中的一部分)进行测试。

十二要素应用的各项原则,说明了在设计系统时,不仅要了解背后的部署方式,还要积极地利用它。接下来,我们要介绍与此密切相关的一个话题 — 机械同情心。

培养机械同理心

Martin Thompson 和 Dave Farley 多年来一直在讨论软件开发的“机械同理心”。他们的灵感来自一级方程式赛车手 Jackie Stewart 的名言,“要想成为一名赛车手,你不必成为一名工程师,但你必须有机械同理心。”了解赛车的运作方式会让你成为更好的赛车手,同样,程序员如果理解计算机硬件的工作原理也会有助于编程。你不一定需要获得计算机科学学位或者成为一名硬件工程师,但是你确实需要了解硬件的工作原理,并在设计软件时考虑这一点。

架构师坐在象牙塔里绘制 UML 图的日子已经结束。架构师和开发人员必须通过新的技术,不断增加实践经验和运维经验。PaaS、CaaS 和函数已经从根本上改变了软件与硬件交互的方式。实际上,许多现代的 PaaS 平台和基于函数的解决方案,背后都使用了容器技术来提供进程隔离,所以你应该了解以下这些变化 :

  • PaaS 和容器技术可以限制对系统资源的访问,避免开发、运维操作或者资源竞争导致资源耗尽。
  • 容器技术(偶尔)会将不正确的可用资源暴露给 JVM(例如,通常暴露给容器化 JVM 应用程序的处理器核心数量,是底层主机的硬件属性,而不是该容器的数量限制)。
  • 在运行 PaaS 时,通常会在操作系统上附加一层抽象层(例如,编排框架、容器技术,以及一个附加的 OS)。
  • 与传统的部署平台相比,PaaS 平台和容器编排、调度框架通常会更频繁地停止、启动和移动容器(以及应用程序)。
  • 公有云 PaaS 和容器平台的硬件结构,天生会更为短暂。
  • 容器化和无服务器应用程序,会出现新的安全攻击漏洞,必须加以关注和处理。

开发人员不应该对这些部署结构的变化感到惊讶,因为新技术自然会带来一些变化(例如,升级应用程序的 JVM 版本、在容器中部署 Java 应用程序,以及在云计算平台上运行 Java 应用程序)。通过加强 CD 构建管道中的测试过程,可以预防绝大多数潜在的问题。

面向失败的设计和持续测试

云计算为开发人员提供了绝佳的机会,十年前,我们只能梦想像现在一样,点一下按钮就可以将硬件组装起来。但这种基础设施也带来了新的挑战。由于云计算具有网络化、成本低廉、规模巨大等特点,所以云平台的性能问题和故障也是不可避免的。

云平台中的绝大多数 I/O 操作都通过网络进行。例如,看上去是本地的弹性块存储通常由一个存储区域网络(SAN)提供,性能特征差异很大。如果你在本地计算机上开发一个应用程序,包含三个频繁访问数据库的服务,那么你就会发现,同样是访问 localhost回环网络和直接访问 SSD 块存储的操作,本地环境和云平台上的性能差异很大。这甚至可以决定一个项目的成败。

大多数云计算的基础设施天生就是短暂的,与本地环境的硬件相比,也更容易出现故障。结合我们大多数人在设计分布式系统时所遵循的原则,你必须设计出能够容忍服务消失或者被重新部署的系统。当许多开发人员在考虑如何测试这类故障时,脑海中都会浮现出 Netflix Simian Army 和 Chaos Monkeys 工具。但是,这类测试通常是在生产环境中进行的。在开发 CD 构建管道时,你还需要实现一个有限的但是功能相当的混乱测试,只不过在故障类型和效果方面更加可控。

松耦合的系统通常更容易测试,因为你可以更容易地隔离组件,而高内聚有助于在修复错误时,降低寻找问题原因的难度。本节要介绍的主要内容是,持续交付管道必须尽快在实际环境(类似生产环境)中进行部署和测试,并且必须可以模拟和测试性能和故障场景。

本文节选自新书《Java 持续交付》。持续交付为业务的持续发展和整个软件的交付周期增加了巨大的价值,但是掌握这项技术就意味着普通开发人员要跳出舒适区,学习很多新技能。本书以实践为基础,通过大量的实践指导,帮助Java开发人员掌握针对不同平台的架构设计、自动化质量保证, 以及打包和部署的技巧。左下阅读原文,掀开名著面纱——“持续精进,交付经典”,前沿技墅不变的承诺!

内容简介本书完整介绍了Java软件开发的整个生命周期,还结合大量的成功实践经验,介绍了每个阶段可能会使用到的工具和技能。在基础设施已经极大完善的今天,本书还与时俱进地介绍了如何在Docker、Kubernetes、Cloud、FaaS等新兴环境下进行持续集成和持续交付。

本文分享自微信公众号 - 前沿技墅(Edge-Book)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-11-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券