kubernetes持久化存储

k8s存储介绍

容器内部的的存储在生命周期是短暂的,会随着容器环境的销毁而销毁,具有不稳定性。在k8s里将对容器应用所需的存储资源抽象为存储卷(Volume)概念来解决这些问题。

k8s中持久化存储类型

k8s目前支持的Volume类型包括k8s的内部资源对象类型,开源共享存储类型和公有云存储等。分类如下:

## k8s特定的资源对象:
1. ConfigMap             应用配置
2. Secret                加密数据
3. ServiceAccountToken   token令牌相关数据

## k8s本地存储类型:
1. EmptDir      临时存储
2. HostPath     宿主机目录

## 持久化存储(PV)和网络共享存储:
1. CephFS : 开源共享存储系统   OpenStack,大数据 都在使用CephFS
2. GlusterFS(GFS): 开源共享存储系统(简单理解就是NFS集群 也是挂载的形式)
3. FastDFS :开源共享存储系统
4. NFS : 开源共享存储(单机)
5. PersistentVolumeClaim : 简称PVC,持久化存储的申请空间

持久化存储方式

  • 1,EmptyDir
  • 2,HostPath
  • 3,NFS
  • 4,PV 和 PVC

emptyDir

## 用emptyDir让同一个pod里的两个容器做共享存储 会在宿主机上起一个随机目录放置挂载的内容 但不做持久化 pod结束就会清除目录
[root@master-1 wp]# vim nginx-dp.yml
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
  name: nginx-dp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      volumes:
      - name: empty-path
        emptyDir: {}

      containers:
      - name: nginx
        image: nginx:alpine
        imagePullPolicy: IfNotPresent
        volumeMounts:
        - name: empty-path
          mountPath: /usr/share/nginx/html

[root@master-1 wp]# kubectl apply -f nginx-dp.yml 

## 查看pod IP
[root@master-1 wp]# kubectl get pods -o wide 
NAME                      READY   STATUS    RESTARTS   AGE    IP           NODE     NOMINATED NODE   READINESS GATES
nginx-dp-774d477f-gnqtb   1/1     Running   0          20s    10.2.1.156   node-1   <none>           <none>         <none>
## curl 网站
[root@master-1 wp]# curl 10.2.1.156
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.21.5</center>
</body>
</html>

## 查看随机目录位置的方法:到对应的node节点上 看这个容器的配置
# 找到对应的容器
docker ps
# 查看该容器的配置
docker inspect 容器ID
# 找到
"Mounts": [
    {
        "type": "bind"
        "Source": "随机目录"
        ......
    }
]

## 复制文件中的随机目录 写一个index.html
[root@node-1 ~]# vim /var/lib/kubelet/pods/4bb410e7-2188-49e7-a8b1-88180f9362d2/volumes/kubernetes.io~empty-dir/empty-path/index.html
111

[root@master-1 wp]# curl 10.2.1.156
111

hostPath

## 需要定义一个Volume 然后两个容器里的目录分别挂载到Volume里定义的宿主机目录上(目录自动创建)
[root@elkstack01 volume]# vim nginx-dp.yaml 
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
  name: nginx-dp-empty
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      volumes:
      - name: local-path
        hostPath:
          path: /data/nginx/data
      containers:
      - name: nginx
        image: nginx:alpine
        imagePullPolicy: IfNotPresent
        volumeMounts:
        - name: local-path
          mountPath: /usr/share/nginx/html

[root@master-1 wp]# kubectl apply -f nginx-dp.yml 

## 查看pod IP
[root@master-1 wp]# kubectl get pods -o wide
NAME                            READY   STATUS    RESTARTS   AGE    IP           NODE     NOMINATED NODE   READINESS GATES
nginx-dp-empty-bdcb5cc4-mkm74   1/1     Running   0          16s    10.2.1.158   node-1   <none>           <none>

## curl 网站
[root@master-1 wp]# curl 10.2.1.158
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.21.5</center>
</body>
</html>

## 到对应的node节点上 修改指定的目录里的index.html
[root@node-1 ~]# vim /data/nginx/data/index.html
666

[root@master-1 wp]# curl 10.2.1.158
666

注意:不管是emptyDir还是hostPath都只是,单机POD持久化

wordpress需求

