2.1 迁移至云原生与混合云的挑战
企业迁移至云原生并不是一蹴而就的,而是渐进式地迁移。在迁移的过程中会出现开发、测试和生产环境的基础设施不一致,甚至不同产品线的生产环境基础设施不一致的情况。
为了避免云提供商锁定问题,企业一般会选择不同的云供应商合作以降低风险,这导致企业倾向于采用混合云作为运行工作负载的基础设施。出于高可用的需求,企业往往还需要考虑异地多活的架构,这就使混合云架构变得极其复杂。最终,当生产环境流量激增,需要基础设施能够做到自动伸缩时,这项要求成为压垮混合云架构可维护性的“最后一根稻草”。
除此之外,如何对不同基础环境(如虚拟机、Kubernetes集群及混合云)的凭据进行安全管理也是一大挑战。
2.1.1 凭据管理
迁移至云原生和混合云首先需要考虑的问题便是如何在云中管理凭据。
一个普遍的笑话是“云只是别人的服务器”,即便是在私有云上,存储敏感数据时仍然需要格外注意安全问题,更何况是使用云提供商提供的共享云资源。
云提供商一般会提供身份和访问管理(IAM),并将角色分配到特定的计算资源上,这意味着它们能够安全地访问云资源而不需要管理员级的凭据,但IAM非常容易被盗,并且难以审核用户操作。
此外,数据库密码、Git令牌、虚拟机凭据、集群证书都应该进行加密存储,敏感的客户数据(例如密码、身份信息)也同样应该加密,用于解密的秘钥应该定期更换。
因此,统一化的凭据管理就显得非常重要。在Kubernetes系统中,可以使用Secrets存储敏感信息,并将这些信息通过“挂载”的方式分配给容器。另外,HashiCorp开源的Vault项目也是一个较为流行的秘钥和证书管理方案。
不管选择哪种存储凭据的方案,都应该考虑其与软件交付流程的集成问题,特别是对持续部署来说,不同的环境和平台都需要对应的权限来部署微服务。
2.1.2 多云架构
为了避免云提供商锁定及增强冗余的问题,企业倾向于采用多云架构来提供软件运行的基础设施。
在不同的云之间,同一种类型的产品命名、产品能力、操作逻辑可能会有一定的差异,在集成持续部署时,尤其要注意身份和访问管理及VPC(虚拟私有网络)之间的区别。
在多云架构中,对于网络层的管理是最复杂的,这主要体现在不同的云供应商之间在基础设施层面提供的网络策略不尽相同,例如常见设施级的安全组、防火墙等。此外,可能还存在应用层面的网络策略(Network Policy),例如Kubernetes的网络策略、Linux防火墙等。如何将这些网络策略抽象并统一,是网络管理的核心。
采用多云架构虽然解决了锁定和冗余的问题,但也使网络管理更加复杂。
2.1.3 跨地域部署
出于高可用的要求,云提供商倾向于将其基础设施部署到不同的地域和可用区。可用区一般是在同一地域但物理隔离的数据中心,不同的可用区组成一个地域,地域可能是不同的地区或国家。
跨地域部署也意味着多地域部署,在理想的场景下,当某个地域出现故障时,那么只会使距离这个地域最近的用户访问比原来慢一些,因为接受它们请求的地域离它们更远了。而事实并非如此,当这种情况出现时,其他区域是否有足够的计算资源冗余,缓存和数据库是否能够抵挡流量的瞬间激增,这都是需要考虑的问题。一旦某一个环节产生瓶颈,便会产生雪崩效应,最终导致生产环境停机。
此外,多地域的数据同步、强一致性要求、资源预留的问题都无法立刻解决,所以在生产实践中,如果应用规模还未达到需要跨地域部署的程度,那么建议采用多可用区的部署方式。在不同的可用区之间,网络延迟、数据同步和强一致性都能得到满足,同时云提供商的自动伸缩组件一般能在不同的可用区之间工作,在业务高峰期可以无缝自动扩容。
需要注意的是,在不同可用区部署服务时,每个服务的最小实例数(计算资源)应至少是可用区数量的2倍,这样可以确保当某个可用区宕机时,服务仍然能够稳定运行。
2.1.4 自动伸缩
自动伸缩(或弹性计算)是云原生的基础。例如,如果虚拟机或Kubernetes集群的宿主机发生故障,则应该进行自动迁移并更换实例。自动伸缩通过动态维护计算资源匹配当前工作负载的压力,在流量发生变化时进行扩容或者缩小集群实例数。这对有明显周期性流量变化的应用具有重要意义,因为应用不再需要为潜在的流量峰值冗余大量的计算资源,显著降低了云原生的使用成本。
自动伸缩的触发需要条件,简单的伸缩策略可能是某个系统级指标,例如集群平均CPU使用率或内存使用率;但对某些应用来说,CPU使用率上升并不意味着服务质量的下降,这取决于工作负载。一个更好的方案是通过同时对CPU使用率和请求响应时间进行对比来反映两者的关系,并得到符合响应时间要求的CPU使用率。
除了触发策略,另一个容易忽视的问题是应用的冷启动时间。当流量激增时,好的触发策略能够立即感知系统负载,并进行自动伸缩。如果应用的冷启动时间过长,会导致自动伸缩效率变低、失效甚至宕机。一种更好的伸缩机制是基于近期的流量规律进行自动预测,在峰值来临前对这些启动缓慢的应用提前扩容。
2.1.5 不可变的基础设施和部署制品
不可变的基础设施由微软工程师Chad Fowler在2013年提出:不可变架构能够通过自动化和编程模式使应用程序具备稳定性、高效性和一致性。
现代应用在交付时,一般需要对源码进行编译,产生可执行文件。对脚本语言来说,可运行的代码即为交付物。这些交付物需要有稳定的运行环境,这就涉及OS、系统依赖和Runtime。随着应用规模的扩大,集群的服务器越来越多,为了提高研发效率,需要将环境进一步切分为开发环境、测试环境、预发布环境和生产环境。随着软件的不断迭代,对运行环境的即时修改越来越多,多个运行环境之间很难保持完全一致,软件的发布风险变得越来越高。
在这种背景下,不可变的基础设施的概念被提出。既然环境无法保持一致,那就把环境和部署制品同时打包为一个整体单元进行交付。这个单元包含了运行环境、系统依赖及最新的应用。
对传统的虚拟机部署来说,一种可行的方式是制作虚拟机镜像,并在镜像上添加要部署的应用程序。而在容器化技术出现之后,Docker镜像成为了新的不可变基础设施,通常情况下它们都具有不可变的特性。
2.1.6 服务发现
迁移的最后一个挑战是服务发现,服务发现是微服务在不断变化的拓扑结构中相互发现的方式。
对大多数采用Java语言开发的应用来说,服务发现约等于Eureka,它同样是由Netflix开源的,使用Java编写,提供服务发现、RPC、负载均衡、熔断、请求速率限制等功能。在迁移至云原生和混合云后,采用传统的虚拟机部署的方式仍然可行,但如果集成Kubernetes系统,那么原有的技术栈就略显复杂且重复。
Kubernetes自带服务发现的功能,它通过Service和Endpoint为一组应用提供稳定的访问方式,用户无须考虑Pod重建和漂移导致的IP变化。
此外,由云原生计算基金会(CNCF)托管的许多开源项目也能够满足服务发现的需求,例如Linkerd、Istio、Envoy,这些开源项目提供了与Eureka类似的功能。不同的是,它们都与应用的开发语言和环境无关。