非持久性存储
emptyDir
hostPath
网络连接性存储
SAN:iscsi
NFS:nfs、cfs
分布式存储
glusterfs、cephfs、rbd
云端存储
awsElasticBlockStore、azureDisk、gitRepo
...... volumes: - name: data emptyDir: {} - name: example gitRepo: repository: https://github.com/ikubernetes/k8s_book.git revision: master directory:
...... volumeMounts: - name <string> -required- #指定要挂载的存储卷的名称,必选字段 mountPath <string> -required- #挂载点路径,容器文件系统的路径,必选字段 readOnly <boolean> #是否挂载为只读卷 subPath <string> #挂载存储卷时使用的子路径,及mountPath指定的路径下使用一个子路径作为其挂载点。
spec: containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: data mountPath: /var/log/myapp/ - name: example mountPath: /webdata/example/
[root@k8s-master ~]# mkdir storage [root@k8s-master ~]# cd storage/
emptyDir的作用:
普通空间,基于磁盘的数据存储
作为从崩溃中恢复的备份点
存储那些需要长久保存的数据,例如
web
服务中的数据
[root@k8s-master ~]# kubectl explain pod.spec.volumes.emptyDir medium <string>: #此目录所在的存储介质的类型,可取值为“default”或“Memory”,默认为default,表示使用节点的的默认存储介质;Memory表示使用基于RAM的临时的文件系统temfs,空间受限于内存,但性能非常好,通常用于为容器中的应用提供缓存空间。 sizeLimit <string> #当前存储卷的空间限额,默认值为nil,表示不限制;不过,在medium字段值为“Memory”时建议务必定义此限额。
emptyDir示例:
1)编辑资源清单文件
[root@k8s-master storage]# vim vol-emptydir.yaml apiVersion: v1 kind: Pod metadata: name: vol-emptydir-pod spec: volumes: #定义存储卷 - name: html #定义存储卷的名称 emptyDir: {} #定义存储卷的类型 containers: - name: nginx image: nginx:1.12 volumeMounts: #在容器中定义挂载存储卷的名和路径 - name: html mountPath: /usr/share/nginx/html - name: sidecar image: alpine volumeMounts: #在容器中定义挂载存储卷的名和路径 - name: html mountPath: /html command: ["/bin/sh", "-c"] args: - while true; do echo $(hostname) $(date) >> /html/index.html; sleep 10; done
2)创建并查看状态
[root@k8s-master storage]# kubectl apply -f vol-emptydir.yaml pod/vol-emptydir-pod created [root@k8s-master storage]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES vol-emptydir-pod 2/2 Running 0 63s 10.244.2.79 k8s-node2 <none> <none>
3)访问测试
[root@k8s-master storage]# curl 10.244.2.79 vol-emptydir-pod Wed Oct 9 03:32:43 UTC 2019 vol-emptydir-pod Wed Oct 9 03:32:53 UTC 2019 vol-emptydir-pod Wed Oct 9 03:33:03 UTC 2019 vol-emptydir-pod Wed Oct 9 03:33:13 UTC 2019 vol-emptydir-pod Wed Oct 9 03:33:23 UTC 2019 ...... #进入vol-emptydir-pod中的sidecar容器中查看挂载目录下的index.html文件 [root@k8s-master storage]# kubectl exec vol-emptydir-pod -c sidecar -it -- /bin/sh / # ls bin etc html media opt root sbin sys usr dev home lib mnt proc run srv tmp var / # ls /html index.html / # cat /html/index.html vol-emptydir-pod Wed Oct 9 03:32:43 UTC 2019 vol-emptydir-pod Wed Oct 9 03:32:53 UTC 2019 vol-emptydir-pod Wed Oct 9 03:33:03 UTC 2019 ...... #进入vol-emptydir-pod中的nginx容器中查看挂载目录下的index.html文件 [root@k8s-master storage]# kubectl exec vol-emptydir-pod -c nginx -it -- /bin/sh # cat /usr/share/nginx/html/index.html vol-emptydir-pod Wed Oct 9 03:32:43 UTC 2019 vol-emptydir-pod Wed Oct 9 03:32:53 UTC 2019 vol-emptydir-pod Wed Oct 9 03:33:03 UTC 2019 ......
hostPath字段说明:
[root@k8s-master storage]# kubectl explain pod.spec.volumes.hostPath path <string> -required- #指定工作节点上的目录路径 type <string> #指定存储卷类型 type类型如下: DirectoryOrCreate 指定的路径不存在时自动创建其权限为0755的空目录,属主和属组为kubelet Directory 必须存在的目录路径 FileOrCreate 指定的路径不存在时自动创建其权限为0644的空文件,属主和属组为kubelet File 必须存在的文件路径 Socket 必须存在的Socket文件路径 CharDevice 必须存在的字符设备文件路径 BlockDevice 必须存在的块设备文件路径
hostPath示例:
1)编辑资源清单文件
[root@k8s-master storage]# vim vol-hostpath.yaml apiVersion: v1 kind: Pod metadata: name: pod-vol-hostpath namespace: default spec: containers: - name: myapp image: nginx:1.15 imagePullPolicy: IfNotPresent volumeMounts: - name: html mountPath: /usr/share/nginx/html volumes: - name: html hostPath: path: /data/pod/volume1 type: DirectoryOrCreate
2)创建并查看状态
[root@k8s-master storage]# kubectl apply -f vol-hostpath.yaml pod/vol-hostpath-pod created [root@k8s-master storage]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES vol-hostpath-pod 1/1 Running 0 7s 10.244.1.83 k8s-node1 <none> <none>
3)通过上面查看pod被调度到节点1上面,查看节点1的目录并创建测试文件
[root@k8s-node1 ~]# ll /data/pod/volume1/ 总用量 0 [root@k8s-node1 ~]# echo "<h1>kubernetes hostPath test</h1>" >> /data/pod/volume1/index.html
4)访问测试,及删除测试
[root@k8s-master storage]# curl 10.244.1.83 <h1>kubernetes hostPath test</h1> #删除pod资源再次查看节点1上面的文件 [root@k8s-master storage]# kubectl delete -f vol-hostpath.yaml pod "vol-hostpath-pod" deleted [root@k8s-node1 ~]# ll /data/pod/volume1/ 总用量 4 -rw-r--r-- 1 root root 34 10月 9 16:09 index.html
[root@k8s-master ~]# kubectl explain pod.spec.volumes.nfs server <string> -required- #NFS服务器的IP地址或主机名,必选字段 path <string> -required- #NFS服务器导出(共享)的文件系统路径,必选字段 readOnly <boolean> #是否以只读方式挂载,默认为false
[root@storage ~]# yum -y install nfs-utils #安装软件 [root@storage ~]# mkdir -p /data/k8s/v1 #创建共享目录 [root@storage ~]# vim /etc/exports #编辑配置文件配置共享目录 /data/k8s/v1 192.168.1.0/24(rw,no_root_squash) [root@storage ~]# systemctl start rpcbind #启动rpcbind服务(nfs依赖服务) [root@storage ~]# systemctl start nfs #启动nfs [root@k8s-node1 ~]# showmount -e 192.168.1.34 #k8s节点测试能否正常访问到nfs服务器 Export list for 192.168.1.34: /data/k8s/v1 192.168.1.0/24
2)编辑资源清单文件
[root@k8s-master storage]# vim vol-nfs.yaml
3)创建并查看状态
[root@k8s-master storage]# kubectl apply -f vol-nfs.yaml pod/vol-nfs-pod created [root@k8s-master storage]# kubectl get pods NAME READY STATUS RESTARTS AGE vol-nfs-pod 1/1 Running 0 45s [root@k8s-master storage]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES vol-nfs-pod 1/1 Running 0 51s 10.244.2.80 k8s-node2 <none> <none>
4)测试验证
[root@k8s-master storage]# kubectl exec -it vol-nfs-pod redis-cli 127.0.0.1:6379> set mykey "hello test" OK 127.0.0.1:6379> get mykey "hello test 127.0.0.1:6379> bgsave Background saving started 127.0.0.1:6379> exit #为了测试其数据持久化效果,下面删除Pod资源vol-nfs-pod,并于再次重新创建后检测数据是否依然能够访问 [root@k8s-master storage]# kubectl delete -f vol-nfs.yaml pod "vol-nfs-pod" deleted [root@k8s-master storage]# kubectl apply -f vol-nfs.yaml pod/vol-nfs-pod created [root@k8s-master storage]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES vol-nfs-pod 1/1 Running 0 47s 10.244.1.84 k8s-node1 <none> <none> [root@k8s-master storage]# kubectl exec -it vol-nfs-pod redis-cli 127.0.0.1:6379> get mykey "hello test" 127.0.0.1:6379>
PersistentVolumeClaim
(PVC
)是用户存储的请求。它类似于Pod
。Pod
消耗节点资源,PVC
消耗存储资源。Pod
可以请求特定级别的资源(CPU
和内存)。权限要求可以请求特定的大小和访问模式。
虽然
PersistentVolumeClaims
允许用户使用抽象存储资源,但是常见的是,用户需要具有不同属性(如性能)的PersistentVolumes
,用于不同的问题。集群管理员需要能够提供多种不同于PersistentVolumes
的PersistentVolumes
,而不仅仅是大小和访问模式,而不会使用户了解这些卷的实现细节。对于这些需求,存在StorageClass
资源。
StorageClass
为管理员提供了一种描述他们提供的存储的“类”的方法。不同的类可能映射到服务质量级别,或备份策略,或者由集群管理员确定的任意策略。Kubernetes
本身对于什么类别代表是不言而喻的。这个概念有时在其它存储系统中称为“配置文件”
Provisioning—>Binding—>Using—>Releasing—>Recycling
供应准备Provisioning
Static:集群管理员创建多个
PV
。它们携带可供集群用户使用的真实存储的详细信息。它们存在于Kubernetes API
中,可用于消费。Dynamic:当管理员创建的静态
PV
都不匹配用户的PersistentVolumesClaim
时,集群可能会尝试为PVC
动态配置卷。此配置基于StorageClasses:PVC
必须请求一个类,并且管理员必须已经创建并配置该类才能进行动态配置。要求该类的声明有效地位自己禁用动态配置。
绑定Binding
使用Using
释放Releasing
回收Recycling
字段说明:
[root@k8s-master ~]# kubectl explain pv.spec capacity <map[string]string> #当前PV的容量;目前,capacity仅支持空间设定,将来应该还可以指定IOPS和throughput。 accessModes <[]string> #访问模式;尽管在PV层看起来并无差异,但存储设备支持及启用的功能特性却可能不尽相同。例如NFS存储支持多客户端同时挂载及读写操作,但也可能是在共享时仅启用了只读操作,其他存储系统也存在类似的可配置特性。因此,PV底层的设备或许存在其特有的访问模式,用户使用时必须在其特性范围内设定其功能。参考:https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes - ReadWribeOnce:仅可被单个节点读写挂载;命令行中简写为RWO。 - ReadOnlyMany:可被多个节点同时只读挂载;命令行中简写为ROX。 - ReadWriteMany:可被多个节点同时读写挂载;命令行中简写为RWX。 persistentVolumeReclaimPolicy <string> #PV空间被释放时的处理机制;可用类型仅为Retain(默认)、Recycle或Delete,具体说明如下。 - Retain:保持不动,由管理员随后手动回收。 - Recycle:空间回收,即删除存储卷目录下的所有文件(包括子目录和隐藏文件),目前仅NFS和hostPath支持此操作。 - Delete:删除存储卷,仅部分云端存储系统支持,如AWS EBS、GCE PD、Azure Disk和Cinder volumeMode <string> #卷模型,用于指定此卷可被用作文件系统还是裸格式的块设备;默认为Filesystem。 storageClassName <string> #当前PV所属的StorageClass的名称;默认为空值,即不属于任何StorageClass。 mountOptions <[]string> #挂载选项组成的列表,如ro、soft和hard等。
字段说明:
PersistentVolumeClaim
是存储卷类型的资源,它通过申请占用某个PersistentVolume
而创建,它与PV
是一对一的关系,用户无须关系其底层实现细节。申请时,用户只需要指定目标空间的大小、访问模式、PV
标签选择器和StorageClass
等相关信息即可。PVC
的Spec
字段的可嵌套字段具体如下:
[root@k8s-master ~]# kubectl explain pvc.spec accessModes <[]string> #当前PVC的访问模式,其可用模式与PV相同 resources <Object> #当前PVC存储卷需要占用的资源量最小值;目前,PVC的资源限定仅指其空间大小 selector <Object> #绑定时对PV应用的标签选择器(matchLabels)或匹配条件表达式(matchEx-pressions),用于挑选要绑定的PV;如果同时指定了两种挑选机制,则必须同时满足两种选择机制的PV才能被选出 storageClassName <string> #所依赖的存储卷的名称 volumeMode <string> #卷模型,用于指定此卷可被用作于文件系统还是裸格式的块设备;默认为“Filesystem” volumeName <string> #用于直接指定要绑定的PV的卷名
[root@k8s-master ~]# kubectl explain pod.spec.volumes.persistentVolumeClaim claimName <string> -required- #要调用的PVC存储卷的名称,PVC卷要与Pod在同一名称空间中 readOnly <boolean> #是否将存储卷挂载为只读模式,默认为false。
(1)创建存储卷对应的目录 [root@storage ~]# mkdir /data/volumes/v{1..5} -p (2)修改nfs的配置文件 [root@storage ~]# vim /etc/exports /data/volumes/v1 192.168.1.0/24(rw,no_root_squash) /data/volumes/v2 192.168.1.0/24(rw,no_root_squash) /data/volumes/v3 192.168.1.0/24(rw,no_root_squash) /data/volumes/v4 192.168.1.0/24(rw,no_root_squash) /data/volumes/v5 192.168.1.0/24(rw,no_root_squash) (3)查看nfs的配置 [root@storage ~]# exportfs -arv exporting 192.168.1.0/24:/data/volumes/v5 exporting 192.168.1.0/24:/data/volumes/v4 exporting 192.168.1.0/24:/data/volumes/v3 exporting 192.168.1.0/24:/data/volumes/v2 exporting 192.168.1.0/24:/data/volumes/v1 (4)使配置生效 [root@storage ~]# showmount -e Export list for storage: /data/volumes/v5 192.168.1.0/24 /data/volumes/v4 192.168.1.0/24 /data/volumes/v3 192.168.1.0/24 /data/volumes/v2 192.168.1.0/24 /data/volumes/v1 192.168.1.0/24
(1)编写资源清单文件 [root@k8s-master storage]# vim pv-nfs-demo.yaml apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs-001 labels: name: pv001 spec: nfs: path: /data/volumes/v1 server: 192.168.1.34 readOnly: false accessModes: ["ReadWriteOnce","ReadWriteMany"] capacity: storage: 2Gi persistentVolumeReclaimPolicy: Retain --- apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs-002 labels: name: pv002 spec: nfs: path: /data/volumes/v2 server: 192.168.1.34 readOnly: false accessModes: ["ReadWriteOnce"] capacity: storage: 5Gi persistentVolumeReclaimPolicy: Retain --- apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs-003 labels: name: pv003 spec: nfs: path: /data/volumes/v3 server: 192.168.1.34 readOnly: false accessModes: ["ReadWriteOnce","ReadWriteMany"] capacity: storage: 10Gi persistentVolumeReclaimPolicy: Retain --- apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs-004 labels: name: pv004 spec: nfs: path: /data/volumes/v4 server: 192.168.1.34 readOnly: false accessModes: ["ReadWriteOnce","ReadWriteMany"] capacity: storage: 15Gi persistentVolumeReclaimPolicy: Retain --- apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs-005 labels: name: pv005 spec: nfs: path: /data/volumes/v5 server: 192.168.1.34 readOnly: false accessModes: ["ReadWriteOnce","ReadWriteMany"] capacity: storage: 20Gi persistentVolumeReclaimPolicy: Retain (2)创建PV [root@k8s-master storage]# kubectl apply -f pv-nfs-demo.yaml persistentvolume/pv-nfs-001 created persistentvolume/pv-nfs-002 created persistentvolume/pv-nfs-003 created persistentvolume/pv-nfs-004 created persistentvolume/pv-nfs-005 created (3)查看PV [root@k8s-master storage]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs-001 2Gi RWO,RWX Retain Available 2s pv-nfs-002 5Gi RWO Retain Available 2s pv-nfs-003 10Gi RWO,RWX Retain Available 2s pv-nfs-004 15Gi RWO,RWX Retain Available 2s pv-nfs-005 20Gi RWO,RWX Retain Available 2s
(1)编写资源清单文件 [root@k8s-master storage]# vim vol-nfs-pvc.yaml #创建PVC apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs-pvc spec: accessModes: ["ReadWriteMany"] resources: requests: storage: 6Gi #指定PVC大小为6Gi selector: #这里通过标签选择器指定了所使用的pv卷为key为name,value为pv003的pv资源 matchLabels: name: pv003 --- #创建Pod apiVersion: v1 kind: Pod metadata: name: pvc-mysql labels: app: mysql spec: containers: - name: pvc-mysql-pod image: mysql:latest imagePullPolicy: IfNotPresent ports: - name: mysqlport containerPort: 3306 volumeMounts: - name: mysqldata mountPath: /var/lib/mysql env: - name: MYSQL_ROOT_PASSWORD value: "mysql" volumes: - name: mysqldata persistentVolumeClaim: #通过该字段定义使用pvc claimName: nfs-pvc #指定pvc的名称 readOnly: false #关闭只读 (2)创建PVC和Pod [root@k8s-master storage]# kubectl apply -f vol-nfs-pvc.yaml persistentvolumeclaim/nfs-pvc created pod/pvc-mysql created
[root@k8s-master storage]# kubectl get pvc #查看pvc,可以看到该pvc使用的是pv-nfs-003资源 NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE nfs-pvc Bound pv-nfs-003 10Gi RWO,RWX 12s [root@k8s-master storage]# kubectl get pv #查看pv,可以看出pv-nfs-003资源的状态从Availabel变成了Bound NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs-001 2Gi RWO,RWX Retain Available 64m pv-nfs-002 5Gi RWO Retain Available 64m pv-nfs-003 10Gi RWO,RWX Retain Bound default/nfs-pvc 64m pv-nfs-004 15Gi RWO,RWX Retain Available 64m pv-nfs-005 20Gi RWO,RWX Retain Available 64m [root@k8s-master storage]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pvc-mysql 1/1 Running 0 31s 10.244.2.84 k8s-node2 <none> <none> [root@storage ~]# ls /data/volumes/v3/ #查看nfs服务器的pv3对应的共享目录,里面生成了mysql的数据。 auto.cnf ca-key.pem ib_buffer_pool ibtmp1 performance_schema server-key.pem binlog.000001 ca.pem ibdata1 #innodb_temp private_key.pem sys binlog.000002 client-cert.pem ib_logfile0 mysql public_key.pem undo_001 binlog.index client-key.pem ib_logfile1 mysql.ibd server-cert.pem undo_002
5)测试验证
#(1)进入到pod连接容器mysql并创建一个数据库 [root@k8s-master ~]# kubectl exec -it pvc-mysql -- mysql -u root -pmysql ...... mysql> mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 4 rows in set (0.01 sec) mysql> create database volumes; Query OK, 1 row affected (0.00 sec) mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | volumes | +--------------------+ 5 rows in set (0.00 sec) mysql> exit Bye #(2)删除pvc和pod和pv [root@k8s-master storage]# kubectl delete -f vol-nfs-pvc.yaml #删除pvc persistentvolumeclaim "nfs-pvc" deleted pod "pvc-mysql" deleted [root@k8s-master storage]# kubectl delete -f pv-nfs-demo.yaml #删除pv(如果有pv在被使用的状态,需要先删除pvc方可删除pv) persistentvolume "pv-nfs-001" deleted persistentvolume "pv-nfs-002" deleted persistentvolume "pv-nfs-003" deleted persistentvolume "pv-nfs-004" deleted persistentvolume "pv-nfs-005" deleted [root@storage ~]# ls /data/volumes/v3/ #上面删除了pv和pvc,可以看出存储服务器上面的数据还是存在 auto.cnf ca-key.pem ib_buffer_pool ibtmp1 performance_schema server-key.pem volumes binlog.000001 ca.pem ibdata1 #innodb_temp private_key.pem sys binlog.000002 client-cert.pem ib_logfile0 mysql public_key.pem undo_001 binlog.index client-key.pem ib_logfile1 mysql.ibd server-cert.pem undo_002 #(3)重新创建pv和pvc和pod验证数据 [root@k8s-master storage]# kubectl apply -f pv-nfs-demo.yaml persistentvolume/pv-nfs-001 created persistentvolume/pv-nfs-002 created persistentvolume/pv-nfs-003 created persistentvolume/pv-nfs-004 created persistentvolume/pv-nfs-005 created [root@k8s-master storage]# kubectl apply -f vol-nfs-pvc.yaml persistentvolumeclaim/nfs-pvc created pod/pvc-mysql created [root@k8s-master ~]# kubectl exec -it pvc-mysql -- mysql -u root -pmysql mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | volumes | +--------------------+ 5 rows in set (0.00 sec) ### 测试说明: 如果删除pvc不删除pv,重新创建同样的pvc,那么pvc状态会处于Pending状态,因为pv的当前状态为Released。这也和上面定义的回收策略息息相关。