## wordpress 要运行在k8s中
## MySQL
1.名称空间: blog
2.镜像mysql:5.7
3.环境变量
- root密码:123
- 数据库:wordpress
- 用户:wordpress
- 密码:123
- 参数:字符集
5.数据持久化:在宿主机的/data/mysql/data
## wordpress需求
副本数为2
镜像:自己打(有坑),或者官方
数据库地址:cluster ip
数据库名称:wordpress
用户:wordpress
******** 使用NFS持久化数据,宿主机:/data/wordpress/data

资源清单

[root@master-1 wordpress]# cat kube-wordpress.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: blog

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-dp
  namespace: blog
spec:
  replicas: 1
  selector:
    matchLabels:
      mysql: pod
  template:
    metadata:
      name: mysql-wusuoweila
      labels:
        mysql: pod
    spec:
      volumes:
      - name: mysql-data
        hostPath:
          path: /data/mysql/data
      containers:
      - name: mysql-container
        image: mysql:5.7
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: '123'
        - name: MYSQL_DATABASE
          value: 'wordpress'
        - name: MYSQL_USER
          value: 'wordpress'
        - name: MYSQL_PASSWORD
          value: '123'
        args:
        - --character-set-server=utf8
        - --collation-server=utf8_general_ci
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql

---

apiVersion: v1
kind: Service
metadata:
  name: mysql-svc
  namespace: blog
spec:
  selector:
    mysql: pod
  ports:
  - name: mysql-port
    port: 3306
    protocol: TCP
    targetPort: 3306
  type: ClusterIP

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wp-dp
  namespace: blog
spec:
  replicas: 2
  selector:
    matchLabels:
      wp: pod
  template:
    metadata:
      name: wp-wusuoweila
      labels:
        wp: pod
    spec:
      containers:
      - name: wp-container
        image: wordpress:latest
        imagePullPolicy: IfNotPresent
        env:
        - name: WORDPRESS_DB_HOST
          value: mysql-svc
        - name: WORDPRESS_DB_NAME
          value: 'wordpress'
        - name: WORDPRESS_DB_USER
          value: 'wordpress'
        - name: WORDPRESS_DB_PASSWORD
          value: '123'

---

apiVersion: v1
kind: Service
metadata:
  name: wp-svc
  namespace: blog
spec:
  selector:
    wp: pod
  ports:
  - name: wp-port
    port: 80
    protocol: TCP
    targetPort: 80
  type: ClusterIP

---

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wp-ingress
  namespace: blog
spec:
  rules:
  - host: blog.hg.com
    http:
      paths:
      - path: /
        pathType: ImplementationSpecific
        backend:
          service:
            name: wp-svc
            port:
              number: 80

存在问题

1)没有共享存储

– 访问网站,图片破图

2)没有回话保持

– 访问网站,一边登录 一边没登录

共享存储持久化

## 持久化存储(PV)和网络共享存储:
1. CephFS : 开源共享存储系统   OpenStack,大数据 都在使用CephFS
2. GlusterFS(GFS): 开源共享存储系统(简单理解就是NFS集群 也是挂载的形式)
3. FastDFS :开源共享存储系统
4. NFS : 开源共享存储(单机)
5. PersistentVolumeClaim : 简称PVC,持久化存储的申请空间

NFS方案

1.NFS是物理机

1)node-1 和 node-2挂载宿主机hostPath,将宿主机的hostPath远程挂载到NFS

2)node-1 和 node-2直接挂载NFS

2.NFS是POD

1)node-1 和 node-2挂载宿主机hostPath,将宿主机的hostPath远程挂载到NFS

2)node-1 和 node-2直接挂载NFS

部署NFS

# 1.安装nfs
[root@nfs ~]# yum install -y nfs-utils
[root@node-1 ~]# yum install -y nfs-utils
[root@node-2 ~]# yum install -y nfs-utils
# 2.配置nfs
[root@nfs ~]# vim /etc/exports
/data/wordpress 172.16.1.0/24(rw,sync,no_root_squash)
# 3.创建共享存储目录
[root@nfs ~]# mkdir /data/wordpress -p
# 4.创建用户
[root@nfs ~]# groupadd www -g 666
[root@nfs ~]# useradd www -u 666 -g 666 -s /sbin/nologin -M/sbin/nologin -M
# 5.授权
[root@nfs ~]# chown -R www.www /data/wordpress/
# 6.启动nfs
[root@nfs ~]# systemctl start nfs-server

挂载NFS

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wp-dp
  namespace: blog
