1. StatefulSet

1.1 StatefulSet 简介

StatefulSet 是用来管理有状态应用的工作负载 API 对象。

注意:

  • StatefulSet 也是一个控制器

  • StatefulSet 用来管理 Deployment 和扩展一组 Pod

StatefulSet和Deployment的相同点

  • 都是控制器

  • 都管理了基于相同容器定义的一组 Pod

StatefulSet和Deployment的不同点

  • StatefulSet 为它们的每个 Pod 维护了一个固定的 ID

  • 这些 Pod 是基于相同的声明来创建的,但是不能相互替换。

  • 无论怎么调度,每个 Pod 都有一个永久不变的 ID

StatefulSet的特点

  • 稳定的、唯一的网络标识符。

  • 稳定的、持久的存储。

  • 有序的、优雅的部署和缩放。

  • 有序的、自动的滚动更新。

使用场景:

  • 每个 Pod 可以有自己的存储,重启后,还可以连接上自己固定的存储

  • 不需要创建 PVC,StatefulSet 可以自己创建 PVC,但是 PV 需要手动创建

  • 如果需要自动创建 PV ,那么就需要创建 动态卷

1.2 Pod 标识

StatefulSet Pod 具有唯一的标识,该标识包括顺序标识、稳定的网络标识和稳定的存储。该标识和 Pod 是绑定的,不管它被调度在哪个节点上。

1.3 有序索引

对于具有 N 个副本的 StatefulSet,StatefulSet 中的每个 Pod 将被分配一个整数序号,从 0 到 N-1,该序号在 StatefulSet 上是唯一的。

1.4 稳定的网络 ID

稳定的网络 ID 具有:

  • StatefulSet 中的每个 Pod 根据 StatefulSet 的名称和 Pod 的序号派生出它的主机名。

  • 组合主机名的格式为:(StatefulSet 名称)-(序号)。

  • StatefulSet 可以使用 headless 服务 控制它的 Pod 的网络域。

  • 访问这个服务的格式为: (pod名称).(服务名).$(命名空间)

1.5 稳定的存储

StatefulSet 是有状态的服务,那么必须要有稳定的存储,对数据进行持久化:

  • Kubernetes 为每个 VolumeClaimTemplate 创建一个 PersistentVolumes。

  • 如果没有声明 StorageClass,就会使用默认的 StorageClass。

  • 当一个 Pod 被调度(重新调度)到节点上时,它的 volumeMounts 会挂载与其 PersistentVolumeClaims 相关联的 PersistentVolume。

  • 注意,当 Pod 或者 StatefulSet 被删除时,与 PersistentVolumeClaims 相关联的 PersistentVolume 并不会被删除。要删除它必须通过手动方式来完成。

1.6 StatefulSet 的使用

1.6.1 yaml 模板

Copy to Clipboard

说明:

  • selector : 同 deployment 一样,用来选择一组 pod 的标签进行管理

  • volumeClaimTemplates : 定义一个 pvc 用来与 pv 做绑定

1.7 headless 无头服务

1.7.1 yaml 模板

Copy to Clipboard

说明:

  • 无头服务和正常服务的区别在于,ClusterIP: None

  • 访问这个服务的格式为:

    • 如果不在同一个 namespace 下,那么可以直接通过:(pod名称).(服务名).$(命名空间) 来访问无头服务

    • 如果在同一个 namespace 下,那么可以直接通过:(pod名称).(服务名) 来访问无头服务

注意:这里说的 headless 服务,也就是:

  • 有时不需要或不想要负载均衡,以及单独的 Service IP 。遇到这种情况,可以通过指定 ClusterIP(spec.clusterIP) 的值为 “None” 来创建 Headless Service 。

  • headless Service 并不会分配 Cluster IP, kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。

  • 创建 headless Service 后,虽然不会分配 ClusterIP,但是会在集群内部新增一条 DNS 记录,这条 DNS 记录表示通过这个服务名字可以访问到的后端服务 ip 地址(一组 Pod 的内部 ip 地址,endpoints)

  • headless Service 常用作自定义负载均衡,自己决定某个时刻连接哪个 ip 的服务

