k8s挂载数据卷volumeMounts

5,304 阅读7分钟

概述

K8S适配各种存储系统,包括本地存储EmptyDir,HostPath,网络存储NFS,GlusterFS,PV/PVC等 为什么Pod使用这个PVC就可以实现容器的持久存储呢? 容器的Volume就是将一个宿主机上的目录跟一个容器里的目录绑定挂载。只要宿主机上的这个路径的目录是持久的,那么在容器中的路径Volume也就是持久的了。所谓的持久,就是容器被删除,而Volume可以保留。

### 卷
 

卷就相当于一个存储,卷是Pod的一个组成部分,不属于kubernetes对象,所以不能单独创建和删除, Pod中的所有容器都可以使用卷,但是必须先将它挂载到需要访问它的容器中 在每个容器中都可以在自身文件系统的任意位置挂载卷,一个卷可以被多个容器挂载,当pod删除后,卷也将不存在

  • 生命周期
    该pod启动时创建
    该pod被移除时删除
    容器崩溃数据不丢失

常用挂载类型:

(emptyDir、hostPath、pvc+pv、StorageClass)

挂载YMAL写法


volumeMounts:
          - mountPath: /etc/mysql/mariadb.conf.d/ #容器内的挂载目录
            name: zizhen #随便给一个名字,这个名字必须与volumes.name一致
            
          - mountPath: /var/lib/mysql #容器内的挂载目录**
            name: zizhen-mysql
                        

pvc+pv

PVC表达的是用户对存储的请求(persistant volume claim),也是kubernetes中独立存在的API资源。

Pod 会耗用节点资源,Pod 可以请求特定数量的资源(CPU 和内存) pv:向存储申请空间并关联pvc,不受namespace限制 PVC:请求特定的大小和访问模式, PVC 申领会耗用 PV 资源。 PVC和PV之间的绑定是一种一对一的映射。

  • 一个pod可以挂载多个pvc
  • 多个pod可挂载一个pvc
  • pv和pvc一一对应

kubernetes的API资源,就是storageClass。 storageClass可以说是PV的创建模板。 前面提到,PV可以是集群管理员事先供应的,就是所谓的静态供应(static provisioning)。 这个方法很大的一个问题在于,当kubernetes集群规模很大时,需要管理员手工去创建成千上万的PV来对应存储资源,这是很繁琐的,因此,kubernetes中PV的创建一般会使用动态供应(dynamic provisioning)。

在storageClass中会定义: (1)PV的属性,如存储类型和大小; (2)创建PV需要的存储插件,如NFS。

这样,kubernetes就可以根据PVC指定的storageClass,调用指定的存储插件,创建所需的PV。

Pod、PVC、PV、StorageClass的关系图可以解释如下:

Pod中使用持久存储

例如,用户创建一个PVC,如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: manual
  resources:
    requests:
      storage: 1Gi

集群中的Volume Controller发现这个PVC后,就会主动在集群中寻找合适的PV,来和PVC绑定。 只有和PV绑定了的PVC,才能被pod正常挂载使用。 Volume Controller寻找PV的条件主要是: (1)PVC和PV的spec字段中指定的规格,例如存储(storage)的大小; (2)PVC和PV的storageClassName必须一样。(这里的storage)

如果集群中存在类型下面这样能满足PVC条件的PV,则可能会被绑定:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs
spec:
  storageclassName: manual
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 10.0.0.1
    path: "/"

这个准备“持久化”宿主机目录的过程,我们可以形象地称为“两阶段处理”。

(1)Attach:为宿主机挂载远程存储;(如果是NFS的话,其实没有这个过程,因为不需要“挂载存储设备到宿主机”) (2)Mount:将远程存储格式化挂载到宿主机的指定目录,对应容器中的Volume。

我们可以创建这样的一个Pod来使用PVC

apiVersion: v1
kind: Pod
metadata:
  labels:
    role: web-frontend