spec:
  replicas: 2
  selector:
    matchLabels:
      wp: pod
  template:
    metadata:
      name: wp-wusuoweila
      labels:
        wp: pod
    spec:
      volumes:
      - name: wp-data
        nfs:
          path: /data/wordpress
          server: 172.16.1.31
      containers:
      - name: wp-container
        image: wordpress:latest
        imagePullPolicy: IfNotPresent
        env:
        - name: WORDPRESS_DB_HOST
          value: mysql-svc
        - name: WORDPRESS_DB_NAME
          value: 'wordpress'
        - name: WORDPRESS_DB_USER
          value: 'wordpress'
        - name: WORDPRESS_DB_PASSWORD
          value: '123'
        volumeMounts:
        - name: wp-data
          mountPath: /var/www/html/

## 完整清单
[root@master-1 wordpress]# cat kube-wordpress.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: blog

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-dp
  namespace: blog
spec:
  replicas: 1
  selector:
    matchLabels:
      mysql: pod
  template:
    metadata:
      name: mysql-wusuoweila
      labels:
        mysql: pod
    spec:
      volumes:
      - name: mysql-data
        hostPath:
          path: /data/mysql/data
      containers:
      - name: mysql-container
        image: mysql:5.7
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: '123'
        - name: MYSQL_DATABASE
          value: 'wordpress'
        - name: MYSQL_USER
          value: 'wordpress'
        - name: MYSQL_PASSWORD
          value: '123'
        args:
        - --character-set-server=utf8
        - --collation-server=utf8_general_ci
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql

---

apiVersion: v1
kind: Service
metadata:
  name: mysql-svc
  namespace: blog
spec:
  selector:
    mysql: pod
  ports:
  - name: mysql-port
    port: 3306
    protocol: TCP
    targetPort: 3306
  type: ClusterIP

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wp-dp
  namespace: blog
spec:
  replicas: 2
  selector:
    matchLabels:
      wp: pod
  template:
    metadata:
      name: wp-wusuoweila
      labels:
        wp: pod
    spec:
      volumes:
      - name: wp-data
        nfs:
          path: /data/wordpress
          server: 172.16.1.31
      containers:
      - name: wp-container
        image: wordpress:latest
        imagePullPolicy: IfNotPresent
        env:
        - name: WORDPRESS_DB_HOST
          value: mysql-svc
        - name: WORDPRESS_DB_NAME
          value: 'wordpress'
        - name: WORDPRESS_DB_USER
          value: 'wordpress'
        - name: WORDPRESS_DB_PASSWORD
          value: '123'
        volumeMounts:
        - name: wp-data
          mountPath: /var/www/html/

---

apiVersion: v1
kind: Service
metadata:
  name: wp-svc
  namespace: blog
spec:
  selector:
    wp: pod
  ports:
  - name: wp-port
    port: 80
    protocol: TCP
    targetPort: 80
  type: ClusterIP

---

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wp-ingress
  namespace: blog
spec:
  rules:
  - host: blog.hg.com
    http:
      paths:
      - path: /
        pathType: ImplementationSpecific
        backend:
          service:
            name: wp-svc
            port:
              number: 80

PV和PVC介绍

PV :资源划分(相当于将电脑中一块磁盘分成多个盘符)
PVC :从PV中申请使用资源,POD要跟PVC关联,才可以直接使用

PV是对底层网络共享存储的抽象,将共享存储定义为一种“资源”。
PV由管理员创建和配置
PVC则是用户对存储资源的一个“申请”。
就像Pod消费Node的资源一样,PVC能够“消费”PV资源
PVC可以申请特定的存储空间和访问模式

PV和PVC生命周期

image-20230926193432084

PV和PVC需要注意的地方

### 创建PVC之后,k8s就会自动去查找满足我们声明要求的PV,比如storageClassName(存储类别),accessModes(访问规则)以及容量这些是否满足要求,如果满足要求就将PV和PVC绑定在一起。

### 需要注意的是目前PV和PVC之间是一对一绑定的关系,也就是说一个PV只能被一个PVC绑定。

## 在PV的整个生命周期中,可能会处于4种不同的阶段:
1. Avaliable(可用)   : 表示可用状态,还未被任何PVC绑定
2. Bound    (已绑定) : 表示PV已经被PVC绑定
3. Released (已释放) : PVC被删除,但是资源还未被集群重新声明
4. Failed   (失败)   : 表示该PV的自动回收失败

image-20230926215900421

PV和PVC实践

准备NFS服务器

