kubernetes volume 数据存储详解

news2024/11/24 17:29:47

 写在前面:如有问题,以你为准,

目前24年应届生,各位大佬轻喷,部分资料与图片来自网络

内容较长,页面右上角目录方便跳转

概述

容器的生命周期可能很短,会被频繁的创建和销毁

保存在容器中的数据也会被清除,所以要实现持久化存储(Volume)

通过将本地目录挂载到pod,一个目录可以挂载到多个pod中的容器

Volume是Pod中能够被多个容器访问的共享目录

通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储

Volume的生命周期不和Pod中的单个容器的生命周期有关,当容器终止或者重启的时候,Volume中的数据也不会丢失

  1. kubernetes的Volume支持多种类型,比较常见的有下面的几个:
    1. 简单存储:EmptyDir、HostPath、NFS。
    2. 高级存储:PV、PVC。
    3. 配置存储:ConfigMap、Secret。

EmptyDir:pod中临时存储空间可以被多个容器挂载,实现共享目录,pod删除其也删除

HostPath:将node上的目录挂载到pod里面的容器里面,实现了持久化存储,但是没有实现存储高可用

Kubernetes 目前支持多达 28 种数据卷类型(其中大部分特定于具体的云环境如 GCE/AWS/Azure 等)

非持久性存储:

emptyDir

HostPath

网络连接性存储:

SAN:iSCSI、ScaleIO Volumes、FC (Fibre Channel)

NFS:nfs,cfs

分布式存储

Glusterfs

RBD (Ceph Block Device)

CephFS

Portworx Volumes

Quobyte Volumes

云端存储

GCEPersistentDisk

AWSElasticBlockStore

AzureFile

AzureDisk

Cinder (OpenStack block storage)

VsphereVolume

StorageOS

自定义存储

FlexVolume

基本存储

EmptyDir

EmptyDir是最基础的Volume类型,一个EmptyDir就是Host上的一个空目录。

不是持久化数据存储,生命周期跟pod一样,一般是用于多容器共享目录

  1. EmptyDir是在Pod被分配到Node时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为kubernetes会自动分配一个目录,当Pod销毁时,EmptyDir中的数据也会被永久删除
  2. EmptyDir的用途如下:
    1. 临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留。
    2. 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)。

示例

接下来,通过一个容器之间的共享案例来使用描述一个EmptyDir。

在一个Pod中准备两个容器nginx和busybox,然后声明一个volume分别挂载到两个容器的目录中,然后nginx容器负责向volume中写日志,busybox中通过命令将日志内容读到控制台。

apiVersion: v1

kind: Pod

metadata:

  name: volume-emptydir

  namespace: study

spec:

  containers:

    - name: nginx

      image: nginx:1.17.1

      imagePullPolicy: IfNotPresent

      command: ["/bin/sh","-c","tail -f /var/log/nginx/access.log"]

      ports:

        - containerPort: 80

      volumeMounts: # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx

        - name: logs-volume # 卷名

          mountPath: /var/log/nginx # 要挂载到容器的目录,该目录且要容器必须存在

    - name: busybox

      image: busybox:1.30

      imagePullPolicy: IfNotPresent

      command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,持续读取指定文件

      # 读取的时候是前台,而kubectl logs命令就是读取前台日志/dev/stdout和/dev/stderr

      volumeMounts: # 将logs-volume挂载到busybox容器中的对应目录,该目录为/logs

        - name: logs-volume

          mountPath: /logs

  volumes: # 声明volume,name为logs-volume,类型为emptyDir

    - name: logs-volume # 用于挂载时指定的volume名字

      emptyDir: {} # 指定volume类型
[root@master k8s]# kubectl apply -f emptydir.yaml

pod/volume-emptydir created

[root@master k8s]# kubectl get pod volume-emptydir -n study -o wide

NAME              READY   STATUS    RESTARTS   AGE    IP              NODE    NOMINATED NODE   READINESS GATES

volume-emptydir   2/2     Running   0          142m   10.244.104.44   node2   <none>           <none>
[root@master k8s]# kubectl describe pod -n study

# 访问nginx,查看busybox挂载的目录是否有信息,有则说明nginx的日志在logs-volume,且busybox也挂载了目录

[root@master k8s]# curl 10.244.104.44

<!DOCTYPE html>

<html>

<head>

<title>Welcome to nginx!</title>

[root@master k8s]# kubectl logs -f volume-emptydir -n study -c busybox

