编排的艺术| K8S 中的容器编排和应用编排
众所周知,Kubernetes 是一个容器编排平台,它有非常丰富的原始的 API 来支持容器编排,但是对于用户来说更加关心的是一个应用的编排,包含多容器和服务的组合,管理它们之间的依赖关系,以及如何管理存储。
在这个领域,Kubernetes 用 Helm 的来管理和打包应用,但是 Helm 并不是十全十美的,在使用过程中我们发现它并不能完全满足我们的需求,所以在 Helm 的基础上,我们自己研发了一套编排组件……
什么是编排?
不知道大家有没仔细思考过编排到底是什么意思? 我查阅了 Wiki 百科,了解到我们常说的编排的英文单词为 “Orchestration”,它常被解释为:
- 本意:为管弦乐中的配器法,主要是研究各种管弦乐器的运用和配合方法, 通过各种乐器的不同音色,以便充分表现乐曲的内容和风格。
- 计算机领域:引申为描述复杂计算机系统、中间件 (middleware) 和业务的自动化的安排、协调和管理。
有趣的是 “Orchestration” 的标准翻译应该为“编配”,而“编排”则是 另外一个单词 “Choreography”,为了方便大家理解, 符合平时的习惯,我们还是使用编排 (Orchestration) 来描述下面的问题。至于“编配 (Orchestration)” 和 “编排(Choreography)” 之争,这里有一篇文章,有兴趣可以看一下 。
编配和编排的定义之争Kubernetes 容器编排技术
当我们在说容器编排的时候,我们在说什么?
在传统的单体式架构的应用中,我们开发、测试、交付、部署等都是针对单个组件,我们很少听到编排这个概念。而在云的时代,微服务和容器大行其道,除了为我们显示出了它们在敏捷性,可移植性等方面的巨大优势以外,也为我们的交付和运维带来了新的挑战:我们将单体式的架构拆分成越来越多细小的服务,运行在各自的容器中,那么该如何解决它们之间的依赖管理,服务发现,资源管理,高可用等问题呢?
在容器环境中,编排通常涉及到三个方面:
- 资源编排 - 负责资源的分配,如限制 namespace 的可用资源,scheduler 针对资源的不同调度策略;
- 工作负载编排 - 负责在资源之间共享工作负载,如 Kubernetes 通过不同的 controller 将 Pod 调度到合适的 node 上,并且负责管理它们的生命周期;
- 服务编排 - 负责服务发现和高可用等,如 Kubernetes 中可用通过 Service 来对内暴露服务,通过 Ingress 来对外暴露服务。
在 Kubernetes 中有 5 种我们经常会用到的控制器来帮助我们进行容器编排,它们分别是 Deployment, StatefulSet, DaemonSet, CronJob, Job。
在这 5 种常见资源中,Deployment 经常被作为无状态实例控制器使用; StatefulSet 是一个有状态实例控制器; DaemonSet 可以指定在选定的 Node 上跑,每个 Node 上会跑一个副本,它有一个特点是它的 Pod 的调度不经过调度器,在 Pod 创建的时候就直接绑定 NodeName;最后一个是定时任务,它是一个上级控制器,和 Deployment 有些类似,当一个定时任务触发的时候,它会去创建一个 Job ,具体的任务实际上是由 Job 来负责执行的。他们之间的关系如下图:
一个简单的例子
我们来考虑这么一个简单的例子,一个需要使用到数据库的 API 服务在 Kubernetes 中应该如何表示:
客户端程序通过 Ingress 来访问到内部的 API Service, API Service 将流量导流到 API Server Deployment 管理的其中一个 Pod 中,这个 Server 还需要访问数据库服务,它通过 DB Service 来访问 DataBase StatefulSet 的有状态副本。由定时任务 CronJob 来定期备份数据库,通过 DaemonSet 的 Logging 来采集日志,Monitoring 来负责收集监控指标。
容器编排的困境
Kubernetes 为我们带来了什么?
通过上面的例子,我们发现 Kubernetes 已经为我们对大量常用的基础资源进行了抽象和封装,我们可以非常灵活地组合、使用这些资源来解决问题,同时它还提供了一系列自动化运维的机制:如 HPA, VPA, Rollback, Rolling Update 等帮助我们进行弹性伸缩和滚动更新,而且上述所有的功能都可以用 YAML 声明式进行部署。
困境
但是这些抽象还是在容器层面的,对于一个大型的应用而言,需要组合大量的 Kubernetes 原生资源,需要非常多的 Services, Deployments, StatefulSets 等,这里面用起来就会比较繁琐,而且其中服务之间的依赖关系需要用户自己解决,缺乏统一的依赖管理机制。
应用编排
什么是应用?
一个对外提供服务的应用,首先它需要一个能够与外部通讯的网络,其次还需要能运行这个服务的载体 (Pods),如果这个应用需要存储数据,这还需要配套的存储,所以我们可以认为:
应用单元 = 网络 + 服务载体 +存储
那么我们很容易地可以将 Kubernetes 的资源联系起来,然后将他们划分为 4 种类型的应用:
- 无状态应用 = Services + Volumes + Deployment
- 有状态应用 = Services + Volumes + StatefulSet
- 守护型应用 = Services + Volumes + DaemonSet
- 批处理应用 = Services + Volumes + CronJob/Job
我们来重新审视一下之前的例子:
应用层面的四个问题
通过前面的探索,我们可以引出应用层面的四个问题:
- 应用包的定义
- 应用依赖管理
- 包存储
- 运行时管理
在社区中,这四个方面的问题分别由三个组件或者项目来解决:
- Helm Charts: 定义了应用包的结构以及依赖关系;
- Helm Registry: 解决了包存储;
- HelmTiller: 负责将包运行在 Kubernetes 集群中。
Helm Charts
Charts 在本质上是一个 tar 包,包含了一些 yaml 的 template 以及解析 template 需要的 values, 如下图:templates 是 Golang 的 template 模板,values.yaml 里面包含了这个 Charts 需要的值。
Helm Registry
用来负责存储和管理用户的 Charts, 并提供简单的版本管理,与容器领域的镜像仓库类似这个项目是开源的。( github.com/caicloud/he…)
Tiller
- 负责将 Chart 部署到指定的集群当中,并管理生成的 Release (应用);
- 支持对 Release 的更新,删除,回滚操作;
- 支持对 Release 的资源进行增量更新;
- Release 的状态管理;
- Kubernetes下属子项目(github.com/kubernetes/…) 。
Tiller 的缺陷
- 没有内建的认知授权机制,Tiller 跑在 kube-system 分区下,拥有整个集群的权限;
- Tiller 将 Release 安装到 Kubernetes 集群中后并不会继续追踪他们的状态;
- Helm+Tiller的架构并不符合 Kubernetes 的设计模式,这就导致它的拓展性比较差;
- Tiller 创建的 Release 是全局的并不是在某一个分区下,这就导致多用户/租户下,不能进行隔离;
- Tiller 的回滚机制是基于更新的,每次回滚会使版本号增加,这不符合用户的直觉。
Release Controller
为了解决上述的问题,我们基于 Kubernetes 的 Custom Resource Definition 设计并实现了我们自己的运行时管理系统 – Release Controller, 为此我们设计了两个新的 CRD – Release 和 Release History。
Release 创建
当 Release CRD 被创建出来,controller 为它创建一个新的 Release History, 然后将 Release 中的 Chart 和 Configuration 解析成 Kubernetes 的资源,然后将这些资源在集群中创建出来,同时会监听这些资源的变化,将它们的状态反映在 Release CRD 的 status 中。
Release 更新
当用户更新 Release 的时候,controller 计算出更新后的资源与集群中现有资源的 diff, 然后删除一部分,更新一部分,创建一部分,来使得集群中的资源与 Release 描述的一致,同时为旧的 Release 创建一份 Release History。
Release 回滚和删除
用户希望回滚到某一个版本的 Release, controller 从 Release History 中找到对应的版本,然后将 Release 的 Spec 覆盖,同时去更新集群中对应的资源。当 Release 被删除后,controller 将它关联的 Release History 删除,同时将集群中的其他资源一并删除。
架构图
这样的设计有什么好处?
- 隔离性:资源使用 Namespace 隔离,适应多用户/租户;
- 可读性:Release Controller 会追踪每个 Release 的子资源的状态;
- 版本控制:你可以很容易地会退到某一个版本;
- 拓展性:整个架构是遵循 Kubernetes 的 controller pattern,具有良好的可扩展性,可以在上面进行二次开发;
- 安全性:因为所有的操作都是基于 Kubernetes 的 Resource,可以充分利用 Kubernetes 内建的认证鉴权模块,如 ABAC, RBAC 。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
迈入Docker、Kubernetes容器世界的大门
本文通过简单的示例,带领初学者快速迈入Docker、Kubernetes(K8S)容器世界 的大门。假设,你已拥有一个K8S集群,否则,可通过minikube或minishift快速搭建一实验环境。 Docker Docker与K8S Docker本质上是一种虚拟化技术,类似于KVM、XEN、VMWARE,但其更轻量化,且将Docker部署在Linux环境时,其依赖于Linux容器技术(LXC)。Docker较传统KVM等虚拟化技术的一个区别是无内核,即多个Docker虚拟机共享宿主机内核,简而言之,可把Docker看作是无内核的虚拟机,每Docker虚拟机有自己的软件环境,相互独立。 K8S与Docker之间的关系,如同Openstack之于KVM、VSphere之于VMWARE。K8S是容器集群管理系统,底层容器虚拟化可使用Docker技术,应用人员无需与底层Docker节点直接打交道,通过K8S统筹管理即可。 Docker基础 如下所示,运行docker run -it --name test-docker busybox /bin/sh命令,观察其输出,可发现doc...
- 下一篇
浅谈k8s cni 插件
目前不论是个人还是企业,在使用k8s时,都会采用CNI作为集群网络方案实现的规范。 在早先的k8s版本中,kubelet代码里提供了networkPlugin,networkPlugin是一组接口,实现了pod的网络配置、解除、获取,当时kubelet的代码中有个一个docker_manager,负责容器的创建和销毁,亦会负责容器网络的操作。而如 今我们可以看到基本上kubelet的启动参数中,networkPlugin的值都会设置为cni。 cni插件的使用方式 使用CNI插件时,需要做三个配置: kubelet启动参数中networkPlugin设置为cni 在/etc/cni/net.d中增加cni的配置文件,配置文件中可以指定需要使用的cni组件及参数 将需要用到的cni组件(二进制可执行文件)放到/opt/cni/bin目录下 所有的cni组件都支持两个命令:add和del。即配置网络和解除网络配置。 cni插件的配置文件是一个json文件,不同版本的接口、以及不同的cni组件,有着不同的配置内容结构,目前比较通用的接口版本是0.3.1的版本。 在配置文件中我们可以填入多个c...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS关闭SELinux安全模块
- 2048小游戏-低调大师作品
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Red5直播服务器,属于Java语言的直播服务器
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7