## 部署nfs
[root@nfs ~]# yum install -y nfs-utils

## 修改nfs配置文件
[root@nfs ~]# vim /etc/exports
/data/nginx/data *(rw,sync,no_root_squash)

## 启动nfs
[root@nfs ~]# systemctl restart nfs

## 创建目录
[root@nfs ~]# mkdir /data/nginx/data -p

所有node节点安装nfs

[root@node-1 ~]# yum install -y nfs-utils
[root@node-2 ~]# yum install -y nfs-utils

[root@node-1 ~]# showmount -e 10.0.0.31
Export list for 10.0.0.31:
/data/wordpress 172.16.1.0/24
[root@node-2 ~]#  showmount -e 10.0.0.31
Export list for 10.0.0.31:
/data/wordpress 172.16.1.0/24
[root@node-2 ~]# 

创建PV资源

## 编写PV资源清单(划分共享存储的磁盘空间)
[root@master-1 pv]# vim wp-pv.yml
apiVersion: v1
kind: PersistentVolume      ## kind 指定为 PersistentVolume(持久化卷)
metadata:
  name: pv01                ## 定义pv的名字
spec:
  capacity:
    storage: 5Gi            ## 定义这个pv的大小
  accessModes:              ## 定义访问规则
    - ReadWriteOnce         ## ReadWriteOnce:只有一个node节点可以挂载
  persistentVolumeReclaimPolicy: Recycle  ## 回收资源的规则
  storageClassName: nfs01   ## 存储类别 后端的存储方式是啥 就写啥
  nfs:                      ## 具有特定类别的PV只能与请求了该类别的PVC绑定
    path: /data/wordpress   ## 未指定类型的PV则只能对与不请求任何类别的PVC绑定
    server: 172.16.1.31

## 启动
[root@master-1 pv]# kubectl apply -f wp-pv.yml 

## PV资源关键参数:
capacity:  PV存储的容量

# accessModes: 访问模式,k8s支持的访问模式如下
------------------------------------------------------
ReadWriteOnce(RWO) : 读写权限,并且只能被单个Node挂载
ReadOnlyMany(ROX) :  只读权限,允许被多个Node挂载
ReadWriteMany(RWX) : 读写权限,允许被多个Node挂载
------------------------------------------------------

