分享目的
如上一贴所讲,工作之余学习K8S已有月余,确实略懂皮毛,之所以想到分享,一是本着开源社区的精神,二是权当在线留作记录,保存一些个人的学习所得,兴许理解粗浅,兴许神来之笔,或许将来有一天,工作中有用得上的时候,翻出来再学习学习,也是一种收获。如果还能够帮到别人,也是一种造化。
K8S架构
K8S全称是Kubernetes,其中K和S之间正好8个字母,所以在华语圈内简称K8S。至于它的前世今生,官方社区和网上有很多介绍的,此处就不再赘述。
说到架构,先从容易理解的“物理”层面介绍,因为K8S生来就是为了解决分布式系统调度和管理的,所以就有角色的分工——Master和Node,一般生产环境为了高可靠,避免单点故障,Master至少要三个节点起,至于学习的测试环境,就假设一个Master节点,计算节点Node共3个,当然,在实际的生产环境中,肯定是根据实际业务使用量在规划Node节点,通常是可以水平扩展的。
为了能够清晰的描述K8S架构,手动画了一张图,可以参考。
如图所示,蓝色背景的组件(部署在单个容器里)都是系统组件,在搭建集群系统的时候,会首先部署。
先看Master节点:
kube-apiserver:为k8s各类资源对象的增删改查,及watch等操作提供了HTTP Rest接口,相当于整个系统的数据总线和数据中心。大白话就是看门老大爷。
kube-controller-manager:是k8s的控制中心,通过apiServer的list/watch接口,实时监控Node上各种对象的事件,在事件队列中捕获到事件时,再由对应的hook函数触发并处理。大白话就是k8s的大脑。
kube-scheduler:负责调度Node的,通过apiServer的watch接口监控到新建Pod副本消息,然后基于一定的调度算法,将新建Pod调度到合适的Node节点上。大白话就是Node节点们的调度员。
etcd:本身是一个高可用的分布式键值(key-value)数据库,在k8s中主要记录各种资源对象的状态信息。
calico-kube-controller:是k8s中网络插件控制器,主要是解决k8s中各种Node、Pod等通信需求的。当然也可以用flannel、weave等网络插件。
calico-node:是在各节点上部署的,用于初始化Node上网络的。
除了上面这些提到的常用系统管理组件外,可能还会有其它组件,比如要进行集群监控、GPU管理、存储扩展等。
再看Node节点:
kubelet:每个Node节点都会启动的,用来处理Master节点下发到本节点的任务,管理Pod和其中的容器,kubelet会在API Server上注册节点信息,定期向Master汇报节点资源使用情况,并通过cAdvisor监控容器和节点资源。大白话就是Master派到各Node上的监工小弟。
kube-proxy:运行在每个Node上,负责Pod的网络代理,负责集群内部负载均衡和服务发现。大白话就是负责牵线搭桥的。
calico-node:是在各节点上部署的,用于初始化Node上网络的。
Pod:是k8s管理的最小级单元,相当于超市里的购物车,车子里可以根据用户实际购买需求来装入商品,k8s主要管车子,如果不够的话就会增加一些,如果太多就会减少一些,如果有坏掉的则会安排替换等等。在k8s中,所有业务的编排或者组合,都是基于Pod的,对应到计算机中就是进程。
容器:就是运行在Pod中的最小单元,相当于进程中的线程,目前能够完美配合k8s的容器自然就是docker了,基于微服务的思想,每个容器(docker)里运行一个具体程序,比如Nginx、tomcat、MySQL等等。
以上仅是介绍了“物理”层面上的架构,其实理解起来也容易,一个k8s集群肯定由很多节点组成,节点可以是物理机,也可以是虚拟机,生产环境中使用物理机更好,这些节点中一小部分“当领导”——即作为Master,其它大部分都是“打工仔”——即作为Node(可以理解为计算节点),“当领导”自然要有“两把刷子”的,而Master上的“刷子”可不止两把,是有很多把,比如apiServer、controller、scheduler等等。“打工仔”自然要在管理范围下,所谓“无规矩则不成方圆”,像kubelet、kube-proxy这种“监工”和“关系户”自然少不了。由于“打工仔”数量肯定要远远大于“领导”的,分级分组管理,才能正常运作,Node就是一个大部门,而Node中的Pod就是一个个小组,小组内的容器(docker)才是一个具体的打工仔,但在“领导”眼里只能管到“小组(Pod)”,要是“领导”把所有“打工仔(docker容器)”直接管理了,估计“领导”得累到吐血。
这样一个现实组织终于运作起来了,可面对的问题才刚刚开始,比如这个组织是生产汽车的,这么一个复杂的生产活动,肯定得分工吧,有人负责生产车轮子,有人负责生产车架等等,“领导”就得稍微调整一些组织结构了,规定一些“小组”负责生产车轮子,一些“小组”负责生产车架……还有一些“小组”负责组装,为了便于管理和识别,可以给这些“小组”分别打上标签,比如就叫“车轮生产部”、“车架生产部”、“整车组装部”等,然后根据生产工作量,灵活调整每个部门的人员配额,车轮生产多了,就调一部分“小组”到“车架生产部”提升车架产能,“整车组装部”缺人了,就调一些“小组”加入,从而提升组装产能,“打工仔”还是那些打工仔,只不过是根据公司实际业务情况,被领导调来调去呗,在调动的过程中只不过是换了个“标签”罢了,也可能就是工作牌。
等车子生产出来了,自然需要销售啊,总不能让用户直接到生产厂里来买吧。所谓“家丑不可外扬”,正确的做法,自然是需要成立一个组织——“销售部”,然后负责对外的统一接口,想买车就去找“销售部”,而“销售部”拿到订单后,就找工厂的负责人拿货。
再回到k8s中,那些“打工仔”的“小组”就是Pod,那些标签就是Pod或docker容器里的“label”,那些“车轮生产部”、“车架生产部”、“整车组装部”就是k8s中的Deployment、ReplicaSet、ReplicationController等对象,那些“销售部”就是k8s中的Service对象。这些就是k8s中的“逻辑层面”划分,相对比较难理解,可根据Pod/容器的label等标签,从逻辑上组织起一个或多个业务,从而接受k8s的统一管控,可实现“自愈”式的分布式系统调度和管理。
总之,k8s可以根据这些灵活的设计,从多个维度来打造服务,以“服务”为中心实现自动化、高效的分布式调度和管理。至于还有哪些巧妙的维度,其实我自己还在这条探索道路上。下图只是一部分……
(注:上图来自于网络)
K8S中一些事儿
如果比较熟悉Linux,学习k8s和docker要好很多,毕竟有些技术还是借鉴和发扬了Linux,比如namespace,cgroup等。
1. 声明式和命令式:
先说命令式,就是严谨的告诉计算机如何去做,比如删除一个应用,可能使用delete(参数1,参数2……)等等,添加一个资源,可能使用add(参数1,参数2……),整个过程其实是由“人”来控制的,当发现某个虚拟机挂掉了,通过发送相关命令重启或者删除再重建,当发现存储空间不足了,通过发送相关命令进行扩容等。只是“人”通过代码来“发号施令”,这些代码可能是命令行,也可能是脚本,也可能是程序函数等。既然是“人”在控制,所谓“金无足赤,人无完人”,总会有出错的时候,所以越大的系统,就越不能靠“人拉肩扛”。
再说声明式,按照网上各种解释,像领导跟员工的沟通,告诉员工期望达到什么,具体员工如何实现目标领导可以不管;像告诉机器what——我要什么,而不是告诉机器how——我要教你怎么做……我觉得这些比喻都挺贴切,也总算弄明白了为什么大家都喜欢做领导,原来就是提要求就行啊(非认真脸哈),是不是也可以拿给员工一张百元钞,让去买盒烟再顺带买些珍珠玛瑙房产证什么的,剩下的钱就当跑腿费了(白票德云社相声段子)……不管怎样,我们的目的是为了探索声明式的操作,到底让k8s如何爽了。
其实呢,就是在给机器发送指令时,告诉机器系统期望的状态是什么,让机器自动完成期望目标。比如在k8s中,通过Deployment部署Pod时,只需要指定具体的副本数量,k8s通过自身的监听(List/Watch)、控制(controller)系统来不断的对比当前状态Status和期望状态(spec中设置的)之间的差异,如果两者有差异,触发相关流程进行处理,直到期望状态与当前状态一致。
(注:上图来自阿里云云原生公开课)
2. 基于restful API接口的调用:
简单地说,REST风格就是系统上的所有对象都是资源,这些资源都会分配唯一的访问地址,这个访问地址就是URL,然后基于HTTP协议完成通信和访问,比如(POST、GET、DELETE等方法)。
Restful API就是具有REST风格的API接口,使得各种领域和平台上的开发者都能够遵守统一的接口标准,从而有利于软件开发和传播。
在k8s中,资源对象的调用,都是通过这种restful API接口来实现的,具体就是Master上的apiServer来完成,如下图:
(注:此图来自于网络,链接: )
如果要创建一个cronjob对象,向apiServer发送一条“POST /apis/batch/v2alpha1/cronjobs”,apiServer经过鉴权、认证、控制等一系列操作,触发资源创建,并将最终的状态信息存储到etcd数据库中。当然,POST中还可以携带更多参数,这些参数会以json格式组装,都是key-value键值对,而这些信息的源头就是我们编写的yaml文件,如下示例:
3. 统一的镜像库:
传统的应用安装,一般使用安装包(镜像)来完成,前提肯定是要准备好安装包,并且上传到要安装的节点(机器)上,按照安装指导一步一步完成安装,然后再启动。比如在Linux系统上安装Nginx,可以根据官网的安装指导,选择源文件编译安装,或者yum安装等,假如选择yum安装,需要配置yum源,然后使用yum命令开始安装,当然可以指定版本,安装完成后,启动Nginx服务。再比如要安装MySQL数据库,还得上MySQL官方社区,根据安装指导配置yum源,开始yum安装,最终启动服务。也就是说,每当安装一个程序或应用时,我们都得上对应的官网,根据安装指导手动配置yum源,然后手动敲命令完成安装并启动,而这些程序或应用还是非常流行和常用的,假如是自行开发的应用或程序,肯定得手动上传镜像到待安装机器上,更高级点的搭建一个私有镜像库,以便后续再使用。
在k8s中,其实很方面,在yaml文件中的spec中image后面填写镜像名称和版本即可,如果是公共应用,比如nginx、tomcat、mysql等,直接从docker公共镜像仓库中拉取;如果是自有应用,可以在k8s中搭建私有镜像仓库,一样可以实现即用即取。
至于docker公共镜像仓库,最权威的当然是DockerHub,链接地址:https://hub.docker.com/
对于某个镜像,可以查看下载数量、星级数量、版本信息等等。
除了DockerHub之外,还有其它一些常用的docker镜像仓库,比如Google、Redhat、阿里云、华为云等,可在网上搜索地址。