Kubernetes-Volume

1. 简介

我们都知道 Container 中的文件在磁盘上是临时存放的,这给 Container 中运行的较重要的应用 程序带来一些问题。

  1. 是当容器崩溃时文件丢失。(kubelet 会重新启动容器, 但容器会以干净的状态重启)
  2. 在同一 Pod 中运行多个容器如何共享文件

Kubernetes 卷(Volume)这一抽象概念能够解决这两个问题。

2. 背景

Docker 也有 卷(Volume)的概念,但对它只有少量且松散的管理。 Docker 卷是磁盘上或者另外一个容器内的一个目录。 Docker 提供卷驱动程序,但是其功能非常有限。

Kubernetes 支持很多类型的卷。 Pod 可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁 持久卷。 对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。

卷的核心是一个目录,其中可能存有数据,Pod 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放 的内容。

使用卷时, 在 .spec.volumes 字段中设置为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts 字段中声明卷在容器中的挂载位置。 容器中的进程看到的是由它们的 Docker 镜像和卷组成的文件系统视图。 Docker 镜像 位于文件系统层次结构的根部。各个卷则挂载在镜像内的指定路径上。 卷不能挂载到其他卷之上,也不能与其他卷有硬链接。 Pod 配置中的每个容器必须独立指定各个卷的挂载位置。

3. emptyDir

当 Pod 分派到某个 Node 上时, emptyDir 卷会被创建,并且在 Pod 在该节点上运行期间,卷一直存在。 就像其名称表示的那样,卷最初是空的。 尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时, emptyDir 卷中的数据也会被永久删除。

说明: 容器崩溃并 会导致 Pod 被从节点上移除,因此容器崩溃期间 emptyDir 卷中的数据是安全的。

资源模板如下:

apiVersion: v1
kind: Pod
metadata:
  name: producer-consumer
spec:
  containers:
  - image: busybox
    name: producer
    volumeMounts:
    - mountPath: /producer_dir
      name: shared-volume
    args:
    - /bin/sh
    - -c
    - echo "hello world" > /producer_dir/hello; sleep 3000
  - image: busybox
    name: consumer
    volumeMounts:
    - mountPath: /consumer_dir
      name: shared-volume
    args:
    - /bin/sh
    - -c
    - cat /consumer_dir/hello; sleep 3000
  volumes:
  - name: shared-volume
    emptyDir: {}

创建一个pod,其中有两个container,一个是producer,另一个是consumer,producer负责生成hello文件并且写入内容 hello world,consumer 则负责读取hello文件中的内容,验证同一pod中的container存储共享机制。

Kubernetes-Volume

因为 emptyDir 是 Docker Host 文件系统里的目录,其效果相当于在k8s-woker-01上执行了 docker run -v /producer_dirdocker run -v /consumer_dir。通过 docker inspect 查看容器的详细配置信息,我们发现两个容器都 mount 了同一个目录:

Kubernetes-Volume
$ docker inspect 52305cbb0dec

Kubernetes-Volume
$ docker inspect c946762ccc8c

Kubernetes-Volume

这里 Source指定的路径就是 emptyDir 在 Host 上的真正路径。

Kubernetes-Volume

emptyDir 是 Host 上创建的临时目录,其优点是能够方便地为 Pod 中的容器提供共享存储,不需要额外的配置。但它不具备持久性,如果 Pod 不存在了,emptyDir 也就没有了。根据这个特性,emptyDir 特别适合 Pod 中的容器需要临时共享存储空间的场景,比如前面的生产者消费者用例。

4. hostPath

警告:

HostPath 卷存在许多安全风险,最佳做法是尽可能避免使用 HostPath。 当必须使用 HostPath 卷时,它的范围应仅限于所需的文件或目录,并以只读方式挂载。

hostPath 卷能将主机节点文件系统上的文件或目录挂载到你的 Pod 中。 虽然这不是大多数 Pod 需要的,但是它为一些应用程序提供了强大的逃生舱。

例如, hostPath 的一些用法有:

  • 运行一个需要访问 Docker 内部机制的容器;可使用 hostPath 挂载 /var/lib/docker 路径。
  • 允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在。

除了必需的 path 属性之外,用户可以选择性地为 hostPath 卷指定 type