# persistentVolumeReclaimPolicy: 回收策略
------------------------------------------------------
Retain : 保留数据,需要手工处理完数据,还能让新的PVC申请
Recycle : 简单清除文件的操作(例如运行rm -rf /dada/* 命令),新的PVC可以直接申请
Delete : 与PV相连的后端存储完成Volume的删除操作(连PV都直接删除)

目前只有NFS和HostPath两种类型的PV支持Recycle策略。
------------------------------------------------------

## 查看PV
[root@master-1 pv]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv01   5Gi        RWO            Recycle          Available           nfs01                   47s

## 查看详细信息
[root@master-1 pv]# kubectl describe pv
Name:            pv01
Labels:          <none>
Annotations:     <none>
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    nfs01
Status:          Available
Claim:           
Reclaim Policy:  Recycle
Access Modes:    RWO
VolumeMode:      Filesystem
Capacity:        5Gi
Node Affinity:   <none>
Message:         
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    172.16.1.31
    Path:      /data/nginx/data
    ReadOnly:  false
Events:        <none>

创建PVC资源

## 编写PVC资源清单
[root@master-1 pv]# vim wp-pvc.yml 
apiVersion: v1
kind: PersistentVolumeClaim   ## kind 指定为 PersistentVolumeClaim(持久化要求)
metadata:
  name: wp-pvc                ## 定义pvc名字
spec:
  accessModes:                ## 定义访问规则
    - ReadWriteOnce           ## ReadWriteOnce:只有一个node节点可以挂载 
  resources:
    requests:
      storage: 4Gi            ## 需要的容量
  storageClassName: nfs01     ## 存储类别

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi
  storageClassName: nfs01

## 启动PVC k8s就会自动去查找满足要求的PV,比如storageClassName(存储类别),accessModes(访问规则)以及容量这些是否满足要求,如果满足要求就将PV和PVC绑定在一起。
[root@master-1 pv]# kubectl apply -f wp-pvc.yml 
persistentvolumeclaim/wp-pvc created

## 查看PV 看看是否自动绑定(查看状态是不是Bound)
[root@master-1 pv]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
pv01   5Gi        RWO            Recycle          Bound    default/wp-pvc   nfs01                   11m

## 查看PVC
[root@master-1 pv]# kubectl get pvc
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
wp-pvc   Bound    pv01     5Gi        RWO            nfs01          45s

deploment

## pod管理PVC方式:是通过PVC的名字
## PVC关联PV的方式(匹配规则):PVC根据以下内容去自动发现绑定PV
  - accessModes:访问规则
  - storageClassName:存储类别
  - storage:容量是否足够

## 还是以volumes(挂载)的形式
[root@elkstack01 volume]# cat kube-wordpress.yaml
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
  name: wp-dp
  namespace: blog
spec:
  replicas: 2
  selector:
    matchLabels:
      wp: pod
  template:
    metadata:
      name: wp-wusuoweila
      labels:
        wp: pod
    spec:
      volumes:
      - name: wp-data                   ## 定义这个挂载的名字
        persistentVolumeClaim:          ## 挂载方式为PersistentVolumeClaim(PVC)
          claimName: wp-pvc             ## 指定要挂载的PVC的名字
      containers:
      - name: wp-container
        image: wordpress:latest
        imagePullPolicy: IfNotPresent
        env:
        - name: WORDPRESS_DB_HOST
          value: mysql-svc
        - name: WORDPRESS_DB_NAME
          value: 'wordpress'
        - name: WORDPRESS_DB_USER
          value: 'wordpress'
        - name: WORDPRESS_DB_PASSWORD
          value: '123'
        volumeMounts:
        - name: wp-data                    ## 指定上面的PVC挂载的名字
          mountPath: /var/www/html         ## pod里的挂载路径

### pod 关联PVC主要靠  claimName: wp-pvc

[root@master-1 wp]# cat wp.yml 
apiVersion: v1
kind: Namespace
metadata:
  name: blog
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv01
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs01
  nfs:
    path: /data/wordpress
    server: 172.16.1.31

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-pvc
  namespace: blog
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 4Gi
  storageClassName: nfs01

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-dp
  namespace: blog
spec:
  replicas: 1
  selector:
    matchLabels:
      mysql: pod
  template:
    metadata:
      name: mysql-wusuoweila
      labels:
        mysql: pod
    spec:
      volumes:
      - name: mysql-data
        hostPath:
          path: /data/mysql/data
      containers:
      - name: mysql-container
        image: mysql:5.7
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: '123'
        - name: MYSQL_DATABASE
          value: 'wordpress'
        - name: MYSQL_USER
          value: 'wordpress'
        - name: MYSQL_PASSWORD
          value: '123'
        args:
        - --character-set-server=utf8
        - --collation-server=utf8_general_ci
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql

---

apiVersion: v1
kind: Service
metadata:
  name: mysql-svc
  namespace: blog
spec:
  selector:
    mysql: pod
  ports:
  - name: mysql-port
    port: 3306
    protocol: TCP
    targetPort: 3306
  type: ClusterIP

---

apiVersion: "apps/v1"
kind: "Deployment"
metadata:
  name: wp-dp
  namespace: blog
spec:
  replicas: 2
  selector:
    matchLabels:
      wp: pod
  template:
    metadata:
      name: wp-wusuoweila
      labels:
        wp: pod
    spec:
      volumes:
      - name: wp-data
        persistentVolumeClaim:
          claimName: wp-pvc
      containers:
      - name: wp-container
        image: wordpress:latest
        imagePullPolicy: IfNotPresent
        env:
        - name: WORDPRESS_DB_HOST
          value: mysql-svc
        - name: WORDPRESS_DB_NAME
          value: 'wordpress'
        - name: WORDPRESS_DB_USER
          value: 'wordpress'
        - name: WORDPRESS_DB_PASSWORD
          value: '123'
        volumeMounts:
        - name: wp-data
          mountPath: /var/www/html

---

apiVersion: v1
kind: Service
metadata:
  name: wp-svc
  namespace: blog
spec:
  selector:
    wp: pod
  ports:
  - name: wp-port
    port: 80
    protocol: TCP
    targetPort: 80
  type: ClusterIP

---

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wp-ingress
  namespace: blog
spec:
  rules:
  - host: blog.hg.com
    http:
      paths:
      - path: /
        pathType: ImplementationSpecific
        backend:
          service:
            name: wp-svc
            port:
              number: 80

## 启动
[root@master-1 wp]# kubectl apply -f wp.yml

image-20230926212829091