数据存储

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

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

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

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

650

1 基本存储

1.1 EmptyDir

1.2 HostPath

1.3 NFS

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

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

img

1.3.1 新版步骤

所有机器安装 nfs

# 所有机器安装
yum install -y nfs-utils

主节点配置 nfs

echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports # nfs主节点
mkdir -p /nfs/data
systemctl enable rpcbind --now
systemctl enable nfs-server --now
exportfs -r # 配置生效
exportfs # 查看 nfs 信息

从节点配置 nfs

# 查看可以挂载的远程目录(IP 更改为 master IP)
showmount -e 172.31.0.4
mkdir -p /nfs/data
# 挂载 nfs 服务器上的共享目录到本机路径
mount -t nfs 172.31.0.4:/nfs/data /nfs/data
# 写入一个测试文件,查看共享情况
echo "hello nfs server" > /nfs/data/test.txt

通过原生方式进行容器的数据挂载

步骤一:创建 mount.yaml 文件,并进行修改:

  • 修改 IP 地址,更改为 NFS 主节点 IP 地址
  • 对应节点上创建对应的目录 /nfs/data/nginx-pv
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-pv-demo
  name: nginx-pv-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-pv-demo
  template:
    metadata:
      labels:
        app: nginx-pv-demo
    spec:
      containers:
        - image: nginx
          name: nginx
          volumeMounts:
            - name: html
              mountPath: /usr/share/nginx/html
      volumes:
        - name: html
          nfs:
            server: 172.31.0.4 # 修改 IP 地址
            path: /nfs/data/nginx-pv

步骤二:kubectl apply -f amount.yaml

步骤三:验证

# 进入pod容器内部
kubectl exec -it nginx-pv-demo-5b74676b65-cb9mh -- /bin/bash
# 查看nginx存放页面的信息,此时应该没有数据
ls /usr/share/nginx/html/
# 新建连接,写一个测试文件
echo 111 > /nfs/data/nginx-pv/index.html
# 在 另一个节点上 或者 容器内部 查看是否有对应的数据
ls /usr/share/nginx/html/

1.3.2 旧版步骤

1)首先要准备 nfs 的服务器,这里为了简单,直接是 master 节点做 nfs 服务器

# 在 node 上安装 nfs 服务
[ root@nfs ~]# yum install nfs-utils -y
 
# 准备一个共享目录
[ root@nfs ~]# mkdir /root/data/nfs -pv
 
# 将共享目录以读写权限暴露给 192.168.5.0/24 网段中的所有主机
[ root@nfs ~]# vim /etc/exports
[ root@nfs ~]# more /etc/exports
/root/data/nfs     192.168.5.0/24(rw,no_root_squash)
 
# 启动 nfs 服务
[ root@nfs ~]# systemctl restart nfs

2)接下来,要在的每个 node 节点上都安装下 nfs,这样的目的是为了 node 节点可以驱动 nfs 设备

# 在 node 上安装 nfs 服务,注意不需要启动
[ root@k8s-master01 ~]# yum install nfs-utils -y

3)接下来,就可以编写 pod 的配置文件了,创建 volume-nfs.yaml

apiVersion: v1
kind: Pod
metadata:
  name: volume-nfs
  namespace: dev
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", "tail -f /logs/access.log"]
      volumeMounts:
        - name: logs-volume
          mountPath: /logs
  volumes:
    - name: logs-volume
      nfs:
        server: 192.168.5.6 #nfs服务器地址
        path: /root/data/nfs #共享文件路径

4)最后,运行下 pod,观察结果

# 创建 pod
[ root@k8s-master01 ~]# kubectl create -f volume-nfs.yaml
pod/volume-nfs created
 
# 查看 pod
[ root@k8s-master01 ~]# kubectl get pods volume-nfs -n dev
NAME                  READY   STATUS    RESTARTS   AGE
volume-nfs        2/2     Running   0          2m9s
 
