K8S数据存储(基本存储之 EmptyDir、HostPath、NFS )

文章目录

  • 一、数据存储概述
  • 二、基本存储
    • (一)EmptyDir
    • (二)HostPath
    • (三)NFS

一、数据存储概述

背景

容器中的文件在磁盘上是临时存放的,这给容器中运行的特殊应用程序带来一些问题。 首先,当容器崩溃时,kubelet 将重新启动容器,容器中的文件将会丢失——因为容器会以干净的状态重建。 其次,当在一个 Pod 中同时运行多个容器时,常常需要在这些容器之间共享文件。 Kubernetes 抽象出 Volume 对象来解决这两个问题。

所以,为了持久化保存容器的数据和容器之间数据共享,kubernetes引入了Volume的概念。

概念

Volume是k8s抽象出来的对象,用于解决Pod中的容器运行时,文件存放的问题以及多容器数据共享的问题。Kubernetes 卷具有明确的生命周期——与包裹它的 Pod 相同。卷比 Pod 中运行的任何容器的存活期都长,在容器重新启动时数据也会得到保留。

Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下,kubernetes通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储

更重要的是,Kubernetes 可以支持许多类型的卷,Pod 也能同时使用任意数量的卷。卷的核心是包含一些数据的目录,Pod 中的容器可以访问该目录。 特定的卷类型可以决定这个目录如何形成的,并能决定它支持何种介质,以及目录中存放什么内容。

使用卷时, Pod 声明中需要提供卷的类型(.spec.volumes 字段)和卷挂载的位置 (.spec.containers.volumeMounts 字段)。

Volume的生命容器不与Pod中单个容器的生命周期相关。

kubelet创建Pod时,会先创建一个基础容器pause,Pod里面所有的容器共享一个网络名称空间和文件系统。挂载卷的工作就是由基础容器pause来完成的。

卷的类型

Kubernetes 支持下列类型的卷:

  1. awsElasticBlockStore、azureDisk、azureFile、cephfs、cinder、configMap、csi
  2. downwardAPI、emptyDir、fc (fibre channel)、flexVolume、flocker、
  3. gcePersistentDisk、gitRepo (deprecated)、glusterfs、hostPath、iscsi
  4. local、nfs、persistentVolumeClaim、projected、secret、storageos、vsphereVolume

kubernetes的Volume支持多种类型,比较常见的有下面几个:

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

二、基本存储

(一)EmptyDir

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

EmptyDir是在Pod分配到Node时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为kubernetes会自动分配一个目录, 当Pod销毁时,EmptyDir中的数据也会被永久删除(EmptyDir与Pod生命周期一致,不适合做数据持久化存储)。

EmptyDir用途如下:

  • 临时存储空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留
  • 多容器共享目录,同一个Pod中的不同容器之间共享数据

EmptyDir的使用

通过EmptyDir实现容器之间文件共享

在一个Pod中准备两个容器nginx和busybox,然后声明一个Volume分别挂载到两个容器的目录中,然后nginx容器负责向Volume中写日志,busybox中通过命令将日志内容读到控制台。
K8S数据存储(基本存储之 EmptyDir、HostPath、NFS )_第1张图片

# 示例一:挂载存在的目录
# 将nginx容器的/var/log/nginx目录与busybox容器的/logs目录挂载到同一volume实现共享。通过访问nginx页面,nginx容器会将访问记录写入到/var/log/nginx/access.log文件中,那么可以通过busybox容器的/logs/access.log文件读出访问记录
[root@k8s-master ~]# vim volume-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-emptydir
  namespace: test
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - containerPort: 80
    volumeMounts: # 将下面声明的logs-volume挂载到nginx容器中的/var/log/nginx目录上(只要有访问请求,那么便会有内容写入到该目录下的access.log文件中)
    - name: logs-volume
      mountPath: /var/log/nginx
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c","while true; do if test -f /logs/access.log; then tail -f /logs/access.log; else echo no such file named access.log; sleep 3;fi; done"]	 # 初始命令,动态读取 access.log 文件中内容
    volumeMounts: # 将下面声明的logs-volume挂载到busybox容器中的/logs目录上(所以相当于nginx容器中的/var/log/nginx目录与busybox的/logs目录属于共享目录,下面的文件将会进行同步,当有请求访问nginx时,便会向nginx容器中的/var/log/nginx目录下的accsee.log日志文件追加访问记录,同时在busybox容器中也会看到该文件的内容,因为这两个容器的这两个目录都挂载在同一volume上,所以这两个目录下的文件及内容会实时保持一致)
    - name: logs-volume
      mountPath: /logs
  volumes: #声明volume,name为logs-volume, 类型为emptyDir
  - name: logs-volume
    emptyDir: {}

