虽然 Docker已经很强大了,但是在实际使用上还是有诸多不便,比如集群管理、资源调度、文件管理等等。那么在这样一个百花齐放的容器时代涌现出了很多解决方案,比如 Mesos、Swarm、Kubernetes 等等,其中谷歌开源的 Kubernetes是作为老大哥的存在。Kubernetes的简称K8S,最初源于谷歌内部的Borg,提供了面向应用的容器集群部署和管理系统,所以整体的架构和borg很相似,整个架构有 apiserver,controller-manager,scheduler,etcd,kubelet,kube-proxy,network-plugin 等相关组件完成。
K8s 事前了解
K8S用来管理云平台中多个主机上的容器化应用,它的目的便是让部署容器化的应用简单且高效,它提供了应用部署,规划,更新,维护的一种机制。我们先来看看部署应用的迭代过程:
- 传统部署: 直接将应用程序部署在物理机上
- 虚拟化部署: 可以在一台物理机上运行多个虚拟机,每个虚拟机都是独立的一个环境
- 容器化部署: 与虚拟机类似, 但是共享了操作系统
为什么有了Docker容器化部署,还需要K8S呢。Docker 的容器化部署方式确实给我们带来了很多便利,但也存在一些问题。这个问题应该说不管物理机还是容器化,只要是集群部署都会存在的问题。例如,当一个容器故障停机了,怎么样保证高可用让另外一个容器立刻启动去替补上停机的容器。当并发访问量上来的时候,是否可以做到自动扩容,并发访问量下去的时候是否可以做到自动缩容等等。
而K8S的出现就是用来对docker容器进行管理和编排的工具,其是一个基于docker构建的调度服务,提供资源调度、均衡容灾、服务注册、动态扩容等功能套件
K8S的优点
- 自我修复:一旦某一个容器崩溃,能够在1秒左右迅速启动新的容器
- 弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整
- 服务发现:服务可以通过自动发现的形式找到它所依赖的服务
- 负载均衡:如果一个服务启动了多个容器,能够自动实现请求的负载均衡
- 版本回退:如果发现新发布的程序版本有问题,可以立即回退到原来的版本
- 存储编排:可以根据容器自身的需求自动创建存储卷
- 滚动更新:更新服务不中断,一次更新一个pod,而不是同时删除整个服务
- 资源监控:Node节点组件集成cAdvisor资源收集工具,可通过Heapster汇总整个集群节点资源数据,然后存储到InfluxDB时序数据库,再由Grafana展示
K8s 构成组件
一个完整的 Kubernetes 集群是由 控制节点 master 、工作节点 node 构成的,因此这种集群方式也分为 一主多从 和 多主多从,而每个节点上又会安装不同组件以提供服务。
Master
集群的控制平面,负责集群的决策(管理)。它旗下存在以下组件
- ApiServer :接收用户输入的命令,K8S里所有资源的增删改查等操作的唯一入口,提供认证、授权、Api 注册和发现等机制
- Scheduler:负责集群资源调度,按照预定的调度策略将 pod 调度到相应的 node 节点上
- ControllerManager:K8S里所有资源对象的自动化控制中心,集群内各种资源Controller的核心管理者,负责维护集群的状态,比如程序部署安排,故障检测,自动扩展,滚动更新等
- Etcd:K8S里的所有资源对象以及状态的数据都被保存在etcd中,负责存储集群中各种资源对象的信息
Scheduler
Scheduler负责集群内部资源调度,其会根据调度算法为新创建的pod选择一个node节点,Scheduler在整个集群中起到了承上启下的重要功能,承上是指她负责接收Controller Manager创建的新的pod,为其安排一个node节点,启下指的是当为pod选定node节点后,目标Node上的kubelet服务进程会接管该pod。这里就要提一下创建Pod的流程:
- kubectl发送创建pod的请求,此时这个命令被apiserver拦截,把创建的pod存储到etcd的podQueue
- Scheduler发起调用请求,此时这个命令被apiserver拦截,获取etcd中podQueue.NodeList,使用调度算法(调度算法:预选调度、优选策略)选择一个合适的node节点
- 把选择合适的pod、node存储到etcd中
- node节点上的Kubelet进程,发送请求获取pod、node对应创建资源
- 此时node发现pod是本node需要创建的,kubelet就开始创建pod
Controller Manager
每个资源都对应一个控制器(Kubernets Controller),其用来处理集群中常规的后台任务,而Controller Manager是用来负责管理控制器的。K8S集群有以下控制器:
- Replication Controller:保证Replication Controller中定义的副本数量与实际运行的pod数量一致。
- Node Controller:管理维护Node,定期检查Node节点的健康状态,标识出失效和未失效的Node节点。
- Namespace Controller:管理维护Namespace,定期清理无效的Namespace,包括Namespace下的API对象,例如pod和service等
- Service Controller:管理维护Service,提供负载以及服务代理。
- Endpoints Controller:管理维护Endpoints,即维护关联service和pod的对应关系,其对应关系通过Label来进行关联的
- Service Account Controller:管理维护Service Account,为每个Namespace创建默认的Service Account,同时为Service Account创建Service Account Secret。
- Persistent Volume Controller:持久化数据控制器,用来部署有状态服务
- Deamon Set Controller:让每一个Node节点都运行相同的服务
- Deployment Controller:无状态服务部署控制器
- Job Controller:管理维护Job,为Job创建一次性任务Pod,保证完成Job指定完成的任务数目。
- Pod Autoscaler Controller:实现pod的自动伸缩,定时获取监控数据,进行策略匹配,当满足条件时执行pod的伸缩动作。
etcd
etcd是一个第三方服务,分布式键值对存储系统,用于保存网络配置、集群状态等信息,例如service、pod等对象的信息。K8S中一共有两个服务需要用到etcd来协调和存储数据,分别是网络插件flannel和K8S本身,其中flannel使用etcd存储网络配置信息,K8S本身使用etcd存储各种对象的状态和元信息配置。
Node
集群的数据平面,负责为容器提供运行环境(干活)。它旗下存在以下组件
- Kubelet:负责Pod对应的容器的创建、启停等任务,同时与Master密切协作,实现集群管理的基本功能,即通过控制 docker 来创建、更新、销毁容器
- KubeProxy:负责提供集群内部的服务发现和负载均衡
- Docker Engine:Docker引擎,负责本机的容器创建和管理工作
在默认情况下Kubelet会向Master注册自己,一旦Node被纳入集群管理范围,kubelet进程就会定时向Master汇报自身的信息(例如机器的CPU和内存情况以及有哪些Pod在运行等),这样Master就可以获知每个Node的资源使用情况,并实现高效均衡的资源调度策略。而某个Node在超过指定时间不上报信息时,会被Master判定为失败,Node的状态被标记为不可用,随后Master会触发工作负载转移的自动流程
kubelet
kubelet是Mater在Node节点上的代理,每个Node节点都会启动一个kubelet进程,用来处理Mater节点下发到Node节点的任务,管理本机运行容器的生命周期,比如创建容器、Pod挂载数据卷、下载secret、获取容器和节点的状态等工作,kubelet将每个pod转换成一组容器。kubelet默认监听四个端口:10250、10255、10248、4194
- 10250端口:kubelet API的端口,也就是kubelet server与api server的通讯端口,定期请求apiserver获取自己所应当处理的任务,通过该端口可以访问和获取node资源及状态。
- 10248端口:健康检查的端口,通过访问该端口可以判断kubelet是否正常工作,可以通过 kubelet 的启动 参数 –healthz-port 和 –healthz-bind-address 来指定监听的地址和端口
- 4194端口:kubelet通过该端口可以获取到该节点的环境信息以及node上运行的容器状态等内容,访问 http://localhost:4194 可以看到 cAdvisor 的管理界面,通过 kubelet 的启动参 数 –cadvisor-port 可以指定启动的端口。
- 10255端口:提供了pod和node的信息,接口以只读形式暴露出去,访问该端口不需要认证和鉴权。
kube-proxy
在Node节点上实现Pod网络代理,维护网络规则和四层负载均衡工作,kube-proxy本质上类似于一个反向代理,我们可以把每个节点上运行的kube-proxy看作是service的透明代理兼LB。kube-proxy监听apiserver中service与endpoints的信息,配置iptables规则,请求通过iptables直接转发给pod。
docker
运行容器的引擎,pod内部运行的都是容器,这个容器是由Docker引擎创建的,Docker引擎是node节点的基础服务。
Pod
pod是一个大的容器,一个pod由一个或多个容器组成,pod中共享存储和网络,由K8S创建,pod内部的是docker容器,由Docker引擎创建。K8S不会直接管理容器,而是管理Pod。pod的作用是管理线上运行的应用程序,在通常情况下,在服务上线部署的时候,pod通常被用来部署一组相关的服务。而一个调用链上的服务就叫做一组相关的服务。但是实际生产上一般是一个Pod对应一个服务,不会在一个Pod上部署太多的服务。一个Node中有很多pause容器,这些pause容器和pod是一一对应的,每个Pod里面运行着一个特殊的被称为Pause的容器,其他的容器则为业务容器,这些容器共享Pause容器的网络和存储,因此他们之间的通信更高效,在同一个pod里面的容器之间仅需要通过localhost就可以通信。K8S中的pause容器主要为每个业务容器提供以下功能,从而对各个Pod进行了隔离:
- PID命名空间隔离:pod中不同的应用程序可以看到其他应用程序的进程ID
- 网络命名空间隔离:Pod中多个容器能够访问同一个IP和端口范围
- IPC命名空间隔离:Pod中多个容器能够使用System VIPC或POSIX消息队列进行通信
- UTS命名空间隔离:pod中多个容器共享一个主机名和挂在卷
- Pod中各个容器可以访问在Pod级别定义的Volumes
一个Pod创建的过程:首先kubelet会先创建一个pod,然后立马会创建一个pause容器,pause容器是默认创建的,然后再创建内部其他的业务容器。
K8S工作过程
- 运维人员向 apiserver 发出指令,可以通过API Server的REST API,也可用Kubectl命令行工具
- apiserver 响应命令,通过一系列认证授权,把 pod 数据存储到 etcd,创建 deployment 资源并初始化 (期望状态)
- controller-manager 通过 list-watch 机制,监测发现新的 deployment,将该资源加入到内部工作队列,发现该资源没有关联的 pod 和 replicaset,启用 deployment controller 创建 replicaset 资源,再启用 replicaset controller 创建 pod
- 创建完成后,将 deployment,replicaset,pod 资源更新存储到 etcd
- scheduler 通过 list-watch 机制,监测发现新的 pod,经过主机过滤、主机打分规则,将 pod 绑定 (binding) 到合适的主机
- 将绑定结果存储到 etcd
- kubelet 每隔 20s (可以自定义) 向 apiserver 通过 NodeName 获取自身 Node 上所要运行的 pod 清单,通过与自己的内部缓存进行比较,新增加 pod
- kubelet 调用 Docker API 创建并启动 pod
- kube-proxy 为新创建的 pod 注册动态 DNS 到 CoreOS。给 pod 的 service 添加 iptables/ipvs 规则,用于服务发现和负载均衡
- controller-manager 通过 control loop(控制循环)将当前 pod 状态与用户所期望的状态做对比,如果当前状态与用户期望状态不同,则 controller 会将 pod 修改为用户期望状态,实在不行会将此 pod 删掉,然后重新创建pod