docker的理念回顾:
将应用和运行的环境一起打包形成一个镜像发布出去。
存在问题:
如果我们的数据都在容器中,那么我们把容器删除之后,数据就会丢失。假如有个MySQL容器,如果将这个容器删了,那存的数据也没有了。
解决方案:
所以我们想要数据持久化。我们需要**容器的持久化和同步操作,容器间也是可以数据共享的。**就出现了容器数据卷的技术,容器之间有一个数据共享的技术。docker容器产生的数据同步到本地。说白了就是目录的挂载,将我们容器的目录挂载到Linux上面。
1、什么是容器数据卷?
数据卷(Data Volumes)是宿主机中的一个目录或文件,数据卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。当容器目录和数据卷目录绑定后,对方的修改会立即同步,一个数据卷可以被多个容器同时挂载,一个容器也可以被挂载多个数据卷。
数据卷特性
- 数据卷可以在容器之间共享和重用,本地与容器间传递数据更高效
- 对数据卷的修改会立马有效,在容器内部与本地目录均可对数据卷进行修改
- 对数据卷的更新,不会影响镜像,对数据与应用进行了解耦操作
- 卷会一直存在,直到没有容器使用
2、挂载方式
目前Docker提供了三种不同的方式将数据从宿主机挂载到容器中:
1)volumes(最常用的方式)
Docker管理宿主机文件系统的一部分,默认位于 /var/lib/docker/volumes 目录中;
如果在创建时没有指定卷,Docker会默认创建许多匿名(就上面这一堆很长ID的名字)卷。
2)bind mounts(比较常用的方式)
意为着可以存储在宿主机系统的任意位置;
但是 bind mount 在不同的宿主机系统时不可移植的,比如 Windows 和 Linux 的目录结构是不一样的,bind mount 所指向的 host 目录也不能一样。
这也是为什么 bind mount 不能出现在 Dockerfile 中的原因,因为这样 Dockerfile 就不可移植了。
3)tmpfs(一般都不会用的方式)
挂载存储在宿主机系统的内存中,而不会写入宿主机的文件系统;
示意图如下:
3、使用数据卷
Volume 的基本使用
# 创建一个自定义容器卷
$ docker volume create [容器卷名]
# 查看所有容器卷
$ docker volume ls
# 查看指定容器卷详情信息
$ docker volume inspect [容器卷名]
# 删除自定义数据卷
$ docker volume rm [容器卷名]
直接使用命令来挂载 -v
# -v 代表挂载数据卷 目录以 / 开头
docker run -it -v [主机目录|容器卷名]:[容器内目录]
# 如果没有通过 -v 指定,那么Docker会默认帮我们创建匿名数据卷进行映射和挂载。
测试
[root@localhost volumes]$ docker run -id -v /home/ceshi:/home centos /bin/bash
c525e970834d3e4b97fdb696151b9fd9d6c35cfab3c723db6bc3e8de4e1a581e
[root@localhost volumes]$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c525e970834d centos "/bin/bash" 16 seconds ago Up 14 seconds unruffled_sammet
[root@localhost volumes]$ docker inspect c525e970834d
.....
"Mounts": [ # 挂载
{
"Type": "bind",
"Source": "/home/ceshi", # 主机内目录
"Destination": "/home", # 容器内目录
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
....
再次测试
1、停止容器
2、宿主机上修改文件
3、启动容器
4、容器的数据依旧是同步的
好处:以后修改只需要在本地修改即可,容器内会自动同步!
4、练习 MySQL数据同步
思考:MySQL 数据持久化的问题
# 下载镜像
docker pull mysql:5.7
-d 后台运行
-p 端口映射
-v 卷挂载
-e 环境配置
--name 容器名字
# 运行容器,需要做数据挂载
# 注意:安装MySQL的时候是需要配置密码的
# 官方例子:https://hub.docker.com/_/mysql
# $ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# 启动命令
docker run -d -p 3306:3306 --name mysql \
-v /home/mysql/conf:/etc/mysql \
-v /home/mysql/data:/var/lib/mysql \
-v /home/mysql/log:/var/log/mysql \
-e MYSQL_ROOT_PASSWORD=root mysql:5.7
# 启动成功之后,使用 Navicat 连接测试
# 防火墙记得开启 3306 端口 账号:root
新建 test
数据库做测试,可以看到生成的数据库的文件被挂载出了:
删除容器后,发现挂载到本地的数据卷依旧没有丢失,这就实现了容器数据持久化功能!
5、具名挂载和匿名挂载
5.1、匿名挂载
不指定挂载到主机上的路径,只指定容器内部需要挂载的目录。
例如:
docker run -d -P --name nginx -v /etc/nginx nginx
5.2、具名挂载
给挂载位置添加名字
例如:
# -v 卷名:容器内目录
docker run -d -P --name nginx -v juming-nginx:/etc/nginx nginx
默认位于 /var/lib/docker/volumes
目录中
5.3、判断挂载方式
# 如何确定具名挂载还是指定路径挂载
-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v /宿主机路径:容器内路径 # 指定路径挂载
5.4、拓展
# 通过 -v 容器内路径:ro rw 改变读写权限
ro readonly # 只读
rw readwrite # 可读可写
# 一旦设置了容器权限,容器对我们挂载出来的内容就有限定了!
$ docker run -d -P --name nginx -v juming-nginx:/etc/nginx:ro nginx
$ docker run -d -P --name nginx -v juming-nginx:/etc/nginx:rw nginx
# ro 只要看到 ro 就说明这个路径只能通过宿主机来操作,容器内部是无法操作的!
6、初识 DockerFile
DockerFile 就是用来构建 docker 镜像的构建文件!本质是命令脚本!
通过脚本生成镜像,镜像是一层一层的,脚本一个一个命令,每个命令都是一层!
测试
1、准备工作
# 1、在 /home 目录下新建一个docker-test-volume 文件夹
mkdir docker-test-volume
# 在编写 DockerFile 文件中使用 VOLUME 指令来给镜像添加一个或多个数据卷
vim dockerfile1
2、编写内容
# 2、编写内容
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "-----end-----"
CMD /bin/bash
# 这里每个命令就是镜像的一层
3、构建镜像
# 3、build后生成镜像,获得一个新镜像 mianbao/centos
# . 表示当前目录
[root@localhost docker-test-volume]$ docker build -f /home/docker-test-volume/dockerfile1 -t mianbao/centos:1.0 .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM centos
---> 5d0da3dc9764
Step 2/4 : VOLUME ["volume01","volume02"]
---> Running in 8e82ebee8002
Removing intermediate container 8e82ebee8002
---> c305de8aa209
Step 3/4 : CMD echo "-----end-----"
---> Running in 8cd33b47383f
Removing intermediate container 8cd33b47383f
---> b682a519e338
Step 4/4 : CMD /bin/bash
---> Running in 9778993b03c3
Removing intermediate container 9778993b03c3
---> 17b534236794
Successfully built 17b534236794
Successfully tagged mianbao/centos:1.0
[root@localhost docker-test-volume]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mianbao/centos 1.0 17b534236794 22 seconds ago 231MB
4、启动容器
[root@localhost docker-test-volume]$ docker run -it 17b534236794 /bin/bash
[root@ac30d43bfd39 /]$ ls -l
total 0
lrwxrwxrwx. 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxr-xr-x. 5 root root 360 Jun 29 15:47 dev
drwxr-xr-x. 1 root root 66 Jun 29 15:47 etc
drwxr-xr-x. 2 root root 6 Nov 3 2020 home
lrwxrwxrwx. 1 root root 7 Nov 3 2020 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 Nov 3 2020 lib64 -> usr/lib64
drwx------. 2 root root 6 Sep 15 2021 lost+found
drwxr-xr-x. 2 root root 6 Nov 3 2020 media
drwxr-xr-x. 2 root root 6 Nov 3 2020 mnt
drwxr-xr-x. 2 root root 6 Nov 3 2020 opt
dr-xr-xr-x. 105 root root 0 Jun 29 15:47 proc
dr-xr-x---. 2 root root 162 Sep 15 2021 root
drwxr-xr-x. 11 root root 163 Sep 15 2021 run
lrwxrwxrwx. 1 root root 8 Nov 3 2020 sbin -> usr/sbin
drwxr-xr-x. 2 root root 6 Nov 3 2020 srv
dr-xr-xr-x. 13 root root 0 Jun 29 15:18 sys
drwxrwxrwt. 7 root root 171 Sep 15 2021 tmp
drwxr-xr-x. 12 root root 144 Sep 15 2021 usr
drwxr-xr-x. 20 root root 262 Sep 15 2021 var
# 这个目录就是我们生成镜像时自动挂载的,数据卷目录!(匿名挂载)
drwxr-xr-x. 2 root root 6 Jun 29 15:47 volume01
drwxr-xr-x. 2 root root 6 Jun 29 15:47 volume02
# 在容器内创建文件测试
[root@723ec0b590f5 /]$ cd volume01
[root@723ec0b590f5 volume01]$ touch container.txt
[root@723ec0b590f5 volume01]$ ls
container.txt
5、查看挂载的目录
[root@localhost ~]$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
723ec0b590f5 17b534236794 "/bin/bash" 2 minutes ago Up 2 minutes nice_roentgen
[root@localhost ~]$ docker inspect 723ec0b590f5
在容器外查看挂载的目录是否有该文件
[root@localhost ~]$ cd /var/lib/docker/volumes/16e62338a333ac0640c982f28dd89a975d17a73757d77c3509b479b8cb9205be/_data
[root@localhost _data]$ ls
container.txt
这种方式未来使用的较多 ,因为我们通常会构建自己的镜像!
假设构建镜像时没有挂载卷,就要手动镜像挂载 -v 卷名:容器内路径!
7、数据卷容器
多个mysql同步数据!
被挂载的容器是父容器。
测试
启动三个容器,通过我们刚才自己写的镜像启动
# 启动 docker01 容器
[root@localhost ~]$ docker run -it --name docker01 mianbao/centos:1.0
[root@5c2f15dec8a5 /]$ ls -l
................
# 数据卷
drwxr-xr-x. 2 root root 6 Jun 30 14:45 volume01
drwxr-xr-x. 2 root root 6 Jun 30 14:45 volume02
[root@localhost ~]$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5c2f15dec8a5 mianbao/centos:1.0 "/bin/sh -c /bin/bash" 2 minutes ago Up 2 minutes docker01
再启动一个容器
# 启动 docker02 容器
[root@localhost ~]$ docker run -it --name docker02 --volumes-from docker01 mianbao/centos:1.0
[root@7ee3ba5906c9 /]$ ls -l
...............
# 数据卷
drwxr-xr-x. 2 root root 6 Jun 30 14:45 volume01
drwxr-xr-x. 2 root root 6 Jun 30 14:45 volume02
在 docker01
这个容器中创建文件:
# 进入 docker01 容器
[root@localhost ~]$ docker attach 5c2f15dec8a5
[root@5c2f15dec8a5 /]$ ls -l
drwxr-xr-x. 2 root root 6 Jun 30 14:45 volume01
drwxr-xr-x. 2 root root 6 Jun 30 14:45 volume02
# 查看数据卷下文件,为空
[root@5c2f15dec8a5 /]$ cd volume01/
[root@5c2f15dec8a5 volume01]$ ls -l
total 0
# 创建文件
[root@5c2f15dec8a5 volume01]$ touch docker01
再查看 docker02
的 volume01
文件夹下是否同步了这个文件:
# 查看数据卷下文件,已同步
[root@7ee3ba5906c9 /]$ cd volume01/
[root@7ee3ba5906c9 volume01]$ ls -l
total 0
-rw-r--r--. 1 root root 0 Jun 30 14:53 docker01
再启动一个容器 docker03
[root@localhost ~]$ docker run -it --name docker03 --volumes-from docker01 mianbao/centos:1.0
[root@83d670962cc1 /]$ cd volume01
[root@83d670962cc1 volume01]$ ls
docker01
[root@83d670962cc1 volume01]$ touch docker03
[root@83d670962cc1 volume01]$ ls
docker01 docker03
# docker01 和 docker02 上的数据卷也同步了这个文件。
命令的理解:
--volumes-from docker01
# son extend father 数据卷容器
第一个容器删了,另外两个的数据卷文件也都还在!
只要通过 --volumes-from [容器名]
就能实现容器间的数据共享。
**理解:**容器共享宿主机的某个文件夹,共享文件夹里的内容是可以双向操作的,但是删除容器以后,只是解除了被删除容器的共享连接,并没有操作共享文件夹的内容,所以共享的文件还在,其他容器还能正常实现数据共享。
MySQL 实现数据共享
docker run -d -p 3305:3306 --name mysql_01 \
-v /home/mysql/conf:/etc/mysql \
-v /home/mysql/data:/var/lib/mysql \
-v /home/mysql/log:/var/log/mysql \
-e MYSQL_ROOT_PASSWORD=root mysql:5.7
docker run -d -p 3306:3306 --name mysql_02 \
--volumes-from mysql_01 \
-e MYSQL_ROOT_PASSWORD=root mysql:5.7
结论
- 容器之间配置信息的传递,存储在本机的文件则会一直保留!数据卷的生命周期一直持续到没有容器使用它为止。
- 一旦持久化到了本地,本地的数据是不会删除的!