千家信息网

k8s之volumes持久化存储

发表于:2024-09-22 作者:千家信息网编辑
千家信息网最后更新 2024年09月22日,k8s之数据持久化kubernetes存储卷:我们知道默认情况下容器的数据都是非持久化的,在容器销毁以后数据也跟着丢失,所以docker提供了volume机制以便将数据持久化存储。类似的,k8s提供了
千家信息网最后更新 2024年09月22日k8s之volumes持久化存储

k8s之数据持久化

kubernetes存储卷:
我们知道默认情况下容器的数据都是非持久化的,在容器销毁以后数据也跟着丢失,所以docker提供了volume机制以便将数据持久化存储。类似的,k8s提供了更强大的volume机制和丰富的插件,解决了容器数据持久化和容器间共享数据的问题。

volume:
我们经常会说:容器和 Pod 是短暂的。
其含义是它们的生命周期可能很短,会被频繁地销毁和创建。容器销毁时,保存在容器内部文件系统中的数据都会被清除。为了持久化保存容器的数据,可以使用k8s volume。
Volume 的生命周期独立于容器,Pod 中的容器可能被销毁和重建,但 Volume 会被保留。

k8s支持的volume类型有emptydir,hostpath,persistentVolumeClaim,gcePersistentDisk,awsElasticBlockStore,nfs,iscsi,gitRepo,secret等等,完整列表及详细文档可参考 http://docs.kubernetes.org.cn/429.html。

在本文中主要实践以下几种volume类型:

1,EmptyDir(临时存储):
emptyDir 是最基础的 Volume 类型。正如其名字所示,一个 emptyDir Volume 是 Host 上的一个空目录。也就是宿主机上没有指定的目录或文件,直接由pod内部映射到宿主机上。(类似于docker中的docker manager volume 挂载方式)

我们通过下面的例子来实践emptydir:

[root@master yaml]# vim emptydir.yamlapiVersion: v1kind: Podmetadata:  name: read-writespec:  containers:  - name: write    image: busybox    volumeMounts:     #定义数据持久化    - mountPath: /write     #定义挂载目录,该目录是pod内部的目录      name: share-volume    args:    - /bin/sh    - -c    - echo "hello volumes" > /write/hello; sleep 3000;      - name: read     #在该pod内定义第二个容器    image: busybox    volumeMounts:    - mountPath: /read      name: share-volume    args:    - /bin/sh    - -c    - cat /read/hello; sleep 30000;  volumes:  - name: share-volume    emptyDir: {}       #定义一个数据持久化的类型empytdir

我们模拟一个pod里运行了两个容器,两个容器共享一个volume,一个负责写入数据,一个负责读取数据。

//运行该pod, 并进行查看:[root@master yaml]# kubectl  apply -f  emptydir.yaml pod/read-write created
[root@master yaml]# kubectl  get pod -o wideNAME         READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATESread-write   2/2     Running   0          14s   10.244.2.2   node02              
//我们分别查看两个容器中的挂载内容:[root@master yaml]# kubectl  exec  -it read-write -c read cat /read/hellohello volumes[root@master yaml]# kubectl  exec  -it read-write -c write cat /write/hellohello volumes

参数解释:
-c :为指定某个容器,是--container= 的缩写,可以通过--help进行查看。

因为 emptyDir 是 Docker Host 文件系统里的目录,其效果相当于执行了 docker run -v /write 和 docker run -v /read。我们在node02
上通过 docker inspect 分别查看容器的详细配置信息,我们发现两个容器都 mount 了同一个目录:

    "Mounts": [        {            "Type": "bind",            "Source": "/var/lib/kubelet/pods/756b4f4a-917a-414d-a7ee-523eecf05465/volumes/kubernetes.io~empty-dir/share-volume",            "Destination": "/read",            "Mode": "",            "RW": true,            "Propagation": "rprivate"        },        {            "Type": "bind",            "Source": "/var/lib/kubelet/pods/756b4f4a-917a-414d-a7ee-523eecf05465/volumes/kubernetes.io~empty-dir/share-volume",            "Destination": "/write",            "Mode": "",            "RW": true,            "Propagation": "rprivate"        },

这里的"/var/lib/kubelet/pods/756b4f4a-917a-414d-a7ee-523eecf05465/volumes/kubernetes.io~empty-dir/share-volume"就是emptydir 挂载到dockerhost上的真正路径。
所以我们可以进入到该路径下进行查看:

[root@node02 ~]# cd /var/lib/kubelet/pods/756b4f4a-917a-414d-a7ee-523eecf05465/volumes/kubernetes.io~empty-dir/share-volume/[root@node02 share-volume]# cat hello hello volumes

总结emptydir:
同个pod里边的不同容器,共享同一个持久化目录。当pod节点删除时,volume的内容也会被删除,但如果仅是容器被销毁,pod还在,则volume不受影响。也就是说emptydir的数据持久化的生命周期和使用的pod一致。一般作为临时存储使用,以及长时间任务的中间过程checkpoint的临时保存目录,及多容器共享目录。

2,hostPath:

  • 1)将宿主机上已经存在的目录或文件挂载到容器内部。
  • 2)这种持久化方式,运用场景不多,因为我们使用虚拟化技术的核心就是为了于宿主机进行隔离,但这种方式它增加了pod于节点之间的耦合。
  • 3)一般对于k8s集群本身的数据持久化,和docker本身的数据持久化会使用这种方式。