1.7.2 示例

  1. 创建三个 pv,名称分别为 pv1、pv2、pv3,挂在的目录分别为 pv_a、pv_b、pv_c,storageClassName 名字都为 gsh-pv,yaml 模板为:

Copy to Clipboard
  1. 创建好 pv 后,创建 statefulSet ,3 个副本数,yaml 文件为:

Copy to Clipboard
  1. 创建好 sts 后,就会发现,3 个副本 pod 创建了三个 pvc,并且按顺序绑定了 pv

    • 可以看到,创建的 pvc 名字为: <pvcname>-<stsname>-<序号>

    • 可以看到,创建的 pod 名字为: <stsname>-<序号>

Copy to Clipboard
  1. 当删除了 pod,并重新启动后,

    • 可以看到挂载的 pv 还是没变,并且名字也没变

    • 删除 pod 后,pvc 和 pv 还是绑定的,如果要删除 pvc,需要手动删除

Copy to Clipboard
  1. 创建无头服务,yaml 文件为:

Copy to Clipboard
  1. 通过

    • 相同命名空间下:podName.svcName 来访问无头服务

    • 不同命名空间下:podName.svcName.namespace 来访问无头服务

Copy to Clipboard

2. 动态卷

2.1 动态卷简介

作用

  • 不需要手动创建 pv

  • 在创建 pvc 的时候,动态的创建 pv

  • 删除 pvc 时,pv 也会跟着一起删除掉

动态卷供应的实现基于 StorageClass API 对象。 而每个 StorageClass 对象对应一个 卷插件 provisioner 分配器,provisioner 则是用来提供真实的卷,指定了真实的后端存储,例如 nfs,ceph,aws 等。

步骤

  • 集群管理员可以根据需要定义多个 StorageClass 对象

  • 每个 StorageClass 对象指定一个卷插件(又名 provisioner)

  • 卷插件向卷供应商提供在创建卷时需要的数据卷信息及相关参数。也就是对应真实的卷,例如 nfs

  • 集群管理员可以在集群中定义和公开多种存储(来自相同或不同的存储系统),每种都具有自定义参数集。

因此使用动态卷的过程为

  1. 创建 StorageClass ,一般是管理员创建

  2. 在 StorageClass 定义中,指定 provisioner,

  3. 创建 StorageClass 后,在使用 StorageClass 时,指定的 provisioner 会自动分配一个卷给其使用

  4. 普通用户创建 pvc 时,使用 StorageClass 名,就会自动创建 pv 与之关联

注意

  • k8s 内置的分配器,使用特定的存储

  • 有一些存储,并没有分配器,使用外部分配器,这就需要安装分配器的驱动,例如 CsiDriver

2.2 动态卷的使用

2.2.1 StorageClass yaml 创建

StorageClass 的 yaml 模板为:

Copy to Clipboard

说明:

  • provisioner : 卷插件,也就是 provisioner

  • volumeBindingMode : 卷绑定和动态分配的策略

    • Immediate : 一旦创建了 PersistentVolumeClaim ,立即创建 pv,也就完成了卷绑定和动态分配。

    • WaitForFirstConsumer : 当创建 pvc 后,并不会立即创建 pv,而是在创建 pod,且使用 pvc 时,才会创建 pv

  • allowVolumeExpansion : 表示允许扩展

2.2.2 pvc 使用 StorageClass

当 Pod 中使用了 storageClassName 作为 PVC 挂载后,Kubernetes 会自动创建 PV 与之绑定,而如果创建了 PVC ,那么会根据 storageClassName  自动创建 PV 与之绑定,例如 PVC 的 yaml 模板为:

Copy to Clipboard