learn-tech/专栏/周志明的架构课/50_应用为中心的封装(上):Kustomize与Helm.md
2024-10-16 06:37:41 +08:00

14 KiB
Raw Blame History

                        因收到Google相关通知网站将会择期关闭。相关通知内容
                        
                        
                        50 _ 应用为中心的封装Kustomize与Helm
                        你好,我是周志明。

在理解了前面几节课所讲的容器技术发展的历程之后,不知你会不会有种“套娃式”的迷惑感?

现在你已经知道容器的崛起缘于chroot、namespaces、cgroups等内核提供的隔离能力而系统级的虚拟化技术使得同一台机器上互不干扰地运行多个服务成为了可能 为了降低用户使用内核隔离能力的门槛随后出现了LXC它是namespaces、cgroups特性的上层封装这就让“容器”一词真正走出了实验室开始走入工业界进行实际应用 然后为了实现跨机器的软件绿色部署出现了Docker最初是LXC的上层封装彻底改变了软件打包分发的方式迅速被大量企业广泛采用 而为了满足大型系统对服务集群化的需要又出现了Kubernetes最初是Docker的上层封装从而使得以多个容器共同协作构建出健壮的分布式系统成为了今天云原生时代的技术基础设施。

那这样你的疑惑可能也就出现了Kubernetes会是容器化崛起之路的终点线吗它达到了人们对云原生时代技术基础设施的期望了吗

首先从能力角度来看可以说是的。Kubernetes被誉为云原生时代的操作系统自诞生之日起它就因为出色的管理能力、扩展性和以声明代替命令的交互理念收获了无数喝彩声。

但是从易用角度来看坦白说差距还非常大。云原生基础设施的其中一个重要目标是接管掉业务系统复杂的非功能特性这会让业务研发与运维工作变得足够简单不受分布式的牵绊。然而Kubernetes被诟病得最多的就是复杂它从诞生之日起就因为陡峭的学习曲线而出名。

我举个具体例子吧。如果要用Kubernetes部署一套Spring Cloud版的Fenixs Bookstore你需要分别部署一个到多个的配置中心、注册中心、服务网关、安全认证、用户服务、商品服务、交易服务然后要对每个微服务都配置好相应的Kubernetes工作负载与服务访问为每一个微服务的Deployment、ConfigMap、StatefulSet、HPA、Service、ServiceAccount、Ingress等资源都编写好元数据配置。

这个过程最难的地方不仅在于繁琐,还在于要想写出合适的元数据描述文件,你既需要懂开发(网关中服务调用关系、使用容器的镜像版本、运行依赖的环境变量等等这些参数,只有开发最清楚),又需要懂运维(要部署多少个服务、配置何种扩容缩容策略、数据库的密钥文件地址等等,只有运维最清楚),有时候还需要懂平台(需要什么样的调度策略、如何管理集群资源,通常只有平台组、中间件组或者核心系统组的同学才会关心),一般企业根本找不到合适的角色来为它管理、部署和维护应用。

这个事儿Kubernetes心里其实也挺委屈因为以上提到的复杂性不能说是Kubernetes带来的而是分布式架构本身的原罪。

对于大规模的分布式集群来说无论是最终用户部署应用还是软件公司管理应用都存在着像前面提到的这诸多痛点。这些困难的实质是源于Docker容器镜像封装了单个服务而Kubernetes通过资源封装了服务集群却没有一个载体真正封装整个应用这就使得它会把原本属于应用内部的技术细节给圈禁起来不暴露给最终用户、系统管理员和平台维护者而让使用者去埋单。

那么如此所造成的应用难以管理的矛盾,就在于封装应用的方法没能将开发、运维、平台等各种角色的关注点恰当地分离。

但是,既然在微服务时代,应用的形式已经不再局限于单个进程,那就也该到了重新定义“以应用为中心的封装”这句话的时候了。至于具体怎样的封装才算是正确的,其实到今天也还没有特别权威的结论,不过经过人们的尝试探索,已经能够窥见未来容器应用的一些雏形了。

所以接下来,我会花两节课的时间,给你介绍一下最近几年容器封装的两种主流思路,你可以从中理解容器“以应用为中心的封装”这个理念在不同阶段的内涵变化,这也是对“应用”这个概念的不断扩展升华的过程。

今天这节课呢我们就先来了解下Kustomize和Helm它们是封装“无状态应用”的典型代表。