比如 kube-apiserver 和 kube-controller-manager 就是这样的应用。
我们通过"kubectl edit -n kube-system pod kube-apiserver-master"命令来查看 kube-apiserver Pod 的配置,下面是 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

这里定义了三个 hostPath volume 分别是k8s-certs、ca-certs 和etc- pki,分别对应 Host 目录 /etc/kubernetes/pki、/etc/ssl/certs 和 /etc/pki。

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

3,pv & pvc

  • PersistentVolume(pv):统一的数据持久化目录,是指由集群管理员配置提供的某存储系统上的一段空间,它是对底层共享存储的抽象,将共享存储作为一种可由用户申请使用的资源,实现了"存储消费"机制。
  • PersistentVolumeClaim(PVC):用于pv持久化空间的一个申请(Claim),声明。指定所需要的最低容量要求和访问模式,然后用户将持久卷声明的清单提交给 kubernetes api服务器,kubernetes将找到可匹配的持久卷并将其绑定到持久卷声明。

NFS PersistentVolume
通过 NFS 实践PV和PVC。

1)我们在master节点上部署nfs服务:

[root@master ~]# yum -y install nfs-utils[root@master ~]# mkdir /nfsdata[root@master ~]# vim /etc/exports   #编写nfs配置文件/nfsdata 172.16.1.0/24(rw,sync,no_root_squash)[root@master ~]# systemctl enable rpcbind[root@master ~]# systemctl start rpcbind[root@master ~]# systemctl enable nfs-server[root@master ~]# systemctl start nfs-server
[root@master ~]# showmount -e   #查看是否挂载成功Export list for master:/nfsdata 172.16.1.0/24

2)创建pv:

[root@master yaml]# vim nfs-pv.yamlapiVersion: v1kind: PersistentVolumemetadata:  name: nfs-pvspec:  capacity:      storage: 1Gi  accessModes:    - ReadWriteOnce  persistentVolumeReclaimPolicy: Recycle  storageClassName: nfs  nfs:    path: /nfsdata     #指定nfs共享目录    server: 172.16.1.30    #指定的是nfs服务器的ip地址
//通过以下命令来运行pv:[root@master yaml]# kubectl apply -f  nfs-pv.yaml persistentvolume/nfs-pv created
字段解释:capacity:指定pv的容量大小,目前,capacity仅支持空间设定,将来应该还可以指定IOPS和throughput。accessModes:访问模式,有以下几种模式:                                              ReadWriteOnce: 以读写的方式挂载到单个节点,命令行中简写为RWO。                                              ReadOnlyMany:以只读的方式挂载到多个节点,命令行中简写为ROX。                                              ReadWriteMany: 以读写的方式挂载到多个节点,命令行中简写为RWX。 persistentVolumeReclaimPolicy:pv空间释放时的回收策略,有以下几种策略:                                          Recycle:清除pv中的数据,然后自动回收。(自动回收策略是由pvc的保护机制保护的,当pv删除后,只要pvc还在数据就还在)                                              Retain: 保持不动,由管理员手动回收。                                              Delete: 删除云存储资源,仅部分云储存系统支持,如果AWS,EBS,GCE PD,Azure Disk和Cinder。注意:这里的回收策略是指在pv被删除之后,所存储的源文件是否删除。storageClassName:pv和pvc关联的依据。
//验证pv是否可用:[root@master yaml]# kubectl  get pvNAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGEnfs-pv   1Gi (容量为1GB)       RWO (读写)          Recycle   (自动回收)       Available(可用的,确保是该状态才可被使用)           nfs(基于nfs来做的)                     18m(时间)

3)创建一个pvc:

[root@master yaml]# vim nfs-pvc.yamlapiVersion: v1kind: PersistentVolumeClaimmetadata:  name: nfs-pvcspec:  accessModes:    - ReadWriteOnce     #pv和pvc的访问模式必须一致  resources:             #在该字段下的requests子字段中定义要申请的资源    requests:      storage: 1Gi  storageClassName: nfs
运行该pvc:[root@master yaml]# kubectl apply -f  nfs-pvc.yaml persistentvolumeclaim/nfs-pvc created
//验证pvc是否可用:[root@master yaml]# kubectl  get pvcNAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGEnfs-pvc   Bound   nfs-pv   1Gi        RWO            nfs            3m53s[root@master yaml]# kubectl  get pvNAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGEnfs-pv   1Gi        RWO            Recycle          Bound    default/nfs-pvc   nfs                     29m