# 创建
[root@k8s-master ~]# kubectl create -f volume-emptydir.yaml
pod/volume-emptydir created

# 查看
[root@k8s-master ~]# kubectl get pod -n test -o wide
NAME              READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES
volume-emptydir   2/2     Running   0          7s    10.244.2.195   k8s-node02   <none>           <none>

# 因为nginx容器的/var/log/nginx/与busybox容器中的/logs目录挂载在同一volume,所以这两个目录下的文件会是一致的并实时同步
[root@k8s-master ~]# kubectl exec -it  volume-emptydir -c nginx -n test -- ls /var/log/nginx/
access.log  error.log
[root@k8s-master ~]# kubectl exec -it  volume-emptydir -c busybox -n test -- ls /logs
access.log  error.log

# 此时对nginx进行访问,相当与向nginx容器中的/var/log/nginx/access.log文件中写入访问记录。
[root@k8s-node01 ~]# while true ;do curl 10.244.2.195 ;sleep 2 ;done

# 因为向每访问一次便会在/var/log/nginx/access.log文件写入记录数据,而且会同时同步更新busybox容器/logs目录下的access.log文件
# 资源清单文件在配置时,对于busybox容器会实时输出/logs/access.log文件中的新访问记录到标准输出
# 所以,流程为:在访问nginx时,会将访问记录记录到nginx容器的/var/log/nginx/access.log文件中,同时也会将访问记录数据实时更新到busybox容器的/logs/access.log文件中,于此同时busybox容器又会将/logs/access.log中的访问记录数据信息输出到控制台
# 那么,在访问nginx页面时,只要在busybox容器的控制台能够看到访问记录的标准输出就说明volume挂载成功
# 查看busybox容器的标准输出
[root@k8s-master ~]# kubectl logs volume-emptydir -c busybox -n test
10.244.1.0 - - [04/Jun/2021:02:30:10 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.1.0 - - [04/Jun/2021:02:30:12 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.1.0 - - [04/Jun/2021:02:30:14 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.1.0 - - [04/Jun/2021:02:30:16 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.1.0 - - [04/Jun/2021:02:30:18 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.1.0 - - [04/Jun/2021:02:30:20 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.1.0 - - [04/Jun/2021:02:53:28 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.1.0 - - [04/Jun/2021:02:53:30 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.1.0 - - [04/Jun/2021:02:53:32 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.1.0 - - [04/Jun/2021:02:53:34 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.1.0 - - [04/Jun/2021:02:53:36 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.1.0 - - [04/Jun/2021:02:53:38 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.1.0 - - [04/Jun/2021:02:53:40 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
#  emptydir格式的volume挂载成功

# 示例二:挂载不存在的目录
[root@k8s-master ~]# vim volume-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-emptydir
  namespace: test
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - containerPort: 80
    volumeMounts: # 将下面声明的logs-volume挂载到nginx容器中的/test1目录上(无则创建)
    - name: test-volume
      mountPath: /test1
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c"," while true; do echo test;sleep 2; done"] # 保证容器正常运行
    volumeMounts: # 将下面声明的logs-volume挂载到busybox容器中的/test2目录上(无则创建)
    - name: test-volume
      mountPath: /test2
  volumes: #声明volume,name为logs-volume, 类型为emptyDir
  - name: test-volume
    emptyDir: {}

# 创建
[root@k8s-master ~]# kubectl create -f volume-emptydir.yaml
pod/volume-emptydir created
[root@k8s-master ~]# kubectl get pod -n test
NAME              READY   STATUS    RESTARTS   AGE
volume-emptydir   2/2     Running   0          8s

# 在 nginx 容器的 /test1 目录下创建文件
[root@k8s-master ~]# kubectl exec -it volume-emptydir -c nginx -n test -- sh
# ls
bin  boot  dev	etc  home  lib	lib64  media  mnt  opt	proc  root  run  sbin  srv  sys  test1	tmp  usr  var
# cd test1
# echo 123 > test.txt   
# exit

# 在busybox容器中查看上面创建的文件
[root@k8s-master ~]# kubectl exec -it volume-emptydir -c busybox -n test -- sh
/ # ls
bin    dev    etc    home   proc   root   sys    test2  tmp    usr    var
/ # cd test2
/test2 # ls
test.txt
/test2 # cat test.txt 
123
/test2 # exit

# 删除
[root@k8s-master ~]# kubectl delete -f volume-emptydir.yaml
pod "volume-emptydir" deleted

(二)HostPath

EmptyDir中数据不会被持久化,它会随着Pod的结束而销毁,如果想简单的将数据持久化到主机中,可以选择HostPath。

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

K8S数据存储(基本存储之 EmptyDir、HostPath、NFS )_第2张图片

[root@k8s-master ~]# vim volume-hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-hostpath
  namespace: test
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - containerPort: 80
    volumeMounts:
    - name: logs-volume
      mountPath: /var/log/nginx
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c","while true;do if test -f /logs/access.log;then tail -f /logs/access.log;else echo no such file named access.log;sleep 3;fi;done"]
    volumeMounts:
    - name: logs-volume
      mountPath: /logs
  volumes:
  - name: logs-volume
    hostPath:
      path: /mnt/nginx-logs
      type: DirectoryOrCreate # 主机上的该目录存在就使用,不存在就先创建后使用;如果为Directory,则path指定的目录必须在主机上存在,不存在报错

# 关于type的值的一点说明:
# DirectoryOrCreate  目录存在就使用,不存在就先创建后使用
# Directory  目录必须存在
# FileOrCreate   文件存在就使用,不存在就先创建后使用
# File   文件必须存在
# Socket unix 套接字必须存在
# CharDevice  字符设备必须存在
# BlockDevice 块设备必须存在

# 创建
[root@k8s-master ~]# kubectl create -f volume-hostpath.yaml
pod/volume-hostpath created

# 查看
[root@k8s-master ~]# kubectl get pod -n test -o wide
NAME              READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES
volume-hostpath   2/2     Running   0          4s    10.244.2.213   k8s-node02   <none>           <none>

# 查看目录下的文件
[root@k8s-master ~]# kubectl exec -it volume-hostpath -c nginx -n test -- ls -l /var/log/nginx/
total 0
-rw-r--r-- 1 root root 0 Jun  4 07:09 access.log
-rw-r--r-- 1 root root 0 Jun  4 07:09 error.log
[root@k8s-master ~]# kubectl exec -it volume-hostpath -c busybox -n test -- ls -l /logs
total 0
-rw-r--r--    1 root     root             0 Jun  4 07:09 access.log
-rw-r--r--    1 root     root             0 Jun  4 07:09 error.log
# 主机上的/mnt/nginx-logs,是存在于pod被调度运行后的节点上(k8s-node02)
# 说明:挂载在主机上的持久化文件或目录存在于该pod被调度运行的主机节点上
[root@k8s-node02 mnt]# ll
total 0
drwxr-xr-x 2 root root 41 Jun  4 15:09 nginx-logs
[root@k8s-node02 mnt]# cd nginx-logs/
[root@k8s-node02 nginx-logs]# ls
access.log  error.log

# 访问nginx
[root@k8s-master ~]# for i in {1..5}; do curl 10.244.2.213 ;done

# 查看文件中内容
# nginx容器中
[root@k8s-master ~]# kubectl exec -it volume-hostpath -c nginx -n test -- cat /var/log/nginx/access.log
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
# busybox容器中
[root@k8s-master ~]# kubectl exec -it volume-hostpath -c busybox -n test -- cat /logs/access.log
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
# k8s-node02节点的nginx-logs目录下
[root@k8s-node02 nginx-logs]# ls
access.log  error.log
[root@k8s-node02 nginx-logs]# cat access.log 
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"

# 删除pod
[root@k8s-master ~]# kubectl delete -f volume-hostpath.yaml 
pod "volume-hostpath" deleted
[root@k8s-master ~]# kubectl get pod -n test -o wide
No resources found in test namespace.

# 查看节点上的目录和文件内容
[root@k8s-node02 nginx-logs]# ls
access.log  error.log
[root@k8s-node02 nginx-logs]# cat access.log 
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:07:15:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
# 即使pod被销毁,但是挂载到节点上的目录依旧存在,且文件及内容也依然存在。
# hostpath类型挂载卷可以做到数据文件的持久化存储

(三)NFS

HostPath可以解决数据持久化的问题,但是一旦Node节点故障了,Pod如果转移到了别的节点,又会出现问题了,此时需要准备单独的网络存储系统,比较常用的用NFS、CIFS。

NFS是一个网络文件存储系统,可以搭建一台NFS服务器, 然后将Pod中的存储直接连接到NFS系统上(生产环境中,一般为 nfs服务HA集群),这样的话,无论Pod在节点上怎么转移,只要Node跟NFS的对接没问题,数据就可以成功访问。
K8S数据存储(基本存储之 EmptyDir、HostPath、NFS )_第3张图片
实践

  1. 部署nfs服务

使用一台新节点充当nfs服务器(生产环境中一般为集群化)

# nfs节点IP
[root@nfs ~]# ifconfig ens32 | awk 'NR==2 {print $2}'
192.168.126.12

# 关闭防火墙和selinux
[root@nfs ~]# systemctl stop firewalld
[root@nfs ~]# setenforce 0


# 安装nfs服务
[root@nfs ~]# yum install nfs-utils -y

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

# 将共享目录以读写权限暴露给192.168.126.0/24网段中的所有主机
[root@nfs ~]# vim /etc/exports
/root/data/nfs/ 192.168.126.0/24(rw,no_root_squash)

# 启动nfs服务
[root@nfs ~]# systemctl start nfs

# 开机自启
[root@nfs ~]# systemctl enable nfs
  1. 接下来,在k8s集群的每个节点上都安装nfs,目的是为了node节点可以驱动nfs设备
# k8s-master节点
[root@k8s-master ~]# ifconfig ens32 | awk 'NR==2 {print $2}'
192.168.126.100
[root@k8s-master ~]# yum install nfs-utils -y
# 测试nfs连接性
[root@k8s-master ~]# showmount -e 192.168.126.12
Export list for 192.168.126.12:
/root/data/nfs 192.168.126.0/24	
# k8s-node01节点
[root@k8s-node01 ~]# ifconfig ens32 | awk 'NR==2 {print $2}'
192.168.126.101
[root@k8s-node01 ~]# yum install nfs-utils -y
[root@k8s-node01 ~]# showmount -e 192.168.126.12
Export list for 192.168.126.12:
/data 192.168.126.0/24
# k8s-node02节点
[root@k8s-node02 ~]# ifconfig ens32 | awk 'NR==2 {print $2}'
192.168.126.102
[root@k8s-node02 ~]# yum install nfs-utils -y
[root@k8s-node02 ~]# showmount -e 192.168.126.12
Export list for 192.168.126.12:
/root/data/nfs 192.168.126.0/24
  1. 编写pod的资源清单文件
[root@k8s-master ~]# vim volume-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-nfs
  namespace: test
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - containerPort: 80
    volumeMounts:
    - name: logs-volume
      mountPath: /var/log/nginx
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c","while true;do if test -f /logs/access.log;then tail -f /logs/access.log;else echo no such file named access.log;sleep 3;fi;done"]
    volumeMounts:
    - name: logs-volume
      mountPath: /logs
  volumes:
  - name: logs-volume
    nfs:
      server: 192.168.126.12	# nfs服务器地址
      path: /root/data/nfs 		# 共享文件目录,最后持久化的数据文件都会存放到nfs服务器的该目录下
  1. 运行下pod,观察结果
# 创建
[root@k8s-master ~]# kubectl create -f volume-nfs.yaml 
pod/volume-nfs created

# 查看
[root@k8s-master ~]# kubectl get pod volume-nfs -n test -o wide
NAME         READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES
volume-nfs   2/2     Running   0          11s   10.244.2.214   k8s-node02   <none>  

# 查看nfs服务器目录/root/data/nfs
[root@nfs ~]# cd /root/data/nfs/
[root@nfs nfs]# ls
access.log  error.log

# 访问nginx
[root@k8s-master ~]# for i in {1..3}; do curl 10.244.2.214 ;done

# 查看数据是否实时写入
[root@nfs nfs]# cat access.log 
10.244.0.0 - - [04/Jun/2021:08:18:08 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:08:18:08 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.244.0.0 - - [04/Jun/2021:08:18:08 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"

你可能感兴趣的:(Kubernetes)