数据管理

Docker的数据管理是指在Docker容器中对数据进行存储、共享、备份和持久化的方法和技术。它允许用户在保持容器轻量级的同时,确保容器产生的数据能够安全、高效地存储和管理。主要的技术有三种数据挂载方式

![](3-Docker 数据管理/cd09b2d732da72b743dc5c5d68f7e0f6.png)

数据卷(Volumes)

  • 数据卷是Docker管理的、独立于容器的存储区域,提供了数据持久化和容器间共享的能力。
  • 数据卷存储在Docker主机上的特定目录,默认是 /var/lib/docker/volumes/(Linux系统),并且可以在容器间共享而不依赖于任何单一容器的生命周期。
  • 即使容器被删除,数据依然存在。

绑定挂载(Bind mounts)

  • 绑定挂载是将宿主机上的文件系统路径直接挂载到容器内的过程。
  • 这种方式允许直接利用宿主机的文件系统资源,但可能带来数据管理上的复杂性和安全性考量。
  • 绑定挂载的存储位置和数据格式完全由宿主机控制,不受到Docker的直接管理。
  • 需要注意的是,bind mount 在不同的宿主机系统时不可移植的,比如 Windows 和 Linux 的目录结构是不一样的,bind mount 所指向的 host 目录也不一样。这也是为什么 bind mount 不能出现在 Dockerfile 中的原因所在,因为这样 Dockerfile 就不可移植了。

tmpfs挂载(Tmpfs mounts)

  • Tmpfs挂载是将数据存储在宿主机的内存中而不是磁盘上,适用于临时文件或对速度有高要求的场景。
  • 这种类型的存储不会持久化,容器停止或宿主机重启后数据将会丢失。
  • 适合存储敏感信息或缓存数据,因其不会写入磁盘,可以增加安全性。

数据卷

  • 数据卷是一个可供一个或多个容器使用的特殊目录,他将主机操作系统目录直接映射到容器,它绕过 UFS,可以提供很多有用的特性:

    • 数据卷可以在容器之间共享和重用
    • 数据卷的修改会立马生效
    • 数据卷的更新,不会影响镜像
    • 数据卷默认会一直存在,即使容器被删除
  • 注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会复制到数据卷中(仅数据卷为空时会复制)。

常用命令

volume create 创建数据卷
volume inspect 查看数据卷详情
volume ls 列出数据卷
volume prune 清理无用数据卷
volume rm 删除数据卷

创建一个数据卷

  • 创建数据卷,数据卷路径: /var/lib/docker/volumes/

    1
    $ docker volume create my-vol
  • 查看所有的 数据卷

    1
    2
    3
    4
    $ docker volume ls

    DRIVER VOLUME NAME
    local my-vol
  • 在主机里使用以下命令可以查看指定 数据卷 的信息, 通过Mountpoint, 直接在宿主机上通过找到的路径修改数据卷内的文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ docker volume inspect my-vol
    [
    {
    "Driver": "local",
    "Labels": {},
    "Mountpoint": "/var/lib/docker/volumes/my-vol/_data", # 在宿主机上的路径
    "Name": "my-vol",
    "Options": {},
    "Scope": "local"
    }
    ]

启动一个挂载数据卷的容器

在用 docker run 命令的时候,使用 --mount 标记来将 数据卷 挂载到容器里。在一次 docker run 中可以挂载多个 数据卷--mount source=要挂载的数据卷,target=挂载到容器的路径 镜像名

下面创建一个名为 web 的容器,并加载一个 数据卷 到容器的 /usr/share/nginx/html 目录。

1
2
3
4
5
$ docker run -d -P \
--name web \
# -v my-vol:/usr/share/nginx/html \
--mount source=my-vol,target=/usr/share/nginx/html \
nginx:alpine

查看数据卷的具体信息

在主机里使用以下命令可以查看 web 容器的信息

1
$ docker inspect web

数据卷 信息在 “Mounts” Key 下面

