Docker-存储

一、Docker存储概述

1.数据卷特性

  • Docker镜像由多个只读层构成,启动容器时,Docker会加载只读镜像层并在镜像栈顶部添加一个读写层
  • 如果运行中的容器修改了现有的一个只读的文件,该文件会从只读层复制到读写层,只读版本依旧存在,但已经被读写层的副本所覆盖了此即写时复制(copy-on-write)机制
  • 这个设计使得Docker可以提高镜像构建、存储和分发的效率,节省了时间和存储空间,然而也存在如下问题:
  1. 存在于联合文件系统中,不能在宿主机上很方便地对容器中的文件进行访问。

  2. 多个容器之间的数据无法共享。

  3. 当删除容器时,容器产生的数据将丢失

  • 为了解决这些问题,Docker引入了数据卷(volume)机制,此卷可以绕过联合文件系统,与宿主机上的目录绑定

2.数据卷结构

    • volumes: /var/lib/docker/volumes/ 卷的名字
    • bind mounts:宿主机任意位置目录或文件
    • tmpfs :内存中空间,不会写入主机文件系统

3.数据卷优点

  • volume 在容器创建时就会初始化,在容器运行时就可以使用其中的文件
  • volume 能在不同的容器之间共享和重用
  • 对volume中数据的操作会马上生效
  • 对volume中数据的操作不会影响到镜像本身
  • volume 的生存周期独立于容器的生存周期,即使删除容器,volume 仍然会存在,没有任何容器使用的volume也不会被Docker删除

二、使用 Volume

1.创建 volume

复制代码
# 删除所有容器和卷
[root@localhost ~]# docker rm $(docker ps -aq) -f
[root@localhost ~]# docker volume rm $(docker volume ls -q)

# 创建volume
[root@localhost ~]# docker volume create  my-vol
my-vol
[root@localhost ~]# docker volume  ls
DRIVER    VOLUME NAME
local     my-vol
[root@localhost ~]# ll /var/lib/docker/volumes/
总用量 52
brw-------  1 root root 259, 3 11月 12 08:32 backingFsBlockDev
-rw-------. 1 root root  65536 11月 12 08:40 metadata.db
drwx-----x  3 root root     19 11月 12 08:40 my-vol

注:Docker当前并未对volume的大小提供配额管理;用户在创建volume时无法指定volume的大小

注:my_vol下还有一层目录,挂载的时候实际上是把这个目录挂载到容器中

其他创建方式:

复制代码
# 如果没有创建卷,在启动容器时挂载,也会自动创建
[root@localhost ~]# docker run -d -v nginx-vol:/data nginx:1.22.1
d406b3a496d429452566de1d612edaed5bc493322fde1b8e8a683df028bb0c80
[root@localhost ~]# docker volume ls
DRIVER    VOLUME NAME
local     my-vol
local     nginx-vol

# 匿名卷的创建方式,不选定挂载卷
$ docker run -d -v /data nginx:1.22.1

# 启动mysql容器时,即便不手动挂载,也会自动挂载一个匿名卷
[root@localhost ~]# docker run -d -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0
DRIVER    VOLUME NAME
local     a54dc0b6a13954740884a4b15396e1341c73a8f123453e6e35610c012d53291c
local     my-vol
local     nginx-vol

# 在启动mysql容器时,VOLUME [/var/lib/mysql]指令会在启动mysql容器时创建匿名卷并挂载到/var/lib/mysql目录下
[root@localhost ~]# docker history mysql:8.0
IMAGE          CREATED       CREATED BY                                       SIZE     
 
<missing>      3 weeks ago   VOLUME [/var/lib/mysql]                          0B        buildkit.dockerfile.v0

注:一般情况下尽量不使用匿名卷,匿名卷多了之后我们是看不到是哪个容器在用它;启动mysql容器时也可以直接挂载新卷组覆盖匿名卷