额外知识:无状态应用与有状态应用的区别- 无状态应用Stateless Application与有状态应用Stateful Application说的是应用程序是否要自己持有其运行所需的数据如果程序每次运行都跟首次运行一样不依赖之前任何操作所遗留下来的痕迹那它就是无状态的反之如果程序推倒重来之后用户能察觉到该应用已经发生变化那它就是有状态的。- 下一讲要介绍的Operator与OAM就是支持有状态应用的封装方式这里你可以先了解一下。

Kustomize

最初由Kubernetes官方给出的“如何封装应用”的解决方案是“用配置文件来配置文件”这不是绕口令你可以把它理解为是一种针对YAML的模版引擎的变体。

Kubernetes官方认为应用就是一组具有相同目标的Kubernetes资源的集合如果逐一管理、部署每项资源元数据太麻烦啰嗦的话那就提供一种便捷的方式把应用中不变的信息与易变的信息分离开以此解决管理问题把应用所有涉及的资源自动生成一个多合一All-in-One的整合包以此解决部署问题。

而完成这项工作的工具就叫做Kustomize它原本只是一个独立的小程序从Kubernetes 1.14起被纳入了kubectl命令之中成为随着Kubernetes提供的内置功能。Kustomize使用Kustomization文件来组织与应用相关的所有资源Kustomization本身也是一个以YAML格式编写的配置文件里面定义了构成应用的全部资源以及资源中需根据情况被覆盖的变量值。

Kustomize的主要价值是根据环境来生成不同的部署配置。只要建立多个Kustomization文件开发人员就能以基于基准进行派生Base and Overlay的方式对不同的模式比如生产模式、调试模式、不同的项目同一个产品对不同客户的客制化定制出不同的资源整合包。

在配置文件里无论是开发关心的信息还是运维关心的信息只要是在元数据中有描述的内容最初都是由开发人员来编写的然后在编译期间由负责CI/CD的产品人员针对项目进行定制。最后在部署期间由运维人员通过kubectl的补丁Patch机制更改其中需要运维去关注的属性比如构造一个补丁来增加Deployment的副本个数构造另外一个补丁来设置Pod的内存限制等等。

k8s ├── base │ ├── deployment.yaml │ ├── kustomization.yaml │ └── service.yaml └── overlays └── prod │ ├── load-loadbalancer-service.yaml │ └── kustomization.yaml └── debug └── kustomization.yaml

从上面这段目录结构中我们可以观察到一个由kustomize管理的应用结构它主要由base和overlays组成。Kustomize使用Base、Overlay和Patch生成最终配置文件的思路与Docker中分层镜像的思路有些相似这样的方式既规避了以“字符替换”对资源元数据文件的入侵也不需要用户学习额外的DSL语法比如Lua

从效果来看使用由Kustomize编译生成的All-in-One整合包来部署应用是相当方便的只要一行命令就能够把应用涉及的所有服务一次安装好在“探索与实践”小章节中会介绍的Kubernetes版本和Istio版本的Fenixs Booktstore都使用了这种方式来发布应用你也不妨实际体验一下。

但是毕竟Kustomize只是一个“小工具”性质的辅助功能对于开发人员来说Kustomize只能简化产品针对不同情况的重复配置它其实并没有真正解决应用管理复杂的问题要做的事、要写的配置最终都没有减少只是不用反复去写罢了而对于运维人员来说应用维护不仅仅只是部署那一下应用的整个生命周期除了安装外还有更新、回滚、卸载、多版本、多实例、依赖项维护等诸多问题都很麻烦。

所以说要想真正解决这些问题还需要更加强大的管理工具比如下面我要介绍的主角Helm。不过Kustomize能够以极小的成本在一定程度上分离了开发和运维的工作不用像Helm那样需要一套独立的体系来管理应用这种轻量便捷本身也是一种可贵的价值。

OK下面我们就来具体讲讲Helm。

Helm与Chart

Helm是由Deis公司开发的一种更具系统性的管理和封装应用的解决方案它参考了各大Linux发行版管理应用的思路应用格式是Chart。

Helm一开始的目标就很明确如果说Kubernetes是云原生操作系统的话那Helm就要成为这个操作系统上面的应用商店与包管理工具。