1
2
3
4
5
6
7
8
9
10
11
12
"Mounts": [
{
"Type": "volume",
"Name": "my-vol",
"Source": "/var/lib/docker/volumes/my-vol/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],

删除数据卷

1
$ docker volume rm my-vol

数据卷 是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令。

无主的数据卷可能会占据很多空间,要清理请使用以下命令

1
$ docker volume prune

读写权限

  • 在用docker run命令时,使用-mount选项使用数据卷

    1
    [root@docker  ~]# docker run -d -P --name nginx --mount source=test,target=/webapp nginx
  • 默认挂载的镜像是读写权限,可以设置为只读

    1
    [root@docker  ~]# docker run -d -P --name nginx --mount source=test,target=/webapp:ro nginx
  • 直接挂载主机目录到容器

    1
    2
    3
    # docker run -it -v /宿主机绝对目录:/容器内目录 镜像名
    # 带权限的命令: docker run -it -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
    [root@docker ~]# docker run -it -v /myDataVolume:/dataVolumerContainer centos

权限问题

  • Docker挂载主机目录Docker访问出现cannot open directory .: Permission denied

  • 解决办法:在挂载目录后多加一个--privileged=true参数即可

  • 例如:docker run -t -i --privileged centos:latest bash

挂载一个主机目录作为数据卷

使用 --mount 标记可以指定挂载一个本地主机的目录到容器中去。

1
2
3
4
5
$ docker run -d -P \
--name web \
# -v /src/webapp:/usr/share/nginx/html \
--mount type=bind,source=/src/webapp,target=/usr/share/nginx/html \
nginx:alpine

上面的命令加载主机的 /src/webapp 目录到容器的 /usr/share/nginx/html目录。这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,以前使用 -v 参数时如果本地目录不存在 Docker 会自动为你创建一个文件夹,现在使用 --mount 参数时如果本地目录不存在,Docker 会报错。

Docker 挂载主机目录的默认权限是 读写,用户也可以通过增加 readonly 指定为 只读

1
2
3
4
5
$ docker run -d -P \
--name web \
# -v /src/webapp:/usr/share/nginx/html:ro \
--mount type=bind,source=/src/webapp,target=/usr/share/nginx/html,readonly \
nginx:alpine

加了 readonly 之后,就挂载为 只读 了。如果你在容器内 /usr/share/nginx/html 目录新建文件,会显示如下错误

1
2
/usr/share/nginx/html # touch new.txt
touch: new.txt: Read-only file system

挂载一个本地主机文件作为数据卷

--mount 标记也可以从主机挂载单个文件到容器中

1
2
3
4
5
6
7
8
9
$ docker run --rm -it \
# -v $HOME/.bash_history:/root/.bash_history \
--mount type=bind,source=$HOME/.bash_history,target=/root/.bash_history \
ubuntu:18.04 \
bash

root@2affd44b4667:/# history
1 ls
2 diskutil list

这样就可以记录在容器输入过的命令了。

数据卷容器

  • 数据卷容器是Docker中管理数据卷的一种高级用法,它允许用户创建一个专门用于数据存储的容器,并将其数据卷挂载到其他容器中。这种方式使得数据可以跨容器共享,同时保持数据的持久性和可移植性。

  • 数据卷容器主要用于数据的持久化存储和跨容器共享,它本身并不运行任何实际的应用服务,而是作为一个存储媒介存在。

  • 创建数据卷, 启动一个挂载数据卷的容器

    1
    [root@docker ~]# docker run -it -d -v /dbdata --name dbdata ubuntu
  • 创建db1和db2都挂载到同一个数据卷/dbdata目录下

    1
    2
    [root@docker ~]# docker run -it --volumes-from dbdata --name db1 ubuntu
    [root@docker ~]# docker run -it --volumes-from dbdata --name db2 ubuntu
  • 可以使用--volumes-from参数来从多个容器挂载多个数据卷,也可以从其他已挂载的数据卷容器来挂载数据卷,向三个容器中任意一个/dbdata目录下写入,其他容器都可以看到

    1
    [root@docker ~]# docker run -d --name db3 --volumes-from db1 training/postgres
  • 停止了卷容器创建新容器也可以引用他

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    [root@localhost ~]# docker stop volume_v1
    volume_v1
    [root@localhost ~]# docker ps -a
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    9bff8f66a04d nginx "/docker-entrypoint.…" 5 minutes ago Up 5 minutes 0.0.0.0:8091->80/tcp, :::8091->80/tcp web2
    c036bb587750 nginx "/docker-entrypoint.…" 5 minutes ago Up 5 minutes 0.0.0.0:8090->80/tcp, :::8090->80/tcp web1
    c9cff2a314fe nginx "/docker-entrypoint.…" 8 minutes ago Exited (0) 4 seconds ago volume_v1
    #停止卷容器volume_v1

    [root@localhost ~]# docker run -itd --name web3 -p 8092:80 --volumes-from volume_v1 nginx
    7e8a3e82e6aacf32e5194d4a47d827f718685e0811ffc478b09a6e1748fd997f
    [root@localhost ~]# curl -s 192.168.112.60:8092
    my_volume_test
    #可以获取my_volume数据卷的内容
  • 删除卷容器后无法依据卷容器创建新容器,之前创建好的容器不会有任何影响

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [root@localhost ~]# docker rm -f volume_v1
    volume_v1
    [root@localhost ~]# docker run -itd --name web4 -p 8093:80 --volumes-from volume_v1 nginx
    docker: Error response from daemon: No such container: volume_v1.
    See 'docker run --help'.

    [root@localhost ~]# curl 192.168.112.60:8089
    curl: (7) Failed connect to 192.168.112.60:8089; 拒绝连接
    [root@localhost ~]# curl 192.168.112.60:8090
    my_volume_test
    [root@localhost ~]# curl 192.168.112.60:8091
    my_volume_test
    [root@localhost ~]# curl 192.168.112.60:8092
    my_volume_test
    #之前创建好的容器不会有任何影响
  • 如果删除了挂载卷的容器,数据卷并不会自动删除,如果要删除数据卷,必须在删除最后一个挂载着他的容器时使用

    1
    2
    # 删除容器 nginx01, 并删除容器挂载的数据卷:
    docker rm -v nginx01

数据备份案例

MySQL5.6版本迁移到5.7

  • 创建一个命名的数据卷

    1
    2
    [root@localhost ~]# docker volume create mysql_data
    mysql_data
  • Docker安装MySQL5.6版本

    • -d: 表示后台运行容器(detached模式),即容器会在后台运行而不是与当前终端交互。
    • --name mysql-5.6: 为容器指定一个名字,这里是mysql-5.6,便于后续管理和识别。
    • -p 3306:3306: 端口映射配置,将宿主机的3306端口映射到容器内的3306端口。
    • -v mysql_data:/var/lib/mysql/: 数据卷挂载,mysql_data是数据卷的名称(如果没有事先创建,Docker会自动创建一个匿名数据卷),这个数据卷挂载到容器内的/var/lib/mysql/目录。
    • -e MYSQL_ROOT_PASSWORD=123: 设置环境变量,这里设置了MySQL的root用户的密码为123
    • mysql:5.6: 这是Docker镜像的名称和标签,表示使用mysql镜像的5.6版本来创建容器。
    1
    2
    3
    4
    [root@localhost ~]# docker run -d --name mysql-5.6 -p 3306:6606 -v mysql_data:/var/lib/mysql/ -e MYSQL_ROOT_PASSWORD=123 mysql:5.6
    [root@localhost ~]# cd /var/lib/docker/volumes/mysql_data/_data/
    [root@localhost _data]# ls
    auto.cnf ibdata1 ib_logfile0 ib_logfile1 mysql performance_schema
  • 进入容器创建数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [root@localhost _data]# docker exec -it mysql-5.6 /bin/bash
    root@7ef4b48ecbcf:/# cd /var/lib/mysql
    root@7ef4b48ecbcf:/var/lib/mysql# ls
    auto.cnf ib_logfile0 ib_logfile1 ibdata1 mysql performance_schema
    root@7ef4b48ecbcf:/var/lib/mysql# mysql -uroot -p123
    # 创建了个stu表插入了一条数据
    mysql> create database test;
    mysql> use test;
    mysql> create table stu (id int(5),name varchar(20),age int(5));
    mysql> insert into stu values(1,"zhangsan",18);
  • 删除mysql-5.6容器

    1
    docker rm -f mysql-5.6
  • Docker安装MySQL5.7版本

    1
    [root@localhost ~]# docker run -d --privileged=true --name mysql-5.7 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123 -v mysql_data:/var/lib/mysql/ mysql:5.7
  • 进入容器进行验证 可以看到之前mysql-5.6创建的数据还在

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    [root@localhost ~]# docker exec -it mysql-5.7 /bin/bash
    bash-4.2# mysql -uroot -p123
    mysql> show databases;
    +--------------------+
    | Database |
    +--------------------+
    | information_schema |
    | mysql |
    | performance_schema |
    | test |
    +--------------------+
    4 rows in set (0.00 sec)

    mysql> use test;
    Reading table information for completion of table and column names
    You can turn off this feature to get a quicker startup with -A

    Database changed
    mysql> show tables;
    +----------------+
    | Tables_in_test |
    +----------------+
    | stu |
    +----------------+
    1 row in set (0.00 sec)

    mysql> select * from stu;
    +------+----------+------+
    | id | name | age |
    +------+----------+------+
    | 1 | zhangsan | 18 |
    +------+----------+------+
    1 row in set (0.00 sec)

数据卷容器中的数据进行进行备份

  • 备份:首先使用 --volumes-from 命令创建一个加载 dbdata 的容器卷容器,并将宿主机当前目录挂载到容器的 /backup 目录,命令如下:

    1
    2
    3
    4
    5
    6
    [root@docker ~]# docker run --volumes-from dbdata -v $(pwd):/backup --name worker ubuntu tar cvf /backup/backup.tar /dbdata

    # --name worker ubuntu 使用ubuntu镜像创建名为worker的容器
    # --volumes-from dbdata 让worker容器挂载dbdata容器的数据卷
    # -v $(pwd):/backup 挂载本地的当前目录到worker容器的/backup目录
    # tar cvf /backup/backup.tar /dbdata 将/dbdata下内容备份为容器内的/backup/backup.tar
  • 容器启动后,使用了 tar 命令来将 dbdata 数据卷备份为容器中 /backup/backup.tar 文件,因为挂载了的关系,宿主机的当前目录下也会生成 backup.tar 备份文件。

  • 恢复:如果要恢复数据到一个容器,

    1
    2
    3
    4
    5
    6
    # 首先创建一个带有空数据卷的容器 dbdata2。
    $ sudo docker run -v /dbdata --name dbdata2 ubuntu /bin/bash

    # 然后创建另一个容器,挂载 dbdata2 容器卷中的数据卷,并使用 untar 解压备份文件到挂载的容器卷中
    $ sudo docker run --volumes-from dbdata2 -v $(pwd):/backup busybox tar xvf
    /backup/backup.tar
  • 为了查看/验证恢复的数据,可以再启动一个容器挂载同样的容器卷来查看:

    1
    $ sudo docker run --volumes-from dbdata2 busybox /bin/ls /dbdata

-v--mount

区别

都是挂载命令,使用 -v 挂载时,如果宿主机上没有指定文件不会报错,会自动创建指定文件;当使用 –mount时,如果宿主机中没有这个文件会报错找不到指定文件,不会自动创建指定文件。

bind mount 使用

通过 bind mount 模式可以挂载到宿主机的任意位置,示例如下:

1
2
docker run -d -it --name=test-nginx -p 8011:80 -v /docker/nginx1:/usr/share/nginx/html nginx:1.13.12

  • -v /docker/nginx1:/usr/share/nginx/html : 将宿主机中的 `` 目录挂载到容器中的 /usr/share/nginx/html 目录;

  • bind mount 这种方式会隐藏目录中的内容(非空情况下),这里的 /usr/share/nginx/html 目录下的 html 文件被隐藏了,所以我们看不到。

    1
    2
    3
    4
    5
    #  容器运行成功后,进入容器中:
    docker exec -it test-nginx /bin/bash
    # 目录下的 html 文件被隐藏
    cd /usr/share/nginx/html
    # ls 结果为空
  • 如果将宿主机中创建文件, 该目录中的文件立刻挂载到容器中

    1
    2
    3
    4
    cd /docker/nginx1 
    touch index.html

    # 容器内访问 ls会得到 index.html