支持的 type 值如下:

取值 行为 空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。 DirectoryOrCreate

如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet 相同的组和属主信息。 Directory

在给定路径上必须存在的目录。 FileOrCreate

如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权。 File

在给定路径上必须存在的文件。 Socket

在给定路径上必须存在的 UNIX 套接字。

hostPath 配置示例:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # 宿主上目录位置
      path: /data
      # 此字段为可选
      type: Directory

注意: FileOrCreate 模式不会负责创建文件的父目录。 如果欲挂载的文件的父目录不存在,Pod 启动会失败。 为了确保这种模式能够工作,可以尝试把文件和它对应的目录分开挂载,如 FileOrCreate 配置所示。

hostPath FileOrCreate 配置示例

apiVersion: v1
kind: Pod
metadata:
  name: test-webserver
spec:
  containers:
  - name: test-webserver
    image: k8s.gcr.io/test-webserver:latest
    volumeMounts:
    - mountPath: /var/local/aaa
      name: mydir
    - mountPath: /var/local/aaa/1.txt
      name: myfile
  volumes:
  - name: mydir
    hostPath:
      # 确保文件所在目录成功创建。
      path: /var/local/aaa
      type: DirectoryOrCreate
  - name: myfile
    hostPath:
      path: /var/local/aaa/1.txt
      type: FileOrCreate

5. local

local 卷所代表的是某个被挂载的本地存储设备,例如磁盘、分区或者目录。

local 卷只能用作静态创建的持久卷。尚不支持动态配置。

hostPath 卷相比, local 卷能够以持久和可移植的方式使用,而无需手动将 Pod 调度到节点。系统通过查看 PersistentVolume 的节点亲和性配置,就能了解卷的节点约束。

然而, local 卷仍然取决于底层节点的可用性,并不适合所有应用程序。 如果节点变得不健康,那么 local 卷也将变得不可被 Pod 访问。使用它的 Pod 将不能运行。 使用 local 卷的应用程序必须能够容忍这种可用性的降低,以及因底层磁盘的耐用性特征 而带来的潜在的数据丢失风险。

下面是一个使用 local 卷和 nodeAffinity 的持久卷示例:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  capacity:
    storage: 100Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /mnt/disks/ssd1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - example-node

使用 local 卷时,你需要设置 PersistentVolume 对象的 nodeAffinity 字段。 Kubernetes 调度器使用 PersistentVolume 的 nodeAffinity 信息来将使用 local 卷的 Pod 调度到正确的节点。

PersistentVolume 对象的 volumeMode 字段可被设置为 “Block” (而不是默认值 “Filesystem”),以将 local 卷作为原始块设备暴露出来。

使用 local 卷时,建议创建一个 StorageClass 并将其 volumeBindingMode 设置为 WaitForFirstConsumer。 延迟卷绑定的操作可以确保 Kubernetes 在为 PersistentVolumeClaim 作出绑定决策时, 会评估 Pod 可能具有的其他节点约束,例如:如节点资源需求、节点选择器、Pod 亲和性和 Pod 反亲和性。

你可以在 Kubernetes 之外单独运行静态驱动以改进对 local 卷的生命周期管理。 请注意,此驱动尚不支持动态配置。

说明: 如果不使用外部静态驱动来管理卷的生命周期,用户需要手动清理和删除 local 类型的持久卷。

6. subPath

  1. 同一个pod中多容器挂载同一个卷时提供隔离
  2. 将configMap和secret作为文件挂载到容器中而不覆盖挂载目录下的文件

6.1 同一pod中多容器挂载同一个卷时提供隔离

先创建一个共享资源的 pod 用于 设置 subPath的前后对照比较

暂未设置 subPath,通过 hostPath的方式将文件挂载到了集群节点上

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-test
spec:
  containers:
  - image: busybox
    name: test-c-01
    volumeMounts:
    - mountPath: /opt/test
      name: shared-volume
    args:
    - /bin/sh
    - -c
    - sleep 3000
  - image: busybox
    name: test-c-02
    volumeMounts:
    - mountPath: /opt/test
      name: shared-volume
    args:
    - /bin/sh
    - -c
    - sleep 3000
  volumes:
  - name: shared-volume
    hostPath:
     path: /test
     type: DirectoryOrCreate

