大家好,我是秋意零。
在上一篇中,我们讲解了 StatefulSet 的拓扑状态;我们发现,它的拓扑状态,就是顺序启动/删除、Pod 名称+编号命名、将 Pod 名称设为 Hostname 名称、通过 Service 无头服务的 DNS 记录访问。
今天,就来看看 StatefulSet 的存储状态。
最近搞了一个扣扣群,旨在技术交流、博客互助,希望各位大佬多多支持!
获取方式:
简介
【云原生|探索 Kubernetes-1】容器的本质是进程
【云原生|探索 Kubernetes-2】容器 Linux Cgroups 限制
【云原生|探索 Kubernetes 系列 3】深入理解容器进程的文件系统
【云原生|探索 Kubernetes 系列 4】现代云原生时代的引擎
【云原生|探索 Kubernetes 系列 5】简化 Kubernetes 的部署,深入解析其工作流程
更多点击专栏查看:深入探索 Kubernetes
正文开始:
目录
前言
系列文章目录
一、解决的实际问题
二、PV、PVC 简单介绍
开发者使用 PVC
运维人员负载 PV
三、存储状态
创建 PV 与 StatefulSet
验证 StatefulSet 的存储状态
总结
StatefulSet 的存储状态,主要使用 Persistent Volume Claim(PVC 持久卷声明) 功能实现。
在前面介绍 Pod 时,要在一个 Pod 里生命 Volume,只要在 Pod 里加上 spec.voluems 字段即可。然后,在它里面定义一个具体类型的 Voluem,比如:hostPath。
可如果,你并不知道有那些 Volume 类型可以用,这要怎么办呢?
比如,下面这个例子,就是一个声明了 Ceph RBD 类型 Volume 的 Pod:
apiVersion: v1
kind: Pod
metadata:
name: rbd
spec:
containers:
- image: kubernetes/pause
name: rbd-rw
volumeMounts:
- name: rbdpd
mountPath: /mnt/rbd
volumes:
- name: rbdpd
rbd:
monitors:
- '10.16.154.78:6789'
- '10.16.154.82:6789'
- '10.16.154.83:6789'
pool: kube
image: foo
fsType: ext4
readOnly: true
user: admin
keyring: /etc/ceph/keyring
imageformat: "2"
imagefeatures: "layering"
这也是为什么,Kubernetes 引入了一组叫 Persistent Volume Claim
(PVC)和 Persistent Volume
(PV)的 API 对象,大大降低了用户声明和使用持久化 Volume 的门槛。因为开发人员只需要关心 PVC 的声明,不需要关心 PV 的声明。
PV 和 PVC 之间的关系:
举个例子:有了 PVC 之后,一个开发人员想要使用一个 Volume,只需要简单的两步即可。
1.定义一个 PVC,声明想要的 Volume 的属性:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pv-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
2.在应用的 Pod 中,声明使用这个 PVC:
apiVersion: v1
kind: Pod
metadata:
name: pv-pod
spec:
containers:
- name: pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: pv-storage
volumes:
- name: pv-storage
persistentVolumeClaim:
claimName: pv-claim
PVC 创建之后,会自动为它绑定一个符合条件的 Voluem(这个 Voluem 是 PV),从上述的 PV 和 PVC 的结构图中,这个 Volume 就是从 PV 中来的,并且由运维人员维护 PV 对象。PV 如下:
kind: PersistentVolume
apiVersion: v1
metadata:
name: pv-volume
labels:
type: local
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
rbd:
monitors:
# 使用 kubectl get pods -n rook-ceph 查看 rook-ceph-mon- 开头的 POD IP 即可得下面的列表
- '10.16.154.78:6789'
- '10.16.154.82:6789'
- '10.16.154.83:6789'
pool: kube
image: foo
fsType: ext4
readOnly: true
user: admin
keyring: /etc/ceph/keyring
PV 和 PVC 的设计,就类似于“接口”和“实现”的思想。开发者只要知道并会使用“接口”,不管“接口”背后的实现,类似: PVC;而运维人员负责给“接口”绑定具体的实现,类似:PV。
这种解耦,就避免了因为向开发者暴露过多的存储系统细节而带来的隐患。此外,这种职责的分离,往往也意味着出现事故时可以更容易定位问题和明确责任,从而避免“扯皮”现象的出现。
由于 PV、PVC 的设计,我们就能使用 StatefulSet 利用 PV、PVC 来实现 StatefulSet 存储状态,如下所示:
spec.volumeClaimTemplates
字段,从名字可以看出它和spec.template
(PodTemplate)的作用类似,所以 StatefulSet 会自动创建 PVC 就像自动创建所期望的副本数 Pod 一样;spec.template
:一个 Pod 模板,控制器所创建管理的 Pod,都以这个配置为基础;spec.volumeClaimTemplates
:一个 PVC 模板,控制器所创建管理的 PVC,都以这个配置为基础;apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
这个自动创建的 PVC,与 PV 绑定成功后(前提是首先创建了 PV或通过 Dynamic Provisioning 的方式,自动为 PVC 匹配创建 PV),就会进入 Bound 状态,这就意味着这个 Pod 可以挂载并使用这个 PV 了(详细内容会在存储章节讲解)。PV 的 YAML 文件如下:
[root@master01 yaml]# cat > pv.yaml << EOF
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv1
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /data/my-pv1
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv2
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /data/my-pv2
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv3
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /data/my-pv3
EOF
1.创建 PV 与 StatefulSet:
[root@master01 yaml]# kubectl apply -f pv.yaml
persistentvolume/my-pv1 created
persistentvolume/my-pv2 created
persistentvolume/my-pv3 created
[root@master01 yaml]# kubectl apply -f statefulset-pvc.yaml
statefulset.apps/web created
2.查看 Pod 是否运行:
[root@master01 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 13m
web-1 1/1 Running 0 13m
web-2 1/1 Running 0 13m
3.查看 PV 与 PVC:
[root@master01 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
my-pv1 10Gi RWO Retain Bound default/www-web-1 14m
my-pv2 10Gi RWO Retain Bound default/www-web-0 14m
my-pv3 10Gi RWO Retain Bound default/www-web-2 14m
[root@master01 ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound my-pv2 10Gi RWO 14m
www-web-1 Bound my-pv1 10Gi RWO 14m
www-web-2 Bound my-pv3 10Gi RWO 14m
现在,我们要做这样一件事。
1.在 Pod 中的 Volume 的 Web 目录中写入一个文件:
$ for i in 0 1 2; do kubectl exec web-$i -- sh -c 'echo hello $(hostname) > /usr/share/nginx/html/index.html'; done
2.查看 Volume 的 Web 目录中的数据:
$ for i in 0 1 2; do kubectl exec -it web-$i -- curl localhost; done
hello web-0
hello web-1
hello web-2
3.删除所有 Pod,并再次查看 Volume 的 Web 目录中的数据是否还与之前 Pod 中的数据对应:
1、删除 Pod
[root@master01 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 53m
web-1 1/1 Running 0 53m
web-2 1/1 Running 0 53m
[root@master01 ~]#
[root@master01 ~]# kubectl delete pod --all
pod "web-0" deleted
pod "web-1" deleted
pod "web-2" deleted
2、验证,访问 web 数据是否还一致
[root@master01 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 22s
web-1 1/1 Running 0 20s
web-2 1/1 Running 0 18s
[root@master01 ~]#
[root@master01 ~]# for i in 0 1 2; do kubectl exec -it web-$i -- curl localhost; done
hello web-0
hello web-1
hello web-2
可以看到,我们再次查看访问 web 时,数据还是一致的。也就是说,我们重建之后的 Pod 还会与之前绑定过的 PVC 再次绑定。
这是怎么做到的呢?
StatefulSet 工作原理的三个方面: