007、Kubernetes数据持久化篇
本文最后更新于 68 天前,其中的信息可能已经过时,如有错误请发送邮件到wuxianglongblog@163.com

Kubernetes数据持久化篇

一.为什么需要持久化

1.运行tomcat+mysql的案例

如下图所示,我们运行Pod成功后可以添加自定义的数据,数据被存储在MySQL数据库实例中。

我们可以执行"SELECT * FROM HPE_APP.T_USERS;"命令来查看数据哈。

温馨提示:
    我们可以通过"kubectl exec -it mysql-698607359-dvmzk bash"进入到容器并通过"env"查看MySQL的root用户密码哟。

image-20210729212043375

2.删除容器

如下图所示,我们可以在运行tomcat+mysql的Pod中,批量删除一波容器。

执行操作为"docker rm -f `docker ps -a -q`",删除容器后,这些容器会被K8S重新创建。

image-20210729213227483

3.Pod恢复后对应的数据会丢失

如下图所示,尽管业务恢复了,但我们的数据也丢失了。

综上所述,为了解决容器被删除后数据不丢失,则引入了存储类型,类似于docker中的数据卷。

在kubernetes集群中,其是支持多种存储类型,包括但不限于emptyDir,HostPath,NFS等等。

image-20210729213441163

二.数据卷概述

1.为什么需要数据卷

容器部署过程中一般有以下三种数据:
    (1)启动时需要的初始数据,例如配置文件;
    (2)启动过程中产生的临时数据,该临时数据需要多个容器间共享,例如类似于Redis的数据库;
    (3)启动容器过程中产生的持久化数据,例如MySQL的数据目录(datadir);

如下图所示,我们可以基于数据卷的方式实现数据的共享。

image-20210803160808455

2.数据卷的分类

数据卷:
    (1)kubernetes中的Volume提供了在容器中挂载外部存储的能力;
    (2)Pod需要设置卷来源(po.spec.volumes)和挂载点(po.spec.containers.volumeMounts)两个信息后才可以使用相应的volume;

数据卷类型大致分类:
    本地数据卷:
        hostPath,emptyDir等。
    网络数据卷:
        NFS,Ceph,GlusterFS等。
    公有云:
        AWS,EBS等;
    K8S资源:
        configmap,secret等。

除了上述提到的数据类型分类,但官方文档却要比上述描述的数据卷类型多得多,如下图所示。

推荐阅读:
    https://kubernetes.io/zh/docs/concepts/storage/volumes/

image-20210803154959199

三.emptyDir存储类型(多个Pod不能使用该类型进行数据共享,但同一个Pod的多个容器可以基于该类型进行数据共享哟)

1.emptyDir概述

emptyDir数据卷:
    是一个临时存储卷,与Pod生命周期绑定在一起,如果Pod删除了,这意味着数据卷也会被删除。

emptyDir的作用:
    (1)可以实现持久化的功能;
    (2)多个Pod之间不能通信数据,但是同一个Pod的多个容器是可以实现数据共享的;
    (3)随着Pod的生命周期而存在,当我们删除Pod时,其数据也会被随之删除。

emptyDir的应用场景(同一个Pod中各个容器之间数据的共享):
    (1)缓存空间,例如基于磁盘的归并排序;
    (2)为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行;
    (3)存储Web服务器的访问日志及错误日志等信息;

推荐阅读:
    https://kubernetes.io/docs/concepts/storage/volumes/#emptydir

温馨提示:
    使用emptyDir持久化数据时,删除容器并不会删除数据,因为容器删除并不能说明Pod被删除哟。

2.创建Pod并使用emptyDir案例

[root@k8s101.oldboyedu.com ~]# cat 01-volumes-emptyDir-demo.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: oldboyedu-volume

---

apiVersion: v1
kind: Pod
metadata:
  name: oldboyedu-pod-emptydir
  namespace: oldboyedu-volume
spec:
  # 定义数据卷的来源
  volumes:
  - name: data
    emptyDir: {}  # 我们无需为emptyDir指定任何参数,通常写个"{}"即可,表示创建的是一个空目录。
  - name: log
    emptyDir: {}

  containers:
  # 创建一个负责写入数据的容器
  - name: linux-producer
    image: 172.200.1.101:5000/alpine:latest
    # 启动容器后执行的命令,模拟生产数据即可
    command: ["sh","-c","for i in `seq 1000`;do echo $i >> /mydata/topic;sleep 1;done"]
    # 挂载数据卷
    volumeMounts:
      - name: data  # 注意哈,该名称必须在上面的"volumes"的name字段中存在哟~
        mountPath: /mydata  # 指定挂载到容器的位置

  # 创建一个负责读取数据的容器
  - name: linux-consumer
    image: 172.200.1.101:5000/alpine:latest
    # 启动容器后执行的命令,模拟消费数据即可
    command: ["sh","-c","tail -f /mydata2021/topic"]
    # 挂载数据卷
    volumeMounts:
      - name: data  # 注意哈,该名称必须在上面的"volumes"的name字段中存在哟~
        mountPath: /mydata2021

  - name: oldboyedu-linux
    image: 172.200.1.101:5000/alpine:latest
    command: ["sh","-c","tail -f /etc/hosts"]
    volumeMounts:
      - name: log
        mountPath: /oldboyedu
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# kubectl apply -f 01-volumes-emptyDir-demo.yaml 
namespace "oldboyedu-volume" configured
pod "pod-emptydir" configured
[root@k8s101.oldboyedu.com ~]# 

