前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >详解Docker——你需要知道的Docker进阶知识三

详解Docker——你需要知道的Docker进阶知识三

作者头像
云原生
发布2021-05-31 10:57:26
7090
发布2021-05-31 10:57:26
举报
文章被收录于专栏:云原生实践

Docker 存储

我们可以将数据保存在容器中,但是这样存在一些缺点:

  • 当容器停止运行的时候,我们无法使用数据,并且容器被删除时,数据没有被保存下来。
  • 数据保存在容器中的可写层中,我们无法轻松的将数据移动到其它地方。

针对上述的缺点,有些数据,例如数据库文件,我们不应该将其保存在镜像或者容器的可写层中。Docker 提供三种不同的方式将数据从 Docker 主机挂载到容器中,分别为卷( volumes),绑定挂载( bind mounts),临时文件系统( tmpfs)。很多时候, volumes 总是正确的选择。

  • volumes, 卷存储在 Docker 管理的主机文件系统的某个目录( /var/lib/docker/volumes/) 里
  • bind mounts, 绑定挂载,可以将主机的文件或目录挂载到容器中
  • tmpfs, 仅存储在主机内存中,而不会写入主机文件系统

无论使用上述的哪一种方式,数据在容器内看上去都是一样的。它被认为是容器文件系统的目录或文件。

卷列表

对于三种不同的存储数据的方式来说,卷是唯一完全由 Docker 管理的。它更容易备份或迁移,并且我们可以使用 DockerCLI 命令来管理卷。

列出本地可用的卷列表:

代码语言:javascript
复制
[root@VM_0_17_centos ~]# docker volume ls
DRIVER              VOLUME NAME
local               0e15a7c0f491bbf500f419ec2637be6a298922cd9d9831b3875363a7f34971cb
local               3fdec394f801640e1a39240be7bd627eb92a6af259844bbe8f5c0ba2a5baf9e5
创建卷
代码语言:javascript
复制
[root@VM_0_17_centos ~]# docker volume create
3a513b26b0bf00087149095c6dbbc6101a5f70efdc15d2430d8e62f4e92715f9

这种由系统随机生成名称的卷称为 匿名卷,匿名卷名称不具备可读性,使用起来不太方便,所以创建卷时一般会指定其 name。例如我们创建一个名为 volume1 的卷。

代码语言:javascript
复制
[root@VM_0_17_centos ~]# docker volume create volume1
volume1
[root@VM_0_17_centos ~]# docker volume ls
DRIVER              VOLUME NAME
local               0e15a7c0f491bbf500f419ec2637be6a298922cd9d9831b3875363a7f34971cb
local               3a513b26b0bf00087149095c6dbbc6101a5f70efdc15d2430d8e62f4e92715f9
local               volume1
用卷启动一个容器

