CSI工作流程

CSI 即容器存储接口,借助 CSI 就可以很容易的给工作负载提供存储使用。K8s 的存储插件又分为 in-tree 和 out-of-tree 两种类型,前者是与 K8s 主库共同迭代维护,后者则是独立维护的存储插件。

out-of-tree 类型的插件则是通过 gRPC 与 K8s 的组件进行交互,为了简化 CSI 的开发与部署,K8s 也提供了多个 sidecar 组件:

  1. node-driver-registrar: 监听来自 kubelet 的 gRPC 请求,从 CSI driver 获取驱动程序信息(通过 NodeGetInfo 方法),并使用 kubelet 插件注册机制在该节点上的 kubelet 中对其进行注册
  2. provisioner: 监听来自 kube-apiserver 的 gRPC 请求,监听 PVC 的创建和删除,调用 CSI driver 创建和删除 PV(通过 CreateVolumeDelete Volume)方法
  3. attacher: 监听来自 kube-apiserver 的 gRPC 请求,监听 volumeAttachment 对象并触发 CSI 执行 ControllerPublishVolumeControllerUnpublishVolume 的操作
  4. resizer: 监听来自 kube-apiserver 的 gRPC 请求,监听 PVC 的修改,调用 CSI Controller 执行 ExpandVolume 方法,来调整 Volume 的大小
  5. livenessProbe: 检查 CSI 程序的健康状态,如不健康则会进行重启

CSI 工作流程

CSI 的工作流分为三个阶段:

  1. Provision/Delete
  2. Attach/Detach
  3. Mount/Unmount

这三个阶段会用到 Sidecar 组件,也会用到 K8s 的 PV Controller 和 AD Controller 组件

当然并不是所有的 CSI 都会经历这三个阶段,如 NFS CSI 的工作流就没有涉及 volumeAttachment
nfs-csi

Privision 阶段

在此阶段,Sidecar provisioner 和 PV Controller 都会监听 PVC 资源

  1. 当 PV Controller 观察到新的 PVC 被创建时,就会去判断是否有与之匹配的 in-tree 插件,如果没有则判定为 out-of-tree,并为该 PVC 添加 annotation
    pvc-annotation
  2. provisioner 观察到 PVC 的 annotation 与自己的 CSI 是相匹配的时候,就会去调用 CreateVolume 方法
  3. CreateVolume 调用返回成功时,provisioner 就会创建 PV
  4. PV Controller 监听到该 PV 时,就会将其与 PVC 做绑定

Attach 阶段

在此阶段,会将数据卷附在一个节点上

  1. AD Controller 监听到 Pod 被调度到某个节点后,会调用 in-tree 内部接口创建 volumeAttachment 资源
  2. attacher 监听到 volumeAttachment 就会调用 ControllerPublishVolume 接口
  3. 当接口返回成功时就会将 volumeAttachment 资源的 status.attached 设置为 true
    volume-attachment-status

Mount 阶段

在此阶段,会将数据卷挂载到 Pod 上

  1. kubelet 观察到 volumeAttachment 资源的 status.attached 设置为 true 时,就会调用 in-tree 内部接口进行实际的卷挂载操作

Unmount 阶段

在此阶段,会将数据卷从 Pod 上取消挂载

  1. kubelet 监听到节点上的 Pod 被删除,就会调用 in-tree 内部接口进行实际的卷卸载操作

Detach 阶段

在此阶段,会将对应的 volumeAttachment 资源删除

  1. attacher 会讲被删除 Pod 对应的 volumeAttachment 进行删除
  2. AD Controller 监听到 volumeAttachment 被删除后,会去更新节点的 status.volumesInUse,将对应的卷信息摘除

Delete 阶段

在此阶段,会判断 PV 的回收策略进行不同的操作

  1. provisioner 观察 PV 的 persistentVolumeReclaimPolicy,如果为 Retain 则保留,Delete 则删除

etcd leader选举