温馨提示:
    (1)如下图所示,我们可以在宿主机上查看到数据持久化到本地的文件位置哟;
    (2)当然,我们也可以进入到各个容器内部,查看相应的配置文件信息哟;

image-20210804214943105

3.删除Pod则持久化到本地的数据将被删除哟

[root@k8s101.oldboyedu.com ~]# kubectl get pods -n oldboyedu-volume 
NAME           READY     STATUS    RESTARTS   AGE
pod-emptydir   2/2       Running   0          32s
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# kubectl delete -f 01-volumes-emptyDir-demo.yaml 
namespace "oldboyedu-volume" deleted
pod "pod-emptydir" deleted
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# kubectl get pods -n oldboyedu-volume  -o wide
NAME           READY     STATUS        RESTARTS   AGE       IP            NODE
pod-emptydir   2/2       Terminating   0          1m       172.18.71.3   k8s103.oldboyedu.com
[root@k8s101.oldboyedu.com ~]# kubectl get pods -n oldboyedu-volume  -o wide
No resources found.
[root@k8s101.oldboyedu.com ~]# 

温馨提示:
    如下图所示,当我们删除Pod时,这意味本地的数据也会随之丢失哟。

image-20210804215820882

4.扩展案例

[root@k8s110 volumes]# cat 04-nginx-alpine-emptyDir.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: oldboyedu-linux76-nginx-alpine-emptydir-04
  labels:
    school: oldboyedu
spec:
  nodeName: k8s113.oldboyedu.com
  volumes:  # 定义数据卷
    - emptyDir: {}
      name: "oldboyedu-data"
    - name: "oldboyedu-data-2021"
      emptyDir: {}
  containers:
    - name: oldboyedu-nginx
      image: k8s110.oldboyedu.com:5000/nginx:1.20
      volumeMounts:      
        - name: "oldboyedu-data"
          mountPath: "/oldboyedu-linux76/python"
          # readOnly: true
          # 必须写相对路径,不能以根("/")开头,用来隔离多个容器挂载相同数据卷的情况
          subPath: "oldboyedu-linux76/c1"  
    - image: k8s110.oldboyedu.com:5000/linux/alpine:latest
      name: oldboyedu-alpine
      args: ["sleep","3600"]
      volumeMounts:      
        - name: "oldboyedu-data"
          mountPath: "/oldboyedu-linux76/golang"
          # readOnly: false
          subPath: "oldboyedu-linux76/c2"
[root@k8s110 volumes]# 

温馨提示:
    在执行上述案例时,可用进入到被调度节点的Pod存储目录("/var/lib/kubelet/pods/")查看信息哟.

四.HostPath存储类型(可以解决同一个Node节点的多个Pod数据共享哟,但多个Pod不在同一个Node节点时则无法通过HostPath来查找数据哟)

1.hostPath数据卷概述

hotsPath数据卷:
    挂载Node文件系统(Pod所在节点)上文件或者目录到Pod中的容器。
    如果Pod删除了,宿主机的数据并不会被删除,这一点是否感觉和咱们的数据卷有异曲同工之妙呢?

应用场景:
    Pod中容器需要访问宿主机文件。

推荐阅读:
    https://kubernetes.io/docs/concepts/storage/volumes/#hostpath

2.测试案例

[root@k8s101.oldboyedu.com ~]# vim 02-volumes-hostPath-demo.yaml 
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# cat  02-volumes-hostPath-demo.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: oldboyedu-volume

---

apiVersion: v1
kind: Pod
metadata:
  name: pod-hostpath
  namespace: oldboyedu-volume
spec:
  nodeName: k8s102.oldboyedu.com
  # 定义数据源
  volumes:
  - name: mydir
    # 定义挂载宿主机的源文件或目录
    hostPath:
      path: /oldboyedu  # 在K8S1.5版本中,如果该文件存在,则直接引用,若该文件不存在,则默认创建的是目录哟~
      # 如果给定路径上不存在任何内容,则会根据需要在该目录中创建一个空目录,并将权限设置为0755,该目录与Kubelet具有相同的组和所有权。
      # type: DirectoryOrCreate  # 注意哈,该字段属性在K8S1.5版本中并不支持哟,因此创建的是一个目录哟!
  - name: myfile
    hostPath:
      path: /tmp/k8s.log
      # 如果给定路径上不存在任何内容,则将根据需要在其中创建一个空文件,并将权限设置为0644,该文件具有与Kubelet相同的组和所有权。
      # type: FileOrCreate  # 注意哈,该字段属性在K8S1.5版本中并不支持哟,因此创建的是一个目录哟!
  - name: syslog
    hostPath:
      path: /var/log/messages
      # 文件必须存在于给定的路径
      # type: File  # 注意哈,该字段属性在K8S1.5版本中并不支持哟.

  containers:
  - name: mylinux
    image: k8s101.oldboyedu.com:5000/busybox:latest
    args:
    - /bin/sh
    - -c
    - sleep 36000
    # 将数据源挂载到容器指定的位置
    volumeMounts:
    - name: mydir
      mountPath: /mydata
    - name: myfile
      mountPath: /mydata/k8s.log
    - name: syslog
      mountPath: /mydata/system_messages.log
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# kubectl apply -f 02-volumes-hostPath-demo.yaml 
namespace "oldboyedu-volume" configured
pod "pod-hostpath" configured
[root@k8s101.oldboyedu.com ~]# 

