[云原生] Kubernetes 架构原则和对象设计
云计算是对计算资源做抽象,然后面向抽象资源来做应用。
云计算平台的分类,可以分成以 Openstack 为典型的虚拟化平台和以谷歌 borg 为典型的基于进程的作业调度平台。
- 以 Openstack 为典型的虚拟化平台
- 虚拟机构建和业务代码部署分离。
- 可变的基础架构使后续维护风险变大。
- 以谷歌 borg 为典型的基于进程的作业调度平台
- 技术的迭代引发 borg 的换代需求。 早期的隔离依靠 chroot jail 实现,一些不合理的设计需要在新产品中改进。
- 对象之间的强依赖 job 和 task 是强包含关系,不利于重组。
- 所有容器共享 IP,会导致端口冲突,隔离困难等问题。
- 为超级用户添加复杂逻辑导致系统过于复杂
- 技术的迭代引发 borg 的换代需求。 早期的隔离依靠 chroot jail 实现,一些不合理的设计需要在新产品中改进。
Google Borg的简介可以参考Paper-Reading-Google Borg。
Kubernetes
Kubernetes 是谷歌开源的容器集群管理系统,是 Google 多年大规模容器管理技术 Borg 的开源版本,主要功能包括:
- 基于容器的应用部署、维护和滚动升级;
- 负载均衡和服务发现;
- 跨机器和跨地区的集群调度;
- 自动伸缩;
- 无状态服务和有状态服务;
- 插件机制保证扩展性。
kubnetes源代码可见Github
kubernetes基于声明式系统(Declarative)。声明式系统指程序代码描述系统应该做什么而不是怎么做。仅限于描述要达到什么目的,如何达到目的交给系统。
Kubernetes 的所有管理能力构建在对象抽象的基础上,核心对象包括:
- Node:计算节点的抽象,用来描述计算节点的资源抽象、健康状态等。
- Namespace:资源隔离 的基本单位,可以简单理解为文件系统中的目录结构。
- Pod:用来描述应用实例,包括镜像地址、资源需求等。 Kubernetes 中最核心的对象,也是打通应用和基础架构的秘密武器。
- Service:服务如何将应用发布成服务,本质上是负载均衡和域名服务的声明。
Kubernetes 采用与 Borg 类似的架构
Kubernetes 的主节点(Master Node)
- API服务器 API Server: 这是 Kubernetes 控制面板中 唯一 带有用户可访问 API 以及用户可交互的组件。API 服务器会暴露一个 RESTful 的 Kubernetes API 并使用 JSON 格式的清单文件(manifestfiles)。
- 群的数据存储 Cluster Data Store:Kubernetes 使 用“etcd”。这是一个强大的、稳定的、高可用的键值存储,被Kubernetes 用于长久储存所有的 API 对象。
- 控制管理器 Controller Manager:被称为“kube-controller manager”,它运行着所有处理集群日常任务的控制器。包括了节点控制器、副本控制器、端点(endpoint)控制器以及服务账户等。
- 调度器 Scheduler:调度器会监控新建的 pods(一组或一个容器)并将其分配给节点。
Kubernetes 的工作节点(Worker Node)
- Kubelet:负责调度到对应节点的 Pod 的生命周期管理,执行任务并将 Pod 状态报告给主节点的渠道,通过容器运行时(拉取镜像、启动和停止容器等)来运行这些容器。它还会定期执行被请求的容器的健康探测程序。
- Kube-proxy:它负责节点的网络,在主机上维护网络规则并执行连接转发。它还负责对正在服务的 pods 进行负载平衡。
etcd
etcd 是 CoreOS 基于 Raft 开发的分布式 key-value 存储,可用于服务发现、共享配置以及一致性保障(如数据库选主、分布式锁等)。
- 基本的 key-value 存储;
- 监听机制;通过watch,建立长连接
- key 的过期及续约机制,用于监控和服务发现;
- 原子 CAS 和 CAD,用于分布式锁和 leader 选举。
etcd 是 基于raft算法的工业级的实现。Raft算法是一种用于管理复制日志的一致性算法,其功能与Paxos算法相同类似,从本质上说,Raft 算法是通过一切以领导者为准的方式,实现一系列值的共识和各节点日志的一致。
可以通过kubectl直接访问 etcd 的数据, 可以发现etcd通过watch的方式与API Server通信
kubectl get pod -n kube-system # 查询kube-system namespace(控制面的组件)下的所有pod
kubectl -n kube-system exec -it etcd-cadmin sh # 通过exec -it进入容器查询数据
etcdctl --endpoints https://localhost:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt get --keys-only --prefix / # 查询所有/开头的key
etcdctl --endpoints https://localhost:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt watch --prefix <key name> # 通过watch监听对象变化(长连接)
kubeadm init
--image-repository registry.aliyuncs.com/google_containers
--kubernetes-version v1.24.3
--pod-network-cidr=192.168.0.0/16
--apiserver-advertise-address=192.168.34.2
APIServer
Kube-APIServer 是 Kubernetes 最重要的核心组件之一,主要提供以下功能:
- 提供集群管理的 REST API 接口,包括:
- 认证 Authentication;
- 授权 Authorization;
- 准入 Admission(Mutating & Valiating)。
- 提供其他模块之间的数据交互和通信的枢纽(其他模块通过 APIServer 查询或修改数据,只有 APIServer 才直接操作 etcd)。
- APIServer 提供 etcd 数据缓存 以减少集群对 etcd 的访问。
request处理过程:
request发送到apiserver,首先最开始有HTTP handle,这个handle接收到这个请求之后,请求会被发送到认证鉴权两个模块,认证其实就是就是api网关最基础的能力,我要知道你是谁,这里提供了一系列的认证手段。鉴权:我知道的你是谁之后,然后要知道你有没有操作权限。
认证鉴权之后,比如说某个请求属性没有设置,我希望给你一个默认值,或者修改你的属性值。这个时候我希望在apiserver端对你的request增加一些属性,这个对方就会走mutating,所谓的mutating就是变形,它是支持webhook的,除了kubernetes自身可以对你这个数据做一些修改,你还可以通过一些webhook来针对这些对象做一些修改。mutating做完之后,就走入了k8s自定义的这些对象的schema validation,由于之前做过变形,我要去看变形之后的对象是不是合法的,是不是符合kubermetes的规范。
做完上面的之后,如果你还要去做一些附加的校验,比如说k8s规定的名字不能超过255,但是我希望名字在我自己的生产环境里面不超过63,那么你就可以附加一些最强的validation在webhook里面,通过validating admission plugin来调用你的webhook,把你的校验逻辑加上去。
最后所有的这些流转做完了之后,整个数据才会存放到etcd里面去,到此位置,数据的持久化就做完了。
Controller Manager
- Controller Manager 是集群的大脑,是确保整个集群动起来的关键;
- 作用是确保 Kubernetes 遵循声明式系统规范,确保系统的真实状态(ActualState)与用户定义的期望状态(Desired State)一致;
- Controller Manager 是多个控制器的组合,每个 Controller 事实上都是一个control loop,负责侦听其管控的对象,当对象发生变更时完成配置;
- Controller 配置失败通常会触发自动重试,整个集群会在控制器不断重试的机制下确保最终一致性(Eventual Consistency)
可以理解为生产者与消费者模型,任何的k8s对象都会生成informer(观察框架)和lister(获取全量数据的框架),当我们去生成控制器时,例如基于Kubebuild,或者社区的syncontroller仿写控制器,第一步就是通过lister和informer去观察监听某一个对象,监听的对象就会有一些event(如Add、Delete、Update),不同的event可以注册handler,发生event把对象的key(namespace和name)取出来放到Ratelimitq里面,另外一边会启gorouting,可以配置多个worker,不停的从queue里边获取数据,只要队列里边有数据,说明对象变化还未被处理,worker主要工作就是完成配置,直到队列里空为止,如果出错,就会以ratelimit方式enqueue回去。
Informer的内部机制
Informer提供List&Watch的机制。
k8s所有对象的访问都由apiserver去操作,所谓的informer会提供一个list和watch机制(informer在启动以后会发起一个长链接请求apiserver,通常会第一时间list,接下来会watch,监听apiserver对象的任何变化)
apiserver是一个json的序列化数据,程序要消费这个数据,则需要反序列化(字符串转换为go对象),上图中Reflector,是通过反射机制实现的,会解析对象的key,apiserver中的json tag,添加对象至环状的buffer(Delta Fifo Queue)里边,会通知informer来处理,首先会对序列化好的对象(namespace)放到一个store里边,并通过索引indexer来进行管理,这样后续访问k8s对象的时候就不用去找apiserver访问,直接可以通过本地Localstore获取,减少对apiserver的访问,同时对象的变化会通过event注册到event handler,然后放到workqueue里边,另外一边从woker里边获取数据来进行处理。
注意点:
任何控制器都应该用shareinformer,所有的对象只要在客户端(控制器),所有对象在本地已有缓存,由shareinformer来保证本地缓存和apiserver一致性,例如二开控制器时,尽量减少对apiserver的操作,如果更新操作可以通过apiserver来刷新数据。
控制器的协同工作原理
如果单纯创建一个Pod资源,也是可行的,但在我们生产环境中,一个Pod是无法达到k8s本身平台的机制,例如:故障自愈,高可用(冗余部署)负载均衡(service)等,所以k8s有控制器这样一个更高阶的资源抽象-deployment,以此来描述应用的部署交付。
定义deploy资源——>声明replicas——>image声明——>应用运行
k get deploy
k get pod -owide
k delete pod nginx
cat nginx-deploy.yaml
k create -f ng.yaml
k get deployment .apps/nginx
k describe deployment .apps/nginx ## Event 最近的事件
k get replicaset nginx
k describe replicaset
k get pod
创建了deployment资源对象之后,发给apiserver组件,apiserver会进行认证鉴权(读取本地配置文件),之后会存储到etcd数据库中,接下来就是集群的大脑controller manager组件开始干活了,具体也可通过查看日志kubectl logs -f kube-controller-manager-x.x.x.x -nkube-system,它的行为主要依赖deployment里的yaml定义(例如:需要5个副本,运行nginx镜像),然后会创建一个replicasets对象,包括pod的模板,副本数,创建RS之后会发到apiserver,controller manager里边也有一个ReplicaSet Controller,它也在监听apiserver,发现有一个rs的创建请求,发现pod不存在,就新建一个Pod,创建完之后查看pod详细信息有一个nodeName属性,这时调度器来做调度,它背后的逻辑一直是watch资源的对象(例如目前介绍的Pod),它首先会过滤当前集群不符合调度pod资源的节点,其次在符合要求的节点里选择打分比较高的节点,整个调度结束之后会把结果返回给apiserver;接下来由节点上的kubelet组件干活,首先会看apiserver中的pod信息,对比本地是否存在该容器pod,不存在的话说明是一个新pod,然后执行创建pod的动作:k8s启动docker不会挂载网络,使用cni插件,只启runtime,例如pod需外挂存储,k8s也有对应的csi插件来实现。
Scheduler
特殊的 Controller,工作原理与其他控制器无差别。
Scheduler 的特殊职责在于监控当前集群所有未调度的 Pod,并且获取当前集群所有节点的健康状况和资源使用情况,为待调度 Pod 选择最佳计算节点,完成调度。
调度阶段分为:
- Predict:过滤不能满足业务需求的节点,如资源不足、端口冲突等。
- Priority:按既定要素将满足调度需求的节点评分,选择最佳节点。
- Bind:将计算节点与 Pod 绑定,完成调度。
Kubelet
Kubernetes 的初始化系统(init system)
- 从不同源获取 Pod 清单,并按需求启停 Pod 的核心组件:
- Pod 清单可从本地文件目录,给定的 HTTPServer 或 Kube-APIServer 等源头获取;
- Kubelet 将运行时,网络和存储抽象成了 CRI,CNI,CSI。
- 负责汇报当前节点的资源信息和健康状态.
- 负责 Pod 的健康检查和状态汇报。
Kube-Proxy
- 监控集群中用户发布的服务,并完成负载均衡配置。
- 每个节点的 Kube-Proxy 都会配置相同的负载均衡策略,使得整个集群的服务发现建立在分布式负载均衡器之上,服务调用无需经过额外的网络跳转(Network Hop)。
- 负载均衡配置基于不同插件实现:
- userspace。
- 操作系统网络协议栈不同的 Hooks 点和插件:
- iptables;
- ipvs。
除了核心组件,推荐的 Add-ons
- kube-dns:负责为整个集群提供 DNS 服务;
- Ingress Controller:为服务提供外网入口;
- MetricsServer:提供资源监控;
- Dashboard:提供 GUI;
- Fluentd-Elasticsearch:提供集群日志采集、存储与查询。
了解kubectl
kubectl 是一个 Kubernetes 的命令行工具,它允许Kubernetes 用户以命令行的方式与 Kubernetes 交互,其默认读取配置文件 ~/.kube/config。
- kubectl 会将接收到的用户请求转化为 rest 调用以rest client 的形式与 apiserver 通讯。
- apiserver 的地址,用户信息等配置在 kubeconfig。
k get ns default -v 9 #-v 9打开debug log
kubectl 常用命令
- kubectl get po –oyaml -w kubectl 可查看对象。//ns;
-oyaml 输出详细信息为 yaml 格式。
-w watch 该对象的后续变化。
-owide 以详细列表的格式查看对象。 - Kubectl describe
kubectl describe 展示资源的详细信息和相关 Event。 - kubectl exec
kubectl exec 提供进入运行容器的通道,可以进入容器进行 debug。 - kubectl logs
Kubectl logs 可查看 pod 的标准输入(stdout, stderr),与 tail 用法类似。
深入理解 Kubernetes
![Kubernetes生态系统]
Kubernetes的设计理念:
- 可扩展性
- 基于CRD的扩展
- 插件化的生态系统
- 高可用
- 基于 replicaset,statefulset 的应用高可用
- Kubernetes 组件本身高可用
- 可移植性
- 多种 host Os 选择
- 多种基础架构的选择
- 多云和混合云
- 安全
- 基于 TLS 提供服务
- Serviceaccount 和 user
- 基于 Namespace 的隔离
- secret
- Taints,psp, networkpolicy
分层架构
Kubernetes 设计理念和功能其实就是一个类似 Linux 的分层架构,如下图所示
- 核心层:Kubernetes 最核心的功能,对外提供 API 构建高层的应用,对内提供插件式应用执行环境
- 应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS 解析等)
- 管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态 Provision 等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy 等)
- 接口层:kubectl 命令行工具、客户端 SDK 以及集群联邦
- 生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
- Kubernetes 外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS 应用、ChatOps 等
- Kubernetes 内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等
API 设计原则
- 所有 API 都应是声明式的
- 相对于命令式操作,声明式操作对于重复操作的效果是稳定的,这对于容易出现数据丢失或重复的分布式环境来说是很重要的。
- 声明式操作更易被用户使用,可以使系统向用户隐藏实现的细节,同时也保留了系统未来持续优化的可能性。
- 此外,声明式的 API 还隐含了所有的 API 对象都是名词性质的,例如 Service、Volume 这些 API 都是名词,这些名词描述了用户所期望得到的一个目标对象。
- API 对象是彼此互补而且可组合的
- 这实际上鼓励 API 对象尽量实现面向对象设计时的要求,即“高内聚,松耦合”,对业务相关的概念有一个合适的分解,提高分解出来的对象的可重用性。
- 高层 API 以操作意图为基础设计
- 如何能够设计好 API,跟如何能用面向对象的方法设计好应用系统有相通的地方,高层设计一定是从业务出发,而不是过早的从技术实现出发。
- 因此,针对 Kubernetes 的高层 API 设计,一定是以 Kubernetes 的业务为基础出发,也就是以系统调度管理容器的操作意图为基础设计
- 低层 API 根据高层 API 的控制需要设计
- 设计实现低层 API 的目的,是为了被高层 API 使用,考虑减少冗余、提高重用性的目的,低层 API的设计也要以需求为基础,要尽量抵抗受技术实现影响的诱惑。
- 尽量避免简单封装,不要有在外部 API 无法显式知道的内部隐藏的机制
- 简单的封装,实际没有提供新的功能,反而增加了对所封装 API 的依赖性。
- 例如 StatefulSet 和 ReplicaSet,本来就是两种 Pod 集合,那么 Kubernetes 就用不同 API 对象来定义它们,而不会说只用同一个 ReplicaSet,内部通过特殊的算法再来区分这个 ReplicaSet 是有状态的还是无状态
架构设计原则
- 只有 APIServer 可以直接访问 etcd 存储,其他服务必须通过 KubernetesAPI 来访问集群状态;
- 单节点故障不应该影响集群的状态;
- 在没有新请求的情况下,所有组件应该在故障恢复后继续执行上次最后收到的请求(比如网络分区或服务重启等);
- 所有组件都应该在内存中保持所需要的状态,APIServer 将状态写入 etcd 存储,而其他组件则通过 APIServer 更新并监听所有的变化;
- 优先使用事件监听而不是轮询。
引导(Bootstrapping)原则
- Self-hosting 是目标。
- 减少依赖,特别是稳态运行的依赖。
- 通过分层的原则管理依赖。
- 循环依赖问题的原则:
- 同时还接受其他方式的数据输入(比如本地文件等),这样在其他服务不可用时还可以手动配置引导服务;
- 状态应该是可恢复或可重新发现的;
- 支持简单的启动临时实例来创建稳态运行所需要的状态,使用分布式锁或文件锁等来协调不同状态的切换(通常称为 pivoting 技术);
- 自动重启异常退出的服务,比如副本或者进程管理器等。
核心技术概念和 API 对象
API 对象是 Kubernetes 集群中的管理操作单元。
Kubernetes 集群系统每支持一项新功能,引入一项新技术,一定会新引入对应的 API 对象,支持对该功能的管理操作。
每个 API 对象都有四大类属性:
- TypeMeta
Kubernetes对象的最基本定义,它通过引入GKV(Group,Kind,Version)模型定义了一个对象的类型。 - MetaData
Metadata 中有两个最重要的属性:Namespace和Name,分别定义了对象的Namespace 归属及名字,这两个属性唯一定义了某个对象实例。 - Spec
Spec 是用户的期望状态,由创建对象的用户端来定义。 - Status
Status 是对象的实际状态,由对应的控制器收集实际状态并更新
核心对象概览
Node
- Node 是 Pod 真正运行的主机,可以物理机,也可以是虚拟机。
- 为了管理 Pod,每个 Node 节点上至少要运行 container runtime(比如 Docker 或者 Rkt)、Kubelet 和 Kube-proxy 服务。
Namespace
Namespace 是对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或用户组。
常见的 pods, services, replication controllers 和 deployments 等都是属于某一个 Namespace 的(默认是 default),而 Node, persistentVolumes等则不属于任何 Namespace。
Pod
- Pod 是一组紧密关联的容器集合,它们共享 PID、IPC、Network 和 UTS namespace,是 Kubernetes 调度的基本单位。
- Pod 的设计理念是支持多个容器在一个 Pod 中共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。
- 同一个 Pod 中的不同容器可共享资源:
- 共享网络 Namespace;
- 可通过挂载存储卷共享存储;
- 共享 Security Context。
如何通过 Pod 对象定义支撑应用运行?
环境变量:
- 直接设置值;
- 读取 Pod Spec 的某些属性;
- 从 ConfigMap 读取某个值;
- 从 Secret 读取某个值。
存储卷
通过存储卷可以将外挂存储挂载到 Pod 内部使用。存储卷定义包括两个部分: Volume 和 VolumeMounts。
- Volume:定义 Pod 可以使用的存储卷来源;
- VolumeMounts:定义存储卷如何 Mount 到容器内部。
资源限制
Kubernetes 通过 Cgroups 提供容器资源管理的功能,可以限制每个容器的CPU 和内存使用,比如对于刚才创建的 deployment,可以通过下面的命令限制nginx 容器最多只用 50% 的 CPU 和 128MB 的内存:
$ kubectl set resources deployment nginx-app -c=nginx --limits=cpu=500m,memory=128Mi deployment "nginx" resource requirements updated
健康检查
Kubernetes 作为一个面向应用的集群管理工具,需要确保容器在部署后确实处在正常的运行状态。
- 探针类型:
- LivenessProbe
- 探测应用是否处于健康状态,如果不健康则删除并重新创建容器。
- ReadinessProbe
- 探测应用是否就绪并且处于正常服务状态,如果不正常则不会接收来自 Kubernetes Service 的流量。
- StartupProbe
- 探测应用是否启动完成,如果在 failureThreshold*periodSeconds 周期内未就绪,则会应用进程会被重启。
- 探活方式:
- Exec
- TCP socket
- HTTP
ConfigMap
- ConfigMap 用来将非机密性的数据保存到键值对中。
- 使用时, Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。
- ConfigMap 将环境配置信息和 容器镜像解耦,便于应用配置的修改。
密钥对象(Secret)
- Secret 是用来保存和传递密码、密钥、认证凭证这些敏感信息的对象。
- 使用 Secret 的好处是可以避免把敏感信息明文写在配置文件里。
- Kubernetes 集群中配置和使用服务不可避免的要用到各种敏感信息实现登录、认证等功能,例如访问 AWS 存储的用户名密码。
- 为了避免将类似的敏感信息明文写在所有需要使用的配置文件中,可以将这些信息存入一个 Secret 对象,而在配置文件中通过 Secret 对象引用这些敏感信息。
- 这种方式的好处包括:意图明确,避免重复,减少暴漏机会。
用户(User Account)& 服务帐户(Service Account)
顾名思义,用户帐户为人提供账户标识,而服务账户为计算机进程和 Kubernetes 集群中运行的 Pod 提供账户标识。
用户帐户和服务帐户的一个区别是作用范围:
- 用户帐户对应的是人的身份,人的身份与服务的 Namespace 无关,所以用户账户是跨Namespace 的;
- 而服务帐户对应的是一个运行中程序的身份,与特定 Namespace 是相关的。
Service
Service 是应用服务的抽象,通过 labels 为应用提供负载均衡和服务发现。匹配 labels 的 Pod IP 和端口列表组成 endpoints,由Kube-proxy 负责将服务IP 负载均衡到这些 endpoints 上。
每个 Service 都会自动分配一个 cluster IP(仅在集群内部可访问的虚拟地址) 和 DNS 名,其他容器可以通过该地址或 DNS 来访问服务,而不需要了解后端容器的运行。
副本集(Replica Set)
- Pod 只是单个应用实例的抽象,要构建高可用应用,通常需要构建多个同样的副本,提供同一个服务。
- Kubernetes 为此抽象出副本集 ReplicaSet,其允许用户定义 Pod 的副本数,每一个 Pod 都会被当作一个无状态的成员进行管理,Kubernetes 保证总是有用户期望的数量的 Pod 正常运行。
- 当某个副本宕机以后,控制器将会创建一个新的副本。
- 当因业务负载发生变更而需要调整扩缩容时,可以方便地调整副本数量。
部署(Deployment)
- 部署表示用户对 Kubernetes 集群的一次更新操作。
- 部署是一个比 RS 应用模式更广的 API 对象,可以是创建一个新的服务,更新一个新的服务,也可以是滚动升级一个服务。
- 滚动升级一个服务,实际是创建一个新的 RS,然后逐渐将新 RS 中副本数增加到理想状态,将旧 RS 中的副本数减小到 0 的复合操作。
- 这样一个复合操作用一个 RS 是不太好描述的,所以用一个更通用的 Deployment 来描述。
- 以 Kubernetes 的发展方向,未来对所有长期伺服型的的业务的管理,都会通过 Deployment 来管理。
有状态服务集(StatefulSet)
- 对于 StatefulSet 中的 Pod,每个 Pod 挂载自己独立的存储,如果一个 Pod 出现故障,从其他节点启动一个同样名字的Pod,要挂载上原来 Pod 的存储继续以它的状态提供服务。
- 适合于 StatefulSet 的业务包括数据库服务 MySQL 和 PostgreSQL,集群化管理服务 ZooKeeper、etcd 等有状态服务。
- 使用 StatefulSet,Pod 仍然可以通过漂移到不同节点提供高可用,而存储也可以通过外挂的存储来提供高可靠性,StatefulSet 做的只是将确定的 Pod 与确定的存储关联起来保证状态的连续性。
Statefulset 与 Deployment 的差异
- 身份标识
- StatefulSet Controller 为每个 Pod 编号,序号从0开始。
- 数据存储
- StatefulSet 允许用户定义 volumeClaimTemplates,Pod 被创建的同时,Kubernetes 会以volumeClaimTemplates 中定义的模板创建存储卷,并挂载给 Pod。
- StatefulSet 的升级策略不同
- onDelete
- 滚动升级
- 分片升级
任务(Job)
- Job 是 Kubernetes 用来控制批处理型任务的 API 对象。
- Job 管理的 Pod 根据用户的设置把任务成功完成后就自动退出。
- 成功完成的标志根据不同的 spec.completions 策略而不同:
- 单 Pod 型任务有一个 Pod 成功就标志完成;
- 定数成功型任务保证有 N 个任务全部成功;
- 工作队列型任务根据应用确认的全局成功而标志成功。
后台支撑服务集(DaemonSet)
- 长期伺服型和批处理型服务的核心在业务应用,可能有些节点运行多个同类业务的 Pod,有些节点上又没有这类 Pod 运行;
- 而后台支撑型服务的核心关注点在 Kubernetes 集群中的节点(物理机或虚拟机),要保证每个节点上都有一个此类 Pod 运行。
- 节点可能是所有集群节点也可能是通过 nodeSelector 选定的一些特定节点。
- 典型的后台支撑型服务包括存储、日志和监控等在每个节点上支撑 Kubernetes 集群运行的服务。
存储 PV 和 PVC
- PersistentVolume(PV)是集群中的一块存储卷,可以由管理员手动设置,或当用户创建 PersistentVolumeClaim(PVC)时根据 StorageClass 动态设置。
- PV 和 PVC 与 Pod 生命周期无关。也就是说,当 Pod 中的容器重新启动、Pod 重新调度或者删除时,PV 和 PVC 不会受到影响,Pod 存储于 PV 里的数据得以保留。
- 对于不同的使用场景,用户通常需要不同属性(例如性能、访问模式等)的 PV。
CustomResourceDefinition
- CRD 就像数据库的开放式表结构,允许用户自定义 Schema。
- 有了这种开放式设计,用户可以基于 CRD 定义一切需要的模型,满足不同业务的需求。
- 社区鼓励基于 CRD 的业务抽象,众多主流的扩展应用都是基于 CRD 构建的,比如 Istio、Knative。
- 甚至基于 CRD 推出了 Operator Mode 和 Operator SDK,可以以极低的开发成本定义新对象,并构建新对象的控制器
参考:
Kubernetes 架构原则和对象设计
Kubernetes 文档
A basic instruction for Kubernetes setup and understanding