Kubernetes核心组件
Kubernetes定义了一组构建块,它们可以共同提供部署、维护和扩展应用程序的机制。组成Kubernetes的组件设计为松耦合和可扩展的,这样可以满足多种不同的工作负载。可扩展性在很大程度上由Kubernetes API提供——它被作为扩展的内部组件以及Kubernetes上运行的容器等使用。
Kubernetes 主要由以下几个核心组件组成:
-
etcd 保存了整个集群的状态;
-
kube-apiserver 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;
-
kube-controller-manager 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
-
kube-scheduler 负责资源的调度,按照预定的调度策略将 Pod 调度到相应的机器上;
-
kubelet 负责维持容器的生命周期,同时也负责 Volume(CVI) 和网络 (CNI) 的管理;
-
Container runtime 负责镜像管理以及 Pod 和容器的真正运行 (CRI) ,默认的容器运行时为 Docker
-
kube-proxy 负责为 Service 提供 cluster 内部的服务发现和负载均衡;
除了核心组件,还有一些推荐的 Add-ons: -
kube-dns 负责为整个集群提供 DNS 服务
-
Ingress Controller 为服务提供外网入口
-
Heapster 提供资源监控
-
Dashboard 提供 GUI
-
Federation 提供跨可用区的集群
-
Fluentd-elasticsearch 提供集群日志采集、存储与查询
组件通信
Kubernetes 多组件之间的通信原理为: -
API Server 负责 etcd 存储的所有操作,且只有 API Server 才直接操作 etcd 集群。
-
API Server 对内(集群中的其他组件) 和对外(用户) 提供统一的 REST API,其他组件均通过 - API Server 进行通信,Controller Manager、Scheduler、Kube-proxy 和 Kubelet 等均通过 API - Serverwatch API 监测资源变化情况,并对资源作相应的操作。
所有需要更新资源状态的操作均通过 API Server 的 REST API 进行 -
- API Server 也会直接调用 Kubelet API(如 logs, exec, attach 等) ,默认不校验
-
Kubelet 证书,但可以通过 --kubelet-certificate-authority 开启(而 GKE通过 SSH 隧道保护它们之间的通信)
比如典型的创建 Pod 的流程为:
1、用户通过 REST API 创建一个 Pod
2、API Server 将其写入 etcd
3、Scheduluer 检测到未绑定 Node 的 Pod,开始调度并更新 Pod 的 Node 绑定
4、Kubelet 检测到有新的 Pod 调度过来,通过 Container Runtime 运行该 Pod
5、Kubelet 通过 Container Runtime 取到 Pod 状态,并更新到 API Server 中
Pod的分类
Pod 是 kubernetes 系统的基础单元,是由用户创建或部署的最小组件,也是 kubernetes 系统上运行容器化应用的资源对象。Kubernetes 集群中其他资源对象都是为 pod 这个资源对象做支撑来实现 kubernetes 管理应用服务的目的。
Kubernetes 集群组件主要包括主节点组件API Server、Controller Manager、Scheduler 以及子节点组件 kubelet、container Runtime(如docker)、kube-proxy 等。从与集群各组件交互角度讲述 pod 的创建、运行、销毁等生命周期,Pod 生命周期中的几种不同状态包括pending(待办)、running(正在运行)、succeeded(完成)、failed(失败)、Unknown(未知)。
与API Server交互
API Server 提供了集群与外部交互的接口,通过 kubectl 命令或者其他 API 客户端提交 pod spec 给 API Server 作为pod创建的起始。
- API Server 在接收到创建pod的请求之后,会根据用户提交的参数值来创建一个运行时的pod对象。
- 根据 API Server 请求的上下文的元数据来验证两者的 namespace 是否匹配,如果不匹配则创建失败。
- Namespace 匹配成功之后,会向 pod 对象注入一些系统数据,如果 pod 未提供 pod 的名字,- - 则 API Server 会将 pod 的 uid 作为 pod 的名字。
- API Server 接下来会检查 pod 对象的必需字段是否为空,如果为空,创建失败。
上述准备工作完成之后会将在 etcd 中持久化这个对象,将异步调用返回结果封装成 restful.response,完成结果反馈。
至此,API Server 创建过程完成,剩下的由 scheduler 和 kubelet 来完成,此时 pod 处于 pending(待办) 状态
与scheduler交互
当提交创建 pod 的请求与 API Server 的交互完成之后,接下来由 scheduler 进行工作,该组件主要是完成 pod 的调度来决定 pod 具体运行在集群的哪个节点上。注意,此处声明一点,API Server 完成任务之后,将信息写入到 etcd 中,此时 scheduler 通过 watch 机制监听到写入到 etcd 的信息然后再进行工作。
Scheduler 读取到写入到 etcd 中的 pod 信息,然后基于一系列规则从集群中挑选一个合适的节点来运行它,调度时主要通过三步来确定 pod 运行节点:
- 节点预选:基于一系列预选规则(如 PodFitsResource 和 MatchNode-Selector 等)对每个节点进行检查,将不符合的节点过滤掉从而完成节点预选。
- 节点优选:对预选出的节点进行优先级排序,以便选出最适合运行 pod 对象的节点。
- 从优先级结果中挑选出优先级最高的节点来运行 pod 对象,当此类节点多个时则随机选择一个。
注意:如果有特殊 pod 资源需要运行在特殊节点上,此时可以通过组合节点标签以及 pod 标签和标签选择器等来实现高级调度,如 MatchInterPodAffinity、MatchNodeSelector 和 PodToleratesNodeTaints 等预选策略,他们为用户提供自定义 Pod 亲和性或反亲和性、节点亲和性以及基于污点及容忍度的调度机制。
启动pod流程分析
kubelet 通过 API Server 监听 etcd 目录,同步 pod 列表。如果发现有新的 pod 绑定到本节点,则按照 pod 清单要求创建 pod,如果是发现 pod 被更新,则做出相应更改。
读取到 pod 的信息之后,如果是创建和修改 pod 的任务,则做如下处理:
1.为该 pod 创建一个数据目录
2.从 API Server 读取该 pod 清单
3.为该 pod 挂载外部卷
4.下载 pod 所需的 Secret
5.检查已经运行在节点中 pod,如果该 pod 没有容器或者 Pause 容器没有启动,则先停止pod里所有的容器进程。
6.使用 pause 镜像为每个pod创建一个容器,该容器用于接管 Pod 中所有其他容器的网络。
7.为 pod 中的每个容器做如下处理:1.为容器计算一个 hash 值,然后用容器的名字去查询对于 docker 容器的 hash 值。若查找到容器,且两者的 hash 值不同,则停止 docker 中容器中进程,并停止与之关联的 pause 容器,若相同,则不做处理。若容器被终止了,且容器没有指定的重启策略,则不做任何处理调用 docker client 下载容器镜像,并启动容器
此外,除了上述三个组件在创建 pod 过程中的交互,还有 controller-manager 来保证 pod 处于用户期望状态(即保证 pod 永远处于存活状态)等功能以及 proxy 用于集群内 pod 之间通信等。
Kubernetes的网络模型组成
- Pod内部docker容器之间网络通信
- Pod所在的网络之间通信
- Pod和Service之间网络通信
- 外界与Service之间网络通信
Pod内部docker容器之间网络通信
Kubernetes使用了一种“IP-per-pod”网络模型:为每一个Pod分配了一个IP地址,Pod内部的docker容器共享Pod的网络空间,即它们共享Pod的网卡和IP。其原理是根据docker的“container网络”模型而来。
Pod所在的网络之间通信
Kubernetes把各node主机上的docker的bridge网络“外包”给了flannel,然后通过etcd将各node主机上的bridge网络信息收集起来,因此每个node之间的网络使用的是同网络的不同IP,保证了网络通讯的可靠性。其原理是根据docker的“bridge网络”模型而来。
Pod和Service之间网络通信
在Kubernetes体系中Pod是不稳定的,Pod的IP地址会发生变化,所以Kubernetes引进了Service的概念。Service是一个抽象的实体,Kubernetes在创建Service实体时,为其分配了一个虚拟的IP,当外界需要访问Pod里的容器提供的功能时,不直接使用Pod的IP地址和端口,而是访问Service的这个虚拟IP和端口,由Service把请求转发给它背后的Pod。Kubernetes在创建Service时,根据Service的标签选择器(Label Selector)来查找Pod,据此创建与Service同名的EndPoints对象。当Pod的地址发生变化时,EndPoints也随之变化。Service接受到请求时,就能通过EndPoints找到对应的Pod。再深入探究,Service只是一个虚拟的概念,真正完成请求转发的是运行在node节点上的kube-proxy。Service的虚拟IP就是由kube-proxy实现的。kube-proxy有两种请求转发模式:userspace模式和iptables模式。在Kubernetes v1.1版本之前默认是userspace模式,v1.2版本后默认是iptables模式。
补充说明iptables模式:
当创建Service时,所有node节点上的kube-proxy都会建立两级iptables规则,一级为Service创建,目的是将<服务虚拟IP,端口>的流量转给后端,另一级为EndPoints创建,目的是用于选择Pod。当service.spec.sessionAffinity值为”ClientIP”时,iptables模式选择Pod的算法和userspace模式相同(选择与请求来源IP更接近的Pod)。当service.spec.sessionAffinity值为”None”时,随机选择Pod,所以如果被选择的Pod没有响应,不会尝试选择另一个Pod。
外界与Service之间网络通信
①ClusterIP类型,这种类型的Service只会得到虚拟的IP和端口,只能在Kubernetes集群内部被访问,此模型是为默认类型。
②NodePort类型,这种类型的Service除了会得到虚拟的IP和端口,Kubernetes还会在所有node节点上为其分配端口。分配的端口的值可以通过spec.ports[*].nodePort指定,或由Kubernetes在配置好的区间里分配(默认为30000-32767)。这种Service即可以从Kubernetes集群通过虚拟IP:端口访问,也可以从集群外部通过Node节点的IP:nodePort访问。
③LoadBalancer类型,这种类型的Service除了会得到虚拟的IP和端口,Kubernetes还会在所有Node节点上为其分配端口,然后为其开通负载均衡。这种Service即可以从Kubernetes集群通过虚拟IP:端口访问,也可以从集群外部通过node节点的IP:nodePort访问,还可以通过负载均衡的IP访问。