温馨提示:
    (1)建议进入到Pod中观察挂载目录的情况;
    (2)建议删除Pod观察宿主机的数据是否真的会随之删除呢?答案当然是否定的。

3.实战案例

(1)创建namespace资源清单
[root@k8s101.oldboyedu.com ~/wordpress]# cat 00-wordpress-namespace.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: oldboyedu-wordpress
[root@k8s101.oldboyedu.com ~/wordpress]# 

(2)创建mysql的资源清单
[root@k8s101.oldboyedu.com ~/wordpress]# cat 01-wordpress-mysql.yaml 
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: oldboyedu-wordpress-mysql
  namespace: oldboyedu-wordpress
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: oldboyedu-wordpress-mysql
    spec:
      containers:
      - name: mysql
        image: k8s101.oldboyedu.com:5000/mysql:5.7
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: somewordpress
        - name: MYSQL_DATABASE
          value: wordpress
        - name: MYSQL_USER
          value: wordpress
        - name: MYSQL_PASSWORD
          value: wordpress
[root@k8s101.oldboyedu.com ~/wordpress]# 

(3)创建MySQL的service服务
[root@k8s101.oldboyedu.com ~/wordpress]# cat 02-wordpress-mysql-svc.yml 
apiVersion: v1
kind: Service
metadata:
  name: oldboyedu-mysql
  namespace: oldboyedu-wordpress
spec:
  ports:
    - port: 3306
      targetPort: 3306
  selector:
    app: oldboyedu-wordpress-mysql
[root@k8s101.oldboyedu.com ~/wordpress]# 

(4)创建
[root@k8s101.oldboyedu.com ~/wordpress]# cat 03-wordpress-web.yaml 
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: wordpress
  namespace: oldboyedu-wordpress
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      volumes:
        - name: wordpress-code
          hostPath: 
            path: /data/wordpress-code
      nodeName: k8s102.oldboyedu.com
      containers:
      - name: wordpress
        image: k8s101.oldboyedu.com:5000/wordpress:latest
        ports:
        - containerPort: 80
        volumeMounts:
          - mountPath: /var/www/html
            name: wordpress-code
        env:
        - name: WORDPRESS_DB_HOST
          value: oldboyedu-mysql  # 注意哈,这里我写的是暴露MySQL服务的service名称哟~
        - name: WORDPRESS_DB_USER
          value: wordpress
        - name: WORDPRESS_DB_PASSWORD
          value: wordpress
[root@k8s101.oldboyedu.com ~/wordpress]# 

(5)创建wordpress的service资源
[root@k8s101.oldboyedu.com ~/wordpress]# cat 04-wordpress-svc.yml 
apiVersion: v1
kind: Service
metadata:
  name: wordpress
  namespace: oldboyedu-wordpress
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 80
  selector:
    app: wordpress
[root@k8s101.oldboyedu.com ~/wordpress]# 

(6)应用配置文件
[root@k8s101.oldboyedu.com ~/wordpress]# kubectl apply -f .
namespace "oldboyedu-wordpress" configured
deployment "oldboyedu-wordpress-mysql" configured
service "oldboyedu-mysql" configured
deployment "wordpress" configured
service "wordpress" configured
[root@k8s101.oldboyedu.com ~/wordpress]# 

温馨提示:
    创建成功之后,就可以访问wordpress暴露的端口,从而访问相关的服务即可。

image-20210805173334400

[root@k8s102.oldboyedu.com ~]# vim /data/wordpress-code/info.php
[root@k8s102.oldboyedu.com ~]# 
[root@k8s102.oldboyedu.com ~]# cat /data/wordpress-code/info.php
<?php phpinfo(); ?>
[root@k8s102.oldboyedu.com ~]# 

温馨提示:
    如下图所示,我们可以访问php的测试网页哟,该网页我们可以手动编写即可。

image-20210805174157290

4.课堂练习(将"wordpress"的数据进行持久化)并进行手动伸缩实战案例

[root@k8s110 wordpress]# cat 02-mysql-wordpress-demo.yaml 
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: oldboyedu-linux76-mysql57-volume
spec:
  template:
    metadata:
      labels:
        app: oldboyedu-linux76-mysql
    spec:
      nodeName: k8s111.oldboyedu.com
      volumes:
        - name: "mysql-data"
          hostPath: 
            path: "/oldboyedu-linux76/mysql57"
      containers:
        - name: mysql
          image: k8s110.oldboyedu.com:5000/oldboyedu/mysql:5.7
          volumeMounts:      
            - name: "mysql-data"
              mountPath: "/var/lib/mysql"
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: somewordpress
            - name: MYSQL_DATABASE
              value: wordpress
            - name: MYSQL_USER
              value: wordpress
            - name: MYSQL_PASSWORD
              value: wordpress

---

apiVersion: v1
kind: Service
metadata:
  name: oldboyedu-mysql57-svc-volume
spec:
  ports:
    - port: 3306
      targetPort: 3306
  selector:
    app: oldboyedu-linux76-mysql

---

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: oldboyedu-linux76-wordpress-latest-volume
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: oldboyedu-linux76-wordpress
    spec:
      nodeName: k8s111.oldboyedu.com
      volumes:
        - name: "wordpress-data"
          # emptyDir: {}
          hostPath: 
            path: "/oldboyedu-linux76/wordpress"
      containers:
        - name: wordpress
          image: k8s110.oldboyedu.com:5000/oldboyedu/wordpress:latest
          ports:
            - containerPort: 80
          volumeMounts:      
            - name: "wordpress-data"
              mountPath: "/var/www/html"
          env:
            - name: WORDPRESS_DB_HOST
              value: oldboyedu-mysql57-svc-volume
            - name: WORDPRESS_DB_USER
              value: wordpress
            - name: WORDPRESS_DB_PASSWORD
              value: wordpress

---

apiVersion: v1
kind: Service
metadata:
  name: oldboyedu-wordpress
spec:
  type: NodePort
  ports:
    - port: 80
      nodePort: 32020
      targetPort: 80
  selector:
    app: oldboyedu-linux76-wordpress
[root@k8s110 wordpress]# 

温馨提示:
    kubectl scale --replicas=3 deploy/oldboyedu-linux76-wordpress-latest-volume

五.NFS存储类型(适合不同的node节点的Pod共享存储,但存在单点故障)

1.NFS数据卷概述

NFS数据卷:
    提供对NFS挂载支持,可以自动将NFS共享路径挂载到Pod中。

NFS:
    英文全称为"Network File System"(网络文件系统),是由SUN公司研制的UNIX表示层协议(presentation layer protocol),能使使用者访问网络上别处的文件就像在使用自己的计算机一样。
    NFS是一个主流的文件共享服务器,但存在单点故障,我们需要对数据进行备份哟,如果有必要可以使用分布式文件系统哈。

推荐阅读:
    https://kubernetes.io/docs/concepts/storage/volumes/#nfs

2.安装NFS环境并测试

(1)在所有节点上都安装"nfs-utils"软件包,安装该软件包不仅仅会包含NFS服务端软件,还会包含其客户端软件
yum -y install nfs-utils

(2)将"k8s101.oldboyedu.com"设置为NFS服务端,配置共享"/data/kubernetes"目录
[root@k8s101.oldboyedu.com ~]# vim /etc/exports
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# cat /etc/exports
/data/kubernetes *(rw,no_root_squash)
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# mkdir -pv /data/kubernetes
mkdir: 已创建目录 "/data"
mkdir: 已创建目录 "/data/kubernetes"
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# ll /data/kubernetes
总用量 0
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# systemctl start nfs  && systemctl enable nfs
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# exportfs   # 查看NFS的挂载信息
/data/kubernetes
        <world>
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# touch /data/kubernetes/nfs.log  # 创建测试文件,便于客户端挂载测试验证
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# ll /data/kubernetes
总用量 0
-rw-r--r-- 1 root root 0 3月  16 07:58 nfs.log
[root@k8s101.oldboyedu.com ~]# 

(3)客户端验证NFS的可用性
mount -t nfs k8s101.oldboyedu.com:/data/kubernetes /mnt/  # 手动挂载NFS的服务端进行测试

(4)手动卸载NFS
[root@k8s101.oldboyedu.com ~]# umount /mnt  # 手动卸载NFS

温馨提示:
    建议在Kubernetes的每个工作节点都安装nfs-utils软件包,如果只在NFS服务端安装,客户端未安装的话,可能客户端在使用mount命令手动挂在时会挂在不上哟~    

image-20210805205631818

3.测试案例

[root@k8s101.oldboyedu.com ~]# vim 03-volumes-nfs-demo.yaml 
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# cat 03-volumes-nfs-demo.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: oldboyedu-volume

---

apiVersion: extensions/v1beta1  # 注意观察这个API的版本编号哟~在后期的版本中变为"apps/v1"
kind: Deployment
metadata:
  name: mynginx-deploy
  namespace: oldboyedu-volume
