Skip to content

存储卷

一、简介

存储卷在Kubernetes数据卷存储里介绍的很详细了。

在前面已经提到,容器的生命周期可能很短,会被频繁地创建和销毁。那么容器在销毁时,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器的数据,kubernetes引入了Volume的概念。

Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下,kubernetes通过Volume实现。Volume的生命容器不与Pod中单个容器的生命周期相关,当容器终止或者重启时Volume中的数据也不会丢失

  • 基本存储:EmptyDir、HostPath、NFS
  • 高级存储:PV、PVC
  • 配置存储:ConfigMap、Secret

二、基本存储

1. EmptyDir

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

EmptyDir是在Pod分配到Node时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为kubernetes会自动分配一个目录,当Pod销毁时, EmptyDir中的数据也会被永久删除。 EmptyDir用途如下:

  • ,例如用于某些应用程序运行时所需的临时目录,且无须永久保留
  • 一个容器需要从另一个容器中获取数据的目录(多容器共享目录) 接下来,通过一个的案例来使用一下EmptyDir。

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

资源配置文件

shell
# 创建资源文件volume-emptydir.yaml
Vim volume-emptydir.yaml
#------------------------
apiVersion: v1
kind: Pod
metadata:
  name: emptydir-test
  labels:
    app: emptydir-test
spec:
  containers:
    # 这里我们创建多个容器,一个nginx,一个busybox
    - name: nginx-cont
      image: nginx
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
      volumeMounts:
        - mountPath: /var/log/nginx
          name: logs-volume
    - name: busybox-cont
      image: busybox
      command: ["/bin/sh","-c","tail -f /logs/access.log"]
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - mountPath: /logs
          name: logs-volume
  # volume,创建emptyDir类型的存储空间,并命名为 `logs-volume`
  volumes:
    - name: logs-volume
      # volumes 类型 emptyDir
      emptyDir: {}
  restartPolicy: Always
#------------------------

kubectl apply -f volume-emptydir.yaml
# 检测pod
kubectl get pods | grep empty
emptydir-test                               2/2     Running   0              76s

# 查看pod ip
kubectl get pods emptydir-test -o wide
#----------------------------
NAME            READY   STATUS    RESTARTS   AGE   IP              NODE           NOMINATED NODE   READINESS GATES
emptydir-test   2/2     Running   0          11m   10.244.32.157   k8s-master01   <none>           <none>
#----------------------------

# 测试nginx
curl 10.244.32.157
#----------------------------
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
#----------------------------

