「伸手摘星,即使一无所获,亦不致满手污泥」。
「请关注公众号:星河之码」
我们在运行一个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 59400fa952cf41e9c11c599812290391edcb4b4b0a310dbba95141842964b2e1
59400fa952cf41e9c11c599812290391edcb4b4b0a310dbba95141842964b2e1 是容器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/sh
docker 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进入容器中,发现文件同步(实现文件共享)
原文始发于微信公众号(星河之码):Docker中数据卷挂载
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/60870.html