spec:
  selector:
    matchLabels:
      app: nginx-nfs
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx-nfs
    spec:
      # 配置NFS的数据源
      volumes:
      - name: oldboyedu-nfs
        nfs:
          server: k8s101.oldboyedu.com  # 指定NFS的服务器的地址,如果指定主机名请确保可以正常进行解析哟~
          path: /data/kubernetes  # 指定NFS的挂载路径

      containers:
      - name: my-nginx
        image: k8s101.oldboyedu.com:5000/docker.io/nginx:1.13
        # 在容器中挂载数据卷
        volumeMounts:
        - name: oldboyedu-nfs
          mountPath: /usr/share/nginx/html
        ports:
        - containerPort: 80
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# kubectl apply -f 03-volumes-nfs-demo.yaml 
namespace "oldboyedu-volume" created
deployment "mynginx-deploy" created
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# kubectl get pods -n oldboyedu-volume 
NAME                              READY     STATUS    RESTARTS   AGE
mynginx-deploy-1210620599-0tqll   1/1       Running   0          6s
mynginx-deploy-1210620599-kp0p8   1/1       Running   0          6s
mynginx-deploy-1210620599-rb8z5   1/1       Running   0          6s
[root@k8s101.oldboyedu.com ~]# 

温馨提示:
    (1)如下图所示,建议进入到容器内创建首页文件:
        echo "<h1>oldboyedu linux2021<h1>" > /usr/share/nginx/html/index.html
    (2)如下图所示,当我们修改任意一个Pod(尽管不在同一个node节点)的首页文件时,无论你访问的是哪个Pod都会访问相同的页面哟。
    (3)你可以大胆删除Pod资源,观察数据始终不会丢失哟。
        kubectl delete -f 03-volumes-nfs-demo.yaml
    (4)生产环境中建议购买支持nfs协议的硬件存储,因为其会自动快照数据,从而保证数据的可用性。

image-20210809110602861

image-20210809112504340

六.PV和PVC的基本使用

1.持久化数据卷(persistent volume,简称"pv")和持久化数据卷声明(persistent volume claim,简称"pvc")的区别

什么是pv: 
    全称为:"persistent volume",属于k8s集群的全局资源,因此并不支持名称空间(namespace)。
    该资源对象本身并不负责数据的真实存储,而是负责和后端存储进行关联,每个pv都有提前定义好的存储能力。
    持久卷是对存储资源数据卷(volume)创建和使用的抽象,使得存储作为集群中的资源管理。
    这样我们就可以对数据卷的使用空间,读写权限做一个基本的权限管控,而不是放任Pod使用所有的数据卷资源。

pv的作用:    
    PV解决了企业对于存储的依赖问题,比如A公司使用的是nfs作为存储,而B公司使用的是cephfs作为存储,对于运维人员而言可能需要手动维护不同的组件,而一旦使用pv对存储资源进行抽象后,在迁移服务时就无需关心集群底层存储使用的资源,而是直接使用pv即可。
    简而言之,就是实现了以下三大特性:
        1>后端存储的权限管控(rw,ro);
        2>.存储能力(比如使用多大的存储空间);
        3>.后端存储卷(volumes)存储类型的应用解耦。

什么是pvc: 
    全称为:"persistent volume  claim",   属于k8s集群某一个namespace的局部资源。
    该资源对象本身并不负责数据的真实存储,而是显式声明需要使用的存储资源需求。

pvc的作用: 
    让用户不需要关心具体的Volume实现细节。pvc的一个最重要的作用就是去关联符合条件的pv,从而实现对存储资源的使用。

推荐阅读:
    https://kubernetes.io/docs/concepts/storage/persistent-volumes/

温馨提示:
    (1)全局资源指的是所有的namespace都能看到该资源,很明显是全局唯一的。
    (2)局部资源指的是该资源只属于某个namespace哟。
    (3)pv和pvc均属于k8s资源,而k8s的所有资源通常都支持打标签的哟。
    综上所述,pv和pvc的对应关系如下图所示。

image-20210810210223386

2.创建pv资源

(1)创建pv的存储目录,注意该目录是NFS的挂载目录哟~
mkdir -pv /data/kubernetes/pv0{1,2,3}

(2)编写pv的资源清单
[root@k8s101.oldboyedu.com ~]# cat 01-pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv01
  labels:
    type: ssd
spec:
  capacity:  # 定义PV存储容量的大小
    storage: 10Gi 
  accessModes:  # 指定访问模式
    - ReadWriteMany 
  persistentVolumeReclaimPolicy: Recycle  # 指定PV的回收策略
  nfs:  # 指定PV后端的支持存储设备类型
    path: "/data/kubernetes/pv01"
    server: 172.200.1.101
    readOnly: false
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# cat 02-pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv02
  labels:
    type: ssd
spec:
  capacity:  # 定义PV存储容量的大小
    storage: 30Gi 
  accessModes:  # 指定访问模式
    - ReadWriteMany 
  persistentVolumeReclaimPolicy: Recycle  # 指定PV的回收策略
  nfs:  # 指定PV后端的支持存储设备类型
    path: "/data/kubernetes/pv02"
    server: 172.200.1.101
    readOnly: false
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# cat 03-pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv03
  labels:
    type: ssd