我相信Linux下的包管理工具和封装格式如Debian系的apt-get命令与dpkg格式、RHEL系的yum命令与rpm格式你肯定不会陌生。有了包管理工具你只要知道应用的名称就可以很方便地从应用仓库中下载、安装、升级、部署、卸载、回滚程序而且包管理工具掌握着应用的依赖信息和版本变更情况具备完整的自管理能力每个应用需要依赖哪些前置的第三方库在安装的时候都会一并处理好。

Helm模拟的就是这种做法它提出了与Linux包管理直接对应的Chart格式和Repository应用仓库另外针对Kubernetes中特有的一个应用经常要部署多个版本的特点也提出了Release的专有概念。

Chart用于封装Kubernetes应用涉及到的所有资源通常是以目录内的文件集合的形式存在的。目录名称就是Chart的名称没有版本信息比如官方仓库中WordPress Chart的目录结构是这样的

WordPress ├── templates │ ├── NOTES.txt │ ├── deployment.yaml │ ├── externaldb-secrets.yaml │ └── 版面原因省略其他资源文件 │ └── ingress.yaml └── Chart.yaml └── requirements.yaml └── values.yaml

其中有几个固定的配置文件Chart.yaml给出了应用自身的详细信息名称、版本、许可证、自述、说明、图标等等requirements.yaml给出了应用的依赖关系依赖项指向的是另一个应用的坐标名称、版本、Repository地址values.yaml给出了所有可配置项目的预定义值。

可配置项就是指需要部署期间由运维人员调整的那些参数它们以花括号包裹在templates目录下的资源文件中。当部署应用时Helm会先将管理员设置的值覆盖到values.yaml的默认值上然后以字符串替换的形式传递给templates目录的资源模版最后生成要部署到Kubernetes的资源文件。

由于Chart封装了足够丰富的信息所以Helm除了支持命令行操作外也能很容易地根据这些信息自动生成图形化的应用安装、参数设置界面。

我们再来说说Repository仓库。它主要是用于实现Chart的搜索与下载服务Helm社区维护了公开的Stable和Incubator的中央仓库界面如下图所示也支持其他人或组织搭建私有仓库和公共仓库并能够通过Hub服务把不同个人或组织搭建的公共仓库聚合起来形成更大型的分布式应用仓库这也有利于Chart的查找与共享。

Helm Hub商店

所以整体来说Helm提供了应用全生命周期、版本、依赖项的管理能力同时Helm还支持额外的扩展插件能够加入CI/CD或者其他方面的辅助功能。

如此一来它的定位就已经从单纯的工具升级到应用管理平台了强大的功能让Helm收到了不少支持有很多应用主动入驻到官方的仓库中。而从2018年起Helm项目被托管到CNFC成为其中的一个孵化项目。

总而言之Helm通过模仿Linux包管理器的思路去管理Kubernetes应用在一定程度上确实是可行的。不过在Linux与Kubernetes中部署应用还是存在一些差别最重要的一点是在Linux中99%的应用都只会安装一份而Kubernetes里为了保证可用性同一个应用部署多份副本才是常规操作。

所以Helm为了支持对同一个Chart包进行多次部署每次安装应用都会产生一个ReleaseRelease就相当于该Chart的安装实例。对于无状态的服务来说靠着不同的Release就已经足够支持多个服务并行工作了但对于有状态的服务来说服务会与特定资源或者服务产生依赖关系比如要部署数据库通常要依赖特定的存储来保存持久化数据这样事情就变得复杂起来了。

既然Helm无法很好地管理这种有状态的依赖关系那么这一类问题就是Operator要解决的痛点了。这也是我在下一节课要给你重点介绍的工具。

小结

今天我给你介绍了两种比较常用也较为具体的应用封装方式分别是Kubernetes官方推出的Kustomize以及目前在Kubernetes上较为主流的“应用商店”格式Helm与Chart。这样的封装对于无状态应用已经足够了但对于有状态应用来说仍然不能满足需要。

在下节课,我们将继续应用封装这个话题,一起来探讨如何为有状态应用提供支持。

一课一思

你是否尝试过在Kubernetes中部署一些需共享状态的集群应用比如Etcd、Easticsearch等等你是自己编写YAML定义它们所需的各种资源的吗

欢迎在留言区分享你的答案。如果你觉得有收获,也欢迎把今天的内容分享给更多的朋友。感谢你的阅读,我们下一讲再见。