spec:
  containers:
  - name: web
    image: nginx
    ports:
      - name: web
        containerPort: 80
    volumeMounts:
        - name: nfs
          mountPath: "/usr/share/nginx/html"
  volumes:
  - name: nfs
    persistentVolumeClaim:
      claimName: nfs

本地存储 EmptyDir 容器->宿主机

文件在 pod所在的node节点

docker ps |grep nginx

查找该文件所在位置: find / -name hellohello

apiVersion: v1
kind: Pod        #类型是Pod
metadata:
  labels:
    name: redis
    role: master        #定义为主redis
  name: redis-master
spec:
  containers:
    - name: master
      image: redis:latest
      env:        #定义环境变量
        - name: MASTER
          value: "true"
      ports:        #容器内端口
        - containerPort: 6379
      volumeMounts:        #容器内挂载点
        - mountPath: /data
          name: redis-data        #必须有名称
  volumes:
    - name: redis-data        #跟上面的名称对应
      emptyDir: {}        #宿主机挂载点

kubectl create -f emptydir.yaml

image.png

此时Emptydir已经创建成功,在宿主机上的访问路径为/var/lib/kubelet/pods//volumes/kubernetes.io~empty-dir/redis-data,如果在此目录中创建删除文件,都将对容器中的/data目录有影响,如果删除Pod,文件将全部删除,即使是在宿主机上创建的文件也是如此,在宿主机上删除容器则k8s会再自动创建一个容器,此时文件仍然存在。

HostDir hostPath

在宿主机上指定一个目录,挂载到Pod的容器中,当pod删除时,本地仍然保留文件

ymal

apiVersion: apps/v1
kind: Deployment
metadata:
  name: appname
  namespace: common
spec:
  progressDeadlineSeconds: 600
  replicas: 2
  selector:
    matchLabels:
      app: appname
  template:
    metadata:
      labels:
        app: appname
    spec:
      containers:
        - env:
            - name: MYENV_APPLICATIONNAME
              value: appname
            - name: MYENV_CONFIGPROFILE
              value: zs
            - name: MYENV_CONFIGGROUP
              value: common
            - name: MYENV_OTHERCONFIGDATAID1
              value: zs.properties
            - name: MYENV_OTHERCONFIGGROUP1
              value: common
            - name: cs.yamlFIGGROUP1
              value: common
            - name: MYENV_NACOSURL
              value: 'ip:8848'
            - name: MYENV_TOMCATPORT
              value: '0000'
            - name: JAVA_OPTS
              value: >-
                -Xms6g -Xmx6g -Xmn2g -XX:MetaspaceSize=256m
                -XX:MaxDirectMemorySize=1g -XX:SurvivorRatio=8
                -XX:+UseConcMarkSweepGC -XX:CMSMaxAbortablePrecleanTime=5000
                -XX:+CMSClassUnloadingEnabled
                -XX:CMSInitiatingOccupancyFraction=80
                -XX:+UseCMSInitiatingOccupancyOnly
                -XX:+ExplicitGCInvokesConcurrent
                -Dsun.rmi.dgc.server.gcInterval=2592000000
                -Dsun.rmi.dgc.client.gcInterval=2592000000
                -XX:ParallelGCThreads=2
                -Dsun.net.client.defaultConnectTimeout=10000
                -Dsun.net.client.defaultReadTimeout=30000
                -javaagent:/usr/local/soft/agent/skywalking-agent.jar
                -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=ip:1100   //skywalking的collector服务的IP及端口
                -DSW_AGENT_NAME=springboot-skywalking-demo
                -DSW_KAFKA_BOOTSTRAP_SERVERS=ip:9092
          image: >-
            registry-vpc.cn-beijing.aliyuncs.com/appname:release
          imagePullPolicy: IfNotPresent
          livenessProbe:
            failureThreshold: 3
            initialDelaySeconds: 1000
            periodSeconds: 10
            successThreshold: 1
            tcpSocket:
              port: 0001
            timeoutSeconds: 10
          name: appname
          ports:
            - containerPort: 0001
              hostPort: 0001
              protocol: TCP
            - containerPort: 9292
              hostPort: 9292
              protocol: TCP
          readinessProbe:
            failureThreshold: 3
            initialDelaySeconds: 100
            periodSeconds: 5
            successThreshold: 1
            tcpSocket:
              port: 0001
            timeoutSeconds: 10
          resources:
            limits:
              cpu: 2500m
              memory: 10Gi
            requests:
              cpu: 100m
              memory: 4Gi
          volumeMounts:
            - mountPath: /home/nacfg.ini
              name: nacfg   #必须要有名字
            - mountPath: /srv/access/resPinYin.dat
              name: pinyin
            - mountPath: /tmp/dump
              name: dump
      dnsPolicy: ClusterFirstWithHostNet
      hostNetwork: true
      imagePullSecrets:
        - name: regcred
      nodeSelector:
        env: common
      restartPolicy: Always
      volumes:
        - hostPath:
            path: /home/nacfg.ini  #宿主机挂载点
            type: ''
          name: nacfg
        - hostPath:
            path: /app/programfiles/resPinYin.dat  #宿主机挂载点
            type: ''
          name: pinyin
        - hostPath:
            path: /tmp/dump  #宿主机挂载点
            type: ''
          name: dump

