docker与k8s的容器数据卷

Docker容器数据卷

特性

  • docker镜像由多个只读层叠加而成,启动容器时,Docker会加载只读镜像层并在镜像栈顶部添加一个读写层。
  • 如果运行中的容器修改了现有的一个已经存在的文件,那么该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏。
  • 关闭并重启容器,其数据不受影响;但删除Docker容器,则其改变将会全部丢失。

存在的问题

  • 存在于联合文件系统(在docker中,只读层以及在顶层的读写层组合被称为联合文件系统)中,不易于宿主机的访问
  • 容器间数据共享不便
  • 删除容器其数据会丢失

解决方案:"卷"

  • "卷"是容器上的一个或多个"目录",此类目录可绕过联合文件系统,与宿主机上的某目录"绑定"。
  • "卷"可以在运行容器时即完成创建与绑定操作。当然,前提需要拥有对应的申明。
  • "卷"的初衷就是数据持久化。

创建数据卷

clike 复制代码
C:\Users\dell>docker volume create -d local volume_demo
volume_demo

C:\Users\dell>docker volume ls
DRIVER    VOLUME NAME
local     volume_demo

C:\Users\dell>docker volume inspect volume_demo
[
    {
        "CreatedAt": "2025-07-27T10:54:30Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/volume_demo/_data",//数据卷的存储目录
        "Name": "volume_demo",
        "Options": null,
        "Scope": "local"
    }
]

卷的分类

当在创建数据卷的时候,可以在创建容器时将主机本地的路径挂载到容器内作为数据卷。也可以在创建后挂载数据卷到容器使用,可以在运行容器时通过指定-v或--mount参数来使用该volume,并且可以依据数据卷类型不同挂载不同格式的数据卷。

  • Volume:普通数据卷,映射到特定位置(/var/lib/docker/volumes/)。
  • Bind:绑定数据卷,映射到主机的任意位置。
  • tmpfs:临时数据卷,只存在于内存中。

使用默认的Volume数据卷,运行一个Nginx容器,source指定数据卷,destination指定source映射在容器中的目录:

clike 复制代码
docker run -d \
--name=nginx_demo_docker \
--mount source=nginx_demo_volume,destination=/usr/share/nginx.html \
nginx:latest

可以通过使用-v来指定数据卷信息:

clike 复制代码
docker run -d \
--name=nginx_demo_docker \
-v nginx_demo_volume,/usr/share/nginx.html \
nginx:latest

使用bind指定Volume数据卷类型,运行一个nginx容器,source指定数据卷,destination指定source映射在容器的目录:

clike 复制代码
docker run -d \
--name=nginx_demo_docker \
--mount type=bind, source=/usr/local/nginx_demo_volume,destination=/usr/share/ \
nginx:latest

可以通过使用-v来指定数据卷信息:

clike 复制代码
docker run -d \
--name=nginx_demo_docker \
-v /usr/local/nginx_demo_volume:/usr/share/ \
nginx:latest

挂载成功后,容器从/usr/share/目录下读取或写入数据,实际上是从宿主机的/usr/local/nginx_demo_volume目录中读取或写入数据。

使用Bind mount挂载宿主机目录到一个容器中的非空目录,那么此容器的非空目录中的文件会被隐藏,容器访问这个目录时能够访问到的文件均来自宿主机目录。这也是Bind mount模式和Volume模式在行为上最大的不同。

Volume和Bind mount模式能够在宿主机和容器间共享文件,从而能够将数据持久化到宿主机上,以避免写入容器存储层带来的容器停止后数据丢失的问题。

如果使用Linux运行Docker,那么避免写入数据到容器存储层还有一个方案:tmpfs mount。tmpfs mounts,顾名思义,是一种非持久化的数据存储。它只是将数据保存在宿主机的内存中,一旦容器停止运行,tmpfs mount会被移除,从而造成数据丢失。可以在运行容器时通过指定--tmpfs参数或--mount参数来使用tmpfs mount:

clike 复制代码
docker run -d --tmpfs /mydata:size=100m --name mycontainer myimage

数据卷容器

如果用户需要在多个容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器。

