docker 保存 环境持久化_第三部分、Docker 数据持久化

1、Docker 数据管理简介

默认情况下,在容器内创建的所有文件都存储在可写容器层上。这意味着:

当该容器不再存在时,数据将不会持久保存,并且如果另一个进程需要它,则可能很难从容器中取出数据;

容器的可写层与运行容器的主机紧密耦合,不能轻易地将数据移动到其他地方;

写入容器的可写层需要 存储驱动程序来管理文件系统。存储驱动程序使用Linux内核提供联合文件系统,与使用直接写入主机文件系统的数据卷相比,这种额外的抽象降低了性能 。

Docker为容器提供了两种方案来将文件存储在主机中,以便即使容器停止后文件也可以持久存储:volumes 和 bind mounts。如果您在Linux上运行Docker,则还可以使用tmpfs挂载。如果您在Windows上运行Docker,则还可以使用named pipe。

2、Docker 数据持久化之 Data Volume

2.1、Volume 简介

Volume 存储在由 Docker 管理的主机文件系统的一部分中(在Linux上为/var/lib/docker/volumes/)。非 Docker 进程不应修改文件系统的这一部分,Volume方式是在Docker中持久保存数据的最佳方法(官方文档说的,不过实际情况中你可以按需选择)。

Volumes 由 Docker 创建和管理,您可以使用 docker volume create 命令显式创建卷,Docker 也可以在容器或服务创建期间创建 Volumes:

$ sudo docker volume --help

Usage: docker volume COMMAND

Manage volumes

Commands:

create Create a volume

inspect Display detailed information on one or more volumes

ls List volumes

prune Remove all unused local volumes

rm Remove one or more volumes

Run 'docker volume COMMAND --help' for more information on a command.

创建 Volume 时,它存储在 Docker 主机上的目录中,将 Volume 装入容器时,此目录就是装入容器的目录。这类似于 bind mount 的工作方式,只是 Volume 由 Docker 管理并且与主机的核心功能隔离。

多个容器可以同时使用同一个 Volume ,当没有正在运行的容器使用 volume 时,或者删除某个容器后,对应的 volume 不会自动删除,过后启动新的容器时照样可以使用此 volume ,当然,你也可以手动删除没被容器使用的 volumes ,命令: docker volume prune。

挂载 volume 时,默认是随机名称,你也可以在启动容器时指定 volume 名称。

Volume 还支持将数据存储在远程主机或云提供商上。

2.2、简单实验

接下来,我们一次来实际操作体验一下 docker 的volume 挂载模式的实现。

2.2.1、从 docker hub 官网 pull 一个 MySQL 镜像

pull 一个MySQL镜像,这一步没什么说的

# pull mysql镜像

$ sudo docker pull mysql:5.7

2.2.2、启动一个 MySQL 容器

接下来我们要启动一个MySQL容器,进一步感受下 volume 挂载方式

# 启动一个MySQL容器

$ sudo docker run --name test1_mysql -e MYSQL_ROOT_PASSWORD=password -d mysql:5.7

-e MYSQL_ROOT_PASSWORD 为此镜像环境变量(环境变量不用解释了吧?每个镜像都有自己的环境变量),具体参数及意义见此链接

-d 后台运行

--name 指定容器名称

2.2.3、查看启动 MySQL 容器默认创建的 volume

通过 docker volume ls 查看本机 volumes 列表,因为我自始至终只启动了一个容器,所以,看到的这一个 volume 就是我启动的这个 MySQL 容器对应的 volume 了,你可以通过不同的方式查看到这个 volume 。

# 查看当前 volume 列表

$ sudo docker volume ls

DRIVER VOLUME NAME

local 325c3adc386ba4f649f429abe820d56212057a7dc475611a9639bcb66f41df7d

# 你也可以通过 docker inspect 命令查看当前这个 volume 的详情

$ sudo docker inspect 325c3adc386ba4f649f429abe820d56212057a7dc475611a9639bcb66f41df7d

[

{

"CreatedAt": "2019-12-25T16:10:31+08:00",

"Driver": "local",

"Labels": null,

"Mountpoint": "/var/lib/docker/volumes/325c3adc386ba4f649f429abe820d56212057a7dc475611a9639bcb66f41df7d/_data",

"Name": "325c3adc386ba4f649f429abe820d56212057a7dc475611a9639bcb66f41df7d",

"Options": null,

"Scope": "local"

}

]

# 当然,此时你也可以通过查看 /var/lib/docker/volumes/ 目录来查看生成的 volume

$ sudo ll /var/lib/docker/volumes/

总用量 28

drwxr-xr-x 3 root root 4096 12月 25 16:10 325c3adc386ba4f649f429abe820d56212057a7dc475611a9639bcb66f41df7d

-rw------- 1 root root 32768 12月 25 16:10 metadata.db

2.3.4、启动两个 MySQL 容器,查看生成的 volume

# 启动第二个 MySQL 容器

$ sudo docker run --name test2_mysql -d -e MYSQL_ROOT_PASSWORD=password mysql:5.7

67358cf2b8feecd2274700e833de1a6565a207017cc42fd39e92b56b2537d7ab

