博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

docker VOLUME 数据卷,网络

Posted on 2018-03-13 16:14  bw_0927  阅读(117)  评论(0)    收藏  举报

http://seanlook.com/2014/10/31/docker-command-best-use-1/

 

docker run -it --name mytest centos:centos6 /bin/bash 会启一个伪终端

在伪终端执行top,可发现只有top和bash两个进程

因为容器的核心是所执行的应用程序,所需要的资源都是应用程序运行所必需的,除此之外,并没有其它的资源,可见Docker对资源的利用率极高。

 

exit退出伪终端

docker ps 看不到刚刚的container应用了

但docker ps -a,可以看到

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
851a967f07e9 centos:centos6 "/bin/bash" 2 minutes ago Exited (0) About a minute ago mytest

 

再次执行docker run -it --name mytest centos:centos6 /bin/bash,会启动失败

/usr/bin/docker-current: Error response from daemon: Conflict. The name "/mytest" is already in use by container 851a967f07e9555edb2a548f37628844543a75eff15bf920d03d4808ebf43313. You have to remove (or rename) that container to be able to reuse that name..
See '/usr/bin/docker-current run --help'.

 

 

如果第一次,docker run -it --rm=true --name mytest centos:centos6 /bin/bash的话,就不会报这错。执行完就删除container

 

 

============

数据卷

数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

  • 数据卷 可以在容器之间共享和重用

  • 对 数据卷 的修改会立马生效

  • 对 数据卷 的更新,不会影响镜像

  • 数据卷 默认会一直存在,即使容器被删除

创建一个数据卷

docker volume create --name my-vol          

docker volume ls

docker volume inspect my-vol
[
    {
        "Name": "my-vol",
        "Driver": "local",
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",               #在本地主机下的目录
        "Labels": {},
        "Scope": "local"
    }
]

  

启动一个挂载数据卷的容器

在用 docker run 命令的时候,使用 --mount 标记来将 数据卷 挂载到容器里。在一次 docker run 中可以挂载多个 数据卷

下面创建一个名为 web 的容器,并加载一个 数据卷 到容器的 /webapp 目录。

$ docker run -d -P \
    --name web \
    # -v my-vol:/wepapp \
    --mount source=my-vol,target=/webapp \
    training/webapp \
    python app.py
 使用名字进行挂载

 

目录映射其实是“绑定挂载”host的路径到container的目录,这对于内外传送文件比较方便,使用比较简单,-v <host_path:container_path>,绑定多个目录时再加-v

-v /tmp/docker:/tmp/docker


另外在两个container之间建立联系可用--link,详见高级部分或官方文档。 

下面是一个例子:

docker run --name nginx_test -v /tmp/docker:/usr/share/nginx/html:ro  -p 90:80 -d nginx


在主机的/tmp/docker下建立index.html,就可以通过http://localhost:80/http://host-ip:80访问了。 

 

 

#例子 生成ffmpeg镜像
#Dockerfile
FROM centos:centos6
WORKDIR /data/
COPY ./ffmpeg ./ffserver ./ffprobe ./ 
ENTRYPOINT [ "./ffmpeg" ]

#生成镜像
docker build -t ffmpeg_in_centos:v1 .

#测试

   # docker run ffmpeg_in_centos:v1
   ./ffmpeg: error while loading shared libraries: libasound.so.2: cannot open shared object file: No such file or directory


#docker run   ffmpeg_in_centos:v1 -v /lib64/:/lib64 错误,选项应该在image前面, docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

docker run  -v /lib64/:/lib64 ffmpeg_in_centos:v1

  

https://www.binss.me/blog/learn-docker-with-me-about-volume/ 

 https://www.binss.me/blog/learn-docker-with-me-about-volume/

在使用Docker的过程中,常常需要通过Volume来挂载目录。可以通过以下两种方式创建Volume

  1. 在Dockerfile中指定VOLUME /some/dir

  2. 生成容器时执行docker run -v /some/dir命令来指定

有个问题我纠结了很久:Dockerfile中使用VOLUME指令挂载目录和docker run时通过-v参数指定挂载目录有什么区别?

区别

根据官方文档,Dockerfile生成目标镜像的过程就是不断docker run + docker commit的过程,当Dockerfile执行到VOLUME /some/dir(这里为/var/lib/mysql)这一行时,输出:

  Step 6 : VOLUME /var/lib/mysql
  ---> Running in 0c842ec90849
  ---> 214e3dccd0f2

在这一步,docker生成了临时容器0c842ec90849,然后commit容器得到镜像214e3dccd0f2。因此VOLUME /var/lib/mysql是通过docker run -v /var/lib/mysql,即第二种方式来实现的,随后由于容器的提交,该配置被保存到了镜像214e3dccd0f2中,通过inspcet可以查看到:

  "Volumes": {
  "/var/lib/mysql": {},
  }

通过inspect该镜像生成的容器可以发现挂载配置:

  "Mounts": [
  {
  "Name": "8827c361d103c1272907da0b82268310415f8b075b67854f27dbca0b59a31a1a",
  "Source": "/mnt/sda1/var/lib/docker/volumes/8827c361d103c1272907da0b82268310415f8b075b67854f27dbca0b59a31a1a/_data",
  "Destination": "/var/lib/mysql",
  "Driver": "local",
  "Mode": "",
  "RW": true,
  "Propagation": ""
  }
  ]

