存储卷
同一个pod内的所有容器共享存储 网络 用户等空间 pod内的容器都是以pause镜像为基础镜像来运行的
k8s.gcr.io/pause 3.1 da86e6ba6ca1 17 mon 742 kB
emptyDir
临时存储目录 pod删除 存储卷也随即会被删除
hostPath 主机目录
pod所在主机上的目录
搭建NFS网络存储
nfs搭建 yum install nfs-utils mkdir -pv /data/volumes vi /etc/exports /data/volumes 192.168.11.0/16(rw,no_root_squash) systemctl start nfs 1.确保k8s集群中的所有节点都能驱动nfs yum install nfs-utils 2.[root@node2 ~]# mount -t nfs 192.168.11.157:/data/volumnes/ /mnt mount.nfs: access denied by server while mounting 192.168.11.157:/data/volumnes/ 3.在nfs服务器查看/var/log/messages查看 volumnes单词拼写错误 [root@node2 ~]# mount -t nfs 192.168.11.157:/data/volumes /mnt [root@localhost ~]# vi /etc/exports /data/volumes/ 192.168.11.0/16(insecure,rw,async,no_root_squash) [root@localhost ~]# exportfs -r exportfs: Invalid prefix `24*' for 192.168.11.0/24* [root@localhost ~]# showmount -e Export list for localhost.localdomain: /data/volumes 192.168.11.0/16
apiVersion: v1 kind: Pod metadata: name: pod-vol-hostpath namespace: default spec: containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: html mountPath: /usr/share/nginx/html/ volumes: - name: html hostPath: path: /data/pod/volume1 type: DirectoryOrCreate
apiVersion: v1 kind: Pod metadata: name: pod-vol-nfs namespace: default spec: containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: html mountPath: /usr/share/nginx/html/ volumes: - name: html nfs: path: /data/volumes server: 192.168.11.157
nfs远程文件存储并没有一个“存储设备”需要挂载在宿主机上 所以不需要attach阶段
直接将远端 NFS 服务器的目录(比如:“/”目录)挂载到Volume的宿主机目录上即可
mount -t nfs
通过这个挂载操作,Volume的宿主机目录就成为了一个远程 NFS 目录的挂载点,后面你在这个目录里写入的所有文件,都会被保存在远程 NFS 服务器上。
所以,我们也就完成了对这个 Volume 宿主机目录的“持久化”
pv和pvc的使用
存储工程师把分布式存储系统上的总空间划分成一个一个小的存储块
k8s管理员根据存储块创建与之一一对应的pv资源
pv属于集群级别资源 不属于任何名称空间 定义的时候不能指定名称空间
用户在创建pod的时候同时创建与pv一一对应的pvc资源
创建Pod的时候,系统里并没有合适的 PV 跟它定义的 PVC 绑定 也就是说此时容器想要使用的Volume不存在。这时候,Pod的启动就会报错
[root@localhost volumes]# mkdir v{1,2,3,4,5} [root@localhost volumes]# ls index.html v1 v2 v3 v4 v5 [root@localhost volumes]# vi /etc/exports /data/volumes/v1 192.168.11.0/16(insecure,rw,async,no_root_squash) /data/volumes/v2 192.168.11.0/16(insecure,rw,async,no_root_squash) /data/volumes/v3 192.168.11.0/16(insecure,rw,async,no_root_squash) /data/volumes/v4 192.168.11.0/16(insecure,rw,async,no_root_squash) /data/volumes/v5 192.168.11.0/16(insecure,rw,async,no_root_squash) [root@localhost volumes]# exportfs -r [root@localhost volumes]# showmount -e Export list for localhost.localdomain: /data/volumes/v5 192.168.11.0/16 /data/volumes/v4 192.168.11.0/16 /data/volumes/v3 192.168.11.0/16 /data/volumes/v2 192.168.11.0/16 /data/volumes/v1 192.168.11.0/16
apiVersion: v1 kind: PersistentVolume metadata: name: pv001 labels: name: pv001 spec: nfs: path: /data/volumes/v1 server: 192.168.11.158 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 2Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv002 labels: name: pv002 spec: nfs: path: /data/volumes/v2 server: 192.168.11.158 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 5Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv003 labels: name: pv003 spec: nfs: path: /data/volumes/v3 server: 192.168.11.158 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 2Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv004 labels: name: pv004 spec: nfs: path: /data/volumes/v4 server: 192.168.11.158 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 2Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv005 labels: name: pv005 spec: nfs: path: /data/volumes/v5 server: 192.168.11.158 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 2Gi
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mypvc namespace: default spec: accessModes: ["ReadWriteMany"] resources: requests: storage: 4Gi --- apiVersion: v1 kind: Pod metadata: name: pod-vol-pvc namespace: default spec: containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: html mountPath: /usr/share/nginx/html volumes: - name: html persistentVolumeClaim: claimName: mypvc
[root@k8s-master ~]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv001 2Gi RWO,RWX Retain Available 8m pv002 5Gi RWO,RWX Retain Bound default/mypvc 8m pv003 2Gi RWO,RWX Retain Available 8m pv004 2Gi RWO,RWX Retain Available 8m pv005 2Gi RWO,RWX Retain Available 8m [root@k8s-master ~]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mypvc Bound pv002 5Gi RWO,RWX 8m [root@k8s-master ~]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-deploy-67f6f6b4dc-2986w 1/1 Running 0 4h myapp-deploy-67f6f6b4dc-czvq4 1/1 Running 0 4h myapp-deploy-67f6f6b4dc-tpggj 1/1 Running 0 10d pod-vol-pvc 1/1 Running 0 9m tomcat-deploy-588c79d48d-mdgml 1/1 Running 0 10d tomcat-deploy-588c79d48d-mvttj 1/1 Running 0 4h tomcat-deploy-588c79d48d-w2mxb 1/1 Running 0 10d 在创建pvc的时候如果后端没有合适的pv 那么挂载此pvc的pod将会一致处于等待状态直到pvc匹配到一个符合条件的pv
pv和pvc的机制
pv和pvc绑定要求
1.PV和PVC的spec字段。比如PV的存储(storage)大小
2.PV和PVC的storageClassName字段必须一样
3. PVC 可以理解为持久化存储的“接口”,它提供了对某种持久化存储的描述,但不提供具体的实现
4. 持久化存储的实现部分则由PV负责完成
5.PV与PVC进行“绑定”,其实就是将这个PV对象的名字,填在了 PVC 对象的 spec.volumeName 字段上
pv对象转换成持久化volume的原理
所谓容器的 Volume,其实就是将一个宿主机上的目录,跟一个容器里的目录绑定挂载在了一起
持久化宿主机目录
远程存储服务,比如:远程文件存储(比如,NFS、GlusterFS)、远程块存储(比如,公有云提供的远程磁盘)
“持久化”宿主机目录的过程,我们可以形象地称为“两阶段处理”
先把远程存储设备附加到指定节点上 然后在节点上格式化存储设备再挂载到节点上的具体目录下 这样才能通过节点目录访问远程存储设备
Attach阶段
为虚拟机挂载远程磁盘的操作
当一个Pod调度到一个节点上之后,kubelet就要负责为这个Pod创建它的 Volume 目录。默认情况下,kubelet为Volume创建的目录是一个宿主机上的路径如
/var/lib/kubelet/pods/volumes/kubernetes.io~/
这个目录是宿主机后面用来和远程存储服务mount的关联目录
Kubernetes 提供的可用参数是 nodeName,即宿主机的名字
Mount阶段
将磁盘设备格式化并挂载到Volume宿主机目录的操作
把格式化的磁盘mount到/var/lib/kubelet/pods/volumes/kubernetes.io~/
Kubernetes 提供的可用参数是 dir,即 Volume 的宿主机目录
经过了“两阶段处理” 我们就得到了一个“持久化”的 Volume 宿主机目录 但是还没有关联到docker容器
接下来 kubelet 只要把这个 Volume目录通过 CRI 里的 Mounts 参数,传递给 Docker 然后就可以为 Pod 里的容器挂载这个“持久化”的 Volume
docker run -v /var/lib/kubelet/pods/
上面是Kubernetes 处理 PV 的具体原理
StorageClass自动创建pv
Kubernetes只会将StorageClass相同的PVC和PV 绑定起来
自动创建PV的机制Dynamic Provisioning
人工管理PV的方式就叫作Static Provisioning
StorageClass对象就是创建PV的模板
StorageClass对象会定义如下两个部分内容
第一,PV的属性。比如,存储类型、Volume 的大小等等
第二,创建这种PV需要用到的存储插件。比如Ceph 等等
有了这样两个信息之后,Kubernetes 就能够根据用户提交的 PVC,找到一个对应的 StorageClass 了
Kubernetes 就会调用该 StorageClass 声明的存储插件 创建出需要的 PV
有了 Dynamic Provisioning 机制 运维人员只需要在 Kubernetes 集群里创建出数量有限的 StorageClass 对象就可以了
当开发人员提交了包含 StorageClass 字段的 PVC 之后,Kubernetes 就会根据这个 StorageClass 创建出对应的 PV
默认情况下运维人员对每个开发人员创建的pvc都要手动创建一个对应的pvc与其进行绑定