一、如果你并不知道有哪些 Volume 类型可以用,要怎么办呢?
1、如果你并不知道有哪些 Volume 类型可以用,要怎么办呢?
1、一窍不通

2、暴露公司基础设施秘密的风险

比如,下面这个例子,就是一个声明了 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 之后,一个开发人员想要使用一个 Volume,只需要简单的两步即可
1、第一步:定义一个 PVC,声明想要的 Volume 的属性:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pv-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
在这个 PVC 对象里,不需要任何关于 Volume 细节的字段,只有描述性的属性和定义。

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

3、这些符合条件的 Volume 又是从哪里来的呢?
答案是,它们来自于由运维人员维护的 PV(Persistent Volume)对象。接下来,我们一起看一个常见的 PV 对象的 YAML 文件:
kind: PersistentVolume
apiVersion: v1
metadata:
name: pv-volume
labels:
type: local
spec:
capacity:
storage: 10Gi
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"
4、“接口”和“实现
PVC 和 PV 的设计

避免“扯皮”

5、volumeClaimTemplates
而 PVC、PV 的设计,也使得 StatefulSet 对存储状态的管理成为了可能。我们还是以上一篇文章中用到的 StatefulSet 为例(你也可以借此再回顾一下《深入理解 StatefulSet(一):拓扑状态》中的相关内容):
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: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
字段

PVC和POD编号一致

PVC 与 PV 的绑定

所以,我们在使用 kubectl create 创建了 StatefulSet 之后,就会看到 Kubernetes 集群里出现了两个 PVC:
$ kubectl create -f statefulset.yaml $ kubectl get pvc -l app=nginx NAME STATUS VOLUME CAPACITY ACCESSMODES AGE www-web-0 Bound pvc-15c268c7-b507-11e6-932f-42010a800002 1Gi RWO 48s www-web-1 Bound pvc-15c79307-b507-11e6-932f-42010a800002 1Gi RWO 48s
<PVC 名字 >-<StatefulSet 名字 >-< 编号 >
三、验证一下上述 Volume 的分配情况:
我们前面已经讲到过,这个 StatefulSet 创建出来的所有 Pod,都会声明使用编号的 PVC。比如,在名叫 web-0 的 Pod 的 volumes 字段,它会声明使用名叫 www-web-0 的 PVC,从而挂载到这个 PVC 所绑定的 PV。
所以,我们就可以使用如下所示的指令,在 Pod 的 Volume 目录里写入一个文件,来验证一下上述 Volume 的分配情况:
$ for i in 0 1; do kubectl exec web-$i -- sh -c 'echo hello $(hostname) > /usr/share/nginx/html/index.html'; done
如上所示,通过 kubectl exec 指令,我们在每个 Pod 的 Volume 目录里,写入了一个 index.html 文件。这个文件的内容,正是 Pod 的 hostname。比如,我们在 web-0 的 index.html 里写入的内容就是 "hello web-0"。
此时,如果你在这个 Pod 容器里访问“http://localhost”,你实际访问到的就是 Pod 里 Nginx 服务器进程,而它会为你返回 /usr/share/nginx/html/index.html 里的内容。这个操作的执行方法如下所示:
如上所示,通过 kubectl exec 指令,我们在每个 Pod 的 Volume 目录里,写入了一个 index.html 文件。这个文件的内容,正是 Pod 的 hostname。比如,我们在 web-0 的 index.html 里写入的内容就是 "hello web-0"。
此时,如果你在这个 Pod 容器里访问“http://localhost”,你实际访问到的就是 Pod 里 Nginx 服务器进程,而它会为你返回 /usr/share/nginx/html/index.html 里的内容。这个操作的执行方法如下所示:
$ for i in 0 1; do kubectl exec -it web-$i -- curl localhost; done hello web-0 hello web-1
现在,关键来了。
如果你使用 kubectl delete 命令删除这两个 Pod,这些 Volume 里的文件会不会丢失呢?
$ kubectl delete pod -l app=nginx pod "web-0" deleted pod "web-1" deleted

# 在被重新创建出来的 Pod 容器里访问 http://localhost $ kubectl exec -it web-0 -- curl localhost hello web-0

这是怎么做到的呢?
1、恢复这个 Pod 的过程

2、需要注意的是

3、StatefulSet 创建 Pod 的标准流程。

通过这种方式,Kubernetes 的 StatefulSet 就实现了对应用存储状态的管理。
详细梳理一下
1、首先
2、其次

3、最后

四、小结
1、StatefulSet 的设计思想

2、编号

实际上,在下一篇文章的“有状态应用”实践环节,以及后续的讲解中,你就会逐渐意识到,StatefulSet 可以说是 Kubernetes 中作业编排的“集大成者”。
因为,几乎每一种 Kubernetes 的编排功能,都可以在编写 StatefulSet 的 YAML 文件时被用到。