192.168.100.53 - - [19/Feb/2023:08:15:33 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.61.1" "-"

HostPath

HostPath就是将Node主机中的一个实际目录挂载到Pod中,以供容器使用,这样的设计就可以保证Pod销毁了,但是数据依旧可以保存在Node节点主机上

但是一旦Node节点故障了,Pod如果转移到别的Node节点上,又会出现问题

还有就是将容器的日志导出等场景

  volumes:

  - name: test-volume

    hostPath:

      # directory location on host

      path: /data

      # this field is optional

      type: Directory # 目录必须存在指定路径

      # FileOrCreate 不会创建文件的父目录,如果挂载文件的父目录不存在,则pod启动失败

      # DirectoryOrCreate 如果不存在就创建,且权限为0755,与 Kubelet 具有相同的组和所有权

      # File 文件必须存在于给定路径

      # 如果不写type就跳过检测,则如果目录不存在,yaml创建时候也不会报错,类似于可选项

示例

apiVersion: v1

kind: Pod

metadata:

  name: volume-hostpath

  namespace: study

spec:

  containers:

    - name: nginx

      image: nginx:1.17.1

      imagePullPolicy: IfNotPresent

      ports:

        - containerPort: 80

      volumeMounts: # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx

        - name: logs-volume

          mountPath: /var/log/nginx

    - name: busybox

      image: busybox:1.30

      imagePullPolicy: IfNotPresent

      command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件

      volumeMounts: # 将logs-volume挂载到busybox容器中的对应目录,该目录为/logs

        - name: logs-volume

          mountPath: /logs

  volumes: # 声明volume,name为logs-volume,类型为hostPath

    - name: logs-volume

      hostPath:

        path: /root/logs

        type: DirectoryOrCreate # 目录存在就使用,不存在就先创建再使用

  nodeName: master # 测试方便查看hostPath被挂载的目录
[root@master k8s]# kubectl apply -f emptydir.yaml

pod/volume-hostpath created

[root@master k8s]# kubectl get pod -n study -o wide

NAME              READY   STATUS    RESTARTS   AGE   IP              NODE     NOMINATED NODE   READINESS GATES

volume-hostpath   2/2     Running   0          58s   10.244.219.71   master   <none>           <none>

[root@master k8s]# curl 10.244.219.71

<!DOCTYPE html>

<html>

<head>

<title>Welcome to nginx!</title>

[root@master k8s]# cat /root/logs/access.log

192.168.100.53 - - [20/Feb/2023:06:23:56 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.61.1" "-"

subPath

subpath 主要是为了防止覆盖

使用 mountPath 挂载 /etc/nginx ,但是我告诉 kubernetes 我挂载那个文件,此时就可以使用 subPath 了,此时 Kubernetes 就只会覆盖 /etc/nginx/nginx.conf 文件

换言之,mountPath 告诉 Kubernetes 我需要覆盖容器中的那个目录,但是有了 subPath ,Kubernetes 就知道了,哦,原来你只需要覆盖 mountPath 下边的子文件啊(subPath )

apiVersion: v1

kind: Pod

metadata:

  name: my-lamp-site

spec:

    containers:

    - name: mysql

      image: mysql

      env:

      - name: MYSQL_ROOT_PASSWORD

        value: "rootpasswd"

      volumeMounts:

      - mountPath: /var/lib/mysql

        name: site-data

        subPath: m

    - name: php

      image: php:7.0-apache

      volumeMounts:

      - mountPath: /var/www/html

        name: site-data

        subPath: m

    volumes:

    - name: site-data

      emptyDir: {}

NFS

hostpath可以实现持久化,但是没有实现高可用,但是一旦Node节点故障了,Pod如果转移到别的Node节点上,又会出现问题,而NFS就是为解决持久化存储高可用

常用的有:NFS CIFS 等

  1. NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统上,这样,无论Pod在节点上怎么转移,只要Node和NFS的对接没有问题,数据就可以成功访问。

Master 搭建 NFS

首先需要准备NFS服务器,这里为了简单,直接在Master节点做NFS服务器。

在Master节点上安装NFS服务器:

yum install -y nfs-utils rpcbind

mkdir -pv /root/data/k8s-nfs

vim /etc/exports

# 将共享目录以读写权限暴露给192.168.100.0/24网段中的所有主机

/root/data/k8s-nfs 192.168.100.0/24(rw,no_root_squash)

# 读写权限,不使用root权限

systemctl start rpcbind

systemctl enable rpcbind

systemctl start nfs-server

systemctl enable nfs-server

在Node节点上都安装NFS服务器,目的是为了Node节点可以驱动NFS设备

# 在Node节点上安装NFS服务,不需要启动

yum -y install nfs-utils

showmount -e 192.168.100.53

mount -t  nfs 192.168.100.53:/root/data/k8s-nfs /mnt #关机后

mount -t  nfs 192.168.100.53:/root/data/k8s-nfs/mnt

示例

apiVersion: v1

kind: Pod

metadata:

  name: volume-nfs

  namespace: dev

spec:

  containers:

    - name: nginx

      image: nginx:1.17.1

      imagePullPolicy: IfNotPresent

      ports:

        - containerPort: 80

      volumeMounts: # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx

        - name: logs-volume

          mountPath: /var/log/nginx

    - name: busybox

      image: busybox:1.30

      imagePullPolicy: IfNotPresent

      command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件

      volumeMounts: # 将logs-volume挂载到busybox容器中的对应目录,该目录为/logs

        - name: logs-volume

          mountPath: /logs

  volumes: # 声明volume

    - name: logs-volume

      nfs:

        server: 192.168.100.53 # NFS服务器地址

        path: /root/data/k8s-nfs # 共享文件路径
[root@master k8s]# kubectl apply -f  emptydir.yaml

pod/volume-hostpath created

[root@master k8s]# kubectl get pod -n study -o wide

NAME              READY   STATUS    RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES

volume-hostpath   2/2     Running   0          64s   10.244.104.53   node2   <none>           <none>

[root@master k8s]# curl 10.244.104.53

[root@master k8s]# cat /root/data/k8s-nfs/access.log

192.168.100.53 - - [20/Feb/2023:07:15:55 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.61.1" "-"

192.168.100.53 - - [20/Feb/2023:07:49:20 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.61.1" "-"

高级存储(pv pvc)

概述架构

此时就要求用户会搭建NFS系统等,并且会在yaml配置nfs

由于kubernetes支持的存储系统有很多,要求客户全部掌握,显然不现实。为了能够屏蔽底层存储实现的细节,方便用户使用,kubernetes引入了PV和PVC两种资源对(类似于资源池化,pv和pvc是抽象的存储资源)

  1. 使用了PV和PVC之后,工作可以得到进一步的提升:
    1. 存储:存储工程师维护。
    2. PV:kubernetes管理员维护。
    3. PVC:kubernetes用户维护。

下面实操的架构:pod-pvc-pv-NFS

生命周期

  1. PVC和PV是一一对应的,PV和PVC之间的相互作用遵循如下的生命周期。
  2. 资源供应:管理员手动创建底层存储和PV。
  3. 资源绑定
    1. 用户创建PVC,kubernetes负责根据PVC声明去寻找PV,并绑定在用户定义好PVC之后,系统将根据PVC对存储资源的请求在以存在的PV中选择一个满足条件的。
      1. 一旦找到,就将该PV和用户定义的PVC进行绑定,用户的应用就可以使用这个PVC了。
      2. 如果找不到,PVC就会无限期的处于Pending状态,直到系统管理员创建一个符合其要求的PV。
    2. PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再和其他的PVC进行绑定了。
  4. 资源使用:用户可以在Pod中像volume一样使用PVC,Pod使用Volume的定义,将PVC挂载到容器内的某个路径进行使用。
  5. 资源释放
    1. 上一个 pod还有东西存在目录中
    2. 用户删除PVC来释放PV。
    3. 当存储资源使用完毕后,用户可以删除PVC,和该PVC绑定的PV将会标记为“已释放”,但是还不能立刻和其他的PVC进行绑定。通过之前PVC写入的数据可能还留在存储设备上,只有在清除之后该PV才能再次使用
  6. 资源回收
    1. kubernetes根据PV设置的回收策略进行资源的回收。
    2. 对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收,才能供新的PVC绑定和使用。

理论与使用 PV

类似于linux lvm 中的 vg

一般由kubernetes运维管理人员创建,然后提供给需要使用的人创建pvc

apiVersion: v1

kind: PersistentVolume

metadata:

  name: pv2

  # 没有namespace 是集群级别的资源,跨namespace使用

spec:

  nfs: # 存储类型,和底层正则的存储对应

    path:

    server:

  capacity: # 存储能力,目前只支持存储空间的设置

    storage: 2Gi

  accessModes: # 访问模式

    - ReadWriteOnce

    # ReadOnlyMany

    # ReadWriteMany

  storageClassName: # 存储类别

  persistentVolumeReclaimPolicy: # 回收策略

pv的关键配置参数说明:

  1. 存储类型:底层实际存储的类型,kubernetes支持多种存储类型,每种存储类型的配置有所不同。
  2. 存储能力(capacity):目前只支持存储空间的设置(storage=1Gi),不过未来可能会加入IOPS、吞吐量等指标的配置。
  3. 访问模式(accessModes)
    1. 用来描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
      1. ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载(单pvc挂载)。
      2. ReadOnlyMany(ROX):只读权限,可以被多个节点挂载。
      3. ReadWriteMany(RWX):读写权限,可以被多个节点挂载。
    2. 需要注意的是,底层不同的存储类型可能支持的访问模式不同
  4. 回收策略( persistentVolumeReclaimPolicy)
    1. 当PV不再被使用之后,对其的处理方式,目前支持三种策略:
      1. Retain(保留):保留数据,需要管理员手动清理数据。
      2. Recycle(回收):清除PV中的数据,效果相当于 rm -rf /volume/*。
      3. Delete(删除):和PV相连的后端存储完成volume的删除操作,常见于云服务器厂商的存储服务。
    2. 需要注意的是,底层不同的存储类型可能支持的回收策略不同
  5. 存储类别(storageClassName):PV可以通过storageClassName参数指定一个存储类别。
    1. 具有特定类型的PV只能和请求了该类别的PVC进行绑定。
    2. 未设定类别的PV只能和不请求任何类别的PVC进行绑定。
  6. 状态(status):一个PV的生命周期,可能会处于4种不同的阶段。
    1. Available(可用):表示可用状态,还未被任何PVC绑定。
    2. Bound(已绑定):表示PV已经被PVC绑定。
    3. Released(已释放):表示PVC被删除,但是资源还没有被集群重新释放。
    4. Failed(失败):表示该PV的自动回收失败。

示例

mkdir -pv /root/data/{pv1,pv2,pv3}

chmod 777 -R /root/data

# 修改 NFS 的配置文件

vim /etc/exports

    /root/data/pv1     192.168.18.0/24(rw,no_root_squash)

    /root/data/pv2     192.168.18.0/24(rw,no_root_squash)

    /root/data/pv3     192.168.18.0/24(rw,no_root_squash)

systemctl restart nfs-server
apiVersion: v1

kind: PersistentVolume

metadata:

  name: pv1

spec:

  nfs: # 存储类型,和底层正则的存储对应

    path: /root/data/pv1

    server: 192.168.100.53

  capacity: # 存储能力,目前只支持存储空间的设置

    storage: 1Gi

  accessModes: # 访问模式

    - ReadWriteMany

  persistentVolumeReclaimPolicy: Retain # 回收策略



---

apiVersion: v1

kind: PersistentVolume

metadata:

  name: pv2

spec:

  nfs: # 存储类型,和底层正则的存储对应

    path: /root/data/pv2

    server: 192.168.100.53

  capacity: # 存储能力,目前只支持存储空间的设置

    storage: 2Gi

  accessModes: # 访问模式

    - ReadWriteMany

  persistentVolumeReclaimPolicy: Retain # 回收策略

 

---

apiVersion: v1

kind: PersistentVolume

metadata:

  name: pv3

spec:

  nfs: # 存储类型,和底层正则的存储对应

    path: /root/data/pv3

    server: 192.168.100.53

  capacity: # 存储能力,目前只支持存储空间的设置

    storage: 3Gi

  accessModes: # 访问模式

    - ReadWriteMany

  persistentVolumeReclaimPolicy: Retain # 回收策略
[root@master k8s]# kubectl apply -f pv.yaml

persistentvolume/pv1 created

persistentvolume/pv2 created

persistentvolume/pv3 created

[root@master k8s]# kubectl get pv

NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE

pv1    1Gi        RWX            Retain           Available                                   27s

pv2    2Gi        RWX            Retain           Available                                   27s

pv3    3Gi        RWX            Retain           Available                                   27s

 CLAIM 如果被使用这里会显示pvc的名字

理论与使用 PVC

PVC是对 PV 资源的申请,用来声明对存储空间、访问模式、存储类别需求信息

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: pvc

  namespace: dev

spec:

  accessModes: # 访客模式

    - ReadWriteMany

  selector: # 采用标签对PV选择

  storageClassName: # 存储类别

  resources: # 请求空间

    requests:

      storage: 5Gi

PVC的关键配置参数说明:

  1. 访客模式(accessModes):用于描述用户应用对存储资源的访问权限。
    1. 必须和要连接的pv声明的访问模式要一样
      1. ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载(单pvc挂载)。
      2. ReadOnlyMany(ROX):只读权限,可以被多个节点挂载。
      3. ReadWriteMany(RWX):读写权限,可以被多个节点挂载。
  2. 用于描述用户应用对存储资源的访问权限:
    1. 选择条件(selector):通过Label Selector的设置,可使PVC对于系统中已存在的PV进行筛选。
    2. 存储类别(storageClassName):PVC在定义时可以设定需要的后端存储的类别,只有设置了该class的pv才能被系统选出。
    3. 资源请求(resources):描述对存储资源的请求。
      1. 优先级为
        1.  pvc(1Gi)= pv(1Gi)
        2. 同等容量的pv没有了则申请比自己大即可 pvc(1Gi)= pv(2Gi)
        3. 如果没有比自己大的,也没有相等的即失败
  3. 状态(status)
    1. Bound(已绑定):表示PVC已经绑定PV。

创建PVC后一直绑定不了PV的原因

  1. ①PVC的空间申请大小比PV的空间要大。
  2. ②PVC的storageClassName和PV的storageClassName不一致。
  3. ③PVC的accessModes和PV的accessModes不一致。

上面 pv 申请pv1:1g pv2:2g pv3:3g

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: pvc1

  namespace: study

spec:

  accessModes: # 访客模式

    - ReadWriteMany # 和pv一样

  resources: # 请求空间

    requests:

      storage: 1Gi #申请pv1



---

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: pvc2

  namespace: study

spec:

  accessModes: # 访客模式

    - ReadWriteMany

  resources: # 请求空间

    requests:

      storage: 1Gi # 上面pv1已经被申请了没有同等容量的pv,所以申请pv2(2G)



---

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: pvc3

  namespace: study

spec:

  accessModes: # 访客模式

    - ReadWriteMany

  resources: # 请求空间

    requests:

      storage: 5Gi # 没有与这个相等也没有比他大的,所以申请失败
[root@master k8s]# kubectl apply -f pvc.yaml

persistentvolumeclaim/pvc1 created

persistentvolumeclaim/pvc2 created

persistentvolumeclaim/pvc3 created

[root@master k8s]# kubectl get pvc -n study

NAME   STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE

pvc1   Bound     pv1      1Gi        RWX                           54s

pvc2   Bound     pv2      2Gi        RWX                           54s

pvc3   Pending                                                     54s

# Bound 被使用

# pvc3 没找到存储容量相等或大于所以一直在ping,可以另外创建新的pv

[root@master k8s]# kubectl get pv

NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM        STORAGECLASS   REASON   AGE

pv1    1Gi        RWX            Retain           Bound       study/pvc1                           23m

pv2    2Gi        RWX            Retain           Bound       study/pvc2                           23m

pv3    3Gi        RWX            Retain           Available                                        23m

创建pod连接 PVC

apiVersion: v1

kind: Pod

metadata:

  name: pod1

  namespace: study

spec:

  containers:

  - name: busybox

    image: busybox:1.30

    command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]

    volumeMounts:

    - name: volume

      mountPath: /root/

  volumes:

    - name: volume

      persistentVolumeClaim: # 这个字段是pvc的kind: PersistentVolumeClaim

        claimName: pvc1

        readOnly: false # 只读



---

apiVersion: v1

kind: Pod

metadata:

  name: pod2

  namespace: study

spec:

  containers:

    - name: busybox

      image: busybox:1.30

      command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]

      volumeMounts:

        - name: volume

          mountPath: /root/

  volumes:

    - name: volume

      persistentVolumeClaim:

        claimName: pvc2

        readOnly: false
[root@master k8s]# kubectl apply -f pod-pvc.yaml

pod/pod1 created

pod/pod2 created

[root@master k8s]# ls /root/data/pv1/out.txt

/root/data/pv1/out.txt

删除

[root@master k8s]# kubectl delete -f pod-pvc.yaml

pod "pod1" deleted

pod "pod2" deleted

[root@master k8s]# kubectl get pvc -n study

NAME   STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE

pvc1   Bound     pv1      1Gi        RWX                           123m

pvc2   Bound     pv2      2Gi        RWX                           123m

pvc3   Pending                                                     123m

[root@master k8s]# kubectl get pv

NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM        STORAGECLASS   REASON   AGE

pv1    1Gi        RWX            Retain           Bound       study/pvc1                           126m

pv2    2Gi        RWX            Retain           Bound       study/pvc2                           126m

pv3    3Gi        RWX            Retain           Available                                        126m

[root@master k8s]# kubectl delete -f pvc.yaml

persistentvolumeclaim "pvc1" deleted

persistentvolumeclaim "pvc2" deleted

persistentvolumeclaim "pvc3" deleted

[root@master k8s]# kubectl get pv -n study

NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM        STORAGECLASS   REASON   AGE

pv1    1Gi        RWX            Retain           Released    study/pvc1                           127m

pv2    2Gi        RWX            Retain           Released    study/pvc2                           127m

pv3    3Gi        RWX            Retain           Available                                        127m

# persistentVolumeReclaimPolicy:Retain 所以要手动释放,并重新声明
  1. persistentVolumeReclaimPolicy:Retain

将pv从Released 变为 Available

使用edit命令删除pv中的pvc的绑定信息即可变为available

注意:先将资源拿出来备份,反正数据还没备份就被pvc使用了

[root@master k8s]# kubectl get pv -n study

NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM        STORAGECLASS   REASON   AGE

pv1    1Gi        RWX            Retain           Released    study/pvc1                           129m

pv2    2Gi        RWX            Retain           Released    study/pvc2                           129m

pv3    3Gi        RWX            Retain           Available                                        129m

[root@master k8s]# kubectl edit pv pv2 # pv1
# 删除下面字段

  claimRef:

    apiVersion: v1

    kind: PersistentVolumeClaim

    name: pvc2

    namespace: study

    resourceVersion: "849619"

    uid: fac30e1d-90a5-4e8b-9092-e6b83fd55d62

persistentvolume/pv2 edited
[root@master k8s]# kubectl get pv -n study

NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE

pv1    1Gi        RWX            Retain           Available                                   134m

pv2    2Gi        RWX            Retain           Available                                   134m

pv3    3Gi        RWX            Retain           Available                                   134m

问题

pv创建不会检测nfs的路径是否连上,哪怕nfs都没有这个配置也连不上也能创建,不会报错,甚至后续的pvc和pod连接pvc都不会报错

动态供应

  1. 静态供应:集群管理员创建若干 PV 卷。这些卷对象带有真实存储的细节信息,并且对集群用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。
  2. 动态供应:集群自动根据 PVC 创建出对应 PV 进行使用

  1. ① 集群管理员预先创建存储类(StorageClass)。
  2. ② 用户创建使用存储类的持久化存储声明(PVC:PersistentVolumeClaim)。
  3. ③ 存储持久化声明通知系统,它需要一个持久化存储(PV: PersistentVolume)。
  4. ④ 系统读取存储类的信息。
  5. ⑤ 系统基于存储类的信息,在后台自动创建 PVC 需要的 PV 。
  6. ⑥ 用户创建一个使用 PVC 的 Pod 。
  7. ⑦ Pod 中的应用通过 PVC 进行数据的持久化。
  8. ⑧ PVC 使用 PV 进行数据的最终持久化处理。

https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

设置 NFS 动态供应

apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

  name: nfs-client

provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # 指定一个供应商的名字

# or choose another name, 必须匹配 deployment 的 env PROVISIONER_NAME'

parameters:

  archiveOnDelete: "false" # 删除 PV 的时候,PV 中的内容是否备份

---

apiVersion: apps/v1

kind: Deployment

metadata:

  name: nfs-client-provisioner

  labels:

    app: nfs-client-provisioner

  namespace: default

spec:

  replicas: 1

  strategy:

    type: Recreate

  selector:

    matchLabels:

      app: nfs-client-provisioner

  template:

    metadata:

      labels:

        app: nfs-client-provisioner

    spec:

      serviceAccountName: nfs-client-provisioner

      containers:

        - name: nfs-client-provisioner

          image: ccr.ccs.tencentyun.com/gcr-containers/nfs-subdir-external-provisioner:v4.0.2

          volumeMounts:

            - name: nfs-client-root

              mountPath: /persistentvolumes

          env:

            - name: PROVISIONER_NAME

              value: k8s-sigs.io/nfs-subdir-external-provisioner

            - name: NFS_SERVER

              value: 192.168.65.100 # NFS 服务器的地址

            - name: NFS_PATH

              value: /nfs/data # NFS 服务器的共享目录

      volumes:

        - name: nfs-client-root

          nfs:

            server: 192.168.65.100

            path: /nfs/data

---

apiVersion: v1

kind: ServiceAccount

metadata:

  name: nfs-client-provisioner

  namespace: default

---

kind: ClusterRole

apiVersion: rbac.authorization.k8s.io/v1

metadata:

  name: nfs-client-provisioner-runner

rules:

  - apiGroups: [""]

    resources: ["nodes"]

    verbs: ["get", "list", "watch"]

  - apiGroups: [""]

    resources: ["persistentvolumes"]

    verbs: ["get", "list", "watch", "create", "delete"]

  - apiGroups: [""]

    resources: ["persistentvolumeclaims"]

    verbs: ["get", "list", "watch", "update"]

  - apiGroups: ["storage.k8s.io"]

    resources: ["storageclasses"]

    verbs: ["get", "list", "watch"]

  - apiGroups: [""]

    resources: ["events"]

    verbs: ["create", "update", "patch"]

---

kind: ClusterRoleBinding

apiVersion: rbac.authorization.k8s.io/v1

metadata:

  name: run-nfs-client-provisioner

subjects:

  - kind: ServiceAccount

    name: nfs-client-provisioner

    namespace: default

roleRef:

  kind: ClusterRole

  name: nfs-client-provisioner-runner

  apiGroup: rbac.authorization.k8s.io

---

kind: Role

apiVersion: rbac.authorization.k8s.io/v1

metadata:

  name: leader-locking-nfs-client-provisioner

  namespace: default

rules:

  - apiGroups: [""]

    resources: ["endpoints"]

    verbs: ["get", "list", "watch", "create", "update", "patch"]

---

kind: RoleBinding

apiVersion: rbac.authorization.k8s.io/v1

metadata:

  name: leader-locking-nfs-client-provisioner

  namespace: default

subjects:

  - kind: ServiceAccount

    name: nfs-client-provisioner

    namespace: default

roleRef:

  kind: Role

  name: leader-locking-nfs-client-provisioner

  apiGroup: rbac.authorization.k8s.io

测试动态供应

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: nginx-pvc

  namespace: default

  labels:

    app: nginx-pvc

spec:

  storageClassName: nfs-client # 注意此处

  accessModes:

  - ReadWriteOnce

  resources:

    requests:

      storage: 2Gi

---

apiVersion: v1

kind: Pod

metadata:

  name: nginx

  namespace: default

  labels:

    app: nginx

spec:

  containers:

  - name: nginx

    image: nginx:1.20.2

    resources:

      limits:

        cpu: 200m

        memory: 500Mi

      requests:

        cpu: 100m

        memory: 200Mi

    ports:

    - containerPort:  80

      name:  http

    volumeMounts:

    - name: localtime

      mountPath: /etc/localtime

    - name: html

      mountPath: /usr/share/nginx/html/

  volumes:

    - name: localtime

      hostPath:

        path: /usr/share/zoneinfo/Asia/Shanghai

    - name: html   

      persistentVolumeClaim:

        claimName:  nginx-pvc

        readOnly: false 

  restartPolicy: Always

设置 SC 为默认驱动

kubectl patch storageclass <your-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

kubectl patch storageclass nfs-client -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: nginx-pvc

  namespace: default

  labels:

    app: nginx-pvc

spec:

  # storageClassName: nfs-client 不写,就使用默认的

  accessModes:

  - ReadWriteOnce

  resources:

    requests:

      storage: 2Gi

---

apiVersion: v1

kind: Pod

metadata:

  name: nginx

  namespace: default

  labels:

    app: nginx

spec:

  containers:

  - name: nginx

    image: nginx:1.20.2

    resources:

      limits:

        cpu: 200m

        memory: 500Mi

      requests:

        cpu: 100m

        memory: 200Mi

    ports:

    - containerPort:  80

      name:  http

    volumeMounts:

    - name: localtime

      mountPath: /etc/localtime

    - name: html

      mountPath: /usr/share/nginx/html/

  volumes:

    - name: localtime

      hostPath:

        path: /usr/share/zoneinfo/Asia/Shanghai

    - name: html   

      persistentVolumeClaim:

        claimName:  nginx-pvc

        readOnly: false 

  restartPolicy: Always

展望

  1. 目前,只需要运维人员部署好各种 storageclass,开发人员在使用的时候,创建 PVC 即可;但是,存储系统太多太多,运维人员也未必会一一掌握,此时就需要 Rook 来统一管理了。

特殊存储卷

ConfigMap

ConfigMap是一个比较特殊的存储卷,它的主要作用是用来存储配置信息的

创建configmap

apiVersion: v1

kind: ConfigMap

metadata:

  name: configmap-test

  namespace: study

data: # 下面都是要存储的数据

  info: # 会创建一个名为info文件,文件内容如下

    username:admin

    password:123456

# 如果更新ConfigMap中的内容,容器中的值也会动态更新

# 更改后重新 apply 或 使用 edit命令

# 注意:需要一定时间
[root@master k8s]# kubectl apply  -f configmap.yaml

configmap/configmap-test created

[root@master k8s]# kubectl get cm -n study

NAME               DATA   AGE

configmap-test     1      22s

kube-root-ca.crt   1      4d21h

创建pod挂载configmap

apiVersion: v1

kind: Pod

metadata:

  name: pod-configmap

  namespace: study

spec:

  containers:

    - name: nginx

      image: nginx:1.17.1

      volumeMounts:

        - mountPath: /configmap/config

          name: config

  volumes:

    - name: config

      configMap:

        name: configmap-test # 绑定 configmap

[root@master k8s]# kubectl apply -f configmap.yaml

pod/pod-configmap created

[root@master k8s]# kubectl get pod -n study

NAME            READY   STATUS    RESTARTS   AGE

pod-configmap   1/1     Running   0          11s

[root@master k8s]# kubectl exec -it -n study pod-configmap -c nginx /bin/sh
# ls /configmap/config

info

# cat /configmap/config/info

username:admin password:123456

Secret

在kubernetes中,还存在一种和ConfigMap非常类似的对象,称为Secret对象,它主要用来存储敏感信息,例如密码、密钥、证书等等

Secret 是一个用于存储敏感数据的资源,所有的数据要经过base64编码,数据实际会存储在K8s中Etcd,然后通过创建Pod时引用该数据。

查询 Secret 的时候是加密,在pod容器里面显示的是解密后的内容

Pod 使用 secret 数据有两种方式:

  1. 变量注入
  2. 数据卷挂载

kubectl create secret 支持三种数据类型:

  1. docker--registry:存储镜像仓库认证信息
  2. generic:从文件、目录或者字符串创建,例如存储用户名密码
  3. ts:存储证书,例如HTTPS证书

type字段

内置类型

用法

Opaque

用户定义的任意数据

kubernetes.io/service-account-token

服务账号令牌

kubernetes.io/dockercfg

~/.dockercfg

文件的序列化形式

kubernetes.io/dockerconfigjson

~/.docker/config.json

文件的序列化形式

kubernetes.io/basic-auth

用于基本身份认证的凭据

kubernetes.io/ssh-auth

用于 SSH 身份认证的凭据

kubernetes.io/tls

用于 TLS 客户端或者服务器端的数据

bootstrap.kubernetes.io/token

启动引导令牌数据

yaml 解析

# 准备base64加密数据

echo -n "admin" | base64

echo -n "123456" | base64

apiVersion: v1

kind: Secret

metadata:

  name: secret

  namespace: dev

type: Opaque

data: # 下面存存储敏感信息,已base64加密

  # 这里 username 是一个文件名,这里 password 是一个文件名

  username: YWRtaW4=

  password: MTIzNDU2

  # mysql-root-password:"MTIzNDU2"

stringData: #如果不手动进行编码加密,可以将这个操作让k8s来做,使用stringdata

  username: admin

  password: "123456"

# 不能直接写数字,不然会报错 cannot convert int64 to string,如果要写数字就加""

注:如果同时使用data和stringData,那么data会被忽略

[root@master k8s]# kubectl apply -f secret.yaml

secret/secret created

[root@master k8s]# kubectl describe secrets -n study

Name:         secret

Namespace:    study

Labels:       <none>

Annotations:  <none>



Type:  Opaque



Data

====

password:  6 bytes

username:  5 bytes

 数据卷挂载

apiVersion: v1

kind: Pod

metadata:

  name: pod-secret

  namespace: study

spec:

  containers:

    - name: nginx

      image: nginx:1.17.1

      volumeMounts:

        - mountPath: /secret/config

          name: secret-config

  volumes:

    - name: secret-config

      secret:

        secretName: secret

kubectl exec -it -n study pod-secret -c nginx /bin/sh

# cat /secret/config/password

123456

解码 Secret

kubectl get secret db-user-pass -o jsonpath='{.data}' | base64

变量注入

示例:将Mysql用户密码保存到Secret中存储

[root@master cks]# echo -n "123456" | base64

MTIzNDU2
apiVersion: v1

kind: Secret

metadata:

  name: mysql

type: Opaque

data:

  mysql-root-password: "MTIzNDU2"

---

apiVersion: apps/v1

kind: Deployment

metadata:

  name: mysql

spec:

  selector:

    matchLabels:

      app: mysql

  template:

    metadata:

      labels:

        app: mysql

    spec:

      containers:

      - name: mysql-db

        image: mysql:5.7.30

        env:

        - name: MYSQL_ROOT_PASSWORD

          valueFrom:

            secretKeyRef:

              name: mysql

              key: mysql-root-password

将 secret 的mysql中的mysql-root-passwd变量注入到 容器中的 MYSQL_ROOT_PASSWORD 变量

[root@master cks]# kubectl exec -it mysql-6c8b6c4d74-vbflz /bin/sh

# mysql -uroot -p123456

mysql: [Warning] Using a password on the command line interface can be insecure.

Welcome to the MySQL monitor.  Commands end with ; or \g.

Your MySQL connection id is 4

Server version: 5.7.30 MySQL Community Server (GPL)



Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.



Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

owners.



Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.



mysql>

镜像拉取密码

imagePullSecret:Pod拉取私有镜像仓库的时使用的账户密码,会传递给kubelet,然后kubelet就可以拉取有密码的仓库里面的镜像

kubectl create secret docker-registry docker-harbor-registrykey --docker-server=192.168.18.119:85 \

          --docker-username=admin --docker-password=Harbor12345 \

          --docker-email=1900919313@qq.com

apiVersion: v1

kind: Pod

metadata:

  name: redis

spec:

  containers:

    - name: redis

      image: 192.168.18.119:85/yuncloud/redis # 这是Harbor的镜像私有仓库地址

  imagePullSecrets:

    - name: docker-harbor-registrykey

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1369782.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode刷题笔记题解(C++):无重复字符的最长子串

思路&#xff1a; 利用滑动窗口的思想&#xff0c;用起始位置startindex和curlength来记录这个滑动窗口的大小&#xff0c;并且得出最长距离&#xff1b;利用哈希表来判断在滑动窗口中是否存在重复字符&#xff0c;代码如下所示&#xff1a; class Solution { public:int len…

6.1 截图工具HyperSnap6简介

图片是组成多媒体作品的基本元素之一&#xff0c;利用图片可以增强多媒体作品的亲和力和说说服力。截取图片最简单的方法是直接按下键盘上的“PrintScreen”键截取整个屏幕或按下“AltPrintScreen”组合键截取当前活动窗口&#xff0c;然后在画笔或者其它的图片处理软件中进行剪…

基于SSM的在线电影票购买系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的在线电影票购买系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring…

【模拟IC学习笔记】Cascode OTA 设计

辅助定理 增益Gm*输出阻抗 输出短路求Gm 输入置0求输出阻抗 求源极负反馈的增益 随着Vin的增加&#xff0c;Id也在增加&#xff0c;Rs上压降增加&#xff0c;所以&#xff0c;Vin的一部分电压体现在Rs上&#xff0c;而不是全部作为Vgs&#xff0c;因此导致Id变得平滑。 Rs足…

软件测试|MySQL DISTINCT关键字过滤重复数据

简介 在MySQL中&#xff0c;有时候我们需要从表中检索唯一的、不重复的数据。这时&#xff0c;我们可以使用DISTINCT关键字来过滤掉重复的数据行。在本文中&#xff0c;我们将深入探讨MySQL中DISTINCT的用法以及如何在查询中使用它来得到不重复的结果集。 基本语法 DISTINCT…

Influxdb2修改管理员密码

通过恢复管理员令牌来重置InfluxDB2管理员的密码 1.找到数据库的配置文件 一般为config.json 2.配置文件的的blod文件配置 3.在这个混合文本和二进制json文件中搜索已知的用户名或token之类的字符串。 例如&#xff1a; "id":"0bd73badf2941000","…

系列十四、理解MySQL varchar(50)

一、理解MySQL varchar(50) 1.1、概述 日常开发中&#xff0c;数据库建表是必不可少的一个环节&#xff0c;建表的时候通常会看到设定某个字段的长度为varchar(50)&#xff0c;例如如下建表语句&#xff1a; 那么怎么理解varchar(50)&#xff1f;这个分情况的&#xff0c;MySQ…

美创科技葛宏彬:夯实安全基础,对医疗数据风险“逐个击破”

导读 解决医疗机构“临床业务数据合规流动”与“重要数据安全防护”两大难题。 2023年11月11日&#xff0c;在2023年南湖HIT论坛上&#xff0c;HIT专家网联合杭州美创科技股份有限公司&#xff08;以下简称美创科技&#xff09;发布《医疗数据安全风险分析及防范实践》白皮书…

遇到U盘写保护怎么办

U盘写保护 为什么出现写保护的情况 U盘写保护&#xff0c;就是无法对U盘数据进行修改&#xff08;添加、删除、修改名称&#xff09;。 u盘写保护分为硬件写保护、系统或软件异常导致的写保护。 硬件写保护一般是U盘上硬件写保护开关被开启&#xff08;常见于SD卡读卡器侧面会…

【大数据架构】日志采集方案对比

整体架构 日志采集端 Flume Flume的设计宗旨是向Hadoop集群批量导入基于事件的海量数据。系统中最核心的角色是agent&#xff0c;Flume采集系统就是由一个个agent所连接起来形成。每一个agent相当于一个数据传递员&#xff0c;内部有三个组件&#xff1a; source: 采集源&…

HubSpot线索管理系统怎么样?适合哪些企业?

HubSpot的线索管理系统是该平台中销售和市场营销工具的一部分&#xff0c;被称为HubSpot CRM&#xff08;客户关系管理&#xff09;。以下是对HubSpot CRM的一些特点以及适合的企业类型的概述&#xff1a; HubSpot CRM 特点&#xff1a; 1. 用户友好的界面&#xff1a; HubS…

OpenAI ChatGPT-4开发笔记2024-02:Chat之text generation之completions

API而已 大模型封装在库里&#xff0c;库放在服务器上&#xff0c;服务器放在微软的云上。我们能做的&#xff0c;仅仅是通过API这个小小的缝隙&#xff0c;窥探ai的奥妙。从程序员的角度而言&#xff0c;水平的高低&#xff0c;就体现在对openai的这几个api的理解程度上。 申…

【hcie-cloud】【20】容器详解【容器介绍,容器工作机制、容器常用命令说明】【上】

文章目录 前言容器是什么虚拟化技术的四个特点容器也是一种虚拟化技术容器是怎么实现虚拟化的&#xff1f;容器对比虚拟机有哪些优势&#xff1f;容器对比虚拟机有哪些不足&#xff1f;容器不仅是一种虚拟化技术&#xff0c;更重要的是一种应用打包机制容器提供的是PaaS服务常见…

JavaSec基础 反射修改Final修饰的属性及绕过高版本反射限制

反射重拾 半年没碰java了 先写点基础回忆一下 反射弹计算器 public class Test {public static void main(String[] args) throws Exception {Class<?> clazz Class.forName("java.lang.Runtime");clazz.getDeclaredMethod("exec", String.cla…

Aging:浙大学者研究发现,多吃这类抗氧化饮食,延缓衰老

撰文 | 宋文法 衰老&#xff0c;是一个复杂、多阶段、渐进的过程&#xff0c;发生在生命的整个过程。随着时间的流逝&#xff0c;人体的器官、肌肉会逐渐衰老&#xff0c;一些疾病也伴随着年龄的增长而发生&#xff0c;包括癌症、糖尿病、心血管疾病等。 衰老过程是由体内自由基…

autoxjs 安卓爬虫自动化

autoxjs 安卓爬虫自动化 我这里只是测试请勿用于违法的 我这里是小红书 文章目录 autoxjs 安卓爬虫自动化前言一、自动刷直播间并且抓取商品已经粉丝数量等&#xff1f;总结 前言 欢迎来到AutoXJS的世界&#xff0c;这是一个充满创新、挑战和技术探索的领域。在这个引领未来的…

【huggingface】【pytorch-image-models】timm框架中使用albumentations库数据增广

文章目录 一、前言二、实操2.1 声明库2.2 定义你的数据增广算子2.3 加入其中 一、前言 问题是这样的&#xff0c;在使用timm框架训练时&#xff0c;发现数据增广不够&#xff0c;想用Albumentations库的数据增广&#xff0c;怎么把后者嵌入到前者的训练中。 其实也是比较简单…

CHS_02.1.4+操作系统体系结构 二

CHS_02.1.4操作系统体系结构 二 操作系统的结构 上篇文章我们只介绍过宏内核 也就是大内核以及微内核分层结构的操作系统模块化是一种很经典的程序设计思想宏内核和微内核外核 操作系统的结构 上篇文章我们只介绍过宏内核 也就是大内核以及微内核 今年大纲又增加了分层结构 模块…

unity编辑器Scene界面输出位置及路径

工程Asset下新建Editor文件夹&#xff1b; Editor文件夹下新建脚本LogPosition using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; public class LogPosition : EditorWindow {//最终输出的数据.static string logtext;//增…

GCF:在线市场异质治疗效果估计的广义因果森林

英文题目&#xff1a;GCF: Generalized Causal Forest for Heterogeneous Treatment Effects Estimation in Online Marketplace 中文题目&#xff1a;GCF&#xff1a;在线市场异质治疗效果估计的广义因果森林 单位&#xff1a;滴滴&美团 时间&#xff1a;2022 论文链接…