复制代码
[root@localhost ~]# docker run -d -e MYSQL_ROOT_PASSWORD=123456 -v mysql-vol:/var/lib/mysql mysql:8.0
30e6d41f8e92920c77c345076ecd423bf15b106ed4aa156b77b4471ee8daba3e
[root@localhost ~]# docker volume ls
DRIVER    VOLUME NAME
local     a54dc0b6a13954740884a4b15396e1341c73a8f123453e6e35610c012d53291c
local     my-vol
local     mysql-vol
local     nginx-vol

2.添加 volume

复制代码
[root@localhost ~]# docker run -d -v my-vol:/data nginx:1.22.1
06cbbfe23d1c5f6333414c60d4e153d88e55e7c41e4aa2269f2eed74b8f9f164
[root@localhost ~]# docker ps
CONTAINER ID   IMAGE          COMMAND                   CREATED          STATUS          PORTS     NAMES
06cbbfe23d1c   nginx:1.22.1   "/docker-entrypoint...."   15 seconds ago   Up 14 seconds   80/tcp    priceless_napier
[root@localhost ~]# docker exec -it 06cbbfe23d1c bash
root@06cbbfe23d1c:/# cd /data/
root@06cbbfe23d1c:/data#

# 宿主机中创建文件
[root@localhost _data]# touch host_file
# 容器中同样可以查看
root@06cbbfe23d1c:/data# ls
host_file

root@06cbbfe23d1c:/data# touch docker-file
[root@localhost _data]# ll
总用量 0
-rw-r--r-- 1 root root 0 11月 12 08:49 docker-file
-rw-r--r-- 1 root root 0 11月 12 08:48 host_file

注:容器中的目录和宿主机中的/var/lib/docker/volumes/my-vol/_data是一样的

3.查看 volume

复制代码
[root@localhost ~]# docker volume inspect mysql-vol
[
    {
        "CreatedAt": "2025-11-12T09:01:23+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/mysql-vol/_data",
        "Name": "mysql-vol",
        "Options": null,
        "Scope": "local"
    }
]

4.挂载宿主机目录

复制代码
[root@localhost ~]# docker run -d -v /data/docker-volume:/data nginx:1.22.1
[root@localhost ~]# ll /data/
总用量 0
drwxr-xr-x 2 root root   6 11月 12 09:09 docker-volume
drwxr-xr-x 8 root root 100 11月  6 14:55 registry

# 宿主机创建文件
[root@localhost ~]# cd /data/docker-volume/
[root@localhost docker-volume]# touch host-file

[root@localhost ~]# docker ps
CONTAINER ID   IMAGE          COMMAND                   CREATED              STATUS              PORTS                 NAMES
1d8fe1c0e18d   nginx:1.22.1   "/docker-entrypoint...."   About a minute ago   Up About a minute   80/tcp                exciting_maxwell
# 容器中查看
[root@localhost ~]# docker exec -it 1d8fe1c0e18d bash
root@1d8fe1c0e18d:/# cd /data/
root@1d8fe1c0e18d:/data# ls 
host-file

注:可以是宿主机的任意目录

5.挂载宿主机文件

复制代码
[root@localhost ~]# docker run -d -v /data/docker-volume/host-file:/data/docker-volume.txt nginx:1.22.1

# 在宿主机的目录下写入数据
[root@localhost docker-volume]# echo "hello world" >> host-file

[root@localhost ~]# docker ps
CONTAINER ID   IMAGE          COMMAND                   CREATED         STATUS         PORTS                 NAMES
5e33da3c14da   nginx:1.22.1   "/docker-entrypoint...."   2 seconds ago   Up 2 seconds   80/tcp                naughty_herschel
# 启动容器查看
[root@localhost ~]# docker exec -it 5e33da3c14da bash
root@5e33da3c14da:/# cd /data/
root@5e33da3c14da:/data# cat docker-volume.txt 
hello world

注:文件必须使用绝对路径

6.指定挂载权限

复制代码
# 设为只读模式
[root@localhost ~]# docker run -d -v /data/docker-volume:/data:ro nginx:1.22.1
d4bc2ac50d63be9cc9eb1dcb802950ba710ddfff90361bcbb3afd5c9cbdc792e
[root@localhost ~]# docker ps
CONTAINER ID   IMAGE          COMMAND                   CREATED         STATUS         PORTS     NAMES
d4bc2ac50d63   nginx:1.22.1   "/docker-entrypoint...."   3 seconds ago   Up 2 seconds   80/tcp    zealous_chebyshev