创建好卷之后,我们可以用卷来启动一个容器,这里首先需要学习 docker container run 命令的两个参数:

  • -v--volume
  • 由三个冒号(:)分隔的字段组成, [HOST-DIR:]CONTAINER-DIR[:OPTIONS]
  • HOST-DIR 代表主机上的目录或数据卷的名字。省略该部分时,会自动创建一个匿名卷。如果是指定主机上的目录,需要使用绝对路径。
  • CONTAINER-DIR 代表将要挂载到容器中的路径
  • OPTIONS 代表配置,例如设置为只读( ro),或者仅能被该容器使用( Z),或者可以被多个容器共享 ( z)。多个配置项由逗号分隔。
  • 例如,我们使用 -v volume1:/volume1:ro,z。代表的是意思是将卷 volume1 挂载到容器中的 /volume1 目录。 ro,z 代表该卷被设置为只读( ro),并且可以被多个容器同时使用( z
  • --mount
  • 由多个键值对组成,键值对之间由逗号分隔。例如: type=volume,source=volume1,destination=/volume1,ro=true
  • type 指定类型,可以指定为 bindvolumetmpfs
  • source 当类型为 volume 时,指定卷名称,省略该字段会新建一个卷。当类型为 bind 时,指定路径。可以使用缩写 src
  • destination 挂载到容器中的路径。可以使用缩写 dsttarget
  • readonly 读写配置项, truefalse。可以使用缩写 ro

对于前面创建的卷 volume1,可使用如下命令来在容器中使用:

代码语言:javascript
复制
[root@VM_0_17_centos ~]# docker container run -it --name hellodocker3 -v volume1:/volume1 --rm ubuntu bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
423ae2b273f4: Pull complete
de83a2304fa1: Pull complete
f9a83bce3af0: Pull complete
b6b53be908de: Pull complete
Digest: sha256:04d48df82c938587820d7b6006f5071dbbffceb7ca01d2814f81857c631d44df
Status: Downloaded newer image for ubuntu:latest
root@a23cea8fe875:/#

使用 --mount 的命令如下:

代码语言:javascript
复制
[root@VM_0_17_centos ~]# docker run -it --name hellodocker4 --mount type=volume,src=volume1,target=/volume1 --rm ubuntu bash

可以看出 --mount 的可读性更好。所以推荐大家使用 --mount

上述操作,我们运行了两个容器,并分别挂载了一个卷。对于这两个容器来说,由于挂载的是同一个卷,所以它们将共享该数据卷。多个容器共享数据卷时,需要注意并发读写问题。可以分别连接到两个容器中,操作数据来验证数据是同步的。

bind-mounts

绑定挂载( bind-mounts)通过将主机上的目录挂载到容器中,使得容器可以操作和修改主机上的文件。

例如,我们将 /home/hellodocker 目录挂载到容器中的 /home/hellodocker 目录下,使用的命令如下:

代码语言:javascript
复制
[root@VM_0_17_centos hellodocker]#  docker run -it -v /home/hellodocker:/home/hellodocker --name hellodocker4 --rm ubuntu bash
root@9d89e44a06a8:/# ^C
root@9d89e44a06a8:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@9d89e44a06a8:/# cd home/
root@9d89e44a06a8:/home# ls
hellodocker
root@9d89e44a06a8:/home# cd hellodocker/
root@9d89e44a06a8:/home/hellodocker# ls
test1  test2
root@9d89e44a06a8:/home/hellodocker#

而如果使用 --mount,相应的语句如下:

代码语言:javascript
复制
[root@VM_0_17_centos hellodocker]# docker run -it --mount type=bind,src=/home/hellodocker,target=/home/hellodocker --name hellodocker5 --rm ubuntu bash

如果挂载时指定的容器目录已存在,则该目录将被覆盖(还存在只是被隐藏了,因为其位于更下层)。并且如果主机上的目录不存在,会自动创建该目录。

上述两个操作针对的是目录,而对于挂载文件来说,可能会出现一些特殊情况,涉及到绑定挂载和使用卷的区别。下面我们重现这一操作:

首先在当前目录,即 /home/hellodocker 目录下,创建一个 test.txt 文件。并向其中写入文本内容 "test1":

代码语言:javascript
复制
echo "test1" > test.txt

接着创建一个容器 hellodocker6,将 test.txt 文件挂载到容器中的 /test.txt 路径,并查看容器中 /test.txt 文件的内容:

代码语言:javascript
复制
[root@VM_0_17_centos hellodocker]# docker run -it -v /home/hellodocker/test.txt:/test.txt --name hellodocker6 ubuntu /bin/bash
root@99d55b8d4d8a:/# cat test.txt
test1

这时新打开一个终端,通过 echo 命令向 /home/hellodocker/test.txt 文件追加内容 "test2",并在容器中查看 /test.txt 文件的内容:

代码语言:javascript
复制
echo "test2" >> test.txt
代码语言:javascript
复制
root@99d55b8d4d8a:/# cat test.txt
test1
test2

这时无论是在容器中还是主机上都能查看到该文件的最新内容。接下来在主机上查看 test.txtinode 号,并使用 vim 编辑该文件,添加 "test3",并查看该文件的内容:

代码语言:javascript
复制
[root@VM_0_17_centos hellodocker]# ls -i test.txt
527823 test.txt
[root@VM_0_17_centos hellodocker]# vim test.txt
[root@VM_0_17_centos hellodocker]# ls -i test.txt
527050 test.txt

在主机上使用 vim 编辑后,通过 vim 做出的修改并不能在容器中查看到

代码语言:javascript
复制
root@68c277025b02:/# cat test.txt
test1
test2

这是因为 vim 编辑文件的时候,会将文件内容暂存到一个临时文件中,退出保存后会删除原来的文件,并将临时文件重命名为原文件。但是我们标识文件是通过 inode,因此 Docker 绑定的依然是旧的主机文件,所以容器中看到的依然是旧的内容。

对于数据卷来说,由 docker 完全管理,而绑定挂载需要我们自己去维护。我们需要自己手动去处理这些问题,这些问题并不仅仅是上面演示的这些,还有用户权限, SELINUX 等。

使用tmpfs挂载数据

tmpfs 只存储在主机的内存中。当容器停止时,相应的数据就会被移除。

代码语言:javascript
复制
$ docker run -it --mount type=tmpfs,target=/test --name hellodocker7 --rm ubuntu bash
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云原生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Docker 存储
    • 卷列表
      • bind-mounts
        • 使用tmpfs挂载数据
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档