spec:
  capacity:  # 定义PV存储容量的大小
    storage: 50Gi 
  accessModes:  # 指定访问模式
    - ReadWriteMany 
  persistentVolumeReclaimPolicy: Recycle  # 指定PV的回收策略
  nfs:  # 指定PV后端的支持存储设备类型
    path: "/data/kubernetes/pv03"
    server: 172.200.1.101
    readOnly: false
[root@k8s101.oldboyedu.com ~]# 

(3)创建pv资源
[root@k8s101.oldboyedu.com /oldboyedu/persistentVolume]# kubectl apply -f .
persistentvolume "pv01" created
persistentvolume "pv02" created
persistentvolume "pv03" created
[root@k8s101.oldboyedu.com /oldboyedu/persistentVolume]# 

温馨提示:
    如下图所示,我们在任意的名称空间都能看到PV资源哟,因为它是全局资源!
    常用的字段如下:
        NAME:
            有关PV资源的名称。

        CAPACITY:   
            该PV资源的容量大小。

        ACCESSMODES(访问模式):   
            是用来对PV进行访问模式的设置,用于描述用户应用对存储资源的访问权限,访问权限包括以下几种方式:
            ReadWriteOnce(RWO):
                读写权限,但是只能被单个节点挂载。
            ReadOnlyMany(ROX):
                只读权限,可以被多个节点挂载。
            ReadWriteMany(RWX):
                读写权限,可以被多个节点挂载。
            ReadWriteOncePod:
                卷可以由单个Pod以读写方式挂载。这仅支持CSI卷和Kubernetes 1.22+版。

        RECLAIMPOLICY(回收策略):   
            目前较新版本中PV支持的策略有三种:
                Retain(保留):
                    保留数据,需要管理员手工清理数据,这是默认策略哟。
                Recycle(回收):
                    清除PV中的数据,效果相当于执行"rm -rf /data/kubernetes/*"。
                Delete(删除):
                    与PV相连的后端存储同时删除。

        STATUS(状态):      
            一个PV的生命周期中,可能会处于四种不同的阶段:
                Available(可用):
                    表示可用状态,还未被任何PVC绑定。
                Bound(已绑定):
                    表示PV已经被PVC绑定。
                Released(已释放):
                    PVC被删除,但是资源还未被集群重新声明。换句话说,这种状态下PV是不可重新分配的,需要手动清理保留的数据(因为默认的回收策略是"Retain")。
                Failed(失败):
                    表示该PV的自动回收失败。

        CLAIM:     
            被那个pvc声明使用。

        REASON:    
            PV故障原因。

        AGE:
            资源创建的时间。

参考链接:
    https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes

image-20210810220657392

3.创建PVC测试

(1)创建namespace,以便于将PVC放入该名称空间
[root@k8s101.oldboyedu.com ~]# cat ns-oldboyedu-tomcat.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: oldboyedu-tomcat
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# kubectl apply -f ns-oldboyedu-tomcat.yaml 
namespace "oldboyedu-tomcat" created
[root@k8s101.oldboyedu.com ~]# 

(2)创建PVC
[root@k8s101.oldboyedu.com ~]# cat 01-pvc.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: tomcat-mysql-pvc
  namespace: oldboyedu-tomcat
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 35Gi
[root@k8s101.oldboyedu.com ~]#

温馨提示:
    (1)当我们创建的PVC申请的存储大小要小于PV现有存储大小时,K8S会自动评估PVC和与哪一个后端PV进行绑定(Bound),如果pvc找不到合适的PV则pvc始终会停留在"Pending"状态哟~
    (2)如下图所示,本案例PVC仅申请了35G内存,但实际上PVC会自动去关联全局资源,匹配最合适的PV,这个过程是由K8s集群自动实现的,无需运维人员手动绑定,绑定成功请注意观察PV和PVC的状态均为"Bound"。
    (3)如下图所示,如果将PVC申请的数量过大(本案例是32GB),就会导致PVC无法自动关联全局PV资源,因此PVC的状态始终为"Pending",因为此时并没有一个PV的存储能力能够满足该pvc哟。

image-20210811112454102

image-20210811115503468

4.使用mysql案例来测试使用pvc

[root@k8s101.oldboyedu.com ~]# cat mysql-deploy.yml 
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: mysql
  namespace: oldboyedu-tomcat
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: oldboyedu-mysql
    spec:
      volumes:
        - name: datadir
          persistentVolumeClaim:
            claimName: tomcat-mysql-pvc  # 指定PVC的名称,注意要创建的Pod的名称空间中必须存在该PVC哟~
            readOnly: false  # PVC是否只读,默认值就为false,如果想要只读请设置为"true"
        - name: logbin  # 我们可以定义多个存储卷,但下面若不挂载,则始终不会应用它哟!
          emptyDir: {}

      containers:
        - name: mysql
          image: k8s101.oldboyedu.com:5000/mysql:5.7
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: '123456'
          volumeMounts:
            - name: datadir
              mountPath: /var/lib/mysql
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# kubectl apply -f mysql-deploy.yml 
deployment "mysql" created
[root@k8s101.oldboyedu.com ~]# 
[root@k8s101.oldboyedu.com ~]# kubectl get pods -n oldboyedu-tomcat 
NAME                    READY     STATUS    RESTARTS   AGE
mysql-967063035-s89z7   1/1       Running   0          2s
[root@k8s101.oldboyedu.com ~]# 

