1 前情提要
1.1 StatefulSet 两种状态
StatefulSet 的设计,它把真实世界里的应用状态,抽象为了两种:
- 拓扑状态
- 存储状态
在前面一篇文章中,我们已经了解了拓扑状态,今天主要学习一下存储状态
2 PVC
StatefulSet 对存储状态的管理机制,主要使用的是一个叫作 Persistent Volume Claim 的功能。所以我们需要先来看下这个功能的介绍与使用。
2.1 PVC基本介绍
持久卷(Persistent Volume)是一种独立于 Pod 的存储资源。持久卷可以独立于 Pod 的生命周期存在,并且可以在 Pod 重新调度后仍然保留数据。
在 Kubernetes 中,要使用持久卷,需要创建一个持久卷声明(Persistent Volume Claim),
Persistent Volume Claim (PVC) 是一种用于声明对持久存储资源的需求的资源对象。
PVC 允许将应用程序和存储资源的生命周期分开,以便在应用程序重新部署或迁移时保留持久存储数据。
2.2 PVC 的工作原理
Persistent Volume Claim(PVC)是在 Kubernetes 中定义一个存储资源的方式。PVC 允许 Pod 请求特定大小和访问模式的存储。
PVC 实际上是一个声明,它声明了 Pod 需要的存储资源。
Kubernetes 系统会根据 PVC 的要求选择或创建一个相应的持久卷来满足 Pod 的需求。
如果 PV 满足 PVC 的要求,则将 PV 绑定到 PVC,并将 PVC 暴露给应用程序使用。
PVC 可以在多个 Pod 之间共享,但每次只能绑定到一个 PV 上。
2.3 PVC 的访问模式
PVC 的访问模式指定了允许哪些节点以哪种方式使用持久卷。以下是 PVC 的三种访问模式:
- ReadWriteOnce:允许单个节点以读写模式挂载持久卷。
- ReadOnlyMany:允许多个节点以只读模式挂载持久卷。
- ReadWriteMany:允许多个节点以读写模式挂载持久卷。
需要注意的是,不是所有的存储插件都支持所有的访问模式。因此,在使用 PVC 的时候,需要确保所使用的存储插件支持所需的访问模式。
2.4 PVC 和 Pod 的关系
- 关系:PVC 和 Pod 是紧密相关的。
- 请求:一个 Pod 可以请求一个或多个 PVC,并将 PVC 挂载到容器中。
- 挂载:在 Pod 被调度到节点上时,Kubernetes 系统会为 Pod 中声明的每个 PVC 创建一个持久卷,并将其挂载到 Pod 上。
- 删除:当 Pod 被删除时,与之关联的 PVC 不会被删除,这些 PVC 可以在之后的 Pod 中继续使用。
2.5 代码示例
2.5.1 yaml 示例
下面是一个 PVC 的 YAML 文件示例:
apiVersion: v1 kind: PersistentVolumeClaim # PVC资源类型 metadata: name: my-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
在这个示例中,创建了一个名为 my-pvc 的 PVC,并指定了访问模式为 ReadWriteOnce,请求的存储容量为 1Gi。
2.5.2 PVC 的 Pod yaml
接下来,可以创建一个使用这个 PVC 的 Pod。
下面是一个使用 my-pvc 的 Pod 的 YAML 文件示例:
apiVersion: v1 kind: Pod #资源类型 metadata: name: my-pod spec: containers: - name: my-container image: nginx volumeMounts: - name: my-pvc-volume mountPath: /data volumes: - name: my-pvc-volume #当前pod对应的pvc名称 persistentVolumeClaim: claimName: my-pvc #使用刚刚创建声明的pvc
在这个示例中,创建了一个名为 my-pod 的 Pod,并将其容器的 /data 目录挂载到 my-pvc-volume 卷中。该卷使用 my-pvc PVC。
Tips:
当创建 my-pod Pod 时,Kubernetes 会自动扫描所有可用的 PV,并选择一个 PV 来满足 my-pvc PVC 的要求。
如果找到了可用的 PV,则将其绑定到 my-pvc,并将 my-pvc-volume 卷挂载到容器中的 /data 目录。
2.6 结论
PVC 是 Kubernetes 中非常有用的资源对象,它允许将应用程序和存储资源的生命周期分开,并在应用程序重新部署或迁移时保留持久存储数据。
通过上面的示例,可以了解如何创建 PVC,并将其用于 Pod。
拓展一下:
Kubernetes 中 PVC 和 PV 的设计,实际上类似于“接口”和“实现”的思想。
- 开发者只要知道并会使用“接口”,即:PVC;
- 而运维人员则负责给“接口”绑定具体的实现,即:PV。
这种解耦,就避免了因为向开发者暴露过多的存储系统细节而带来的隐患。
此外,这种职责的分离,往往也意味着出现事故时可以更容易定位问题和明确责任,从而避免“扯皮”现象的出现。
3 存储状态
3.1 问题
思考一下这个问题:
3.2 问题解答
前面已经了解到,当 Pod 被删除时,与之关联的 PVC 不会被删除,这些 PVC 可以在之后的 Pod 中继续使用。
来看下StatefulSet 怎么做到的:
首先:
StatefulSet 的控制器直接管理的是 Pod。
因为,StatefulSet 里的不同 Pod 实例,不像 ReplicaSet 中那样都是完全一样的,而是有了细微区别的。
比如,每个 Pod 的 hostname、名字等都是不同的、携带了编号的。而 StatefulSet 区分这些实例的方式,就是通过在 Pod 的名字里加上事先约定好的编号。
其次:
Kubernetes 通过 Headless Service,为这些有编号的 Pod,在 DNS 服务器中生成带有同样编号的 DNS 记录。
只要 StatefulSet 能够保证这些 Pod 名字里的编号不变,那么 Service 里类似于
web-0.nginx.default.svc.cluster.local 这样的 DNS 记录也就不会变,而这条记录解析出来的 Pod 的 IP 地址,则会随着后端 Pod 的删除和再创建而自动更新。这是 Service 机制本身的能力,不需要 StatefulSet 操心。
最后:
StatefulSet 还为每一个 Pod 分配并创建一个同样编号的 PVC。
这样,Kubernetes 就可以通过 Persistent Volume 机制为这个 PVC 绑定上对应的 PV,从而保证了每一个 Pod 都拥有一个独立的 Volume。
在这种情况下,即使 Pod 被删除,它所对应的 PVC 和 PV 依然会保留下来。
所以,当这个 Pod 被重新创建出来之后,Kubernetes 会为它找到同样编号的 PVC,挂载这个 PVC 对应的 Volume,从而获取到以前保存在 Volume 里的数据。
3.3 yaml示例
apiVersion: apps/v1 kind: StatefulSet #资源类型 metadata: name: web spec: serviceName: "nginx" replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.9.1 ports: - containerPort: 80 name: web volumeMounts: - name: mypvc mountPath: /usr/share/nginx/html volumeClaimTemplates: #pvc声明 - metadata: name: mypvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
在这个yaml中:添加了一个 volumeClaimTemplates 字段。
它跟 Deployment 里 Pod 模板(PodTemplate)的作用类似。凡是被这个 StatefulSet 管理的 Pod,都会声明一个对应的 PVC;
而这个 PVC 的定义,就来自于 volumeClaimTemplates 这个模板字段。更重要的是,这个 PVC 的名字,会被分配一个与这个 Pod 完全一致的编号。
这个自动创建的 PVC,与 PV 绑定成功后,就会进入 Bound 状态,这就意味着这个 Pod 可以挂载并使用这个 PV 了。
3.4 创建
使用 kubectl create 创建了 StatefulSet 之后,就会看到 Kubernetes 集群里出现了两个 PVC:
kubectl create -f statefulset.yaml
3.5 查看结果
执行创建命令:
kubectl get pvc -l app=nginx
结果如下:
NAME STATUS VOLUME CAPACITY ACCESSMODES AGE mypvc-web-0 Bound pvc-15c268c7-b507-11e6-932f-42010a800002 1Gi RWO 48s mypvc-web-1 Bound pvc-15c79307-b507-11e6-932f-42010a800002 1Gi RWO 48s
前面已讲到过,这个 StatefulSet 创建出来的所有 Pod,都会声明使用编号的 PVC。
可以看到,这些 PVC,都以“<PVC 名字 >-<StatefulSet 名字 >-< 编号 >”的方式命名,并且处于 Bound 状态。
3.6 总结
1. 可以看出StatefulSet 其实就是一种特殊的 Deployment,它的特别之处在于,赋予了pod在整个集群里唯一的、可被的访问身份,主要通过它对创建的每个 Pod 都进行了编号。
2. 就是这个编号,给了pod一个唯一的身份,并且,这个编号还会体现在 Pod 的名字和 hostname 等标识信息上,这不仅代表了 Pod 的创建顺序,也是 Pod 的重要网络标识
有了编号后,StatefulSet 就使用 Kubernetes 里的两个标准功能:
- Headless Service
- PV/PVC
实现了对 Pod 的拓扑状态和存储状态的维护。