# 查看 nfs 服务器上的共享目录,发现已经有文件了
[ root@k8s-master01 ~]# ls /root/data/
access.log  error.log

1.3.3 仍然存在的弊端

  1. 文件夹是必须自己创建好。
  2. pod / dep 删除后,节点上的文件夹是不会随之删除的。随着后续创建删除的 pod /dep 增加,废弃的文件夹会越来越多。
  3. 对能使用的空间没有限制,可能会导致占用空间过多,影响其他使用。

2 高级存储

前面已经学习了使用 NFS 提供存储,此时就要求用户会搭建 NFS 系统,并且会在 yaml 配置 nfs。由于 kubernetes 支持的存储系统有很多,要求客户全都掌握,显然不现实。为了能够屏蔽底层存储实现的细节,方便用户使用, kubernetes 引入 PV 和 PVC 两种资源对象。

  • PV(Persistent Volume)是持久化卷的意思,是对底层的共享存储的一种抽象。一般情况下 PV 由 kubernetes 管理员进行创建和配置,它与底层具体的共享存储技术有关,并通过插件完成与共享存储的对接。

  • PVC(Persistent Volume Claim)是持久卷声明的意思,是用户对于存储需求的一种声明。换句话说,PVC 其实就是用户向 kubernetes 系统提交的 PV 申请书。

Kubernetes @8 数据存储-2.png

2.1 PV

预先创建文件夹

# nfs 主节点
mkdir -p /nfs/data/01
mkdir -p /nfs/data/02
mkdir -p /nfs/data/03

创建 PV

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv01-10m  # 名字只能小写
spec:
  capacity:
    storage: 10M
  accessModes:
    - ReadWriteMany
  storageClassName: nfs  # 这个是随便取的名字
  nfs:
    path: /nfs/data/01
    server: 172.31.0.4  # 改一下 IP 地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv02-1gi  # 名字只能小写
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  storageClassName: nfs  # 这个是随便取的名字
  nfs:
    path: /nfs/data/02
    server: 172.31.0.4  # 改一下 IP 地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv03-3gi  # 名字只能小写
spec:
  capacity:
    storage: 3Gi
  accessModes:
    - ReadWriteMany
	  storageClassName: nfs  # 这个是随便取的名字
  nfs:
    path: /nfs/data/03
    server: 172.31.0.4  # 改一下 IP 地址

查看 PV

Kubernetes @8 数据存储-3.png

2.2 PVC

创建 PVC

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nginx-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 200Mi
  storageClassName: nfs # 和上面的对应

部署和查看 PVC

# 部署
kubectl apply -f pvc.yaml
# 查看 PVC(选择了 pv02-1gi 这个 PV)
kubectl get pvc
# 查看 PV
kubectl get pv

625

625

删除后重启部署与查看 PVC

# 删除
kubectl delete -f pvc.yaml
# 重新部署
kubectl apply -f pvc.yaml
# 查看(选择了 pv03-3gi 这个 PV)
kubectl get pvc

625

625

总结:会选用大于 200M 的 pv,正在使用的 pv 会被 Bound,之前使用的,后面删除了,就会释放。

创建 Pod 绑定 PVC

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-deploy-pvc
  name: nginx-deploy-pvc
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-deploy-pvc
  template:
    metadata:
      labels:
        app: nginx-deploy-pvc
    spec:
      containers:
        - image: nginx
          name: nginx
          volumeMounts:
            - name: html
              mountPath: /usr/share/nginx/html
      volumes:
        - name: html
          persistentVolumeClaim:
            claimName: nginx-pvc # 和 PVC 名称对应
# 部署
kubectl apply -f pod-pvc.yaml
# 查看 pvc 和 pv 绑定关系
kubectl get pvc,pv

675

PV 还有动态池,创建正正好好大小的。

3 配置存储

3.1 ConfigMap

抽取应用配置,并且可以自动更新。

3.1.1 基于配置文件创建 ConfigMap

基于配置文件创建 ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: configmap
  namespace: dev
