前言
随着微服务的盛行,使用Docker作为微服务应用部署环境也越来越盛行。伴随着业务的进一步发展,系统中遍布着各种各样的容器,真实的场景可能是这样的:每一个服务包含成百上千个应用,而每一个应用又有着成百上千个实例。于是,容器的资源调度,部署运行,扩容缩容就是我们要面临的问题。
基于 Kubernetes 作为容器集群的管理平台被广泛应用,k8s已经成为容器编排调度领域的事实标准,是云原生组织CNCF主推的头号项目,容器化时代的操作系统,k8s将成为我们打开云原生时代大门的钥匙。
正文
核心概念
k8s的主要功能:
核心组件
Master 与 Node
k8s集群有两类角色,一种是 master ,一种是 Node(也叫worker)。
- master 是集群的"大脑",是集群中的控制节点,负责管理整个集群:调度、更新、扩缩容。
- Node 就是具体"干活"的,是集群中的工作节点,一个Node一般是一个虚拟机或物理机。我们的容器就是部署在Node节点上的。
下图展示的k8s集群中的一个master节点和多个Node节点。
Pod
出于像易用性、灵活性、稳定性等的考虑,Kubernetes 提出了一个“ Pod ”的概念,作为 Kubernetes 的最小调度单位。所以我们的应用在每个 Node 上运行的其实是一个 Pod。Pod 也只能运行在 Node 上。如下图:
那么,肯定有人会问,为什么提出pod这玩意呢?不是增加复杂度么?直接调度容器不是更加简单直接么?没错,笔者也曾经有过这样的疑问。但是后来慢慢理解了设计者的初衷:
Pod可以理解为容器组的概念。首先,容器本身就是一个小盒子了,Pod 相当于在容器上又包了一层小盒子。这个盒子里面的容器有什么特点呢?
- 可以通过 volume 共享存储。
- 有共享相同的网络配置。通俗点说就是有一样的ip地址,有一样的网卡和网络设置,在进行内网通信时的成本更低。
简而言之,在同一个pod内的多个容器可以共享相同的存储、网络等配置,这样就无需为每个容器单独去做配置,极大地降低了配置成本。
service
使用pod部署好了应用,下面我们会考虑如何访问到我们部署的应用呢?
最直接想到的方法就是直接通过Pod-ip+port
去访问,但如果实例数很多呢?好,拿到所有的 Pod-ip 列表,配置到负载均衡器中,轮询访问。但上面我们说过,Pod 可能会死掉,甚至 Pod 所在的 Node 也可能宕机,Kubernetes 会自动帮我们重新创建新的Pod。再者每次更新服务的时候也会重建 Pod。而每个 Pod 都有自己的 ip。所以 Pod 的ip 是不稳定的,会经常变化的。
面对这种变化我们就要借助另一个概念:Service。它就是来专门解决这个问题的。不管部署的Pod有多少个,不管它是更新、销毁还是重建,Service总是能发现并维护好它的ip列表。那么这个Service到底是怎么做到的呢?
Service是一个逻辑上的概念,可以与我们常规意义上理解的服务相对应,是一个大应用内提供单一职责的应用集合体,例如淘宝的账号服务、淘宝的订单服务等,都可以理解为这里的Service。Service为了实现动态路由的功能,对外也提供了多种入口:
- ClusterIP:Service 在集群内的唯一 ip 地址,我们可以通过这个 ip,均衡的访问到后端的 Pod,而无须关心具体的 Pod。这个IP地址本质上是一个虚拟IP地址,通过node节点上的kube-proxy组件来实现的,这块我们后面会详细介绍到。
- NodePort:Service 会在集群的每个 Node 上都启动一个端口,我们可以通过任意Node 的这个端口来访问到 Pod。
- LoadBalancer:在 NodePort 的基础上,借助公有云环境创建一个外部的负载均衡器,并将请求转发到 NodeIP:NodePort。
-
ExternalName:将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定)。
Label and Label Selector
最容易想到的方法是使用 Deployment 的名字。一个 Service 对应一个 Deployment 。当然这样确实可以实现。但kubernetes 使用了一个更加灵活、通用的设计 - Label 标签,通过给 Pod 打标签,Service 可以只负责一个 Deployment 的 Pod 也可以负责多个 Deployment 的 Pod 了。Deployment 和 Service 就可以通过 Label 解耦了。
滚动升级
滚动升级是Kubernetes中最典型的服务升级方案,主要思路是一边增加新版本应用的实例数,一边减少旧版本应用的实例数,直到新版本的实例数达到预期,旧版本的实例数减少为0,滚动升级结束。在整个升级过程中,服务一直处于可用状态。并且可以在任意时刻回滚到旧版本。
架构设计
Kubernetes 是用来管理容器集群的,下图是k8s集群的系统架构图,从中我们可以看到整个系统通常由一个Master节点和多个Node节点组成。
- Master 作为管理者,包括 APIServer,Scheduler,Controller Manager。
- Node作为副本部署的载体,包含多个 Pod,每个 Pod 又包含多个容器(container)。
下面我们再来详细了解下Master节点和Node 节点中的核心组件:
kubectl组件
-命令行客户端工具,作为控制整个k8s集群的操作入口。
master节点组件
master节点是主集群中的大脑,负责处理外部的api请求,分配调度任务以及管理容器的副本数等
- kube-scheduler:负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上
- kube-apiserver:提供了资源(Node,Pod,RC,Service等)操作的唯一入口,以REST API服务形式提供接口,作为整个系统的控制入口。并提供认证、授权、访问控制、API注册和发现等机制(kubectl client 连接该server)
- kube-controller-manager:负责维护集群的状态,比如故障检测、自动扩展、滚动更新等
Node节点组件
node节点负责干活,执行master节点指派的相关任务
- kubelet:控制容器运行,负责启动停止和容器;运行在每个计算节点上,作为agent,接收分配该节点的pods任务及管理容器,周期性获取容器状态,反馈给kube-apiserver
- kube-proxy:负责根据service生成网络规则,生成路由规则。
- Docker Engine:Docker容器引擎。
etcd集群
- etcd是kubernetes集群用来存储集群相关数据的数据仓库,保存了整个集群的状态。kubectl对集群的配置信息以及集群运行时的状态信息的持久化存储都是通过Etcd来完成的。
DNS
- 一个可选的DNS服务,用于为每个serivce对象创建DNS记录,这样所有的pod就可以通过DNS访问服务。
上图是k8s集群的系统架构及组件功能图,下面我们将逐个解析上文提到的各个核心组件。
API Server
这里,我们在重点介绍一下Master节点中的APIServer组件,它是。APIServer 的架构从上到下分为四层:
- API 层:主要以 REST 方式提供各种 API 接口,针对 Kubernetes 资源对象的 CRUD 和 Watch 等主要 API,还有健康检查、UI、日志、性能指标等运维监控相关的 API。
- 访问控制层:负责身份鉴权,核准用户对资源的访问权限,设置访问逻辑(Admission Control)。
- 注册表层:选择要访问的资源对象。PS:Kubernetes 把所有资源对象都保存在注册表(Registry)中,例如:Pod,Service,Deployment 等等。
- etcd 数据库:保存创建副本的信息。用来持久化 Kubernetes 资源对象的 Key-Value 数据库。
运行原理
我们来结合上面的交互图,来看一下k8s运行的原理:
- 用户通过 kubectl 给 Master 中的 APIServer 下部署命令。命令主体是以“.yaml”结尾的配置文件,包含副本的类型,副本个数,名称,端口,模版等信息。
- APIServer 接受到请求以后,会分别进行以下操作:权限验证(包括特殊控制),取出需要创建的资源,保存副本信息到etcd。
- APIServer 和 Controller Manager,Scheduler 以及 kubelete 之间通过 List-Watch 方式通信(事件发送与监听)。
- Controller Manager 通过 etcd 获取需要创建资源的副本数,交由 Scheduler 进行策略分析。
- 最后 kubelet 负责最终的 Pod 创建和容器加载。部署好容器以后,通过 Service 进行访问,通过 cAdvisor 监控资源。
模型对象
有印象的读者应该还记得,上文中曾经提到“用户通过 kubectl 给 Master 中的 APIServer 下部署命令。命令主体是以“.yaml”结尾的配置文件,包含副本的类型,副本个数,名称,端口,模版等信息。”。
我们在前面了解了k8s的核心概念、架构设计、运行原理,在实际使用过程中我们需要直接操作的还是各种各样的“.yaml”的配置文件,那么怎么配就成了必须要掌握的要点了。再具体实践怎么配之前,我们先了解一下模型对象,它是k8s对于各种配置文件的分类,模型对象决定了配置文件的骨架,把握它我们才能更好地再一堆复杂的配置中找到关键。
那到底都有哪些模型对象?他们之间都有什么关系呢?通过上图我们做了一个详细的总结。
kubernetes对象:
- 一种声明式的意图的记录,一般使用yaml文件描述对象
- kubernetes集群使用kubernetes对象来表示集群状态
- 通过API/kubectl管理kubernetes对象
通过多层次、多维度的标签定义,一个对应现实管理结构的k8s对象关系就建立起来了,不同角色的人员可以在同一个k8s集群上完成不同的工作而不会相互影响。
整个k8s集群都是以pod为中心,其他的各种对象也基本上是为pod提供各种功能服务的。
Controller控制器用于维护pod的状态,配置存储对象则为pod注入持久化数据,并且为pod持久化pod运行时产生的产生的数据,service将pod聚合在一起,统一对外提供服务。
Pod对象是不稳定了,ip+port的结合体不能保证稳定的存在,那么如何保证用户能够稳定地访问到我们的服务呢?这个时候就出现了一个抽象的service的概念:
全文小结
参考资料
- 我花了10个小时,写出了这篇 K8S 架构解析