与Docker中一样,同样是通过Volume来管理存储资源,Kubernetes的Volume比Docker的类型多,支持的存储也多,如:nfs、fastdfs、iscsi等

Volume

使用k8s Volume将数据持久化存储映射到容器

Pod的生命周期是短暂的,会被频繁的销毁和创建。容器销毁时,保存在容器内的数据也会被全部清除。

为了持久化保存数据,可以使用Kubernetes Volume

Volume生命周期独立于容器,Pod中的容器可能被销毁和重建,但Volume会被保留

与Docker Volume类似,Kubernetes Volume也是一个目录,当Volume被mount到Pod,Pod中的所有容器都可以访问这个Volume。Kubernetes Volume也支持多种backend类型,包括emptyDir、hostPath、GCE Persistent Disk、AWS Elastic Block Store、NFS、Ceph 等,完整列表参考https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes

Volume 提供了对各种 backend 的抽象,容器在使用 Volume 读写数据的时候不需要关心数据到底是存放在本地节点的文件系统中呢还是云硬盘上。对它来说,所有类型的 Volume 都只是一个目录。

emptyDir存储

emptyDir 是最基础的 Volume 类型。正如其名字所示,一个 emptyDir Volume 是 Host 上的一个空目录

emptyDir Volume 对于容器来说是持久的,对于 Pod 则不是。当 Pod 从节点删除时,Volume 的内容也会被删除。但如果只是容器被销毁而 Pod 还在,则 Volume 不受影响。

也就是说:emptyDir Volume 的生命周期与 Pod 一致。

Pod 中的所有容器都可以共享 Volume,它们可以指定各自的 mount 路径。

配置emptyDir

[root@node1 ~]# vim emptyDir.yml
apiVersion: v1
kind: Pod
metadata:
  name: emptydir
spec:
  containers:
  - name: emptydir
    image: busybox
    volumeMounts:    # 将最下方volumes名为test的目录挂载到容器内的/emptydir目录中
    - mountPath: /emptydir
      name: test
    args:    # 在挂载后的目录中创建文件test
    - /bin/sh
    - -c
    - echo "hello world!" > /emptydir/test; sleep 3000000

  - name: test   # 第二个Pod
    image: busybox
    volumeMounts:   # 将最下方volumes名为test的目录挂载到容器内的/testdir目录中
    - mountPath: /testdir
      name: test
    args:   # 因为第一个容器已经将test的volumes中写入了一个test文件,这里直接查看,能看到则挂载成功
    - /bin/sh
    - -c
    - cat /testdir/test; sleep 30000000
  volumes:
  - name: test
    emptyDir: {}

运行Pod

[root@node1 ~]# kubectl apply -f emptyDir.yml 
pod/emptydir created

查看Pod

[root@node1 ~]# kubectl get pod
NAME       READY   STATUS    RESTARTS   AGE
emptydir   2/2     Running   0          82s

查看第二个容器是否可以执行cat成功

[root@node1 ~]# kubectl logs emptydir test
hello world!

挂载成功~,使用第一个容器emptydir写入hello world!,第二个容器挂载后,可以直接看到第一个容器写入的内容。

目录挂载流程

创建emptydir容器,将emptyDir类型的空目录挂载到容器内的/emptydir目录。

在/emptydir目录中创建一个test文件,内容为hello world!

创建第二个容器test,将emptyDir类型的目录(目录内已有test文件),挂载到容器内的/testdir目录

在test容器中使用cat /testdir/test查看该文件内容,与emptydir容器创建的test一致

前面说到emptyDir类型是主机上的一个空目录,目录可以查看详细信息看到,Pod被创建到node2中,所以在node2中查看

test容器

[root@node2 ~]# docker container inspect k8s_test_emptydir_default_a0caa7aa-3a2f-46ac-ab74-d7ff09898ae4_0 
...
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/var/lib/kubelet/pods/a0caa7aa-3a2f-46ac-ab74-d7ff09898ae4/volumes/kubernetes.io~empty-dir/test",
                "Destination": "/testdir",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            },
...

emptydir容器