演示步骤如图所示:

此时pod 中容器挂载的卷是共享的

Kubernetes-Volume

宿主机上的挂载情况如下

Kubernetes-Volume

接下来加入 subPath配置,再来查看卷共享的情况

资源文件内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-test
spec:
  containers:
  - image: busybox
    name: test-c-01
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - mountPath: /opt/test
      name: shared-volume
      subPath: c01
    args:
    - /bin/sh
    - -c
    - sleep 3000
  - image: busybox
    name: test-c-02
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - mountPath: /opt/test
      name: shared-volume
      subPath: c02
    args:
    - /bin/sh
    - -c
    - sleep 3000
  volumes:
  - name: shared-volume
    hostPath:
     path: /test02
     type: DirectoryOrCreate

演示步骤如图所示:

此时pod 中容器挂载的卷是不共享的

Kubernetes-Volume

宿主机上的挂载情况如下

通过查看宿主机上的挂载目录可以判断出,其实是通过制定的 subPath的创建了各自的子目录,每个容器都是用各自的 subPath,所以实现了挂载的隔离。

Kubernetes-Volume

6.2 将configMap/secret作为文件挂载到容器中而不覆盖挂载目录下的文件

首先我们run一个普通的nginx作为前后的对照

apiVersion: v1
kind: Pod
metadata:
  name: helloworld
spec:
  containers:
  - image: nginx
    name: helloworld
    imagePullPolicy: IfNotPresent

演示步骤如下:

可以看到 正常的nginx配置文件有很多

Kubernetes-Volume

接下来 我们通过 ConfigMap方式挂载一下 nginx.conf文件

  1. 创建一个cm资源

    普普通通,极其简单的一个配置文件

apiVersion: v1
data:
  nginx-conf: |
    worker_processes  1;
    events {
      worker_connections  1024;
    }
    http {
      server {
        listen       80;
        location / {
            root   html;
            index  index.html index.htm;
        }
      }
    }
kind: ConfigMap
metadata:
  name: conf-nginx
  1. 创建nginx-pod

    也是一个很简单的nginx-pod,挂载了上面的cm资源

apiVersion: v1
kind: Pod
metadata:
  name: helloworld
spec:
  containers:
  - image: nginx
    name: helloworld
    imagePullPolicy: IfNotPresent
    volumeMounts:
      - name: config-vol
        # 这里不能写成 /etc/nginx/nginx.conf 是因为这样写会被解析成nginx.conf文件夹,从而导致启动失败
        mountPath: /etc/nginx
  volumes:
    - name: config-vol
      configMap:
        name: conf-nginx
        items:
          - key: nginx-conf
            path: nginx.conf

Kubernetes-Volume

这时就发现直接通过cm挂载配置文件其实是有问题的,自己想挂载的文件虽然挂载成功了,但是挂载点目录下的其他资源会丢失,这其实是个很大的隐患,因为有的服务少了部分配置文件会导致启动失败。

如果我们既想要挂载cm资源,又不想把挂载点中的其他配置文件丢失掉,这时 subPath就有作用了

apiVersion: v1
kind: Pod
metadata:
  name: helloworld
spec:
  containers:
  - image: nginx
    name: helloworld
    imagePullPolicy: IfNotPresent
    volumeMounts:
      - name: config-vol
        # 修改挂载点
        mountPath: /etc/nginx/nginx.conf
        # 添加subPath信息
        subPath: nginx.conf
  volumes:
    - name: config-vol
      configMap:
        name: conf-nginx
        items:
          - key: nginx-conf
            path: nginx.conf

演示步骤如下:

Kubernetes-Volume

