伸手摘星,即使一无所获,亦不致满手污泥。
请关注公众号:星河之码
我们在运行一个docker容器的时候,会产生一系列的数据文件,这些数据文件在我们删除docker容器的时候也会被删除,如果当我们希望把这些数据留下来当做其他的用途的时候,就需要将docker的文件持久化下来,实现数据共享,那么这个数据怎么共享呢。
在docker中提供了两种方实现宿主机与容器之间的数据共享:
- cp命令
- Docker数据卷
下面就来看看这种种方式是怎么用的
一、cp命令
docker cp 命令用于容器与主机之间的数据拷贝,它是docker最原始的管理数据方式,基本不会用到。但是还是要了解一下的
1.1 docker cp 介绍
-
主要作用
在容器和主机之间复制文件/文件夹
-
语法格式
#宿主机文件复制到容器内 docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH #容器内文件复制到宿主机 docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH- container :容器名字或者Id,可以是正在运行或已停止的容器
- SRC_PATH 或 DEST_PATH可以是文件或目录,当 DEST_PATH不存在时,会自动创建
- 该命令会默认容器路径相对于容器的 /(根)目录
- 主机路径则是相对于执行 docker cp 命令的当前目录
-
常用参数说明
option 作用 -a 存档模式(复制所有uid / gid信息) -L 保持源目标中的链接 这两个参数一般都用不到
1.2 SRC_PATH 和 DEST_PATH
SRC_PATH是指的被cp的文件, DEST_PATH是目标地址,它们都可以是文件或者目录,所以主要可以分为一下几种情况
-
SRC_PATH 是文件,DEST_PATH 不存在
创建 DEST_PATH 所需的文件夹,文件正常保存到创建的DEST_PATH 中
-
SRC_PATH 是文件,DEST_PATH 不存在,并以 / 结尾
报错:目标目录不能直接在/下,并以 / 结尾 目标目录则必须存在
-
SRC_PATH 是文件,DEST_PATH 存在且是一个文件
DEST_PATH 被源文件的内容覆盖
-
SRC_PATH 是文件,DEST_PATH 存在且是一个文件夹
SRC_PATH 的文件会被复制到DEST_PATH文件夹中
-
SRC_PATH是文件夹,DEST_PATH 不存在
创建 DEST_PATH 文件夹,并将源文件夹的内容复制到该文件夹中
-
SRC_PATH是文件夹, DEST_PATH存在并且是一个文件
错误:无法将文件夹复制到文件
-
SRC_PATH是文件夹,DEST_PATH存在并且是文件夹
- SRC_PATH 不以 /. 结尾,源文件夹复制到此DEST_PATH文件夹中
- SRC_PATH 以 /. 结尾,源文件夹的内容被复制到DEST_PATH文件夹中
1.3 docker cp 案例
- 以nginx镜像为例
docker pull nginx:1.19.3-alpine
- 宿主机文件 copy to 容器内
#启动nginx容器
docker run -itd --name nginx -p 80:80 nginx:1.19.3-alpine
#在宿主机的/data 创建一个index.html 并且追加一些内容
cd /data
echo "星河之码" > /data/index.html
#将宿主机/data 下的index.html cp 到 nginx 容器的 /usr/share/nginx/html/index.html
docker cp /data/index.html nginx:/usr/share/nginx/html/index.html
- 容器内文件 copy to 宿主机
#启动nginx容器
docker run -itd --name nginx -p 80:80 nginx:1.19.3-alpine
#将nginx容器下的/etc/nginx/nginx.conf 文件 cp 到宿主机的/data
docker cp nginx:/etc/nginx/nginx.conf /data
文件的用法也是一样的
二、数据卷
2.1 什么是数据卷
数据卷(Data Volumes)是一个可供一个或多个容器使用的特殊目录,它将主机操作系统目录直接映射进容器。
数据卷存在于宿主机中,独立于容器,和容器的生命周期是分离的,数据卷存在于宿主机的文件系统中,数据卷可以目录也可以是文件,容器可以利用数据卷与宿主机进行数据共享,实现了容器间的数据共享和交换。
docker容器数据卷可以看成一个u盘,它可以存在于一个或多个的容器中,由docker挂载到容器,但不属于联合文件系统,Docker不会在容器删除时删除其挂载的数据卷。
docker容器数据卷的特点:
- 数据卷可以在容器之间共享或重用数据
- 数据卷中的更改可以立即生效
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷默认会一直存在,即使容器被删除
- 数据卷的生命周期一直持续到没有容器使用它为止
容器中的管理数据主要有两种方式:
- 数据卷:Data Volumes 容器内数据直接映射到本地主机环境
- 数据卷容器:Data Volume Containers 使用特定容器维护数据卷
注意事项:
-
docker官网推荐尽量进行目录挂载,不要进行文件挂载
前面虽然介绍可以挂载一个文件,但是我们一般不这么做,都是直接挂载整个目录
-
挂载数据卷,一般通过run而非create/start创建启动容器
run 可以启动的同时通过-v参数挂载,而create/start命令创建启动容器后,然后再挂载数据卷,要修改很多配置文件,非常麻烦。
2.2 数据卷类型
一般用数据卷来持久化数据,而数据卷类型有三种:
-
宿主机数据卷(推荐)
数据卷存在于宿主机的文件系统中,但是容器可以访问。
-
命名的数据卷
磁盘上Docker管理的数据卷,它有个自己的名字。
-
匿名数据卷
磁盘上Docker管理的数据卷,它没有名字,所以不太容易找到,由Docker来管理这些文件。
数据卷一般都在宿主机文件系统里面(网络文件系统等情况下除外),宿主机数据卷是在宿主机内的指定目录下,而其他两种则在docker管理的目录下,一般是 /var/lib/docker/volumes/目录下
2.3 宿主机数据卷
2.3.1 宿主机数据卷介绍
宿主机数据卷(bind mounts):容器内的数据被存放到宿主机文件系统的任意指定位置,也可以挂载到特定的文件系统中,不由docker管理,除了docker之外的进程也可以任意对他们进行修改。
当使用bind mounts时,宿主机的目录或文件被挂载到容器中。容器将按照挂载目录或文件的绝对路径来使用或修改宿主机的数据。宿主机中的目录或文件不需要预先存在,在需要的使用会自动创建。
-
宿主机有一个目录妥善结构化的文件系统时,bind mounts在性能上是有很大优势
-
bind mounts的容器可以在通过容器内部的进程对主机文件系统进行修改,包括创建,修改和删除重要的系统文件和目录
也因为docker可以操作宿主机的文件,因此可能会影响到宿主机上Docker以外的进程,或造成安全方面的影响
2.3.2 宿主机数据卷数据覆盖问题
-
如果挂载一个空的数据卷到容器中的一个非空目录中,那么容器的非空目录下的文件会被复制到数据卷中。
-
如果挂载一个非空的数据卷到容器中的一个目录中,那么容器中的目录会显示数据卷中的数据。
也即是说只要数据卷中的数据非空,即使容器中的目录有数据,这些容器原始数据会被隐藏掉,而使用数据卷的数据
2.2.3 宿主机数据卷的语法使用
-
语法
docker run -v /宿主机绝对路径目录:/容器内目录 镜像名例如:启动mysql 的时候
docker run -itd --name mysql -v /data/mysql:/var/lib/mysql mysql:5.7.31
当然上述命令只是演示一下-v的写法,实际上启动mysql 还可以加上其他的一些参数,完整命令如下
docker run -itd --name mysql --restart always --privileged=true -p 3306:3306 -e MYSQL_ROOT_PASSWORD=admin -v /data/mysql:/var/lib/mysql mysql:5.7.31 -- character-set-server=utf8 --collation-server=utf8_general_ci
-
拉取MySQL镜像
docker pull mysql:5.7.31 -
运行镜像
docker run -itd --name mysql --restart always --privileged=true -p 3306:3306 -e MYSQL_ROOT_PASSWORD=admin -v /data/mysql:/var/lib/mysql mysql:5.7.31 --character-set-server=utf8 --collation-server=utf8_general_ci这里是直接运行mysql镜像,并且将宿主机的/data/mysql目录挂载到容器的/var/lib/mysql
我们一般推荐先在宿主机下创建好/data/mysql,再进行数据挂载,不然可能有权限问题
2.2.4 容器目录权限
docker run -v 有三种模式,可以控制对容器内文件的读写权限,分别是ro,rw,不指定,其用法如下:
语法
ro:readonly 只读
rw:readwrite 可读可写
docker run -it -v /宿主机绝对路径目录:/容器内目录: 镜像名
docker run -it -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
docker run -it -v /宿主机绝对路径目录:/容器内目录:rw 镜像名
例如:
docker run -d -P --name mysql -v edwinMysql:/etc/mysql: nginx
docker run -d -P --name mysql -v edwinMysql:/etc/mysql:ro nginx
docker run -d -P --name mysql -v edwinMysql:/etc/mysql:rw nginx
-
不指定(默认)
-
文件:
- 宿主机修改文件后容器里面看不到变化
- 容器里面修改该文件,宿主机也看不到变化
-
文件夹:不管是宿主机还是容器内修改、新增、删除文件 都会相互同步
所以我们一般直接挂载整个目录
-
-
ro(readonly 只读)
- 文件:容器内不能修改,会提示read-only
- 文件夹:容器内不能修改、新增、删除文件夹中的文件,会提示read-only
-
rw(readwrite 可读可写)
-
文件:不管是宿主机还是容器内修改,都会相互同步
但容器内不允许删除,会提示Device or resource busy
宿主机删除文件,容器内的不会被同步
-
文件夹:不管是宿主机还是容器内修改、新增、删除文件,都会相互同步
-
2.2.5 宿主机目录权限
既然容器目录有权限,那么宿主机目录肯定也有权限,docker推荐我们先创建宿主机目录,然后挂载也是为了提前赋权,因为某些镜像挂载需要宿主机的赋权可以,不然会报错,比如nexus3,下面就以nexus3为例,来看看宿主机目录权限
2.2.5.1 不挂载启动nexus3
-
nexus3的docker官网
https://hub.docker.com/r/sonatype/nexus3 -
拉取镜像
docker pull sonatype/nexus3:3.28.1 -
备份镜像
docker save sonatype/nexus3:3.28.1 -o sonatype.nexus3.3.28.1.tar -
导入镜像
docker load -i sonatype.nexus3.3.28.1.tar -
运行容器
docker run -d -p 8081:8081 --name nexus3 sonatype/nexus3:3.28.1 -
查看启动日志
docker logs -f nexus3 -
进入容器查找初始化密码
#进入容器 docker exec -it nexus3 /bin/bash #/nexus-data目录 cd /nexus-data/ #查看密码 vi admin.password 或者 cat admin.password当使用cat admin.password 命令的时候,密码会跟目录一起显示,如上图,如果觉得不好看可以使用
vi admin.password
-
浏览器端访问
http://192.168.242.128:8081/第一次登陆会让我们修改密码,设置一个新密码就可以
2.2.5.2 挂载目录启动nexus3
-
退出容器
exit -
删除所有运行容器的命令
docker rm $(docker stop $(docker ps -aq))这是删除刚才启动的容器,如果没有执行上面的不挂载启动,不要执行,这只是为了试验,所以启动了多次,需要把之前的删除,删除也有很多种方式,可以自由选择,这个命令会把所有容器都停止然后删除
-
数据卷挂载启动
docker run -d -p 8081:8081 --name nexus3 -v /data/nexus3/:/nexus-data/ sonatype/nexus3:3.28.1拉取的动作上面已经做了,这里就不再重复了
将/nexus-data/挂载到宿主机的/data/nexus3下,这样即使容器停了,下载jar 不会被删除
nexus3下载的jar就是在/nexus-data下
-
查看容器启动日志
docker logs -f nexus3报错信息如下:
而报错的原因以及解决方案,在官网也给出了说明文档
-
删除容器
docker rm -f nexus3报错信息告诉我们没有权限,就先删除感刚刚启动失败的容器,然后创建目录赋权后在重新运行
-
先在宿主机创建挂载目录
mkdir /data/nexus3 -
为挂载目录授权
chown -R 200 /data/nexus3开发环境中推荐为挂载目录授最高权限777;生产环境需要查看官网文档,结合实际生产环境进行授权,这里是200的权限
-
再次运行容器
docker run -d -p 8081:8081 --name nexus3 -v /data/nexus3/:/nexus-data/ sonatype/nexus3:3.28.1 -
查看容器启动日志
docker logs -f nexus3 #成功启动nexus3
2.5 命名的数据卷
命名的数据卷顾名思义就是给数据卷起一个名字,以nginx为例,看看命名的数据卷是怎么挂载的
-
拉取基础镜像
docker pull nginx:1.19.3-alpine -
挂载数据卷启动容器
docker run -itd --name nginx -p 80:80 -v edwin-nginx:/etc/nginx nginx:1.19.3-alpine以上命令,没有宿主机目录位置,只有一个名字和一个容器的目录,edwin-nginx 就是数据卷的名字
-
查看docker数据卷
docker volume ls -
查看edwin-nginx宿主机目录
docker volume inspect edwin-nginx -
进入docker数据卷默认目录
cd /var/lib/docker/volumes/edwin-nginx前文提到命名的数据卷和匿名数据卷的目录在docker管理下,一般是 /var/lib/docker/volumes/目录下
-
查看文件 ls
会发现edwin-nginx有一个 data目录 ,所有的文件docker默认保存在_data目录中
cd _data -
删除容器
此时当我们删除容器后,会发现宿主机中的数据还存在
docker rm $(docker stop $(docker ps -aq))
2.6 匿名数据卷
命名的数据卷顾名思义就是没有给数据卷起一个名字,还是以nginx为例,看看匿名的数据卷是怎么挂载的
-
拉取基础镜像
docker pull nginx:1.19.3-alpine -
挂载数据卷启动容器
docker run -itd --name nginx -p 80:80 -v /etc/nginx nginx:1.19.3-alpine以上命令,没有宿主机目录位置,也没有起名字,只有一个容器的目录
-
查看docker数据卷
docker volume ls -
查看宿主机目录
docker volume inspect 59400fa952cf41e9c11c599812290391edcb4b4b0a310dbba95141842964b2e159400fa952cf41e9c11c599812290391edcb4b4b0a310dbba95141842964b2e1 是容器Id,
-
进入docker数据卷默认目录
cd /var/lib/docker/volumes/59400fa952cf41e9c11c599812290391edcb4b4b0a310dbba95141842964b2e1前文提到命名的数据卷和匿名数据卷的目录在docker管理下,一般是 /var/lib/docker/volumes/目录下
-
查看文件 ls
会发现dbd07daa4e40148b11.... 下有一个 data目录 ,所有的文件docker默认保存在_data目录中
cd _data -
查看id宿主机目录
docker volume inspect 59400fa952cf41e9c11c599812290391edcb4b4b0a310dbba95141842964b2e1 -
删除容器
此时当我们删除容器后,同样会发现宿主机中的数据还存在
docker rm $(docker stop $(docker ps -aq))
通过上面的演示,发现命名数据卷和匿名数据卷本质上区别不大,就是启动的时候不指定名字,在docker数据卷默认目录分别创建了一个以名字和id的挂载目录。
2.7 清理数据卷
在上面的演示中最后一步我们删除容器后,发现数据卷仍然存在,这个时候如果确认这些数据卷是没有用的,就需要去清理它,不然会占用资源
-
查看docker数据卷
docker volume ls可以看到,虽然容器删除了,但是数据卷仍然存在,暂用空间。可以通过prune命令清理
-
清理数据卷
docker volume prune -
查看docker数据卷
docker volume ls数据卷被清理,资源被释放
三、数据卷容器
实现容器之间的数据同步,其他容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称之为数据卷容器。
- 如果需要在多个容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器。
- 数据卷容器也是一个容器,它的目的是专门用来提供数据卷给其他容器挂载。
数据卷容器一般也被称为父容器
基本结构如下:
通过这个图可以看出来,数据卷容器就是介于容器和宿主机之间的一个容器,数据卷容器挂载到宿主机,其他容器跟数据卷容器进行挂载,从而到底多个容器数据共享的目的
-
语法
docker run --volumes-from:通过--volumes-from:进行挂载
下面来实现上图中两个Nginx的挂载
-
基础镜像
docker pull centos:7.8.2003 docker pull nginx:1.19.3-alpine这里是使用centos:7.8.2003作为一个数据卷容器
-
在宿主机中创建挂载目录
mkdir /data/mountFile -
为挂载目录授权
chown -R 777 /data/mountFile/ -
启动父镜像并挂载到宿主机
docker run -d --name data-volume -v /data/mountFile:/usr/nginx centos:7.8.2003可以发现,这个父容器虽然启动了,但是它并没有执行,这在官网也有解释,可以不用管
创建好的数据卷容器是处于停止运行的状态,因为使用 —volumes-from 参数所挂载数据卷的容器 自己并不需要保持在运行状态。
-
启动子容器nginx1
创建
nginx1,让它继承(关键字:--volumes-from)data-volume容器docker run -itd --name nginx1 -p 80:80 --volumes-from data-volume nginx:1.19.3-alpine -
进入容器
docker exec -it nginx1 /bin/shdocker exec -it nginx1 /bin/bash会报错
-
在nginx1中创建一个index.html文件
echo "Edwin nginx" > /usr/nginx/index.html -
退出容器
exit -
查看宿主机中是否存在index.html
cd /data/mountFile发现在容器nginx1中创建的文件,在宿主机的挂载目录下也有同步了过来
-
启动子容器nginx2
创建
nginx2,让它继承(关键字:--volumes-from)data-volume容器docker run -itd --name nginx2 -p 81:80 --volumes-from data-volume nginx:1.19.3-alpine -
进入容器nginx2
docker exec -it nginx2 /bin/sh -
在nginx2中创建一个index2.html文件
echo "Edwin nginx2" > /usr/nginx/index2.html -
退出容器nginx2
exit -
查看宿主机中是否存在index2.html
cd /data/mountFile ls进入容器中,发现文件同步(实现文件共享)