1.3 常见的声明式系统
本节介绍常见的声明式系统,例如用于持续部署的Ansible、“代码即基础设施”的典型代表Terraform,以及在云原生背景下大获成功的Kubernetes。
这3种典型的声明式系统有共性,也有各自的独特之处,学习它们有助于我们进一步理解声明式范式在软件领域的最佳实践。
1.3.1 Kubernetes
Kubernetes是用于管理和编排容器化应用程序的系统,最初由Google开发并开源。它是一个平台,旨在为应用提供可预测性、可伸缩性及高可用性的方法来治理容器化的应用程序。
Kubernetes的使用者可以定义应用程序的运行方式及应用与外界暴露的方式,可以放大或缩小服务,执行滚动更新,并在不同程序版本之间切换流量或回滚有问题的部署。
Kubernetes控制面(Control Plane)主要由以下部分组成。
• etcd:存储配置数据,并用于服务发现。
• kube-apiserver:主节点上负责提供Kubernetes API服务的组件,是Kubernetes控制面的前端。
• kube-scheduler:负责将工作负载调度到符合要求的节点。
• kube-controller-manager:控制器组件,包括节点控制器(Node Controller)、副本控制器(Replication Controller)、端点控制器(Endpoints Controller)、服务账户和令牌控制器(Service Account & Token Controllers)。
• cloud-controller-manager:负责与不同的基础架构提供商进行交互,使Kubernetes可以从云提供商处收集信息并调整云资源。
Kubernetes的节点组件由以下部分组成。
• kubelet:运行在集群中的每个节点上,与控制面板进行通信并维护当前节点的工作负载状态。
• kube-proxy:集群中每个节点上运行的网络代理,负责维护节点上运行Pod的网络规则。
• 容器运行时(Container Runtime):负责运行容器的软件,支持Docker、containerd、cri-o、rktlet等。
Kubernetes的最小调度单位是Pod,以下声明式清单(Manifest)文件描述了如何创建一个包含Nginx容器的Pod,并对外开放80端口。
Kubernetes使用YAML语法来描述声明式清单文件,通过声明式描述可以创建多种工作负载类型,例如Deployment、DaemonSet、StatefulSet等。
Kubernetes使用声明式描述对使用者屏蔽了其内部复杂的实现方式,例如使用replicas关键字定义期望工作负载Pod的数量,Kubernetes便能够根据当前Pod的数量自动调谐,最终达到期望状态。在循环控制的过程中,使用者不需要关心被扩容的Pod调度在哪个节点、节点宕机后如何重新调度、节点是否满足Pod的资源需求等问题,因为在声明式描述中,使用者只关心最终结果,即Pod的数量。
1.3.2 Terraform
Terraform是一个IT基础架构自动化编排工具,旨在实现“代码即基础设施”的思想,允许使用声明式配置文件来创建基础设施,例如AWS EC2、S3 Bucket、Lambda、VPC等。
它主要有以下特点。
• 基础架构即代码(Infrastructure as Code):使用HCL高级配置语法描述基础架构,使基础设施可以像代码一样进行版本控制、共享和重用。
• 执行计划(Execution Plans):生成执行计划的步骤并显示,有效避免人为误操作。
• 资源图表(Resource Graph):生成所有资源的拓扑结构和依赖关系,确保被依赖的资源优先执行,并以并行的方式创建和修改依赖,保证资源高效地执行。
• 变更自动化(Change Automation):当模板中的资源发生变化时,Terraform会生成新的资源拓扑图,在确认无误后,我们只需要一个命令即可自动化完成变更操作,避免人为误操作。
下面来看一个简单的例子,使用声明式配置文件创建AWS S3。
以上配置文件使用aws provider(基础设施提供商),创建了一个名为my-tf-test-bucket的私有S3存储桶及对tags定义了Name和Environment标签。
最后,运行terraform plan和terraform apply命令对声明式配置文件生成执行计划和创建资源。
使用Terraform HCL语法进行声明式的配置,还可以实现对其他类型的基础设施的创建和修改,将原来需要在控制台进行的操作变更为对代码的编写和定义,实现了“代码即基础设施”。
1.3.3 Ansible
Ansible是使用Python开发的自动化运维工具,其核心原理是将声明式的YAML描述文件转化成Python脚本上传至服务器端运行,实现自动化工作。
Ansible主要包含以下结构。
• 模块:由不同功能的自动化脚本组成。
• 模块程序:与模块不同,当多个模块使用相同的代码时,Ansible将这些功能存储为模块实用程序,以最大程度地减少重复和维护工作,模块程序只能用Python或PowerShell编写。
• 插件:提供Ansible增强能力,也可以编写自定义插件。
• 清单:一组需要被管理的远程服务器,例如IP或域名。
• Playbooks:声明式自动化编排脚本。
和直接编写Python脚本不同,Ansible通过将各种底层能力封装为模块及声明式Playbooks脚本编排,同时支持自定义插件的能力。
例如,以下Playbooks声明了将本地Jar包上传至远程服务器,并终止当前运行的Java进程,最后运行新的Jar程序包。
在声明式的Playbooks中,可以将需要执行的部署行为转变为简单地声明对应模块,并提供参数,例如使用copy模块上传文件,使用shell模块运行命令,使用wait_for模块运行等待。与传统的编写shell部署脚本相比,Playbooks通过模块封装降低了书写难度,同时使部署脚本变得标准化。