Original: https://www.cnblogs.com/ludangxin/p/15808614.html
Author: 张铁牛
Title: Kubernetes-Volume

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/581149/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • HashSet源码分析

    HashSet是使用HashMap来实现的 总结 (1)由于在hashMap中key不可以重复所以HashSet中的元素不可重复。 (2)同理hashMap中允许key为null,…

    Java 2023年6月8日
    071
  • Spring Cloud 微服务优雅下线 + 灰度发布的正确姿势,写得太好了!

    前言 在生产环境中,如何保证在服务升级的时候,不影响用户的体验,这个是一个非常重要的问题。如果在我们升级服务的时候,会造成一段时间内的服务不可用,这就是不够优雅的。那什么是优雅的呢…

    Java 2023年6月15日
    091
  • 67.我会是你手边静候的树

    dfds posted @2022-09-28 08:39 随遇而安== 阅读(5 ) 评论() 编辑 Original: https://www.cnblogs.com/55zj…

    Java 2023年6月7日
    072
  • Android自定义控件1

    概述 Android已经为我们提供了大量的View供我们使用,但是可能有时候这些组件不能满足我们的需求,这时候就需要自定义控件了。自定义控件对于初学者总是感觉是一种复杂的技术。因为…

    Java 2023年6月13日
    082
  • C# 线程手册 第七章 网络和线程

    在本书的之前章节,我们已经深入地了解了C#.NET 中的线程并探讨了多线程编程中的不同概念和技术。现在你已经是一个线程专家啦,我们将要使用C#实现一个简单的多线程客户端-服务端程序…

    Java 2023年5月29日
    0139
  • java_lambda表达式

    场景:找出满足条件的Hero,从普通方法,匿名类,以及Lambda这几种方式展开: 1.普通方法: 使用一个普通方法,在for循环遍历中进行条件判断,筛选出满足条件的数据:hp &…

    Java 2023年6月5日
    061
  • rabbitMQ的Streams

    (其实一点都不新,kafka都加入10年了吧?) 几个特点(其实就是抄kafka的 stream) 1 持久化 2 append-only(不可变数据) 3 非破坏性消费者语义(n…

    Java 2023年5月30日
    054
  • java 欢迎页 主页 设置为servlet的方法

    使用HTML标签跳转至servlet 使用JSP(自动跳转到Servlet)→Servlet→JSP的方式request.getRequestDispatcher(“M…

    Java 2023年5月29日
    086
  • JeeSite 快速开发平台、架构特点、安全方面、为什么好、优势

    1、架构特点 以 Spring Boot 2 为基础,Maven 多项目依赖,模块分项目,松耦合,方便模块升级、增减模块 模块化的数据库自动升级程序,当模块升级代码需要更新数据库时…

    Java 2023年6月7日
    086
  • android多文件上传,java服务端接收

    Android多文件上传,java服务端接收 1、Android端 代码: String uploadUrl = "http://xxx/uploadFiles&quot…

    Java 2023年6月5日
    068
  • 后端开发学习记录(四)——Mybatis的学习

    Mybaits Mybaits官方文档 官方文档mybatis – MyBatis 3 | Introduction Mybaits 一、简介 Ⅰ什么是Mybaits MyBati…

    Java 2023年6月13日
    085
  • Nginx配置https 之 找不到 ./configure

    Nginx配置https 之 找不到 ./configure 需求 要配置个https 问题 找不到文件在哪里 教程很简单,发现就是找不到 ./configure 这个文件这个文件…

    Java 2023年5月30日
    079
  • Spring 框架的设计理念与设计模式分析

    Spring 作为现在最优秀的框架之一,已被广泛的使用,并且有很多对其分析的文章。本文将从另外一个视角试图剖析出 Spring 框架的作者设计 Spring 框架的骨骼架构的设计理…

    Java 2023年5月30日
    062
  • Java核心技术-内部类(下)

    Day8 局部内部类 优势: 对外部完全隐蔽 可以访问外部类字段和局部变量 package cn.gyk; import javax.swing.*; import java.aw…

    Java 2023年6月5日
    077
  • MYSQL进阶

    之前写了篇文章, 记录了MySQL的一些常用命令, 现在看来仍然有遗漏的地方, 所以补充一下 组合查询 即 UNION 使用 UNION来组合两个查询,如果第一个查询返回 M 行,…

    Java 2023年6月7日
    088
  • 【github项目】-CRM客户管理系统(基于SSM)

    CRM客户管理系统 基于SSM框架开发的CRM客户管理系统,适合刚学完SSM的同学,帮助夯实javase到ssm之间的知识,提升学生的逻辑思维,也了解到企业软件开发的流程及代码编写…

    Java 2023年6月8日
    088
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球