Docker中数据卷挂载

「伸手摘星,即使一无所获,亦不致满手污泥」

「请关注公众号:星河之码」

我们在运行一个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

    Docker中数据卷挂载
  • 「拉取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
    Docker中数据卷挂载

    当使用cat admin.password 命令的时候,密码会跟目录一起显示,如上图,如果觉得不好看可以使用

    vi admin.password

  • 「浏览器端访问」

    http://192.168.242.128:8081/
    Docker中数据卷挂载

    第一次登陆会让我们修改密码,设置一个新密码就可以

    Docker中数据卷挂载
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中数据卷挂载

    而报错的原因以及解决方案,在官网也给出了说明文档

    Docker中数据卷挂载
  • 「删除容器」

    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
    Docker中数据卷挂载

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
    Docker中数据卷挂载
  • 「查看edwin-nginx宿主机目录」

    docker volume inspect edwin-nginx
    Docker中数据卷挂载
  • 「进入docker数据卷默认目录」

    cd /var/lib/docker/volumes/edwin-nginx

    前文提到命名的数据卷和匿名数据卷的目录在docker管理下,一般是 /var/lib/docker/volumes/目录下

  • 「查看文件 ls」

    会发现edwin-nginx有一个 data目录 ,「所有的文件docker默认保存在_data目录中」

    cd _data
    Docker中数据卷挂载
  • 「删除容器」

    此时当我们删除容器后,会发现宿主机中的数据还存在

    docker rm $(docker stop $(docker ps -aq))
    Docker中数据卷挂载

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中数据卷挂载
  • 「进入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中数据卷挂载
  • 「删除容器」

    此时当我们删除容器后,同样会发现宿主机中的数据还存在

    docker rm $(docker stop $(docker ps -aq))

「通过上面的演示,发现命名数据卷和匿名数据卷本质上区别不大,就是启动的时候不指定名字,在docker数据卷默认目录分别创建了一个以名字和id的挂载目录」

2.7 清理数据卷

在上面的演示中最后一步我们「删除容器后,发现数据卷仍然存在,这个时候如果确认这些数据卷是没有用的,就需要去清理它,不然会占用资源」

  • 「查看docker数据卷」

    docker volume ls
    Docker中数据卷挂载

    「可以看到,虽然容器删除了,但是数据卷仍然存在,暂用空间。可以通过prune命令清理」

  • 「清理数据卷」

    docker volume prune
  • 「查看docker数据卷」

    docker volume ls

    数据卷被清理,资源被释放

    Docker中数据卷挂载

三、数据卷容器

「实现容器之间的数据同步,其他容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称之为数据卷容器」

  • 如果需要在多个容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器。
  • 数据卷容器也是一个容器,它的目的是专门用来提供数据卷给其他容器挂载。

数据卷容器一般也被称为父容器

基本结构如下:

Docker中数据卷挂载

通过这个图可以看出来,数据卷容器就是介于容器和宿主机之间的一个容器,数据卷容器挂载到宿主机,其他容器跟数据卷容器进行挂载,从而到底多个容器数据共享的目的

  • 「语法」

    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
    Docker中数据卷挂载

    可以发现,这个父容器虽然启动了,但是它并没有执行,这在官网也有解释,可以不用管

    「创建好的数据卷容器是处于停止运行的状态,因为使用 —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中数据卷挂载
  • 「进入容器」

    docker exec -it nginx1 /bin/sh

    docker exec -it nginx1 /bin/bash会报错

    Docker中数据卷挂载
  • 「在nginx1中创建一个index.html文件」

    echo "Edwin nginx" > /usr/nginx/index.html
  • 「退出容器」

    exit
  • 「查看宿主机中是否存在index.html」

    cd /data/mountFile
    Docker中数据卷挂载

    「发现在容器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中数据卷挂载

    进入容器中,发现文件同步(实现文件共享)


原文始发于微信公众号(星河之码):Docker中数据卷挂载

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/60870.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!