[root@node2 ~]# docker container inspect k8s_emptydir_emptydir_default_a0caa7aa-3a2f-46ac-ab74-d7ff09898ae4_0 
...
                "Type": "bind",
                "Source": "/var/lib/kubelet/pods/a0caa7aa-3a2f-46ac-ab74-d7ff09898ae4/volumes/kubernetes.io~empty-dir/test",
                "Destination": "/emptydir",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
...

两个容器内的Source目录都是/var/lib/kubelet/pods/a0caa7aa-3a2f-46ac-ab74-d7ff09898ae4/volumes/kubernetes.io~empty-dir/test,这个就是emptyDir在主机上的真正路径。且只在容器所在主机有此目录

emptyDir 是 Host 上创建的临时目录,其优点是能够方便地为 Pod 中的容器提供共享存储,不需要额外的配置。但它不具备持久性,如果 Pod 不存在了,emptyDir 也就没有了。根据这个特性,emptyDir 特别适合 Pod 中的容器需要临时共享存储空间的场景

删除方便以下实验

[root@node1 ~]# kubectl delete -f emptyDir.yml 
pod "emptydir" deleted

hostPath Volume

hostPath Volume 的作用是将 Docker Host 文件系统中已经存在的目录 mount 给 Pod 的容器。大部分应用都不会使用 hostPath Volume,因为这实际上增加了 Pod 与节点的耦合,限制了 Pod 的使用。不过那些需要访问 Kubernetes 或 Docker 内部数据(配置文件和二进制库)的应用则需要使用 hostPath。

kube-apiserver 和 kube-controller-manager 就是这样的应用

查看kube-apiserver的挂载方式

[root@node1 ~]# kubectl edit --namespace kube-system pod kube-apiserver-node1
# 只截取了关于volume部分的内容
...
    volumeMounts:
    - mountPath: /etc/ssl/certs
      name: ca-certs
      readOnly: true
    - mountPath: /etc/pki
      name: etc-pki
      readOnly: true
    - mountPath: /etc/kubernetes/pki
      name: k8s-certs
      readOnly: true
...
  volumes:
  - hostPath:
      path: /etc/ssl/certs
      type: DirectoryOrCreate
    name: ca-certs
  - hostPath:
      path: /etc/pki
      type: DirectoryOrCreate
    name: etc-pki
  - hostPath:
      path: /etc/kubernetes/pki
      type: DirectoryOrCreate
    name: k8s-certs

如果 Pod 被销毁了,hostPath 对应的目录也还会被保留,从这点看,hostPath 的持久性比 emptyDir 强。不过一旦 Host 崩溃,hostPath 也就没法访问了。

例:

[root@node1 ~]# vim httpd.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: htdocs
          mountPath: /usr/local/apache2/htdocs
          readOnly: True
      volumes:
      - name: htdocs
        hostPath:
          path: /root/htdocs

因为hostPath方式的目录需要存在,所以需要创建,注意创建的时候,因为master不会运行Pod容器,所以要在node2和node3都有这个目录,否则是映射不到的

[root@node2 ~]# mkdir /root/htdocs
[root@node2 ~]# echo "Chai" >> htdocs/index.html
[root@node2 ~]# scp -r htdocs/* root@node3:/root

运行Pod

[root@node1 ~]# kubectl apply -f httpd.yml 
deployment.apps/httpd-deployment created

查看Pod的ip尝试访问

[root@node1 ~]# kubectl get pod -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP           NODE
httpd-deployment-767654dffc-ltjjq   1/1     Running   0          74s   10.244.1.9   node2
httpd-deployment-767654dffc-qhp8x   1/1     Running   0          74s   10.244.1.8   node2
httpd-deployment-767654dffc-vhs2x   1/1     Running   0          74s   10.244.1.7   node2

访问这些Pod的ip

[root@node1 ~]# curl 10.244.1.7
Chai
[root@node1 ~]# curl 10.244.1.8
Chai
[root@node1 ~]# curl 10.244.1.9
Chai

外部存储

如果 Kubernetes 部署在诸如 AWS、GCE、Azure 等公有云上,可以直接使用云硬盘作为 Volume

腾讯云:https://cloud.tencent.com/document/product/457/44237

PV and PVC