# 尝试创建目录,发现没有权限
[root@localhost ~]# docker exec -it d4bc2ac50d63 bash
root@d4bc2ac50d63:/# cd /data/
root@d4bc2ac50d63:/data# mkdir file
mkdir: cannot create directory 'file': Read-only file system

7.共享 Volume

注:在使用 docker run或docker create创建新容器时,可以使用 --volumes-from 标签使得容器与已有的容器共享volum

复制代码
[root@localhost ~]# ll /data/docker-volume/nginx-conf/
总用量 4
-rw-r--r-- 1 root root 398  2月 21  2025 nginx.repo

[root@localhost ~]# docker run --name nginx-01 -d -v /data/docker-volume/nginx-conf/:/data nginx:1.22.1
d1c3edb2b260071d17f29c47188169bf4bc06133d78501f284b9c8b4dbfdd587
[root@localhost ~]# docker run --name nginx-02 -d --volumes-from nginx-01 nginx:1.22.1
aecc3eddc812c7202bb46ed778977137a7706fa299483c472bd106335f9d923a

# 查看
[root@localhost ~]# docker exec nginx-01 ls /data
nginx.repo
[root@localhost ~]# docker exec nginx-02 ls /data
nginx.repo

# 新创建的容器 nginx-02 与之前创建的容器 nginx-01 共享 volume

8.删除 Volume

在删除容器时一并删除volume有以下两种方法。

  • 使用 docker rm -v <container_name> 删除容器。

  • 在运行容器时使用 docker run --rm, --rm 标签会在容器停止运行时删除容器以及容器所挂载的volume。

注意:

  1. 在使用docker volume rm 删除 volume 时,只有在没有任何容器使用时,该 volume 才能成功删除。

  2. 两种方法只会删除未命名的 volume,而对用户指定名字的 volume 进行保留。

  3. 如果 volume 是从宿主机中挂载的,无论对容器进行任何操作都不会导致其在宿主机中被删除。

9.Docker&宿主机复制文件

复制代码
# 宿主文件 nginx.repo 复制到容器中

[root@localhost nginx-conf]# docker cp /data/docker-volume/nginx-conf/nginx.repo nginx-01:/tmp/
Successfully copied 2.05kB to nginx-01:/tmp/
[root@localhost nginx-conf]# docker ps
CONTAINER ID   IMAGE          COMMAND                   CREATED          STATUS          PORTS     NAMES

d1c3edb2b260   nginx:1.22.1   "/docker-entrypoint...."   6 minutes ago    Up 6 minutes    80/tcp    nginx-01

[root@localhost nginx-conf]# docker exec -it d1c3edb2b260 bash
root@d1c3edb2b260:/# ls /tmp/
nginx.repo

10.-v vs --mount

挂载 Volume:

复制代码
[root@localhost ~]# docker run -d \
  --name devtest \
  --mount type=volume,source=myvol2,target=/app \
  nginx:latest

挂载本地目录:

复制代码
[root@localhost ~]# docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest

[root@localhost ~]# docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app,readonly \
  nginx:latest

挂载 tmpfs:

复制代码
[root@localhost ~]# docker run -d \
  -it \
  --name tmptest \
  --mount type=tmpfs,destination=/app \
  nginx:latest

-v 和 --mount 的区别

  • -v 或 --volume:由三个字段组成,用冒号字符(:)分隔。这些字段必须按照正确的顺序,并且每个字段的含义不太清晰

  • --mount:由多个键值对组成,用逗号分隔,每个键值对由<key>=<value>组成。--mount 语法比 -v 或 --volume 更冗长,但键的顺序并不重要,并且该标志的值更易于理解

三、Docker 资源限制

1.cgroup 介绍

  • cgroup 是 Linux 内核提供的一种机制
  • cgroup 可以限制、记录进程组所使用的物理资源(包括 CPU、Memory、IO等)
  • 实现 cgroup 的主要目的是为不同用户层面的资源管理,提供一个统一化的接口