etcd 是基于 raft 算法进行选举,而 raft 是一种管理日志一致性的协议,将系统中的角色分为三个

  1. leader: 接受客户端的请求,并向 follower 发送同步请求日志
  2. follower: 接收 leader 同步的日志
  3. candidate: 候选者角色,在选举过程中发挥作用

leader 选举

  1. raft 是通过心跳机制来触发 leader 的选举,每一个实例(例如 etcd pod)启动后都会初始化为一个 follower,leader 则会周期性的向所有 follower 发送心跳包,在 etcd 的编排中就能看到相关的参数

etcd编排

  1. 如果 follower 如果在选举超时时间内没有收到 leader 的心跳包,就会等待一个随机的时间,然后发起 leader 选举。每个 follower 都有一个时钟,这个时钟是一个随机的值,集群中谁的时钟先跑完,那么就由谁来发起 leader 选举

  2. 该 follower 会将当前的任期(term) + 1 然后转化为 candidate,先给自己投票然后向集群中的其他 follower 发送 RequestVote RPC

那么最终的结果会有三种:

  1. 自己赢得了最多的票数,成为 leader
  2. 收到了 leader 的消息,表示已经有其他服务抢先成为了 leader
  3. 没有服务获得最高票数,即选举失败,会等待选举时间超时后进行下一次选举

在 raft 协议中,所有的日志条目都只会是 leader 往 follower 写入,且 leader 的日志只增不减,所以能被选举成为 leader 的节点,一定包含了所有已经提交的日志条目

容器网络实现

容器的网络是基于 linux 的网络命名空间(networke namespace)虚拟网络设备(veth pair)实现的。

linux文件系统

文件系统是一种用于组织和管理计算机存储设备上数据的系统。它将存储设备上的物理空间划分为逻辑结构,并提供对数据的访问和管理机制。

文件系统的基本功能包括:

  1. 将数据组织成文件和目录
  2. 提供对文件的读写访问
  3. 管理存储空间
  4. 提供文件安全和保护

常见的文件系统有:

  1. ext4:最常用的 Linux 文件系统之一,支持大容量存储、高性能和良好的扩展性
  2. xfs:另一种高性能文件系统,支持大文件和高 I/O 负载
  3. fat32:兼容 Windows 和其他操作系统的文件系统,适用于需要跨平台文件共享的场景
  4. ntfs:Windows 的默认文件系统,支持大容量存储和一些高级功能,例如文件权限和加密

ext4 和 xfs 的区别

  1. xfs 相比于 ext4 有更高的性能,例如在 IO 密集型的负载下
  2. ext4 的最大文件系统大小为 1EB,而 xfs 的最大文件系统大小为 8EB
  3. ext4 的最大文件大小为 16TB,而 xfs 的最大文件大小为 16EB
  4. ext4 相比于 xfs 有着更高的兼容性,被大多数 linux 发行版都支持

Pod是怎么诞生的

K8s 本身不提供容器创建的实现,容器的创建是通过 CRI 接口调用外部插件实现的,常见的 CRI 有这几种:

  1. docker
  2. containerd
  3. cri-dockerd

Pod 是如何被创建出来的

  1. 客户端通过 kubectl 等将创建 pod 的请求发送给 kube-apiserver
  2. kube-apiserver 将 pod 信息写入 etcd,etcd 将写入的结果返回给 kube-apiserver,然后 kube-apiserver 再返回给客户端
  3. kube-scheduler 通过 kube-apiserver 的 watch 接口,获取到未被调度的 pod 信息,根据调度算法选择集群内的一个节点,然后将节点信息发送给 kube-apiserver
  4. kube-apiserver 将这个 pod 和节点的绑定信息写入到 etcd,etcd 将结果返回给 kube-apiserver
  5. kubelet 通过 kube-apiserver 的 watch 接口,获取到本节点有创建 pod 的信息,然后会调用 CRI 创建容器,并将 pod 的运行状态发送给 kube-apiserver
  6. kube-apiserver 将 pod 状态信息更新到 etcd

pod创建流程