# 我们查看buxybox 的日志
kubectl logs -f emptydir-test  -c busybox-cont
#----------------------------
10.0.2.15 - - [14/Jul/2022:07:54:55 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.29.0" "-"
#----------------------------

# 我们进入到pod中取一探究竟

# 我们进入nginx pod container
# 语法 我们可以通过 -c 来指定容器container
kubectl exec -it emptydir-test -c nginx-cont bash
# 查看 mountPath `/var/log/nginx`
cd /var/log/nginx
# 查看目录信息
ls -l
#--------------------
total 8
-rw-r--r-- 1 root root  90 Jul 14 07:54 access.log
-rw-r--r-- 1 root root 626 Jul 14 07:42 error.log
#--------------------
# 退出
Ctrl + D

# 我们进入busybox pod container
kubectl exec -it emptydir-test -c busybox-cont sh
# 查看 mountPath `/logs`
cd /logs
# 查看目录信息
ls -l
#-------------------
total 8
-rw-r--r--    1 root     root            90 Jul 14 07:54 access.log
-rw-r--r--    1 root     root           626 Jul 14 07:42 error.log
#-------------------
# 退出
Ctrl + D

# 结论: emptydir 实现了容器间的磁盘共享; 将 nginx -> /var/log/nginx 与 busybox -> /logs 关联到了一起。

2. HostPath

我们知道EmptyDir 是一个临时空间,数据不会被持久化,Pod销毁了就消失了,那么我们如何持久化存储数据呢?此时我们就可以使用HostPath。

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

HostPath

资源配置文件

shell
# 创建资源清单文件volume_hostpath.yml
vim volume_hostpath.yml
#----------------------
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-test
  labels:
    app: hostpath-test
spec:
  containers:
    - name: nginx-cont
      image: nginx
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
      volumeMounts:
        - mountPath: /var/log/nginx
          name: logs-volume
    - name: busybox-cont
      image: busybox
      command: [ "/bin/sh","-c","tail -f /logs/access.log" ]
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - mountPath: /logs
          name: logs-volume
  restartPolicy: Always
  # 挂载卷
  volumes:
    - name: logs-volume
      # volumes 类型 hostPath
      hostPath:
        path: /root/logs  # node 设置节点机器的目录
        type: DirectoryOrCreate  # 节点机器是否存在,如果不存在创建上边配置的路径
#----------------------

kubectl apply -f volume_hostpath.yml

# 查看
kubectl get pods -o wide | grep hostpath
#----------------------
hostpath-test                               2/2     Running   0            4m14s   10.244.211.208   k8s-slave03    <none>           <none>
#----------------------

# 测试访问
curl 10.244.211.208

# 查看busybox 容器日志
kubectl logs -f hostpath-test  -c busybox-cont
#----------------------
10.244.32.128 - - [14/Jul/2022:08:36:22 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.29.0" "-"
#----------------------

# 从上面的信息我们知道, pod节点调度到了`k8s-slave03`节点上,我们到此节点上查看路径`/root/logs`
cd /root/logs
ll -l
#----------------------
总用量 8
-rw-r--r-- 1 root root  94 7月  14 16:36 access.log
-rw-r--r-- 1 root root 510 7月  14 16:35 error.log
#----------------------

# 然后我们删除pod,再次查看`/root/logs`
#----------------------
总用量 8
-rw-r--r-- 1 root root  94 7月  14 16:36 access.log
-rw-r--r-- 1 root root 510 7月  14 16:35 error.log
#----------------------


# 结论: hostpath 实现了容器与宿主机间的磁盘共享; 将 nginx -> /var/log/nginx 与 busybox -> /logs 与 宿主机node`k8s-slave03` -> /root/logs 关联到了一起。

3. NFS

HostPath可以解决数据持久化的问题,但是一旦node节点机器故障,pod转移到其他Node上,就又会出现问题。此时需要准备单独的网络存储系统,常用的:NFS、CIFS

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

NFS

NFS实现

3.1 搭建NFS网络存储系统

我们准备一台新的虚拟机192.168.56.109

shell
# 安装nfs服务
yum install nfs-utils -y

# 创建一个共享nfs目录
mkdir /root/data/nfs -pv
# 赋予权限
chmod 777 /root/data/nfs

# 查看虚拟机网段
ip route show
# 我们找到enp0s3 网卡,得知当前虚拟机网段为 `10.0.2.0/24`
#-----------------------
default via 10.0.2.2 dev enp0s3 proto dhcp metric 100
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.56.0/24 dev enp0s8 proto kernel scope link src 192.168.56.109 metric 101
#-----------------------

# 设置共享目录读写权限,并暴露给虚拟机所在网段。
vim /etc/exports

# 重启nfs服务并开机自启
systemctl restart nfs && systemctl enable nfs

nfs常用操作及命令

shell
# 安装nfs服务
yum install nfs-utils -y

# 设置nfs暴露
vim /etc/exports

# 查看本机发布的nfs服务
showmount -e
#------------------
Export list for localhost.localdomain:
/root/data/nfs 10.0.2.0/24
#------------------

#  重启nfs服务并开机自启
systemctl restart nfs && systemctl enable nfs

### 配置详解
## 客户机地址可以是主机名、IP地址、网段地址,允许使用“*”、“?”通配符;
## “rw”表示允许读写,“ro”表示为只读;
## “sync”:表示同步写入到内存与硬盘中;
## “no_root_squash”:表示当客户机以root身份访问时赋予本地root权限(默认是root_squash);
## “root_squash”:表示客户机用root用户访问更改共享目录时,将root用户映射成匿名用户;

## “all_sauash”:所有访问用户都映射为匿名用户或者用户组;
## “async”:	将数据先保存在内存缓冲区中,必须时才写入磁盘;
## “subtree_check”(默认):	若输出目录是一个子目录,则nfs服务器将检查其父目录的权限;
## “no_subtree_check”:	即使输出目录是一个子目录,nfs服务器也不检查其父目录的权限,这样做可以提高效率。
## “anonuid=xxx”:	指定NFS服务器/etc/passwd文件中的匿名用户的UID
## “anongid=xxx”:	指定NFS服务器/etc/passwd文件
3.2 将NFS网络系统与kubernetes相结合

为了能在kubernetes集群访问到nfs,我们需要在每个Node节点上都安装nfs服务

shell
yum install nfs-utils -y

# 验证Node 节点是否可以介入nfs,`192.168.56.109` 为nfs服务器所在机器ip
showmount -e 192.168.56.109
#--------------
Export list for 192.168.56.109:
/root/data/nfs 192.168.56.0/24
#--------------

好了,至此我们的准备工作已经完成了。

资源配置文件
shell
vim volume_nfs.yaml
#---------------------
apiVersion: v1
kind: Pod
metadata:
  name: nfs-test
  labels:
    app: nfs-test
spec:
  containers:
    - name: nginx-cont
      image: nginx
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
      volumeMounts:
        - mountPath: /var/log/nginx
          name: logs-volume
    - name: busybox-cont
      image: busybox
      command: [ "/bin/sh","-c","tail -f /logs/access.log" ]
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - mountPath: /logs
          name: logs-volume
  restartPolicy: Always
  # 挂载卷
  volumes:
    - name: logs-volume
      # volumes 类型 nfs
      nfs:
        path: /root/data/nfs
        server: 192.168.56.109
#---------------------

kubectl apply -f volume_nfs.yaml

# 而后我们查看describe信息
kubectl describe pod nfs-test
#-------------------
Name:         nfs-test
Namespace:    default
Priority:     0
Node:         k8s-slave03/192.168.56.107
Start Time:   Fri, 15 Jul 2022 09:21:13 +0800
Labels:       app=nfs-test
Annotations:  cni.projectcalico.org/containerID: ed6f0aa3c78cb33eed2ca69a946fc198aa84d95a60e086eb997b42910e4c0d8b
              cni.projectcalico.org/podIP: 10.244.211.205/32
              cni.projectcalico.org/podIPs: 10.244.211.205/32
Status:       Running
IP:           10.244.211.205
IPs:
  IP:  10.244.211.205
Containers:
  nginx-cont:
    Container ID:   docker://04d7e871a6777170f40d1e52397d30024f9f21f45eb4487f6870c8bc7191f493
    Image:          nginx
    Image ID:       docker-pullable://nginx@sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Fri, 15 Jul 2022 09:21:14 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/log/nginx from logs-volume (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-jmjqk (ro)
  busybox-cont:
    Container ID:  docker://502123e2940afa07eec4803beb64819594f2892aa3c4453b12eae74c1b6b674c
    Image:         busybox
    Image ID:      docker-pullable://busybox@sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678
    Port:          <none>
    Host Port:     <none>
    Command:
      /bin/sh
      -c
      tail -f /logs/access.log
    State:          Running
      Started:      Fri, 15 Jul 2022 09:21:14 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /logs from logs-volume (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-jmjqk (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
# --- 这里是我们挂载的nfs信息  
Volumes:
  logs-volume:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    192.168.56.109
    Path:      /root/data/nfs
    ReadOnly:  false
  kube-api-access-jmjqk:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
# --- 这里是我们挂载的nfs信息      
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  4m31s  default-scheduler  Successfully assigned default/nfs-test to k8s-slave03
  Normal  Pulled     4m30s  kubelet            Container image "nginx" already present on machine
  Normal  Created    4m30s  kubelet            Created container nginx-cont
  Normal  Started    4m30s  kubelet            Started container nginx-cont
  Normal  Pulled     4m30s  kubelet            Container image "busybox" already present on machine
  Normal  Created    4m30s  kubelet            Created container busybox-cont
  Normal  Started    4m30s  kubelet            Started container busybox-cont
#-------------------

# 我们去nfs服务器上`192.168.56.109`查看共享磁盘`/root/data/nfs`
cd /root/data/nfs
ls -l
#------------------
总用量 4
-rw-r--r--. 1 root root   0 7月  15 09:21 access.log
-rw-r--r--. 1 root root 510 7月  15 09:21 error.log
#------------------

# 我们在共享磁盘下创建一个test.json文件
tee test.json <<-'EOF'
{
  "key": "value"
}
EOF

# 而后我们进入容器里查看磁盘信息
# 查看nginx容器
cd /var/log/nginx
ls -l
#----------------
total 8
-rw-r--r-- 1 root root   0 Jul 15 01:21 access.log
-rw-r--r-- 1 root root 510 Jul 15 01:21 error.log
-rw-r--r-- 1 root root  21 Jul 15 01:32 test.json
#----------------

# 查看busybox容器
cd /logs
ls -l 
#----------------
total 8
-rw-r--r--    1 root     root             0 Jul 15 01:21 access.log
-rw-r--r--    1 root     root           510 Jul 15 01:21 error.log
-rw-r--r--    1 root     root            21 Jul 15 01:32 test.json
#----------------

# 结论: nfs 实现了Node节点-容器-宿主机间的磁盘共享; 将 nginx -> /var/log/nginx 与 busybox -> /logs 与 集群Node节点 ->  与nfs服务器 /root/data/nfs 关联到了一起。 他们之间实现文件共享

三、高级存储

上面我们已经通过NFS来实现可靠性较高的持久化存储。但是这需要 用户会搭建NFS系统,且集群的每个节点都需要部署,这样对用户有一定要求且部署麻烦。因此Kubernetes引入了 PVPVC 的概念:

NameDescribeAll Name
PV持久化卷
是对底层的共享存储的一种抽象。
一般情况下PV由kubernetes管理员进行创建和配置,
它与底层具体的共享存储技术有关,并通过插件完成与共享存储的对接。
Persistent Volume
PVC持久卷声明
是用户对于存储需求的一种声明。换句话说,
PVC其实就是用户向kubernetes系统发出的一种资源需求申请。
Persistent Volume Claim

pvpvc

使用了PV和PVC之后,工作可以得到进一步的细分:

  • 存储:存储工程师维护
  • PV: kubernetes管理员维护
  • PVC:kubernetes用户维护,我们只需要关注yaml文件即可,不需要考虑实际存储是如何实现的。

kubernetes中定义一种了资源类型Stateful Service即有状态服务,有状态服务需要的持久化数据动态绑定我们可以利用存储的API PersistentVolume(PV)和PersistentVolumeClaim(PVC)来进行需要的相关数据的绑定和存储。

PV & PVC

  • PV就好比是一个仓库,我们需要先购买一个仓库,即定义一个PV存储服务,例如CEPH,NFS,LocalHostpath等等。
  • PVC就好比租户,pv和pvc是一对一绑定的,挂载到POD中,一个pvc可以被多个pod挂载。

1. PV

PV是由管理员设置的存储,它是集群的一部分。就像Node节点是集群中的资源一样,PV也是集群中的资源。PV是Volumes之类的卷插件,但具有独立于使用PV的pod的生命周期。此API对象包含存储实现的细节,即NFS、iSCSI或者特定于云供应商的存储系统

PV 的关键配置参数说明:

存储类型

底层实际存储的类型,kubernetes支持多种存储类型,每种存储类型的配置都有所差异

存储能力(capacity)

目前只支持存储空间的设置( storage=1Gi ),不过未来可能会加入IOPS、吞吐量等指标的配置

访问模式(accessModes)

用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:

  • ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载

  • ReadOnlyMany(ROX): 只读权限,可以被多个节点挂载

  • ReadWriteMany(RWX):读写权限,可以被多个节点挂载

回收策略(persistentVolumeReclaimPolicy)

当PV不再被使用了之后,对其的处理方式。目前支持三种策略:

  • Retain (保留) 保留数据,需要管理员手工清理数据

  • Recycle(回收) 清除 PV 中的数据,效果相当于执行 rm -rf /volume/*

  • Delete (删除) 与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务

存储类别

PV可以通过storageClassName参数指定一个存储类别

  • 具有特定类别的PV只能与请求了该类别的PVC进行绑定

  • 未设定类别的PV则只能与不请求任何类别的PVC进行绑定

状态(status)

一个 PV 的生命周期中,可能会处于4中不同的阶段:

  • Available(可用): 表示可用状态,还未被任何 PVC 绑定
  • Bound(已绑定): 表示 PV 已经被 PVC 绑定
  • Released(已释放): 表示 PVC 被删除,但是资源还未被集群重新声明
  • Failed(失败): 表示该 PV 的自动回收失败

准备NFS环境

shell
# 批量创建目录pv1、pv2、pv3
mkdir /root/data/{pv1,pv2,pv3} -pv

# 暴露服务
vim /etc/exports
#-----------------------
/root/data/pv1 192.168.56.0/24(rw,no_root_squash)
/root/data/pv2 192.168.56.0/24(rw,no_root_squash)
/root/data/pv3 192.168.56.0/24(rw,no_root_squash)
#-----------------------

# 重启生效
systemctl restart nfs

资源配置文件

shell
vim volume_pv.yml
#----------------
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
spec:
  # 设置pv磁盘空间大小
  capacity:
    storage: 1Gi
  # 访问模式:ReadWriteOnce、ReadOnlyMany、ReadWriteMany
  # ReadWriteOnce:可读可写,但只支持被单个节点挂载
  # ReadOnlyMany:可以以只读的方式被多个节点挂载
  # ReadWriteMany:可以以读写的方式被多个节点共享
  accessModes:
    - ReadWriteMany
  # 回收策略: Retain、Recycle、Delete
  # Retain:不清理, 保留 Volume
  # Recycle:删除数据
  # Delete:删除存储资源
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /root/data/pv1
    server: 192.168.56.109
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /root/data/pv2
    server: 192.168.56.109
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv3
spec:
  capacity:
    storage: 3Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /root/data/pv3
    server: 192.168.56.109
#----------------

kubectl apply -f volume_pv.yml

# 查看describe
kubectl describe pv pv1 pv2 pv3
#----------------
Name:            pv1
Labels:          <none>
Annotations:     <none>
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:
Status:          Available
Claim:
Reclaim Policy:  Retain
Access Modes:    RWX
VolumeMode:      Filesystem
Capacity:        1Gi
Node Affinity:   <none>
Message:
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    192.168.56.109
    Path:      /root/data/pv1
    ReadOnly:  false
Events:        <none>


Name:            pv2
Labels:          <none>
Annotations:     <none>
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:
Status:          Available
Claim:
Reclaim Policy:  Retain
Access Modes:    RWX
VolumeMode:      Filesystem
Capacity:        2Gi
Node Affinity:   <none>
Message:
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    192.168.56.109
    Path:      /root/data/pv2
    ReadOnly:  false
Events:        <none>


Name:            pv3
Labels:          <none>
Annotations:     <none>
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:
Status:          Available
Claim:
Reclaim Policy:  Retain
Access Modes:    RWX
VolumeMode:      Filesystem
Capacity:        3Gi
Node Affinity:   <none>
Message:
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    192.168.56.109
    Path:      /root/data/pv3
    ReadOnly:  false
Events:        <none>
#----------------

kubectl get pv -o wide
#----------------
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE     VOLUMEMODE
pv1    1Gi        RWX            Retain           Available                                   6m13s   Filesystem
pv2    2Gi        RWX            Retain           Available                                   6m13s   Filesystem
pv3    3Gi        RWX            Retain           Available                                   6m13s   Filesystem
#----------------

2. PVC

PVC是用户存储的请求。它与pod相似,pod消耗Node节点资源,PVC消耗PV资源。pod可以请求特定级别的资源(CPU和内存)。PVC可以请求特定的大小和访问模式。例如:可以以 读/写一次只读多次 模式挂载。

PVC 的关键配置参数说明:

  • 访问模式(accessModes) 用于描述用户应用对存储资源的访问权限
  • 选择条件(selector)

​ 通过Label Selector的设置,可使PVC对于系统中己存在的PV进行筛选

  • 存储类别(storageClassName)

​ PVC在定义时可以设定需要的后端存储的类别,只有设置了该class的pv才能被系统选出

  • 资源请求(Resources )

​ 描述对存储资源的请求

创建PVC

shell
vim volume_pvc.yml
#-----------------
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc2
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 2Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc3
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 3Gi
#-----------------

kubectl apply -f volume_pvc.yml

# 查看pvc
kubectl get pvc -o wide
#-----------------
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE   VOLUMEMODE
pvc1   Bound    pv1      1Gi        RWX                           22s   Filesystem
pvc2   Bound    pv2      2Gi        RWX                           22s   Filesystem
pvc3   Bound    pv3      3Gi        RWX                           22s   Filesystem
#-----------------

# 查看pv
kubectl get pv -o wide
#-----------------

NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM          STORAGECLASS   REASON   AGE   VOLUMEMODE
pv1    1Gi        RWX            Retain           Bound    default/pvc1                           15m   Filesystem
pv2    2Gi        RWX            Retain           Bound    default/pvc2                           15m   Filesystem
pv3    3Gi        RWX            Retain           Bound    default/pvc3                           15m   Filesystem
#-----------------

# 至此,我们实现了pv 和 pvc 的绑定。

pod使用PVC

shell
vim volume_pod_pvc.yml
#-----------------
apiVersion: v1
kind: Pod
metadata:
  name: pod-pvc1
  labels:
    app: pod-pvc1
spec:
  containers:
    - name: nginx-cont1
      image: nginx
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
      volumeMounts:
        - name: logs-volume
          mountPath: /var/log/nginx
  restartPolicy: Always
  volumes:
    - name: logs-volume
      persistentVolumeClaim:
      	# 选择要使用的pvc名称
        claimName: pvc1
        readOnly: false
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-pvc2
  labels:
    app: pod-pvc2
spec:
  containers:
    - name: nginx-cont2
      image: nginx
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
      volumeMounts:
        - name: logs-volume
          mountPath: /var/log/nginx
  restartPolicy: Always
  volumes:
    - name: logs-volume
      persistentVolumeClaim:
        claimName: pvc2
        readOnly: false
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-pvc3
  labels:
    app: pod-pvc3
spec:
  containers:
    - name: nginx-cont3
      image: nginx
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
      volumeMounts:
        - name: logs-volume
          mountPath: /var/log/nginx
  restartPolicy: Always
  volumes:
    - name: logs-volume
      persistentVolumeClaim:
        claimName: pvc3
        readOnly: false
#-----------------

kubectl apply -f  volume_pod_pvc.yml
kubectl get pods -o wide |grep pod-pvc
#-----------------
pod-pvc1                                    1/1     Running   0               5m8s    10.244.211.203   k8s-slave03    <none>           <none>
pod-pvc2                                    1/1     Running   0               5m8s    10.244.211.197   k8s-slave03    <none>           <none>
pod-pvc3                                    1/1     Running   0               5m8s    10.244.211.202   k8s-slave03    <none>           <none>
#-----------------

# 我们到NFS`192.168.56.109`服务器上查看
# pv1
cd /root/data/pv1
#-----------------
总用量 4
-rw-r--r--. 1 root root   0 7月  15 11:19 access.log
-rw-r--r--. 1 root root 510 7月  15 11:19 error.log
#-----------------

# pv2
cd /root/data/pv2
#-----------------
总用量 4
-rw-r--r--. 1 root root   0 7月  15 11:19 access.log
-rw-r--r--. 1 root root 510 7月  15 11:19 error.log
#-----------------

# pv3
cd /root/data/pv3
#-----------------
总用量 4
-rw-r--r--. 1 root root   0 7月  15 11:19 access.log
-rw-r--r--. 1 root root 510 7月  15 11:19 error.log
#-----------------

3. PV 、 PVC 生命周期

PVC和PV是一一对应的,PV和PVC之间的相互作用遵循以下生命周期:

资源供应:管理员手动创建底层存储和PV

资源绑定:用户创建PVC,kubernetes负责根据PVC的声明去寻找PV,并绑定

在用户定义好PVC之后,系统将根据PVC对存储资源的请求在已存在的PV中选择一个满足条件的

  • 一旦找到,就将该PV与用户定义的PVC进行绑定,用户的应用就可以使用这个PVC了

  • 如果找不到,PVC则会无限期处于Pending状态,直到等到系统管理员创建了一个符合其要求的PV

PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再与其他PVC进行绑定了

资源使用:用户可在pod中像volume一样使用pvc

Pod使用Volume的定义,将PVC挂载到容器内的某个路径进行使用。

资源释放:用户删除pvc来释放pv

当存储资源使用完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为“已释放”,但还不能立刻与其他PVC进行绑定。通过之前PVC写入的数据可能还被留在存储设备上,只有在清除之后该PV才能再次使用。

资源回收:kubernetes根据pv设置的回收策略进行资源的回收

对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收,才能供新的PVC绑定和使用

pvc-running


四、配置存储

1. ConfigMap

ConfigMap顾名思义,是用于保存配置数据的键值对(kv)。通常我们用ConfigMap来实现配置文件与pod 的解耦。

创建ConfigMap

shell
vim configmap.yaml
#-------------------
apiVersion: v1
kind: ConfigMap
metadata:
  name: mariadbconfigmap
data:
  mysql-driver: com.mysql.jdbc.Driver
  mysql-url: jdbc:mysql://localhost:3306/test
  mysql-user: root
  mysql-password: admin
#-------------------

kubectl apply -f configmap.yml

# 查看
kubectl describe cm mariadbconfigmap
#-------------------
Name:         mariadbconfigmap
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
mysql-driver:
----
com.mysql.jdbc.Driver
mysql-password:
----
admin
mysql-url:
----
jdbc:mysql://localhost:3306/test
mysql-user:
----
root

BinaryData
====

Events:  <none>
#-------------------

# 我们也可以以 配置文件的方式导入配置创建ConfigMap
cat nginx_8000.conf
#--------------------
server {

  listen  8000;
  server_name _;

  location / {


    root /usr/share/nginx/html;
    index index.html index.htm;
}
#--------------------

# 以配置文件方式创建
kubectl crate configmap nginxconf --from-file=nginx_8000.conf

Pod以挂载文件的形式使用ConfigMap

我们可以使用这种方式把配置文件通过挂载的形式配置到pod中

shell
vim configmap_pod.yml
#--------------------
apiVersion: v1
kind: Pod
metadata:
  name: configmap-test
  labels:
    app: configmap-test
spec:
  containers:
    - name: configmap-test
      image: nginx
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - mountPath: /configmap/config
          name: config-volume
  restartPolicy: Always
  volumes:
    - name: config-volume
      # 指定configmap 资源文件的name
      configMap:
        name: mariadbconfigmap
#--------------------

Pod以环境变量的形式使用ConfigMap

shell
apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
    - name: pod1
      image: reg.westos.org/k8s/busyboxplus
      command: ["/bin/sh","-c","env"]
      envFrom:
        - configMapRef:
            name: mariadbconfigmap

2. Secret

Secret于ConfigMap类似,只不过它主要用于存储敏感数据,例如密码、秘钥、证书等。

Secret 解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者 Pod Spec中。Secret 可以以 Volume 或者环境变量的方式使用 。

Secret 有三种类型

  • Service Account :用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod的/run/secrets/kubernetes.io/serviceaccount 目录中。(了解即可,不需要我们操作)
  • Opaque :base64编码格式的Secret,用来存储密码、密钥等。---- 最常用
  • kubernetes.io/dockerconfigjson :用来存储私有 docker registry 的认证信息

Service Account

我们演示下Service Account

shell
# 我们查看namespace = kube-system 的pod
kubectl get pod -n kube-system

# 选中一个pod,进入sh
kubectl exec -it kube-proxy-f4z2h -n kube-system sh
cd /run/secrets/kubernetes.io/serviceaccount
ls
#---------------------------
ca.crt  namespace  token
#---------------------------
cat ca.crt
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl
cm5ldGVzMB4XDTIyMDYyODA3MTkwOFoXDTMyMDYyNTA3MTkwOFowFTETMBEGA1UE
AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL/k
tltT9cbuFx7B00h7X5KBiejLdWp8hJwgcfqGtacHQ/3yHSKa7+/hwx8dc2SIqoQa
ewEIZfsiNbVAz5FZ5wqaxa2vh2TlnqfrmHMKlUkEn08WD0IkQtpmQvp6HcE54ii1
RfEeChRQ+1SLbUu+EYUcE/qQQw9WouLmf4CTiJKLhBmAZadd5J8xGbGp3FKSfjqd
/bOU90Ah+kj6GOOaw9nWwQX5h9UhdLzHqXPhsRtMqzlzZKY1+S0+RtTYPzD5KozP
SNsVSeJ4EeLUYjS7qJO3mxcpUpQi80Wf+jjdBZkPWTB4DjV2aK3YqYjF5IS5DDWQ
ngD1IjcnRhODDSOaDZMCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB
/wQFMAMBAf8wHQYDVR0OBBYEFHb+eWAz/e9QNbfsaKeEP8UMoaFBMBUGA1UdEQQO
MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBAFeNIoBlbZSQCmowq+GU
TdWm6Y9sbeobZZMpPS9wrx2NDln4v/C8S6ZOG9bGETlrWEfkS7JQ5qn5dCH62hBL
1bJHhRMV17YPNB2Fo4uP2AJRpJoNEChUb4F1GZzq0A3lRiIeSpYt3WqmJ7cPE4uc
5NSjr/fL6Z9wddCF0WbB9bEszV3snFXe2SDCBm6iSaTjAD7HbMAJwxQr+WIi/4O4
saZVd+x0c5KLm/OqJ7M4EBU1cUqNy8kQkLd8sUKnI30DiCWVYbgBdpib1f1+/IZr
iQBAdLmcP2AGk59x5tSoqiaqhZUkMz6+qnio3Fwe957NJdoocqQWeGfG+RL7zbsg
LdU=
-----END CERTIFICATE-----

Opaque Secret

Opaque 类型的数据是一个 map 类型,要求 valuebase64 编码格式。

创建Secret

shell
# 对admin字符串进行base64加密:获得admin的加密字符串"YWRtaW4="
echo -n "admin" | base64
# base64解密:对加密后的字符串进行解密
echo -n "YWRtaW4=" | base64 -d

vim secret.yml
#---------------
apiVersion: v1
kind: Secret
metadata:
  name: secret-test
type: Opaque
data:
  password: YWRtaW4=
  username: cm9vdA==
#---------------

kubectl apply -f secret.yml

# 查看详情
kubectl describe secret secret-test
#----------------
Name:         secret-test
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
password:  5 bytes
username:  4 bytes
#----------------

Pod使用Secret

shell
vim secret_pod.yml
#-------------------
apiVersion: v1
kind: Pod
metadata:
  name: secret-pod-test
  labels:
    app: secret-pod-test
spec:
  containers:
    - name: secret-pod-test
      image: nginx
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - mountPath: /secret/config
          name: volume-secret
  restartPolicy: Always
  volumes:
    - name: volume-secret
      secret:
        secretName: secret-test
#-------------------

kubectl apply -f secret.yml

# 进入pod
kubectl exec -it secret-pod-test sh
cd /secret/config
ls
#------------------
password  username
#------------------

more password
#------------------
# admin
#------------------
more username
#------------------
# root
#------------------

附录

参考资料

Kubernetes数据卷存储