默认情况下容器中的磁盘文件是非持久化的,对于运行在容器中的应用来说面临两个问题,第一:当容器挂掉kubelet将重启启动它时,文件将会丢失;第二:当Pod中同时运行多个容器,容器之间需要共享文件时。Kubernetes的Volume解决了这两个问题。

Kubernetes Volume具有明确的生命周期 - 与pod相同。因此,Volume的生命周期比Pod中运行的任何容器要持久,在容器重新启动时能可以保留数据,当Pod被删除不存在时,Volume也将消失。注意,Kubernetes支持许多类型的Volume,Pod可以同时使用任意类型/数量的Volume。

内部实现中,一个Volume只是一个目录,目录中可能有一些数据,pod的容器可以访问这些数据。至于这个目录是如何产生的、支持它的介质、其中的数据内容是什么,这些都由使用的特定Volume类型来决定。

Kubernetes支持Volume类型有:

  • emptyDir
  • hostPath
  • gcePersistentDisk
  • awsElasticBlockStore
  • nfs
  • iscsi
  • fc (fibre channel)
  • flocker
  • glusterfs
  • rbd
  • cephfs
  • gitRepo
  • secret
  • persistentVolumeClaim
  • downwardAPI
  • projected
  • azureFileVolume
  • azureDisk
  • vsphereVolume
  • Quobyte
  • PortworxVolume
  • ScaleIO
  • StorageOS
  • local

这么多看着头晕啊,下面介绍几个常用的:

emptyDir

使用emptyDir,当Pod分配到Node上时,将会创建emptyDir,并且只要Node上的Pod一直运行,Volume就会一直存。当Pod(不管任何原因)从Node上被删除时,emptyDir也同时会删除,存储的数据也将永久删除。注:删除容器不影响emptyDir。

emptyDir是在Pod分配到node时创建的,它的初始内容为空,并且无需指定宿主机上对应的目录文件,因为这是k8s自动分配的,当Pod从node上移除时,emptyDir中的数据也会被永久删除。

下面看一个Pod中有两个容器,共享同一个emptyDir的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: v1
kind: Pod
metadata:
  name: datagrand
spec:
  containers:
    - name: test1
      image: nginx:1.7.9
      volumeMounts:
        - name: log-storage
          mountPath: /usr/share/nginx/html
    - name: test2
      image: centos
      volumeMounts:
        - name: log-storage
          mountPath: /html
      command: [ "/bin/sh","-c" ]
      args:
        - while true;do
          data >> /html/index.html;
          sleep 1;
          done
  volumes:
    - name: log-storage
      emptyDir: { }

将上面的配置文件保存成emptyDir.yaml,在K8s主机上执行:

1
2
kubectl create -f emptyDir.yaml
kubectl get pods

下面分别进入两个容器操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 进入容器test1
kubectl exec -it datagrand -c test1 /bin/bash -n test
root@datagrand:/# cd /usr/share/nginx/html
root@datagrand:/usr/share/nginx/html# ls
index.html
# 添加内容
root@datagrand:/usr/share/nginx/html# echo "this is a test" >> index.html 

# 进入容器test2
kubectl exec -it datagrand -c test2 /bin/bash -n test
[root@datagrand /]		# html
[root@datagrand html]	# ls
index.html
[root@datagrand html]	# cat index.html 
this is a test

# 看出来emptyDir卷是两个容器(test1和test2)共享的

hostPath

hostPath允许挂载Node上的文件系统到Pod里面去。如果Pod需要使用Node上的文件,可以使用hostPath。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /chende

这里/chende就是node上的目录。/test-pd是容器中的目录。

这种挂载方式比emptyDir更为持久,除非所在Node发生故障。不过,除了挂载一些配置文件和二进制文件之外,一般不采用该类挂载方式,因为这样的挂载操作增加了Pod文件与Node主机文件的耦合,不利于统一管理。

persistentVolumeClaim

PersistentVolume(PV)和PersistentVolumeClaim(PVC)

这两个概念用于pod和volume之间解耦。Pod根据自己的需要提出数据卷的申请,k8s系统将符合条件的数据卷返回给pod。这样一来pod就无需直接和数据卷本身强绑定了。Pod无需知道数据卷的细节信息,比如具体用的是什么存储。

Pod中对数据卷的申请为PVC,用来和PVC绑定的数据卷称为PV。

PV可以有多种存储方式,比如NFS,iSCSI等。

PV和PVC的生命周期

A. 供应阶段

PV有两种供应方式

  • static 静态方式由系统管理员创建PV
  • dynamic 如果没有静态的PV。系统会动态创建出PV来满足PVC。PV的提供者为StorageClass。要使用动态供应,PVC必须要指定一个StorageClass。如果StorageClass设置为空,那么针对这个PVC的动态供应特性会被禁用。

B. 绑定阶段

这个阶段指的是PV和PVC绑定的过程。系统有一个机制,循环检查新创建的PVC,然后找到一个符合条件的PV,把他们绑定到一起。如果有多个满足要求的PV可以绑定,会使用消耗资源最小的那个。PV和PVC之间是一对一的关系。如果找不到符合条件的PV,PVC会一致保持未绑定状态。

C. 使用阶段

PVC绑定合适的PV之后,Pod使用这个PV。和PVC绑定并处于使用状态的PV不会被系统删除,防止数据丢失。如果用户删除了Pod正在使用的PVC,这个PVC不会被立即移除。直到这个PVC不被任何Pod使用的时候,才会被真正的删除。

D. 重新声明阶段

当PV不再使用的时候,k8s根据不同的策略来回收这些PV。如下三种策略:

  1. Retain策略在volume不再使用的时候,不删除旧的数据,等待管理员处理。
  2. Delete策略会删除不再使用的PV,同时删除这些PV对应真实存储的资源。
  3. Recycle策略删除volume内的内容,使得volume可供下次使用。

下面看看简单示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# pv.yaml
kind: PersistentVolume
apiVersion: v1
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/chende"
    
    
# pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: task-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
      
# pvc-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
        claimName: task-pv-claim
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage

K8s Master上分别执行kubectl create -f xxx.yaml之后,看看效果。

参考:

https://www.jianshu.com/p/b3665b72126e

(完)