运行中的docker实例添加-v挂载文件夹

2020-12-13 19:44:07

看着貌似可以。 我没仔细看,但感觉很麻烦的样子。就先记下来吧。

参考链接 给一个正在运行的Docker容器动态添加Volume

之前有人问我Docker容器启动之后还能否再挂载卷,考虑到mnt命名空间的工作原理,我一开始认为这很难实现。不过现在Petazzoni通过使用nsenter和绑定挂载实现了这个需求,你可以在你的环境中测试下。

之前有人问我Docker容器启动之后还能否再挂载卷,考虑mnt命名空间的工作原理,我一开始认为这很难实现。不过现在我认为是它实现的。

简单来说,要想将磁盘卷挂载到正在运行的容器上,我们需要:



  • 使用nsenter将包含这个磁盘卷的整个文件系统mount到临时挂载点上;



  • 从我们想当作磁盘卷使用的特定文件夹中创建绑定挂载(bind mount)到这个磁盘卷的位置;



  • umount第一步创建的临时挂载点。

  • </ul>




  • 注意事项

  • 在下面的示例中,我故意包含了$符号来表示这是Shell命令行提示符,以帮助大家区分哪些是你需要输入的,哪些是机器回复的。有一些多行命令,我也继续用>。我知道这样使得例子里的命令无法轻易得被拷贝粘贴。如果你想要拷贝粘贴代码,请查看文章最后的示例脚本。




  • 详细步骤

  • 下面示例的前提是你已经使用如下命令启动了一个简单的名为charlie的容器:




  • $ docker run --name charlie -ti ubuntu bash

  • 我们需要做的是将宿主文件夹/home/jpetazzo/Work/DOCKER/docker挂载到容器里的/src目录。好了,让我们开始吧。



  • nsenter

  • 首先,我们需要

  • nsenter
  • 以及 docker-enter帮助脚本。为什么?因为我们要从容器中mount文件系统。由于安全性的考虑,容器不允许我们这么做。使用nsenter,我们可以突破上述安全限制,在容器的上下文(严格地说,是命名空间)中运行任意命令。当然,这必须要求拥有Docker宿主机的root权限。


  • nsenter
  • 最简单的安装方式是和docker-enter脚本关联执行:




  • $ docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter

  • 更多细节,请查看

  • nsenter
  • 项目主页。



  • 找到文件系统

  • 我们想要在容器里挂载包含宿主文件夹(/home/jpetazzo/Work/DOCKER/docker)的文件系统。那我们就需要找出哪个文件系统包含这个目录。



  • 首先,我们需要canonicalize(或者解除引用)文件,以防这是一个符号链接,或者它的路径包含符号链接:




  • $ readlink --canonicalize /home/jpetazzo/Work/DOCKER/docker
    /home/jpetazzo/go/src/github.com/docker/docker

  • 哈,这的确是一个符号链接!让我们将其放入一个环境变量中:




  • $ HOSTPATH=/home/jpetazzo/Work/DOCKER/docker
    $ REALPATH=$(readlink --canonicalize $HOSTPATH)

  • 接下来,我们需要找出哪个文件系统包含这个路径。我们使用一个有点让人意想不到的工具来做,它就是df:




  • $ df $REALPATH
    Filesystem     1K-blocks      Used Available Use% Mounted on
    /sda2          245115308 156692700  86157700  65% /home/jpetazzo

  • 使用-P参数(强制使用POSIX格式,以防是exotic df,或者是其他人在Solaris或者BSD系统上装Docker时运行的df),将结果也放到一个变量里:




  • $ FILESYS=$(df -P $REALPATH | tail -n 1 | awk '{print $6}')

    找到文件系统的设备(和sub-root)

  • 现在,系统里已经没有绑定挂载(bind mounts)和BTRFS子卷了,我们仅仅需要查看/proc/mounts,找到对应于/home/jpetazzo文件系统的设备就可以了。但是在我的系统里,/home/jpetazzo是BTRFS池的子卷,要想得到子卷的信息(或者bind mount信息),需要查看/proc/self/moutinfo。



  • 如果你从来没有听说过mountinfo,可以查看内核文档的

  • proc.txt


  • 首先,得到文件系统设备信息:




  • $ while read DEV MOUNT JUNK
    > do [ $MOUNT = $FILESYS ] && break
    > done </proc/mounts
    $ echo $DEV
    /dev/sda2

  • 接下来,得到sub-root信息(比如,已挂载文件系统的路径):




  • $ while read A B C SUBROOT MOUNT JUNK
    > do [ $MOUNT = $FILESYS ] && break
    > done < /proc/self/mountinfo 
    $ echo $SUBROOT
    /jpetazzo

  • 很好。现在我们知道需要挂载/dev/sda2。在文件系统内部,进入/jpetazzo,从这里可以得到到所需文件的剩余路径(示例中是/go/src/github.com/docker/docker)。


  • 让我们计算出剩余路径:




  • $ SUBPATH=$(echo $REALPATH | sed s,^$FILESYS,,)



    注意:这个方法只适用于路径里没有符号“,”的。如果你的路径里有“,”并且想使用本文方法挂载目录,请告诉我。(我需要调用Shell Triad来解决这个问题:jessiesoulshaketianon?)
  • 在进入容器之前最后需要做的是找到这个块设备的主和次设备号。可以使用stat:




  • $ stat --format "%t %T" $DEV
    8 2

  • 注意这两个数字是十六进制的,我们之后需要的是二进制。可以这么转换:




  • $ DEVDEC=$(printf "%d %d" $(stat --format "0x%t 0x%T" $DEV))



    总结

  • 还有最后一步。因为某些我无法解释的原因,一些文件系统(包括BTRFS)在挂载多次之后会更新/proc/mounts里面的设备字段。也就是说,如果我们在容器里创建了名为/tmpblkdev的临时块设备,并用其挂载我们自己的文件系统,那么文件系统(在宿主机器里!)会显示为/tmpblkdev,而不是/dev/sda2。这听起来无所谓,但实际上这会让之后试图得到文件系统块设备的操作都失败。



  • 长话短说,我们想要确保块设备节点在容器里位于和宿主机器上的同一个路径下。



  • 需要这么做:




  • $ docker-enter charlie -- sh -c \
    > "[ -b $DEV ] || mknod --mode 0600 $DEV b $DEVDEC"

  • 创建临时挂载点挂载文件系统:




  • $ docker-enter charlie -- mkdir /tmpmnt
    $ docker-enter charlie -- mount $DEV /tmpmnt

  • 确保卷挂载点存在,bind mount卷:




  • $ docker-enter charlie -- mkdir -p /src
    $ docker-enter charlie -- mount -o bind /tmpmnt/$SUBROOT/$SUBPATH /src

  • 删除临时挂载点:




  • $ docker-enter charlie -- umount /tmpmnt
    $ docker-enter charlie -- rmdir /tmpmnt

  • (我们并不清除设备节点。一开始就检查设备是否存在可能有点多余,但是现在再检查就已经很复杂了。)



  • 大功告成!





  • 让一切自动化

  • 下面这段可以直接拷贝粘贴了。




  • #!/bin/sh
    set -e
    CONTAINER=charlie
    HOSTPATH=/home/jpetazzo/Work/DOCKER/docker
    CONTPATH=/src上述方法不适用于不基于块设备的文件系统,只有在/proc/mounts能正确得到块设备节点(上面谈到,并不总是能正确


  • 2020-04-17 11:28:35

    TweenMax中文初级教程二

    TimelineMax是GreenSock 动画平台中的动画组织、排序、管理工具,可创建时间轴(timeline)作为动画或其他时间轴的容器,这使得整个动画控制和精确管理时间变得简单,避免了通过反复delay和回调进行动画。 作者:李霖弢 链接:https://www.jianshu.com/p/8c0361e43bf5 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 2020-04-17 11:28:57

    TweenMax中文初级教程三

    动画关键词:CSS(一般可以省略) CSSPlugin用于在TweenMax中对DOM元素的CSS相关属性进行动画 在CSSPlugin中CSS属性需要写成驼峰式,例如font-size应当写作fontSize。有时候你可以在一些默认px为单位的属性中省略单位,CSSPlugin还可以在不同的单位间做动画:

  • 2020-04-17 11:29:23

    TweenMax中文初级教程四

    用于滚动窗口(类似于window.scrollTo(x, y))或DOM元素(如myDiv.scrollTop = y; myDiv.scrollLeft = x;)。滚动窗口时使用window作为动画目标。

  • 2020-04-17 14:06:29

    图片解释EaseIn,EaseOut,EaseInOut

    1.EaseIn:即缓动发生在入口处,也就是刚开始的时候。 2.EaseOut:即缓动发生在出口处,也就是结束之前。 3.EaseInOut:就是两边都有缓动了.

  • 2020-04-21 14:47:13

    Redis危险命令重命名、禁用

    flushdb,清空数据库 flushall,清空所有记录,数据库 config,客户端连接后可配置服务器 keys,客户端连接后可查看所有存在的键

  • 2020-04-21 15:13:15

    redis 简单使用

    Redis和Memcached类似,也属于k-v数据存储 Redis官网 https://redis.io支持更多value类型,除了和string外,还支持hash、lists(链表)、sets(集合)和sorted sets(有序集合) Redis是可以把数据存储在磁盘上的并且使用了两种文件格式:全量数据(RDB)和增量请求(aof)。一般叫做redis持久化 全量数据格式是把内存中的数据写入磁盘,便于下次读取文件进行加载。

  • 2020-04-21 15:14:20

    SpringBoot + Redis:基本配置及使用

    # Redis数据库索引(默认为0) spring.redis.database=0# Redis服务器地址 spring.redis.host=127.0.0.1# Redis服务器连接端口 spring.redis.port=6379# Redis服务器连接密码(默认为空) spring.redis.password=# 连接池最大连接数(使用负值表示没有限制) spring.redis.jedis.pool.max-active=20# 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.jedis.pool.max-wait=-1# 连接池中的最大空闲连接 spring.redis.jedis.pool.max-idle=10# 连接池中的最小空闲连接 spring.redis.jedis.pool.min-idle=0# 连接超时时间(毫秒) spring.redis.timeout=1000