使用前面的emptyDir和hostPath都是需要在k8s集群主机必须有目录存在的,一旦集群节点损坏,数据也随之消失,在可管理性上还有不足

如果要使用外部存储的话,事先要知道Volume来自哪个云厂商,创建好的Volume的id。

Pod通常由应用的开发人员维护,而Volume则通常是由存储系统的管理员维护。开发人员要知道云硬盘的具体信息,需要去询问管理员。

这就造成了管理上的问题 ,规模较小或者开发环境还能接受,但当集群规模较大,或者生产环境中,考虑效率和安全,就必须要解决这样的问题

对于这样的问题,Kubernetes有两种解决方案:PersistentVolume和PersistentVolumeClaim,也叫PV和PVC

PersistentVolume(PV)

是外部存储系统中的一块存储空间,由管理员创建和维护。与Volume一样,PV具有持久性,生命周期独立于Pod

PersistentVolumeClaim(PVC)

是对PV的申请(Claim)。PVC通常由普通用户创建和维护,需要为Pod分配存储资源时,用户可以创建一个PVC,指明存储资源的容量代销和访问模式(如:只读)等信息,k8s会查找并提供满足条件的PV。

有了PVC,用户只需要告诉k8s,需要什么样的资源,而不必知道是从哪里分配的资源和如何访问,有外部存储的管理员来处理。

创建NFS PV

搭建nfs

在node1搭建nfs

[root@node1 ~]# yum -y install nfs-utils rpcbind

创建nfs共享目录

[root@node1 ~]# mkdir /nfsdata

将目录共享

[root@node1 ~]# vim /etc/exports
/nfsdata *(rw,no_root_squash,sync)
[root@node1 ~]# exportfs -r
[root@node1 ~]# systemctl start rpcbind nfs-server
[root@node1 ~]# systemctl enable rpcbind nfs-server

在node2查看是否可以看到共享目录

[root@node2 ~]# showmount -e 192.168.1.11
Export list for 192.168.1.11:
/nfsdata *

创建PV

[root@node1 ~]# vim nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
spec:
  capacity:  # 指定PV容量
    storage: 1Gi
  accessModes:    # 指定访问模式
 #  - ReadWriteOnce – PV 能以 read-write 模式 mount 到单个节点。
 #  - ReadOnlyMany – PV 能以 read-only 模式 mount 到多个节点。
 #  - ReadWriteMany – PV 能以 read-write 模式 mount 到多个节点。 
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle # 当容器被删除时,PV里面的数据也被删除
  # 支持的策略有:
  # Retain – 需要管理员手工回收。删除容器后数据被保留下来
  # Recycle – 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*。
  # Delete – 删除 Storage Provider 上的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。
  storageClassName: nfs  
#storageClassName指定PV的class为nfs,相当于为PV设置了一个分类,PVC可以指定class申请相应class的PV
  nfs:   # 指定PV在NFS服务器上对应的目录
    path: /nfsdata/pv1    # nfs服务器共享目录,pv1需要创建
    server: 192.168.1.11  # nfs服务器ip

创建pv1目录

[root@node1 ~]# mkdir /nfsdata/pv1

运行创建PV

[root@node1 ~]# kubectl apply -f nfs-pv1.yml 
persistentvolume/mypv1 created

查看创建好的PV

[root@node1 ~]# kubectl get pv
NAME   CAPACITY  ACCESS MODES  RECLAIM POLICY  STATUS     CLAIM  STORAGECLASS   REASON AGE
mypv1  1Gi       RWO           Recycle         Available          nfs                  6s

STATUSAvailable,表示 mypv1 就绪,可以被 PVC 申请。

创建NFS PVC

用来申请创建的PV存储资源

[root@node1 ~]# vim nfs-pvc1.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc1
spec:
  accessModes:   # 同样是设置访问模式
    - ReadWriteOnce
  resources:   # 指定向PV申请的容量
    requests:
      storage: 1Gi
  storageClassName: nfs  # Class名要和PV中的Class名一致,就像Service指定Pod的标签一样

运行PVC

[root@node1 ~]# kubectl apply -f nfs-pvc1.yml 
persistentvolumeclaim/mypvc1 created