由于没有指定挂载到的宿主机目录,因此会默认挂载到宿主机的/var/lib/docker/volumes下的一个随机名称的目录下,在这里为/mnt/sda1/var/lib/docker/volumes/8827c361d103c1272907da0b82268310415f8b075b67854f27dbca0b59a31a1a/_data。

Source:宿主机的目录

Destination: 容器内的目录

因此Dockerfile中使用VOLUME指令挂载目录和docker run时通过-v参数指定挂载目录的区别在于,run的-v可以指定挂载到宿主机的哪个目录,而Dockerfile的VOLUME不能,其挂载目录由docker随机生成

若指定了宿主机目录,比如:

  docker run --name mysql -v ~/volume/mysql/data:/var/lib/mysql -d mysql:5.7

那么inspect如下:

  "Mounts": [
  {
  "Source": "/Users/binss/volume/mysql/data",
  "Destination": "/var/lib/mysql",
  "Mode": "",
  "RW": true,
  "Propagation": "rprivate"
  }
  ]

这里将/var/lib/mysql挂载到宿主机的/Users/binss/volume/mysql/data目录下,而不再是默认的/var/lib/docker/volumes目录。

这样做有什么好处呢?我们知道,/var/lib/mysql是mysql在linux下默认的数据存储目录。将该目录挂载到宿主机,可以使数据在容器被移除时得以保留,而不会随着容器go die。

VOLUME /path_of_container           #容器的path_of_container将会被挂在到宿主机上的一个目录

VOLUME /path_of_host       /path_of_container           #容器的path_of_container将会被挂在到宿主机上的path_of_host目录

下次新建mysql容器,只需同样挂载到/Users/binss/volume/mysql/data,即可复用原有数据。

有童鞋可能会问,那VOLUME /some/dir和不指定宿主机目录的docker run -v /some/dir有什么用?我们不可能为了复用该目录的数据,先inspect找到目录在哪,然后将新容器挂载上去,多麻烦呀。

查阅官方文档,发现还有另外一种挂载方式:

  docker run --name mysql2 --volumes-from mysql -d mysql:5.7

docker inspect mysql2发现,mysql2的/var/lib/mysql和mysql的/var/lib/mysql都挂载到了相同的目录下,因此在mysql2中可以复用mysql的数据:

  "Mounts": [
  {
  "Name": "8827c361d103c1272907da0b82268310415f8b075b67854f27dbca0b59a31a1a",
  "Source": "/mnt/sda1/var/lib/docker/volumes/8827c361d103c1272907da0b82268310415f8b075b67854f27dbca0b59a31a1a/_data",
  "Destination": "/var/lib/mysql",
  "Driver": "local",
  "Mode": "",
  "RW": true,
  "Propagation": ""
  }
  ]

值得注意的是,虽然/var/lib/mysql没有在run的指令中出现,但其出现在了生成mysql:5.7镜像的Dockerfile中,所以即使在run时忘记使用volume,该目录依然能够从宿主机直接访问,官方真是用心良苦啊。另外,由于复用了mysql的所有数据,因此连接数据库用户名密码也和mysql一样,而不会是run时传入的参数,一切都和原来mysql中的一样。

备份与恢复

既然有了--volumes-from,那么备份也不成问题。

  docker run --rm --volumes-from mysql -v $(pwd):/backup mysql:5.7 tar cvf /backup/backup.tar /var/lib/mysql

该指令将mysql容器的/var/lib/mysql目录打包成backup.tar,存放在宿主机的当前目录下。实现方法很简单,创建一个新容器,通过--volumes-from将新容器的/var/lib/mysql挂载到mysql的相同目录下,同时又将宿主机的当前目录挂载到容器的/backup目录下,然后通过tar将/var/lib/mysql打包存放到容器的/backup目录下,这样宿主机的当前目录就得到了打包文件backup.tar。以上流程完成后,容器由于设置了--rm而被删除,只留下backup.tar,毫无痕迹地备份了容器mysql的数据文件。

恢复时,只需执行以下命令:

  docker run --rm --volumes-from mysql -v $(pwd):/backup mysql:5.7 bash -c "cd /var/lib/mysql && tar xvf /backup.tar --strip 1"

同样是创建了一个临时容器,将刚才打包backup.tar解压到mysql容器的/var/lib/mysql中。因为backup.tar解压出来是backup文件夹,所以通过--strip 1跳过这一级目录。

移除无用的挂载目录

由于poll下来的镜像常常都设置了VOLUME指令,所以如果我们创建容器时没有为其指定宿主机挂载目录,如前文所述,会在/var/lib/docker/volumes目录下生成挂载目录。而在删除容器时,如果忘记使用-v,则该挂载目录会残留在宿主机中,占用硬盘空间。例如/var/lib/mysql文件夹,一个就好几百M。

通过以下指令可以移除这些残余的无用挂载目录:

  docker volume rm $(docker volume ls -qf dangling=true)

总结

通过VOLUME,我们得以绕过docker的Union File System,从而直接对宿主机的目录进行直接读写,实现了容器内数据的持久化和共享化

 

 

[root@ffmpeg]# docker run -it centos:v1 
[root@c2f0ba7b0c35 data]# ls /lib64/ | wc -l
144
[root@c2f0ba7b0c35 data]# exit
exit
[root@10.20.170.131 ffmpeg]# docker run -it -v /lib64:/lib64/ ffmpeg_in_centos:v1 
[root@38dc7bfebfe5 LCMS]# ls /lib64/ | wc -l
1178