确保此时pv和pvc的状态都为Bound,则表示绑定成功。

pv空间的使用。

接下来我们实践mysql的pv使用:
1)创建一个mysql的pod:

[root@master yaml]# vim mysql-pod.yamlapiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: mysqlspec:  template:    metadata:      labels:        app: mysql    spec:      containers:      - name: mysql        image: mysql:5.7        env:             #定义一个变量,将容器中mysqlroot密码映射到本地        - name: MYSQL_ROOT_PASSWORD          value: 123.com     #密码为123.com        ports:        - containerPort: 3306        volumeMounts:         #定义数据持久化        - name: mysql-pv-storage          mountPath: /var/lib/mysql   #该目录为默认的mysql数据持久化目录      volumes:                     #该volumes字段为上面的一个解释      - name: mysql-pv-storage        #注意名称要与上面的名称相同        persistentVolumeClaim:      #指定pvc,注意下面声明的pvc要于之前创建的pvc名称一致          claimName: nfs-pvc       ---apiVersion: v1                  #创建一个service资源对象kind: Servicemetadata:  name: mysqlspec:  type: NodePort  ports:  - port: 3306    targetPort: 3306    nodePort: 30000  selector:    app: mysql
通过以下命令来运行pod:[root@master yaml]# kubectl apply -f  mysql-pod.yaml deployment.extensions/mysql createdservice/mysql created
//查看pod是否正常运行:[root@master yaml]# kubectl  get pod -o wide mysql-68d65b9dd9-hf2bf NAME                     READY   STATUS    RESTARTS   AGE     IP           NODE     NOMINATED NODE   READINESS GATESmysql-68d65b9dd9-hf2bf   1/1     Running   0          9m34s   10.244.1.3   node01              

2)登录mysql数据库,进行写入数据:

[root@master yaml]# kubectl  exec  -it mysql-68d65b9dd9-hf2bf  -- mysql -u root -p123.comType 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql>
mysql> create database volumes_db;   #创建库Query OK, 1 row affected (0.01 sec)mysql> use volumes_db;      #进入库中Database changedmysql> create table my_id(     #创建表    -> id int primary key,    -> name varchar(25)    -> );Query OK, 0 rows affected (0.04 sec)mysql> insert into my_id values(1,'zhangsan');   #往表中写入数据Query OK, 1 row affected (0.01 sec)mysql> select * from my_id;    #查看数据+----+----------+| id | name     |+----+----------+|  1 | zhangsan |+----+----------+1 row in set (0.00 sec)

3)进行验证:
(1)手动删除pod,验证数据库内数据是否还会存在

[root@master ~]# kubectl  delete pod mysql-68d65b9dd9-hf2bf pod "mysql-68d65b9dd9-hf2bf" deleted
[root@master ~]# kubectl  get pod -o wideNAME                     READY   STATUS    RESTARTS   AGE    IP           NODE     NOMINATED NODE   READINESS GATESmysql-68d65b9dd9-bf9v8   1/1     Running   0          26s    10.244.1.4   node01              

删除pod后,kubernetes会生成新的pod,我们登录mysql查看
数据是否还会存在。

[root@master ~]# kubectl  exec  -it mysql-68d65b9dd9-bf9v8 -- mysql -u root -p123.comType 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql> select * from volumes_db.my_id;  +----+----------+| id | name     |+----+----------+|  1 | zhangsan |+----+----------+1 row in set (0.01 sec)

可以看到数据依旧会存在。

2)模拟pod运行所在节点宕机,在新生成的pod内,数据是否恢复正常。
从上面查看pod的信息中,我们知道pod是运行在node01上,所以我们将集群中的node01主机关机。
##[root@node01 ~]# systemctl poweroff

过一段时间后,kubernetes会将pod迁移至集群中node02主机上:

[root@master ~]# kubectl  get nodes   #得知node01节点已经宕机NAME     STATUS     ROLES    AGE   VERSIONmaster   Ready      master   39d   v1.15.0node01   NotReady      39d   v1.15.0node02   Ready         39d   v1.15.0

[root@master ~]# kubectl  get pod -o wideNAME                     READY   STATUS        RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATESmysql-68d65b9dd9-bf9v8   1/1     Terminating   0          15m   10.244.1.4   node01              mysql-68d65b9dd9-mvxdg   1/1     Running       0          83s   10.244.2.3   node02              

可以看到pod已经迁移到了node02上。

最后我们登录mysql,验证数据是否恢复:

[root@master ~]# kubectl exec  -it mysql-68d65b9dd9-mvxdg  -- mysql -u root -p123.comType 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql> select * from volumes_db.my_id;+----+----------+| id | name     |+----+----------+|  1 | zhangsan |+----+----------+1 row in set (0.09 sec)

可以得知在pod迁移之后,mysql服务正常运行,且数据也并没有丢失。。。

pv和pvc实现了mysql数据的持久化,分离了管理员和普通用户的职责,更适合生产环境。

0