data: # data 下面,key 是配置文件名,value 是配置文件内容
  info: |
    username:admin
    password:123456
# 创建 configmap
kubectl create -f configmap.yaml

创建 Pod,将 ConfigMap 挂载进去

apiVersion: v1
kind: Pod
metadata:
  name: pod-configmap
  namespace: dev
spec:
  containers:
    - name: nginx
      image: nginx:1.17.1
      volumeMounts: # 将 configmap 挂载到目录
        - name: config
          mountPath: /configmap/config
  volumes: # 引用 configmap
    - name: config
      configMap:
        name: configmap
# 创建 pod
kubectl create -f pod-configmap.yaml
 
# 进入容器
kubectl exec -it pod-configmap -n dev /bin/sh
# 进入容器内挂载目录
cd /configmap/config/
# 查看 configmap
more info

3.1.2 基于现有配置文件创建 ConfigMap(Redis 示例)

基于现有配置文件创建 ConfigMap

# 基于 redis.conf 直接创建 configmap (存放在 etcd 中)
kubectl create cm redis-conf --from-file=redis.conf
kubectl create configmap  redis-conf --from-file=redis.conf
 
# 查看 configmap
kubectl get cm
 
# 查看 redis-conf configmap (内容如下)
kubectl get cm redis-conf -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-conf
  namespace: default
data:
  redis.conf: |
    appendonly yes

创建 Pod,将 ConfigMap 挂载进去

apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
    - name: redis
      image: redis
      command:
        - redis-server
        - "/redis-master/redis.conf" # 指的是redis容器内部的位置
      ports:
        - containerPort: 6379
      volumeMounts:
        - mountPath: /data
          name: data
        - mountPath: /redis-master
          name: config
  volumes:
    - name: data
      emptyDir: {}
    - name: config
      configMap:
        name: redis-conf # 对应上面创建的 configmap 名字
        items:
          - key: redis.conf # configmap 中的 key
            path: redis.conf # 这个 item 放在什么文件中

容器内的 volumeMounts.mountPath 加上挂在卷的 configMap.path 组成 configmap item 真正对应的容器内目录

# 创建 pod
kubectl create -f redis.yaml
# 查看 pod
kubectl get pod

检查默认配置

kubectl exec -it redis -- redis-cli
 
127.0.0.1:6379> CONFIG GET appendonly
127.0.0.1:6379> CONFIG GET requirepass

修改 ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: example-redis-config
data:
  redis-config: |
    maxmemory 2mb
    maxmemory-policy allkeys-lru

检查是否更新

kubectl exec -it redis -- redis-cli

127.0.0.1:6379> CONFIG GET maxmemory
127.0.0.1:6379> CONFIG GET maxmemory-policy

发现配置文件内容变了,但是 Redis 内部的配置没有变。这是因为 Redis 没有热更新的能力。

重启 Pod

# 重启 redis pod
kubectl replace --force -f redis-pod.yaml

3.2 Secret

Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和 SSH 密钥。将这些信息放在 secret 中比放在 Pod 的定义或者 容器镜像 中来说更加安全和灵活。

kubectl create secret docker-registry leifengyang-docker \
--docker-username=leifengyang \
--docker-password=Lfy123456 \
--docker-email=534096094@qq.com
 
##命令格式
kubectl create secret docker-registry regcred \
  --docker-server=<你的镜像仓库服务器> \
  --docker-username=<你的用户名> \
  --docker-password=<你的密码> \
  --docker-email=<你的邮箱地址>

拉取自己的私有镜像,基于上述的 secret 就可以拉取了。

apiVersion: v1
kind: Pod
metadata:
  name: private-nginx
spec:
  containers:
    - name: private-nginx
      image: leifengyang/guignginx:v1.0
  imagePullSecrets: # 使用 secret 登录
    - name: leifengyang-docker

4 拓展链接

3、k8s 核心实战 - 尐海爸爸 - 博客园