温馨提示:
    如下图所示,我们创建的MySQL容器,数据成功持久化到后端存储啦!

image-20210811183436387

    如下图所示,当持久化后,我们可以通过"kubectl describe pods mysql-967063035-s89z7 -n oldboyedu-tomcat"命令就能看到对应的Pod关于数据卷的挂载信息啦!   

image-20210811184240354

5.课堂练习(课后作业)

作业1: 请分别使用nginx1.16,nginx1.18,nginx1.19,实现多服务部署,要求如下:
    (1)使用alpine镜像;
    (2)对外只暴露一个80端口;
    (3)访问"/"的时候出现的服务器名称,服务器端的IP地址,客户端的user-agent(颜色为粉色);
    (4)访问"/kod"的时候出现可道云的页面;
    (5)访问"/brid"的时候出现"小鸟飞飞";
    (6)访问"/wp"的时候出现wordpress的页面;
    (7)要求镜像命名规则如下:
        k8s110.oldboyedu.com:5000/oldboyedu-alpine/nginx:1.16
        k8s110.oldboyedu.com:5000/oldboyedu-alpine/nginx:1.18
        k8s110.oldboyedu.com:5000/oldboyedu-alpine/nginx:1.20

作业2: 对镜像升级并持久化数据,要求如下:
    (1)要求使用NFS多路径暴露,实现对/kod",/brid","/wp"的数据实现挂载;
        /data/kubernetes/kod
        /data/kubernetes/brid
        /data/kubernetes/wp
    (2)请扩容wordpress数量从1到3,并确认访问的数据的一致;
    (3)请使用deployment资源滚动升级Nginx版本,要求在升级过程对用户透明(换句话说,就是不能影响用户访问);

七.PV资源的回收

1.删除资源

删除PVC
    kubectl delete pvc tomcat-mysql-pvc  -n oldboyedu-tomcat 

温馨提示:
    如下图所示,PVC资源删除完后,但PV并没有回收,而是变为"Released"状态。

image-20210811201200391

2.解决"ErrImagePull"问题

(1)在线编辑"pv-recycler"的POD资源
kubectl edit po recycler-for-pv03 

(2)修改镜像地址为国内的阿里云源
image: registry.aliyuncs.com/google_containers/busybox

image-20210811201943406

3.资源被回收成功

如下图所示,PV的资源被成功释放了,他的状态变为"Available"。

温馨提示:
    k8s1.5版本中可能出现按照上述方法无法实现pv的删除,此时可以使用"kubectl delete pv pv03"强制手动删除PV资源,此时该pv后台存储的数据貌似也会跟着被删除哟。

image-20210811202723612

八.PVC的标签选择器

1.创建测试PV

[root@k8s101.oldboyedu.com /oldboyedu/persistentVolume]# cat 04-pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv04
  labels:
    type: hdd
spec:
  capacity:  # 定义PV存储容量的大小
    storage: 50Gi 
  accessModes:  # 指定访问模式
    - ReadWriteMany 
  persistentVolumeReclaimPolicy: Recycle  # 指定PV的回收策略
  nfs:  # 指定PV后端的支持存储设备类型
    path: "/data/kubernetes/pv03"
    server: 172.200.1.101
    readOnly: false
[root@k8s101.oldboyedu.com /oldboyedu/persistentVolume]# 
[root@k8s101.oldboyedu.com /oldboyedu/persistentVolume]# 
[root@k8s101.oldboyedu.com /oldboyedu/persistentVolume]# kubectl apply -f 04-pv.yaml 
persistentvolume "pv04" created
[root@k8s101.oldboyedu.com /oldboyedu/persistentVolume]# 

温馨提示:
    如下图所示,我创建了一个测试的PV04,其存储能力和pv03相同,但标签名称并不相同哟,以便于我们后续的pvc测试。

image-20210811205536089

2.创建PVC

[root@k8s101.oldboyedu.com /oldboyedu/persistentVolumeClaim]# cat 02-pvc.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: tomcat-mysql-pvc2
  namespace: oldboyedu-tomcat
spec:
  selector:
    matchLabels:
      type: hdd
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 25Gi
[root@k8s101.oldboyedu.com /oldboyedu/persistentVolumeClaim]# 
[root@k8s101.oldboyedu.com /oldboyedu/persistentVolumeClaim]# kubectl apply -f 02-pvc.yaml 
persistentvolumeclaim "tomcat-mysql-pvc2" created
[root@k8s101.oldboyedu.com /oldboyedu/persistentVolumeClaim]# 

温馨提示:
    如下图所示,当我们成功创建了pvc资源时,其存储要求是25GB,尽管pv02和pv04均满足25GB的存储要求,但始终会被调度到pv04,这正是由于标签选择器的存在哟。

image-20210811212316867

九.PVC指定PV名称进行绑定结合标签选择器