# 查看 volumes

$ sudo docker volume ls

DRIVER VOLUME NAME

local 325c3adc386ba4f649f429abe820d56212057a7dc475611a9639bcb66f41df7d

local 06517fdc343cb927b11d4e1d97967066118ce92132435f5214ee97afae967769

# 查看 volumes

$ sudo ll /var/lib/docker/volumes/

总用量 32

drwxr-xr-x 3 root root 4096 12月 25 16:45 06517fdc343cb927b11d4e1d97967066118ce92132435f5214ee97afae967769

drwxr-xr-x 3 root root 4096 12月 25 16:10 325c3adc386ba4f649f429abe820d56212057a7dc475611a9639bcb66f41df7d

-rw------- 1 root root 32768 12月 25 16:45 metadata.db

2.3.4、自定义 volume 名称

以上例子可以看到有个问题,启动容器默认生成的 volume 名字是个很长的字符串,其实我们可以在启动容器时自定义一个 volume 名字。

首先通过 docker inspect 命令查看 MySQL 镜像的 volumes 信息,由以下结果可以看出,MySQL 默认会将 volumes 挂载在 /var/lib/mysql 下,也就是说,MySQL 镜像创建容器后,数据默认存储在容器的 /var/lib/mysql 中。

docker inspect 命令可以查看镜像、容器、网络、存储等等信息

# 查看MySQL镜像的volume信息

$ sudo docker inspect mysql:5.7|nl|grep Volumes -A 3

42 "Volumes": {

43 "/var/lib/mysql": {}

44 },

45 "WorkingDir": "",

--

79 "Volumes": {

80 "/var/lib/mysql": {}

81 },

82 "WorkingDir": "",

通过 -v 参数指定 volume 名称启动一个 MySQL 容器,格式为 -v 自定义volume名称:容器内数据目录 ,注意,如果本地有这个名称的 volume 就引用这个 volume ,没有则会新建自定义名称的volume。

# 通过 -v 参数指定 volume 名称

$ sudo docker run --name test3_mysql -v test3_mysql:/var/lib/mysql -d -e MYSQL_ROOT_PASSWORD=password mysql:5.7

a86213ba65d05296e0bbd22e8bfaff7c6e07ce9cb0818b734959323f728d5d4f

# 查看 volumes 信息

$ sudo docker volume ls

DRIVER VOLUME NAME

local 325c3adc386ba4f649f429abe820d56212057a7dc475611a9639bcb66f41df7d

local 06517fdc343cb927b11d4e1d97967066118ce92132435f5214ee97afae967769

local test3_mysql

你将看到自定义的 test3_mysql 这个 volume 。

2.3.5、引用已有 volume

前文提到,删除或停止某个容器后,对应的 volume 不会自动删除;而且不同的容器 可以同用一个相同的 volume 。

进入第三个 MySQL 容器,创建测试数据库

# 进入容器

$ sudo docker exec -it test3_mysql /bin/bash

# 进入 MySQL 终端

root@a86213ba65d0:/# mysql -uroot -p

Enter password:

Welcome to the MySQL monitor. Commands end with ; or \g.

Your MySQL connection id is 2

Server version: 5.7.28 MySQL Community Server (GPL)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

# 创建测试数据库

mysql> create database docker;

Query OK, 1 row affected (0.00 sec)

mysql> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| docker |

| mysql |

| performance_schema |

| sys |

+--------------------+

5 rows in set (0.00 sec)

mysql> exit

Bye

root@a86213ba65d0:/#

创建第四个 MySQL 容器,引用第三个 MySQL 容器的 volume

$ docker run --name test4_mysql -v test3_mysql -d -e MYSQL_ROOT_PASSWORD=password mysql:5.7

进入第四个 MySQL 容器,通过查看数据库信息,确认第四个 MySQL 容器是否引用了第三个 MySQL 容器的 volume 。

$ sudo docker exec -it test4_mysql /bin/bash

root@8b34ea93e59a:/# mysql -uroot -p

Enter password:

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

root@8b34ea93e59a:/# exit

此时你会发现无法连接到数据库,提示 “Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)” ,我是这样认为的, docker 不仅将数据存储在这个 volume 中,而且 mysqld.sock 也存储在此 volume 中,所以,当你在使用别的容器进行连接这个数据库文件时,就报出这样的错误了。如果仅仅存储数据问题,应该是不会出现这个错误的。(这部分完全自己想的,仅做参考,如有可靠答案,望告知)。

你可以将第三个容器停止后再次进行以上操作,如果你的第四个容器已经停止运行,你可以使用 docker logs 命令来查看容器退出的具体原因,你讲看到如下错误:Check that you do not already have another mysqld process using the same InnoDB data or log files. ,还有其他错误信息,总而言之就是因为资源不可用或者说被占用,导致容器终止运行。

# 停止第三个容器

$ sudo docker stop test3_mysql

test3_mysql

# 启动第四个容器

$ sudo docker start test4_mysql

test4_mysql

# 进入第四个容器

$ sudo docker exec -it test4_mysql /bin/bash