查看PVC

[root@node1 ~]# kubectl get pvc
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc1   Bound    mypv1    1Gi        RWO            nfs            4s

PV被申请后的变化,CLAIM(申请)字段是mypvc1,说明已经被PVC申请了PV的资源

[root@node1 ~]# kubectl get pv
NAME  CAPACITY  ACCESS MODES  RECLAIM POLICY  STATUS  CLAIM           STORAGECLASS   REASON   AGE
mypv1 1Gi       RWO           Recycle         Bound   default/mypvc1  nfs                     6h31m

Pod使用PVC

[root@node1 ~]# vim pod1.yml
apiVersion: v1
kind: Pod
metadata:
  name: mypod1
spec:
  containers:
  - name: mypod1
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 30000000
    volumeMounts:
    - mountPath: '/mydata'
      name: mydata
  volumes:
  - name: mydata
    persistentVolumeClaim:   # 使用PVC类型的持久化方式
      claimName: mypvc1   # 指定使用的PVC的name为mypvc1,容量为1G

运行Pod

[root@node1 ~]# kubectl apply -f pod1.yml 
pod/mypod1 created

查看Pod

[root@node1 ~]# kubectl get pod mypod1 -o wide 
NAME   READY  STATUS   RESTARTS  AGE   IP           NODE   NOMINATED NODE  READINESS GATES
mypod1 1/1    Running  0         106s  10.244.2.84  node3  <none>          <none>

在mypod1容器中的挂载目录/mydata中创建一个文件

[root@node1 ~]# kubectl exec mypod1 -- touch /mydata/cyj
[root@node1 ~]# ls /nfsdata/pv1/
cyj

在nfs目录中创建目录pjf

[root@node1 ~]# mkdir /nfsdata/pv1/pjf
[root@node1 ~]# kubectl exec mypod1 -- ls /mydata
cyj
pjf

回收PV

当PV不再需要时,可通过删除PVC回收(谨慎)

当有Pod正在使用PVC时,是不能进行回收的,必须先将Pod删除才能开始PVC的回收

[root@node1 ~]# kubectl delete -f pod1.yml 
pod "mypod1" deleted

删除PVC

[root@node1 ~]# kubectl delete pvc mypvc1 
persistentvolumeclaim "mypvc1" deleted
[root@node1 ~]# kubectl get pvc
No resources found in default namespace.

但是此时PV还在,只不过没有PVC在申请使用资源了

[root@node1 ~]# kubectl get pv
NAME   CAPACITY  ACCESS MODES  RECLAIM POLICY  STATUS    CLAIM  STORAGECLASS REASON  AGE
mypv1  1Gi       RWO           Recycle         Available        nfs                  7h10m

nfs目录中的内容也被删除了

[root@node1 ~]# ls /nfsdata/pv1/

内容被清除是因为设置的回收PV策略为Recycle,如果想要保留数据,则将策略设置为Retain

修改策略

[root@node1 ~]# vim nfs-pv1.yml
  persistentVolumeReclaimPolicy: Retain

更新PV

[root@node1 ~]# kubectl apply -f nfs-pv1.yml 
persistentvolume/mypv1 configured

验证新的回收策略

# 运行PVC申请PV
[root@node1 ~]# kubectl apply -f nfs-pvc1.yml 
persistentvolumeclaim/mypvc1 created
# 运行Pod使用PVC
[root@node1 ~]# kubectl apply -f pod1.yml 
pod/mypod1 created

在容器内的挂载目录下创建cgj文件

[root@node1 ~]# kubectl exec mypod1 -- touch /mydata/cgj
[root@node1 ~]# ls /nfsdata/pv1/
cgj

删除PVC后,再次查看文件是否存在于nfs共享目录

[root@node1 ~]# kubectl delete -f pod1.yml 
pod "mypod1" deleted
[root@node1 ~]# kubectl delete pvc mypvc1 
persistentvolumeclaim "mypvc1" deleted
[root@node1 ~]# ls /nfsdata/pv1/
cgj

评论




正在载入...
PoweredHexo
HostedAliyun
DNSAliyun
ThemeVolantis
UV
PV
BY-NC-SA 4.0