2.cgroup 功能

  • 资源限制:cgroup 可以对任务使用的资源总额进行限制。如设定应用运行时使用内存的上限,一旦超过这个配额就发出 OOM(Out of Memory)提示。

  • 优先级分配:通过分配的CPU时间片数量及磁盘IO带宽大小,实际上就相当于控制了任务运行的优先级。

  • 资源统计:cgroup 可以统计系统的资源使用量,如CPU使用时长、内存用量等

复制代码
  [root@localhost ~]# cd /sys/fs/cgroup
  [root@localhost cgroup]# mkdir example
  [root@localhost cgroup]# cd example/
  [root@localhost example]# ls
  cgroup.controllers      cpu.max.burst                    hugetlb.1GB.events        io.bfq.weight        memory.oom.group        misc.events
  cgroup.events           cpuset.cpus                      hugetlb.1GB.events.local  io.latency           memory.peak             misc.events.local
  cgroup.freeze           cpuset.cpus.effective            hugetlb.1GB.max           io.max               memory.reclaim          misc.max
  cgroup.kill             cpuset.cpus.exclusive            hugetlb.1GB.numa_stat     io.stat              memory.stat             misc.peak
  cgroup.max.depth        cpuset.cpus.exclusive.effective  hugetlb.1GB.rsvd.current  io.weight            memory.swap.current     pids.current
  cgroup.max.descendants  cpuset.cpus.partition            hugetlb.1GB.rsvd.max      memory.current       memory.swap.events      pids.events
  cgroup.procs            cpuset.mems                      hugetlb.2MB.current       memory.events        memory.swap.high        pids.events.local
  cgroup.stat             cpuset.mems.effective            hugetlb.2MB.events        memory.events.local  memory.swap.max         pids.max
  cgroup.subtree_control  cpu.stat                         hugetlb.2MB.events.local  memory.high          memory.swap.peak        pids.peak
  cgroup.threads          cpu.stat.local                   hugetlb.2MB.max           memory.low           memory.zswap.current    rdma.current
  cgroup.type             cpu.weight                       hugetlb.2MB.numa_stat     memory.max           memory.zswap.max        rdma.max
  cpu.idle                cpu.weight.nice                  hugetlb.2MB.rsvd.current  memory.min           memory.zswap.writeback
  cpu.max                 hugetlb.1GB.current              hugetlb.2MB.rsvd.max      memory.numa_stat     misc.current

任务控制:cgroup 可以对任务执行挂起、恢复等操作

3.查看当前cgroup版本

复制代码
[root@localhost ~]# mount  | grep cgroup 
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)

[root@localhost ~]# cd /sys/fs/cgroup
# 创建一个目录 example ,这个目录表示一个进程组
[root@localhost cgroup]# mkdir example
[root@localhost cgroup]# cd example/

# 在 example 进程组中会有各种配置文件 
[root@localhost example]# ls
cgroup.controllers      cpu.max.burst                    hugetlb.1GB.events        io.bfq.weight        memory.oom.group        misc.events
cgroup.events           cpuset.cpus                      hugetlb.1GB.events.local  io.latency           memory.peak             misc.events.local
cgroup.freeze           cpuset.cpus.effective            hugetlb.1GB.max           io.max               memory.reclaim          misc.max
cgroup.kill             cpuset.cpus.exclusive            hugetlb.1GB.numa_stat     io.stat              memory.stat             misc.peak
cgroup.max.depth        cpuset.cpus.exclusive.effective  hugetlb.1GB.rsvd.current  io.weight            memory.swap.current     pids.current
cgroup.max.descendants  cpuset.cpus.partition            hugetlb.1GB.rsvd.max      memory.current       memory.swap.events      pids.events
cgroup.procs            cpuset.mems                      hugetlb.2MB.current       memory.events        memory.swap.high        pids.events.local
cgroup.stat             cpuset.mems.effective            hugetlb.2MB.events        memory.events.local  memory.swap.max         pids.max
cgroup.subtree_control  cpu.stat                         hugetlb.2MB.events.local  memory.high          memory.swap.peak        pids.peak
cgroup.threads          cpu.stat.local                   hugetlb.2MB.max           memory.low           memory.zswap.current    rdma.current
cgroup.type             cpu.weight                       hugetlb.2MB.numa_stat     memory.max           memory.zswap.max        rdma.max
cpu.idle                cpu.weight.nice                  hugetlb.2MB.rsvd.current  memory.min           memory.zswap.writeback
cpu.max                 hugetlb.1GB.current              hugetlb.2MB.rsvd.max      memory.numa_stat     misc.current