root@8b34ea93e59a:/# mysql -uroot -p

Enter password:

Welcome to the MySQL monitor. Commands end with ; or \g.

Your MySQL connection id is 2

Server version: 5.7.28 MySQL Community Server (GPL)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

# 查看数据库

mysql> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| docker |

| mysql |

| performance_schema |

| sys |

+--------------------+

5 rows in set (0.00 sec)

mysql>

以上操作可以看出,第四个容器和第三个容器使用的是同一个 volume ,你也可以使用 docker volume ls 来查看 volume ,会看到并没有生成新的 volume ,一共还是三个 volume。

以上便是基于 volume 的数据存储方式。

3、Docker 数据持久化之 bind mounts

3.1、bind mounts 简介

bind mounts 的方式实际上可以理解为将本地目录映射给容器内数据存储的目录,本地目录内的内容发生改变,容器内对应的内容也发生变化,同样的,在容器内操作数据目录,本地的目录文件也会发生相同的变化。

3.2、bind mounts 实验

接下来,通过实际操作体验一下 bind mount 的方式。

3.2.1、运行一个 Nginx 容器

运行一个Nginx容器,将本地 /root/testfile 挂载到容器的 /root/testfile 下,如果本地及容器没有这个目录,则会自动创建。

# 运行一个 Nginx 容器

$ sudo docker run --name test1_nginx -d -P -v /root/testfile:/root/testfile nginx

6ef8b9785f8495865f43454fd1b0a261c4a813703019ec0de7bbfc948a9e7caa

# 查看本地的目录

$ sudo ls

testfile

3.2.2、修改文件测试 bind mount 挂载方式

# 进入容器创建文件并写入测试数据

$ sudo docker exec -it test1_nginx /bin/bash

root@6ef8b9785f84:/# ls

bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

root@6ef8b9785f84:/# ls /root

testfile

root@6ef8b9785f84:/# cd /root/testfile/

root@6ef8b9785f84:~/testfile# echo "test docker date" >> hello.txt

root@6ef8b9785f84:~/testfile# cat hello.txt

test docker date

# 另开个shell,在本地查看测试文件

$ sudo ls /root/testfile/

hello.txt

$ sudo cat /root/testfile/hello.txt

test docker date

# 修改测试文件

$ sudo echo "test ok" >> hello.txt

$ sudo cat /root/testfile/hello.txt

test docker date

test ok

# 返回容器终端查看测试文件

$ sudo docker exec -it test1_nginx /bin/bash

root@6ef8b9785f84:/# cat /root/testfile/hello.txt

test docker date

test ok

3.3.3、共享数据目录

你可以在运行一个容器,然后通过以上方法测试数据的变化情况,会得出结论:不同的容器,可以挂载相同的数据目录。

# 运行一个新的nginx容器

$ sudo docker run --name test2_nginx -d -P -v /root/testfile:/root/testfile nginx

# 进入容器创建文件并写入测试数据

$ sudo docker exec -it test2_nginx /bin/bash

...操作内容省略...

以上操作可以看出,通过 bind mount 方式,可以实现容器与本地数据文件目录的映射,数据是同步的。

4、Docker 数据管理之tmpfs

tmpfs挂载仅存储在主机系统的内存中,并且永远不会写入主机系统的文件系统中。

tmpfs挂载不会持久化在磁盘上,无论是在Docker主机上还是在容器内。容器在其生存期内可以使用它来存储非持久状态或敏感信息。

绑定 volume 和 bind mount 都可以使用-v或 --volume标志安装到容器中,但是两者的语法略有不同。对于tmpfs ,使用--tmpfs,此方式不太常用,就不说了,感兴趣可以去官网或者找资料学习。

5、总结

5.1、volume 的使用场景

volume是将数据持久保存在Docker容器和服务中的首选方法。volume的一些使用场景:

在多个运行中的容器之间共享数据。如果未显式创建volume,则在首次运行容器中时创建volume,当该容器停止或卸下时,该 volume 仍然存在,多个容器可以同时使用相同的volume(可读写或只读);

支持将容器的数据存储在远程主机或云上;

当您需要将数据从一个Docker主机备份、还原或迁移到另一个Docker主机时,volume 是一个更好的选择,您可以停止使用该卷的容器,然后备份该卷的目录(例如/var/lib/docker/volumes/)。

5.2、bind mounts 的使用场景

通常,应尽可能使用 volume,bind mounts 适用于以下场景:

将配置文件从主机共享到容器,默认情况下,这就是Docker通过/etc/resolv.conf从主机安装到每个容器的方式为容器提供DNS解析的方式 。

在Docker主机上的开发环境和容器之间共享源代码或构建工件。例如,您可以将Maven target/ 目录 mount 到容器中,这样每次在 Docker 主机上构建Maven项目时,容器都可以访问重建的工件。

当确保Docker主机的文件或目录结构与容器所需的一致时。

5.3、tmpfs挂载的好用例

当您不希望数据在主机上或容器内持久存在时,当您的应用程序需要写入大量非持久状态数据时,mpfs挂载最适合使用;这是出于安全原因或为了保护容器的性能。

参考链接:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值