在上一篇文章中,我介绍了调度的概念,并且调研了2个基础单块(basic monolithic)调度器:fleet和swarm。总结一下:调度器负责项集群的节点发布任务。然而,基础单体调度器,由于设计问题,在性能和吞吐量上都受限。
本文,我们将介绍Kubernetes相对于基础单块设计的改进。
Kubernetes一个用来管理集群间Linux容器的工具。
Kubernetes来自Google,拥有轻量及支持大规模扩展的特点。在设计上,它是高度解耦的,可以被划分为2个组件:控制层面和工作节点服务。控制层面,负责向节点分配容器和管理集群配置。工作节点服务,运行于集群中的单个机器上,负责管理本地容器。
在Kubernetes中,存在pod的概念。它是位于同一位置的容器群,就如同鲸鱼的pod,或者是豌豆的pod(tips:pod来源于DC Network,常指在逻辑上对于节点的划分,通常分布于同一区域)。在同一pod中的容器共享相同的命名空间。它被用于服务发现和分离。
Kubernetes的每个节点都需要专用的子网——或者覆盖网络(overlay network)。这样,每个pod便能够得到一个在集群中独一无二的IP。这一点可以通过在Flannel,OpenVSwitch,Calico,或Weave集群上部署Kubernetes实现。更多关于Kubernetes网络的内容,请看这个文档(http://kubernetes.io/docs/admin/networking/)。
让我们深入讨论这些细节。
pod是Kubernetes的基本单位。一个pod就是一堆容器,它们位于同一主机并且共享相同的Linux命名空间集合。
当一个pod部署完毕,Kubernetes会启动一个容器,它会锁住pod中真实容器需要的资源和命名空间。这就是所谓的暂停容器。
Pod可以支持纵向应用栈。比如,一个后端和前端紧耦合的应用可以位于同一pod。
在单个工作节点中,pod是能够容错的,但是在集群中则不然。该特性可以通过--restart=on-failure
参数进行启用。然后,当pod故障时,它就会自动重启了。但是如果一整个节点宕机,该节点上的pod并不会被重新调度到另一个节点。
Pod并不是被设计用来在多节点间扩展,或者在节点故障后正常运行。因此,我们需要一个备份控制器。
备份控制器负责扩展,更新和重调度pod。它保证在任何时点,运行的pod数量和配置中指定的备份数量一致。备份控制器通过终止或启动pod来实现其与备份数量的一致。
标签是用于描述pod的键值对。它可以用来将pod集合指定为一个组,也可以用来路由网络流量。
命名空间是一种分离pod的方式。来自不同命名空间的2个pod不能参与服务发现。由此,我们便可以在同一集群中创建多个环境。如果我们希望,便可靠此提供应用级的隔离。
一个服务充当一组相关pod的联系点。它会根据配置将流量路由到所有相关的pod。
服务让你可以改动pod组而无需担心单个pod的可用性。你并不需要直接访问pod,只需要访问服务即可。服务将会按照round-robin算法将流量路由到所有匹配到相应标签的pod。
服务也有助于服务发现。当一个pod在某个命名空间中被创建,相同命名空间中的每个服务的主机和端口号会通过环境变量提供。
例如:在deis
命名空间,我可以使用db
这个名字创建一个服务。每个在此命名空间中创建的pod,随后便会持有DB_SERVICE_HOST
和DB_SERVICE_PORT
环境变量集。
守护进程集确保特定的pod正在集群中的每个节点(或一组节点)上运行。这个和fleet的全局单元很像。当一个额外的节点被添加到集群时,Kubernetes会将需要的pod添加到该节点。如果集群的节点数减少时,那些节点上的pod就会被回收。
任务和pod类似,但任务一定会终止。这对批处理工作负载相当有用。
在Kubernetes v1 API中,任务和守护进程集是不可用的。如果可以,请将Kubernetes切换到1.2。否则,你需要在自行开启API扩展,具体的方法是,在API服务器启动时,加上--runtime-config=extensions/v1beta1/daemonsets=true
或在运行kube-up.sh
脚本前 export ENABLE_DAEMONSETS=true
。
控制层拥有4个主要的组件:API服务器,控制器管理服务器,调度器和etcd。
API服务器就是Kubernetes集群的外立面。它是一个提供REST终端的独立服务器,负责认证,授权,处理所有来自kubectl的调用,后者是用来管理Kubernetes安装的CLI工具。API服务器会在etcd中存储Kubernetes集群的状态。
关于Kubernetes API的更加详细的解释,请看这个文档(http://kubernetes.io/docs/api/)。
控制器管理服务器是几个controller管理器的集合,共同确保集群维持在希望的状态。
例如,如果你部署pod时使用了备份控制器,并将副本数设置为3,那么备份controller管理器会确保这3个副本将会及时在任意给定的点运行。如果其中1个副本挂了,那么备份controller管理器将会在相同或不同的节点上调度一个新的副本。
Kubernetes支持为controller管理器定制插件。
调度器负责跟踪集群中的所有资源,并根据资源需求调度pod。
当API服务器接收到请求,要求调度一个pod时,它会将请求置于队列中,然后这个队列会不断被调度器读取,最终完成pod的调度。调度器是可插拔的,并且支持多集群调度算法。
调度器是单体的,但它和API服务器是解耦的,因此并不受API服务器的可用性的影响。即使API服务器是不可用的,调度器依然可以准备pod,读取队列,并在集群中部署pod。
解耦使得Kubernetes克服了单体调度器的短板。
更多内容请看这个文档(http://kubernetes.io/docs/admin/kube-scheduler/)。
etcd是一个高可用的分布式键值对存储。它的设计目标即:简单,快读,可靠和容错。etcd使用raft一致性算法来选举leader和记录日志。
每个Kubernetes节点运行2个程序:kubelet和kube-proxy。
kubelet和代理服务允许单个节点能够完全管理它们自身和其主机本地所运行的容器。反之,这也减少了Kubernetes控制层面所需要负责的事。
从Docker的视角来看,kubelet守护进程管理镜像,容器和本地主机上的卷。从Kubernetes的视角来看,kubelet守护进程是Kubernetes API服务器和进程本地运行的Kubernetes节点之间的联系点,负责部署pod并根据指令执行相关任务。
kube-proxy是一个网络代理,负责连接本地运行的pod和外界。它使用round robins算法将请求按照标签导向pod。对于共享相同标签的pod组而言,它也是一个负载均衡。
在上一篇文章中,我们看到了单体调度器设计带来的局限。Kubernetes通过多种方式改进了这种设计。
相对于单体调度器的改进:
Kubernetes将调度和组件监控解耦,这改善了系统的可用性。
Kubernetes使用etcd作为分布式队列,有意义的状态会被存储于etcd中,并通过不同的组件持续更新。
Pos有助于容器租用。
备份控制器有助于伸缩和容错。
服务有助于高可用性和负载均衡。
标签让你能够分组和隔离应用。
Kubernetes的设计决定了,它不仅仅是一个容器的调度器或编排系统。Kubernetes干净地解决了许多和集群微服务和应用容器化相关的问题。
你可以在这个blog(http://blog.kubernetes.io/2016/03/Kubernetes-1.2-even-more-performance-upgrades-plus-easier-application-deployment-and-management-.html)中找到Kubernetes持续改进的状态。
如果你用Linux,那么kube-up.sh这个脚本是开始使用Kubernetes的一种好方式。在OS X上,kube-sole会在xhyve下运行一个单节点的CoreOS,其中已经安装了Kubernetes。