4.限制进程CPU资源

复制代码
先启动一个占用 CPU 的程序,找到程序的进程号
# ps -ef | grep busyloop​
root      155677  115803 99 16:14 pts/2    00:01:06 ./busyloop​
​
# 把进程号添加到 cgroup.procs 文件 中​
# echo 155677 >> cgroup.procs​
​
# v2 版本限制cpu的文件使用 cpu.max, 这样会限制 155677 进程最多使用 1 核 CPU.​
# echo "100000 100000" > cpu.max

5.限制容器CPU资源

复制代码
# 运行一个容器,限制可以使用1个cpu,使用 stress 占用2个cpu
[root@localhost example]# docker run -it --rm --cpus=1 registry.cn-beijing.aliyuncs.com/xxhf/stress /bin/bash

# top 查看
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                                     
   4668 root      20   0    3704    384    384 R  51.5   0.0   1:40.96 stress                                                                      
   4667 root      20   0    3704    256    256 R  49.8   0.0   1:41.11 stress  

6.限制容器内存资源

复制代码
[root@localhost example]# docker run -it --rm -m 300M  registry.cn-beijing.aliyuncs.com/xxhf/stress /bin/bash
root@243450f7f7e1:/# stress --vm 1 --vm-bytes 300M
stress: info: [11] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: FAIL: [11] (416) <-- worker 12 got signal 9
stress: WARN: [11] (418) now reaping child worker processes
stress: FAIL: [11] (452) failed run completed in 48s

root@243450f7f7e1:/# stress --vm 1 --vm-bytes 290M
stress: info: [13] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd

# 使用 docker stats 命令 可以查看容器资源使用情况
[root@localhost /]# docker stats 243450f7f7e1
CONTAINER ID   NAME              CPU %     MEM USAGE / LIMIT   MEM %     NET I/O     BLOCK I/O         PIDS
243450f7f7e1   naughty_haslett   126.49%   292.2MiB / 300MiB   97.40%    866B / 0B   5.75GB / 7.73GB   3
  • 如果操作系统有swap 分区,容器会使用swap分区来保存内存文件,需要使用 'stress --vm 1 --vm-bytes 600M' 才会触发限制。
  • 或者加一个参数 --memory-swap=300M,表示 可以使用的内存和交换空间的总和
相关推荐
暂时先用这个名字2 小时前
信创时代下,PHP/MySQL应用的平滑迁移与运维管理升级(AI整理)
运维·mysql·php·信创·国产化·国产·迁移
Lucis__2 小时前
STL设计模式探秘:容器适配器&仿函数
c++·容器·stl·仿函数
CS_浮鱼3 小时前
【Linux】进程控制
linux·运维·网络
人工智能训练3 小时前
Docker中容器的备份方法和步骤
linux·运维·人工智能·ubuntu·docker·容器·nvidia
bing.shao4 小时前
Goer-Docker系列-1-管理工具
docker·容器·eureka
MasonYyp4 小时前
Docker安装和使用kkfileview
运维·docker·容器
王煜苏4 小时前
contos7安装dokcer遇到的坑,docker-composer
docker·eureka·composer
渡我白衣4 小时前
深入 Linux 内核启动:从按下电源到用户登录的全景解剖
java·linux·运维·服务器·开发语言·c++·人工智能
DreamLife☼4 小时前
Docker-Dockerfile 完全指南:编写最佳实践的镜像
docker·镜像构建·多阶段构建·docker 安全·最小化镜像·非 root 用户·构建缓存