[root@k8s101.oldboyedu.com /oldboyedu/pv+pvc]# cat 08-pvc.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: tomcat-mysql-pvc-oldboyedu-linux
  namespace: oldboyedu-tomcat
spec:
  selector:
    matchLabels:
      type: hdd
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 25Gi

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: tomcat-mysql-pvc-oldboyedu-python
  namespace: oldboyedu-tomcat
spec:
  volumeName: pv02
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 25Gi
[root@k8s101.oldboyedu.com /oldboyedu/pv+pvc]# 

十.持久卷的动态供给(StorageClass,了解即可,建议使用高于k8s1.11版本进行讲解。)

1.PV静态供给概述

如下图所示,现在PV使用方式称为静态供给,需要K8s运维工程师提前创建一堆PV,供开发者使用。

因此我们在生产环境中可以考虑优化这个环节,让PV动态创建就好了。

image-20210812165435581

2.PV动态概述

PV静态供给明显的缺点就是维护成本太高了,需要K8S运维人员手动创建一堆PV。

因此K8S开始支持PV动态供给,使用StorageClass对象实现。

如下图所示,当你创建的Pod内的PVC需要应用对应的PV时,可以借助StorageClass提供的接口来自动创建PV。

image-20210812170803957

3.K8S内置的动态供给方案

    如下图所示,K8S支持的数据卷中,并不是所有的组件都支持动态供给,其中NFS就不支持动态供给功能,但我们可以在GitHub社区可以找打有人给出了动态供给的可行性方案。

    关于支持动态供给PV的插件,可参考官方链接:
        https://kubernetes.io/docs/concepts/storage/storage-classes/

    推荐阅读:
        https://github.com/kubernetes-retired/external-storage

image-20210812172733956

4.基于NFS实现PV动态供给实战案例-手动创建Pod效果并不明显(只能动态创建,但却无法动态归档!),生产环境不推荐使用,但必须演示!为学员留下印象

一.下载nfs-client插件
    (1)安装git工具便于下载
        yum -y install git
    (2)建议从官方下载,如果GitHub不方便下载,我在码云上找了一个相同的副本,也能凑合着用。
        git clone https://github.com/kubernetes-retired/external-storage.git
        git clone https://gitee.com/yangfanimb/external-storage.git

二.修改nfs-client插件相关的配置文件
    略,推荐使用K8s1.11+版本测试。

image-20210812174653171

image-20210812181759824

5.基于NFS实现PV动态供给实战案例-基于控制器创建Pod,生产环境推荐使用

略,较高版本再来讨论这个话题。推荐使用K8s1.11+版本测试。

6.关于PVC和PV你可能存在的疑问

Q1: 为什么数据没有被真正的删除呢?
    答: 这是因为我们将上面的"class.yaml"文件中的archiveOnDelete值设置为"true",表示删除的时候回进行归档,但如果想要删除时直接删除PV上的数据,则可以将archiveOnDelete的值改为"false",注意,其默认值就是"false"哟~

Q2: PV和PVC什么关系?
    答: PV和PVC是一对一的关系,PVC主要是告诉Pod申请资源的声明,PV是对数据卷的一个更高级的抽象。PVC将Pod所需的资源和对应的PV进行一对一的绑定。

Q3: PVC和PV是怎么匹配的?
    答: 访问模式(PersistentVolumeClaim.spec.accessModes),存储容量(PersistentVolumeClaim.spec.resources.requests.storage),标签选择器(PersistentVolumeClaim.spec.selector.matchLabels)以及PV的名称(PersistentVolumeClaim.spec.volumeName)是PVC主要匹配的依据。

Q4: PVC和PV的容量匹配策略是什么呢?
    答: 匹配就近符合的容量,我们习惯上称之为向上匹配。比如PVC需要的PersistentVolumeClaim.spec.resources.requests.storage大小为"20Gi",但所有的PV中并没有对应的20G,而只有15G,30G,50G空闲的资源时,PVC会优先匹配30Gi的PV哟~

Q5: PVC存储容量是真的用于限制吗?
    答: 经过实验证明,存储容量的确是有限制的功能,但容量字段另一个作用就是用于匹配与之匹配的PV容量。

Q6:有关dd参数说明如下:(可以用于测试Pod占用PVC的实验,但是在K8S 1.5版本中实测,发现PV无法限制容量的大小)
        dd if=/dev/zero of=big_file01.log bs=128M count=8
        dd if=/dev/urandom of=big_file02 bs=1G count=5 iflag=fullblock

        dd命令常用的子选项说明:
            if:
                指定输入文件
            of:
                指定输出文件
            bs:
                指定每次读写的字节数,单位为K、M、G等
            count:
                指定读写的block数

温馨提示:
    如果我们有意将容量字段设置的值非常大,甚至超过了实际数据卷的存储空间,但实际上存储容量取决于后端存储。因此我们上面的案例也看到了容量字段的主要作用还是用于PVC匹配与之符合的PV哟~
谨此笔记,记录过往。凭君阅览,如能收益,莫大奢望。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