kubernetes in action读的差不多了,篇幅比较大,其实是需要一开始把书读薄一些的,对kubernetes的概念讲的比较细,底层也只是大概讲了一下,不涉及源码和kubernetes的设计理念,前几天查阅资料时偶然发现一个极客时间上的教材,深入剖析kubernetes,之前也没了解过这种网课,都是xxx专家xxx架构师啥的,买一个对比一下,看看靠谱不,由于已经看过一本,所以就速读了一下,只看差异。
对比kubernetes在kubernetes调度、kubernetes网络上、kubernetes存储上讲的多一些,里面举得例子也更贴近项目一些,其他内容感觉差不太多,整体读完再评价。
行业发展简单看了看,基本讲了从paas到Docker由来以及后来的kubernetes。
Docker的基础知识介绍的比kubernetes细一些,但是也只是入门级的内容,还是有必要单独对Docker做一下系统的学习,以下列出我任务比较重要的观点。
1、Docker的相较于传统的paas优势在于打包镜像,很好的管理/解决了应用运行时环境本地/现网一致性的问题,原有的paas平台本质也是container,支持资源的隔离,但是没有镜像的概念,在开发者层面看使用成本是比较高的。
2、相比虚拟机,Docker在可维护性和性能方面具备优势,性能上对比虚拟机少了一层(OS直接在硬件的虚拟),镜像的打包部署也很方便;但劣势是:Docker提供的容器化运行环境,并不是100%的平台可移植,只是在rootfs上做了运行时的操作系统环境依赖,而内核层面部署在一台宿主机上的容器是公用的,而且使用的就是宿主机的内核,所以内核级别无法真正隔离
3、Docker运行时的容器(一个镜像)本质也是一个linux进程,这个进程经过以下3步后实现了容器化:
这三点本身也是linux的基本特性,老早就有,后来docker做了很好的封装,其他paas平台也是用的这三个特性来实现的服务资源隔离。
4、Docker镜像分层,分层的策略为Docker提供了良好的打包体验,不重复打包、底层镜像可共享,打包的过程其实就是上面提到的change root的概念,将操作系统中的 / 目录下内容压缩,然后在运行时change root,一般来说应用依赖的运行时环境基本都在这个目录下
所以说一层层的Docker镜像其实就是对rootfs的多次编辑、覆盖、合并过程
进入容器(k8s进入pod),本质是创建一个进程,setns,修改这个进程namespace与要进入的容器进程一致,这样在当前进程就可以共享容器进程的操作系统资源了,讲到这里就可以了解到,Docker容器在经过上述namespace、cgroups、change root之后,相关的操作系统资源如平时用到的/proc,/etc这些,都是在进程内可见,宿主机不可见,在一个容器进程实现了操作系统资源的隔离使用
这里主要说一下第二个点,就拿挂盘来讲,不同的容器在宿主机上其实使用的是同一块磁盘,那么如何做到一个目录在容器内可见并可用,在宿主机上不可见,其他容器也不可见?用到了也是linux的绑定挂载,比如在容器运行时,将容器内的/test目录挂载在宿主机的/temp(命令可以实现),当在容器内attach一个1.txt时,容器内的/test可以看到这个文件,在宿主机上的/var/lib/docker/xxxx(容器id)/test目录下是空的,但是在宿主机的/temp下是可以看到的。
对容器的总结有一个比较容易理解的说法,是否严谨就不好说了,但是很好理解。
- 从静态试图看,容器是一组挂载在/var/lib/docker/mnt/xxxx(容器id)的rootfs(操作系统 / 的拷贝),这一部分称之为“容器镜像”
- 从动态试图看,容器是一个有namespace + cgroups 构成的隔离环境(一个进程),这部分称之为“容器运行时”
这部分都是一些简单的罗列,环境搭建也没有太细看,工程性太强。
以上内容,对比kubernetes in action,是比较好的,主要是介绍了一些Docker的基本概念,因为有一部分读者确实是单纯从工程应用角度去学习了解kubernetes的,所以肯定对Docker和kubernetes的关系不了解,比如pod的request limit是怎么实现的,pod的网络是怎么实现的,这些都和Docker紧密相关,不过kubernetes in action的作者应该是假设读者具备这一些基本概念了,学习顺序也应该是先Docker后kubernetes,但在实际的生产活动中可能不会有很多读者有意愿去系统学习。
这部分内容讲的脉络是,从kubernetes提供的一些基本的资源类型,逐步讲到kubernetes的管理面服务如api-server、scheduler、controller等,然后逐步引出了CRD/CRI/CSI,最终用一个operator的基本实现来整体描述了一下,对比kubernetes in action,kubernetes提供的基本资源讲的不是很细,与原理/流程相关的东西能细一些,不过还是得有一些基础看才会比较有感觉。
这部分讲的由浅入深,从kubernetes提供的基本资源类型讲到CRD/operator,这里只阐述一下一些核心观点的理解总结:
1、对于为什么要有pod,为什么要通过pod管理多个容器,解释是为了解决亲密服务的问题,能够让需要共部署/进程间通信这样的服务能够在一个运行环境里,为了实现这一过程,kubernetes搞了一个infra容器来作为基础容器,声明周期与pod相同,创建了一套namespace、cgroups、rootfs环境,再创建其他的容器共享infra容器的系统资源,整体称之为pod,所以pod的定义是:一组共享namespace、cgroups、rootfs的容器集合
2、pod的基本用法讲了一下,不过比较感兴趣的是podPreset,这个之前没有看过,可以批量的通过selector对一组pod进行修改
3、提到了“声明式API”的概念,提了一个概念比较有趣,pod中的ownerrefrence用来保存这个pod的owner信息,controller(RS controller)也是通过这个字段能控制pod
4、讲了deployment如何通过RS实现的scale和滚动升级
5、讲了statefulset,想表达两个观点,statefulset创建有序(拓扑有序),statefulset可以提供稳定服务,提到稳定的服务是通过headless service表达的,即直接在DNS写pod的ip,不走endPoint
6、描述了statefulset的存储有状态,提到了PVC/PV,不过基本是照着现象过了一遍
7、讲了一个例子,如何在集群里部署一个mysql,多实例,单个pod负责写,其他pod负责读,读pod实时同步写pod更新,这里将statefulset、init container、sidecar、configmap、健康检查、pvc/pv、rook、storageClass用了一遍,最后提了一嘴operator。
以上内容,对比kubernetes in action,讲的不够循序渐进,上来直接deployment、pvc啥的读者容易懵,没有从RC/RS这种简单的开始,另外也没有提到api-server、etcd这个过程中的作用,不过如果有基础看起来还行。
下面继续:
1、讲到了daemonSet,通过Toleration、nodeAffinity字段再加上DaemonSetController实现的每个节点均部署的能力,通知描述了DaemonSet是通过DaemonSet controller + ControllerRevision来实现对pod管理。
2、讲到了job,管家年提到了job涉及的pod也是直接由controller管理的,和RS无关,顺便介绍了几种job常用方法,例如固定任务数场景、队列消费场景
这部分内容收获不是很大,也是比较偏应用,这些内容大多数书里都有。下面介绍的一些内容非常不错,和kubernetes的设计方法、实现原理、流程相关性强一些,不过阅读过程最好有go语言基础,不然看完容易忘
1、kubectl create/apply/replace有啥区别,create不是声明式的更新,而是心创建了一个资源对象,其他两个都是基于原有对象修改etcd中的内容,通过controller进行的声明式修改。所以最好用后两者,用create的过程中为了避免在集群中出现重复的资源,所以内部是有锁的,而其他两者是可以并发以及同一对象修改merge的。
2、介绍api-server接口定义,这部分内容在kubernetes in action也有,接口:组/版本/资源,包括api-server收到请求后基本流程是啥,如何对组/版本/资源进行处理的。这里讲了一个CRD(custom resource definition)的例子,这个例子就得非常好,引出了api-server的核心用法,通过创建一个network的CRD,也能让读者明白其他kubernetes的资源如statefulset、deployment大概是个啥。
3、顺着CRD继续讲,创建出来的CRD,kubernetes是如何对这个CRD进行声明式的管理的?引出了controller,通过一个go语言写的自定义的controller来描述kubernetes的controller是如何工作的,平时用到的statefulset、deployment是怎么和pod搭上边的。
4、RBAC的内容和安全相关没太看
还是那句话,得学一下go,不然看着费劲。
5、讲operator,接着上面的逻辑,用户可以见一个CRD了,可以见一个controller,那就可以很自由的使用kubernetes,我们可以根据自己的需求,定制自己的pod,定制自己的controller,对自己的应用类型进行管理,如果想把CRD/CONTROLLER以及其他相关的内容都做隐藏呢?提出了operator的概念。文中讲的是一个etcd的operator部署的例子,因为etcd本身是一个分部署的存储引擎,支持高可用,在部署过程中和主从mysql、zk、kafka差不多,得搞一些配置,通过一个operator就能把这些东西都封装起来,而且封装的主体也是一个kubernetes的资源对象(deployment)
之前做过spark-operator,其实就是在kubernetes中创建一个deployment,这个deployment对应的pod去做两件事:
1、创建CRD
2、创建controller,管理CRD/pod
讲到这里,kubernetes调度、管理相关的东西差不多完了,还差的是scheduler,应该在后面会有讲到,这里总结两点:
PV/PVC花了不少篇幅来讲,一方面这个东西确实有点绕,在应用过程中也搞不太明白关系,在kubernetes in action中也废了不少内容讲这个,讲的非常细,步骤一步步的。这里相对还算少的。不过在PV/PVC与persistVolumeController、kubelet、Docker的关系上讲到了一些点,值得总结:
pod中对PVC进行配置----为什么不配置pv,为了在pod层面隐藏存储实现细节
persistVolumeController检查PV/PVC是否绑定,没绑定就绑定一个PV的
a.PV手动要自己创建,这样persistVolumeController才会去匹配,匹配不上pod就起不来
b.配置了storageClass就能自动创建pv
pod调度到某个节点后,不会立刻启动容器,kubelet会做一件事:通过代码中的VolumeManagerReconciler检查是否需要mount volume(也就是配置的PVC对应的PV)
这里分两步:
a.把PV作为一个块设备挂载
b.把挂载好的块设备mount的到docker容器的volume上
$ mount -t nfs:/ /var/lib/kubelet/pods/ /volumes/kubernetes.io~ /
但不是所有的PV都需要作为块设备挂在,NFS就可以直接当成目录进行mount。mount后,改pod所使用的磁盘空间就相当于指向了PV提供的目录。
pod用的磁盘准备好了,最后再通过**CRI**启动容器,则容器内就可以正常使用磁盘空间了
$ docker run -v /var/lib/kubelet/pods/
/volumes/kubernetes.io~ / :/<容器内的目标目录> 我的镜像 …
这里附一篇CRI/CSI/CNI的文章
讲完这个基本概念和流程后,又举了一个CSI实现的例子,如何定义自己的PV/PVC,包括storageClass,这里没有太细看,原理上应该都差不多,用到的linux相关的技术应该是和磁盘设备控制、目录权限控制、目录挂载有关的内容。
对比kubernetes讲的比较细,偏底层一些,介绍了几个重点概念:
1、docker0作为网桥的主要作用是逻辑交换机
2、vethPair的两端,一端是container的eth0,一端是物理机创建的虚拟网卡vethxxxxxxxx
3、基于匹配到网桥的虚拟网卡“降级”特征,就能实现数据包发送的ARP过程
4、容器对外部的访问,也要通过docker0再到主机的eth0
单机上的容器通信介绍一个最基本的流程,然后就是多主机多容器的场景,用flannel举了个例子(先介绍了flannel的目的就是创建一个跨主机的overlay网络):
1、flannel的UDP模式,即在主机eth0和docker0之间再创建了一个flannel0的TUN设备,TUN设备在ETCD中统一维护,建立子网关系(随之而来的性能问题也主要来源于TUN设备的用户/内核态切换)
2、flannel的VXLAN模式,其实和上面的差不多,TUN换成了VTEP,全部在内核态(对数据包进行修改),提高了性能
讲完了flannel基本流程,引出了CNI的概念,kubernetes就是为了避免在P层的调度管理逻辑和docker强绑定,所以对docker0这个网桥做了解耦,提供了CNI的接口,任意网络插件都能够实现一个自己的“docker0”,在kubernetes里叫cni0,因此在kubernetes下的pod创建时,就会调用网络插件(dockershim来做的)对pod内容的网络环境进行初始化(就是一大堆linux命令,创建容器内eht0,创建容器外的veth pair,安装到cni0上,同时flush到etcd等等)。
在VXLAN和UDP讲完之后,有介绍了一种更简单的方案,host-gw模式,其实就是省去了一个TUN/VTEP的设备转发过程,提高了性能,同时,替代TUN/VTEP的对象是route,通过配置route规则,让容器的虚拟网络地址都能正确的指向一个没毛病的物理地址,而这个虚拟地址到对应的正确的物理地址关系,是在etcd记录的,flannel插件的作用就是维护这个关系,及时更新到整个kubernetes集群的每个节点上,如图:
最后,介绍了BGP,边界网关协议,一种连网桥都不用创建的容器网络,原理简单理解:
1、DaemonSet收集各个节点的ip和容器ip,共享信息
2、容器eth0创建veth pair到主机,主机端的虚拟网卡不挂网桥,直接挂到主机eth0
3、定期更新主机的router table,让数据包能直接通过eth0发出去
关于ingress/egress在kubernetes容器网络中的生效原理,没细看,基本上也是基于linux的基本特性做的,回头有用再看。整体来看这一章讲的很好,不是普通教材能体现的,前几天和同事还在聊,像这种内容的东西,是不是可以出书的,市面上的数据都是一些基本的应用和介绍,都是比较基于一项技术较为封闭的结构性介绍,缺少这种基于一个技术点,发散性的讲解,可能是这些内容出书的话没人看吧,可能大多数人能把教材看完就不错了,另外就是这些内容看起来高大上,其实在应用过程中比较少用,除非遇到了很奇怪的bug,我觉得这样的内容对于实际的编码过程可能作用不是很大,但是对于软件设计来说还是有一些帮助,在了解机制的前提下,往往能想到更简单更直接扩展性更好的方法。