网络数据卷(NFS) Network File System

映射的是代码目录,在/share 目录中创建index.html文件后,这个文件也将在容器中生效,当Pod删除时,文件不受影响,实现了数据持久化


apiVersion: v1
kind: Pod
metadata:
  name: nfs-web
spec:
  containers:
    - name: web
      image: nginx
      imagePullPolicy: Never        #如果已经有镜像,就不需要再拉取镜像
      ports:
        - name: web
          containerPort: 80
          hostPort: 80        #将容器的80端口映射到宿主机的80端口
      volumeMounts:
        - name : nfs        #指定名称必须与下面一致
          mountPath: "/usr/share/nginx/html"        #容器内的挂载点
  volumes:
    - name: nfs            #指定名称必须与上面一致
      nfs:            #nfs存储
        server: 192.168.1.0        #nfs服务器ip或是域名
        path: "/share"                #nfs服务器共享的目录

配置每个机器上的 机器码、机器文件 都不一样。保证不出现分布式并发线程不安全问题

配置映射 configMap

image.png

image.png

image.png

image.png

image.png

image.png

image.png

创建容器的时候,把宿主机机器码、机器文件对应的目录挂在到容器上

创建卷并挂载到容器

   
 
piVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp
    tier: frontend
 
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    volumeMounts:    #在容器内定义挂载存储名称和挂载路径
    - name: html
      mountPath: /usr/share/nginx/html/
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: html
      mountPath: /data/    #在容器内定义挂载存储名称和挂载路径
      readOnly: true
    command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done']
    volumes:  #定义存储卷
      - name: html    #定义存储卷名称
        emptyDir: {}  #定义存储卷类型
 
    

将卷文件储存在内存

  volumes:
  - name: html
    emptyDir: {}
      medium: Memory

使用Git仓库作为储存卷

gitRepo 卷基本上也是一个emptyDir卷,它通过克隆Git仓库并在pod启动时(在创建之前)将git仓文件填充到卷

apiVersion: v1
kind: Pod
metadata:
  name: gitrepo-volume-pod
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
      readOnly: true
    ports:
    - containerPort: 80
      protocol: TCP
  volumes:
  - name: html
    gitRepo:
      repository: https://github.com/luksa/kubia-website-example.git
      revision: master
      directory: .

hostPath卷指向节点文件系统上的特定文件或目录。

在同一个节点上运行并在其 hostPath 卷中使用相同路径的 pod 可以看到相同的文件

hostPath卷是一种持久性存储,并不会像emptyDir卷和gitRepo卷一样在pod被删除时被删除, 如果删除一个pod,而下一个pod使用了指向主机上相同路径的hostPath卷,则新的pod将会发现上一个pod留下的数据(也就是说hostPath卷数据不会随着pod的删除而清除),前提是两个pod在同一个节点

    volumes:
        - hostPath:
            path: /home/nacfg.ini
            type: ''
          name: nacfg