数据卷容器也是一个容器,但是它的目的是专门提供数据卷给其他容器挂载。

  • 新建数据卷容器

    clike 复制代码
    docker run -it -v /dbdata --name db_data ubuntu
  • 共享数据卷容器

    clike 复制代码
       docker run -it --volumes-from db_data --name db_data_test ubuntu

注意:db_data这个数据卷容器不能随便关闭,如果关闭了,其他挂载了db_data里面数据卷的容器就会用不了。

数据迁移

  • 备份

    clike 复制代码
    docker run --rm --volumes-from [数据卷容器id/name] -v [宿主机目录]:[容器目录] [镜像名称] [备份命令]
    
    # 创建备份的容器,并且挂载/backup,然后执行备份压缩至/data
    docker run --rm --volumes-from vol-test -v /backup:/backup nginx tar zcf /backup/data.tar.gz /data
  • 恢复

    clike 复制代码
    docker run --rm -itd --volumes-from [数据要到恢复的容器] -v [宿主机备份目录]:[容器备份目录] [镜像名称] [解压命令]
    
    docker run --rm --volumes-from vol-test -v /backup:/backup nginx tar xf /backup/data.tar.gz -C /

k8s数据卷

  • 通过emptyDir共享数据

    EmptyDir是一个特殊的Volume类型,如果删除Pod,emptyDir卷中的数据也将被删除,所以一般emptyDir用于Pod中的不同Container共享数据,比如一个Pod存在两个容器A和B,容器A需要使用容器B产生的数据,此时可以采用emptyDir共享数据,类似的使用如Filebeat收集容器内程序产生的日志。

  • 使用HostPath挂载宿主机文件

    HostPath卷可将节点上的文件或目录挂载到Pod上,用于实现Pod和宿主机之间的数据共享,常用的示例有挂载宿主机的时区至Pod,或者将Pod的日志文件挂载到宿主机等。

    以下为使用hostPath卷的示例,实现将主机的/etc/timezone文件挂载到Pod的/etc/timezone(挂载路径可以不一致):

    在配置HostPath时,有一个type的参数,用于表达不同的挂载类型,HostPath卷常用的type(类型)如下:

    • type为空字符串:默认选项,意味着挂载hostPath卷之前不会执行任何检查。
    • DirectoryOrCreate:如果给定的path不存在任何东西,那么将根据需要创建一个权限为0755的空目录,和Kubelet具有相同的组和权限。
    • Directory:目录必须存在于给定的路径下。
    • FileOrCreate:如果给定的路径不存储任何内容,则会根据需要创建一个空文件,权限设置为0644,和Kubelet具有相同的组和所有权。
    • File:文件,必须存在于给定路径中。
    • Socket:UNIX套接字,必须存在于给定路径中。
    • CharDevice:字符设备,必须存在于给定路径中。
    • BlockDevice:块设备,必须存在于给定路径中。
  • 挂载NFS至容器

    在生产环境中,考虑到数据的高可用性,仍然不建议使用NFS作为后端存储。因为NFS存在单点故障,很多企业并非对其实现高可用,并且NFS的性能存在很大的瓶颈。所以生产中,建议使用分布式存储,在公有云中建议使用公有云提供的NAS存储来替代NFS,并且NAS性能更好,可用性更高。NFS作为一个比较流行的远端存储工具,在非生产环境中是一个比较好用的选择,可以快速地搭建使用。接下来演示如何使用Volume挂载NFS为容器提供持久化存储。

    和 emptyDir 、 HostPath 的 配 置 方 法 类 似 , NFS 的 Volume 配 置 也 是 在Volumes字段中配置的,和emptyDir不同的是,NFS属于持久化存储的一种,在Pod删除或者重启后,数据依旧会存储在NFS节点上。配置NFS也相对简单。

  • PersistentVolume

    虽然Volume已经可以接入大部分存储后端,但是实际使用时还有诸多问题,比如:

    • 当某个数据卷不再被挂载使用时,里面的数据如何处理?
    • 如果想要实现只读挂载如何处理?
    • 如果想要只能有一个Pod挂载如何处理?

    如上所述,对于很多复杂的需求Volume可能难以实现,并且无法对存储卷的生命周期进行管理。另一个很大的问题是,在企业内使用Kubernetes的不 仅 仅 是 Kubernetes 管 理 员 , 可 能 还 有 开 发 人 员 、 测 试 人 员 以 及 初 学Kubernetes的技术人员,对于Kubernetes的Volume或者相关存储平台的配置参数并不了解,所以无法自行完成存储的配置。

    为 此 , Kubernetes 引 入 了 两 个 新 的 API 资 源 : PersistentVolume 和PersistentVolumeClaim。PersistentVolume(简称PV)是由Kubernetes管理员设置的存储,PersistentVolumeClaim(简称PVC)是对PV的请求,表示需要什么类型的PV。它们同样是集群中的一类资源,但其生命周期比较独立,管理员可以单独对PV进行增删改查,不受Pod的影响,生命周期可能比挂载它

    的其他资源还要长。如果一个Kubernetes集群的使用者并非只有Kubernetes管理员,那么可以通过提前创建PV,用以解决对存储概念不是很了解的技术人员对存储的需求。和单独配置Volume类似,PV也可以使用NFS、GFS、CEPH等常用的存储后端,并且可以提供更加高级的配置,比如访问模式、空间大小以及回收策略等。目前PV的提供方式有两种:静态或动态。静态PV由管理员提前创建,动态PV无须提前创建。

    1. PV回收策略

    2. PV访问策略

    3. 基于·NFS的PV

    4. 基于HostPath的PV
      注意:使用hostPath需要固定Pod所在的节点,防止Pod飘移造成数据丢失。

    5. PV的状态

    6. PersistentVolumeClaim

      PVC是其他技术人员在Kubernetes上对存储的申请,它可以标明一个程序需要用到什么样的后端存储、多大的空间以及以什么访问模式进行挂载。这一点和Pod的QoS配置类似,Pod消耗节点资源,PVC消耗PV资源,Pod可以请求特定级别的资源(CPU和内存),PVC可以请求特定的大小和访问模式的PV。例如申请一个大小为5Gi且只能被一个Pod只读访问的存储。

      在实际使用时,虽然用户通过PVC获取存储支持,但是用户可能需要具有不同性质的PV来解决不同的问题,比如使用SSD硬盘来提高性能。所以集群管理员需要根据不同的存储后端来提供各种PV,而不仅仅是大小和访问模式的区别,并且无须让用户了解这些卷的具体实现方式和存储类型,达到了存储的解藕,降低了存储使用的复杂度。

    7. 案例如下:

      clike 复制代码
      apiVersion: v1  # API 版本
      kind: PersistentVolume  # 类型为持久卷
      metadata:
        name: jenkins-pv  # 持久卷的名称
      spec:
        capacity:
          storage: 2Gi  # 容量为2GB
        accessModes:
          #- ReadWriteMany  # 访问模式为多路读写
          - ReadWriteOnce  # HostPath 通常只能支持单节点写入,因此使用 ReadWriteOnce  
        hostPath:
          path: /data/jenkins
        #nfs:
        #  server: 192.168.16.2  # NFS 服务器地址
        #  path: /data/jenkins  # 共享的路径
      
      ---
      
      kind: PersistentVolumeClaim  # 类型为持久卷声明
      apiVersion: v1
      metadata:
        name: jenkins-pvc  # 持久卷声明的名称
        namespace: jenkins  # 使用的命名空间
      spec:
        resources:
          requests:
            storage: 2Gi  # 请求2GB的存储空间
        accessModes:
          - ReadWriteOnce  # 访问模式与上面保持一致
      
      ---
      apiVersion: apps/v1                 # 使用的 Kubernetes API 版本
      kind: Deployment                    # 资源类型为 Deployment
      metadata:
        name: jenkins                     # Deployment 的名称为 jenkins
        namespace: jenkins            # 所属的命名空间为 jenkins
      spec:
        replicas: 1                       # 副本数为 1
        selector:
          matchLabels:
            app: jenkins                  # 选择器选择带有标签 app: jenkins 的 Pod
        template:
          metadata:
            labels:
              app: jenkins                # Pod 的标签为 app: jenkins
          spec:
            serviceAccountName: jenkins-sa   # 使用的服务账户为 jenkins-sa
            nodeName: node1
            dnsConfig: #需要添加域名解析,不然容器内部可能无法访问外网
              nameservers:
                - "8.8.8.8"  # Google 的公共 DNS
                - "8.8.4.4"  # Google 的公共 DNS
            containers:
            - name: jenkins                       # 容器的名称为 jenkins
              image: jenkins/jenkins:2.479.1        # 使用的镜像为 jenkins/jenkins:latest,高版本的jenkins会有跨域问题
              ports:
              - containerPort: 8080               # 容器监听的端口为 8080
                name: web                         # 端口名称为 web
                protocol: TCP                    # 使用的协议为 TCP
              - containerPort: 50000              # 容器监听的端口为 50000
                name: agent                       # 端口名称为 agent
                protocol: TCP                    # 使用的协议为 TCP
              resources:
                limits:
                  cpu: "1000m"                    # CPU 的资源限制为 1000m(相当于 1 核)
                  memory: "1Gi"                   # 内存的资源限制为 1GiB
                requests:
                  cpu: "500m"                     # CPU 的资源请求为 500m(相当于 0.5 核)
                  memory: "512Mi"                 # 内存的资源请求为 512MiB
              livenessProbe:                      # 存活探针配置
                httpGet:
                  path: /login                    # 使用 HTTP GET 请求路径 /login
                  port: 8080                      # 请求的端口为 8080
                initialDelaySeconds: 60           # 初始延迟时间为 60 秒
                timeoutSeconds: 5                 # 超时时间为 5 秒
                failureThreshold: 12              # 失败阈值为 12
              readinessProbe:                     # 就绪探针配置
                httpGet:
                  path: /login                    # 使用 HTTP GET 请求路径 /login
                  port: 8080                      # 请求的端口为 8080
                initialDelaySeconds: 60           # 初始延迟时间为 60 秒
                timeoutSeconds: 5                 # 超时时间为 5 秒
                failureThreshold: 12              # 失败阈值为 12
              volumeMounts:                       # 挂载卷配置
              - name: jenkins-volume              # 卷的名称为 jenkins-volume
                subPath: jenkins-home             # 在卷中的子路径为 jenkins-home
                mountPath: /var/jenkins_home      # 挂载到容器中的路径为 /var/jenkins_home
            volumes:                              # 卷配置
            - name: jenkins-volume                # 卷的名称为 jenkins-volume
              #emptyDir: {}
              persistentVolumeClaim:              # 持久卷声明
                claimName: jenkins-pvc        # 使用的持久卷声明的名称为 jenkins-pvc
  • 动态存储StorageClass

    虽然使用PV和PVC能屏蔽一些存储使用上的细节,降低了存储使用的复杂度,但是也会有另一个问题无法解决。当公司Kubernetes集群很多,并且使用它们的技术人员过多时,对于PV的创建是一个很耗时、耗力的工作,并且达到一定规模后,过多的PV将难以维护。所以就需要某种机制用于自动管理PV的生命周期,比如创建、删除、自动扩容等,于是Kubernetes就设计了一个名为StorageClass(缩写为SC,没有命名空间隔离性)的东西,通过它可以动态管理集群中的PV,这样Kubernetes管理员就无须浪费大量的时间在PV的管理中。

    具体如何使用,各位读者就参考下文档,这里就不介绍了。

相关推荐
zmjjdank1ng41 分钟前
k8s问答题(1)
云原生·容器·kubernetes
行思理2 小时前
本地用docker开发的php 程序如何部署到阿里云的ecs上
阿里云·docker·容器
脚踏实地的大梦想家3 小时前
【Docker】P5 Docker Compose 实战指南:一键部署 WordPress + MySQL
mysql·docker·容器
醉卧雕龙舫 、4 小时前
一.docker基础概念
docker
老年DBA6 小时前
Kubernetes 上的 GitLab + ArgoCD 实践(三):使用 ArgoCD 打通 CD 流程
kubernetes·gitlab·argocd
帅帅梓11 小时前
docker图形化管理
docker·容器·eureka
努力搬砖的咸鱼18 小时前
容器之间怎么通信?Docker 网络全解析
网络·docker·云原生·容器
liming49520 小时前
Ubuntu18.04部署k8s
云原生·容器·kubernetes
元直数字电路验证1 天前
ASP.NET Core Web APP(MVC)开发中无法全局配置 NuGet 包,该怎么解?
前端·javascript·ui·docker·asp.net·.net
YC运维1 天前
Kubernetes资源管理全解析
java·容器·kubernetes