docker volume

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了docker volume相关的知识,希望对你有一定的参考价值。

参考技术A 众所周知,Docker Image可以理解成多个只读文件叠加而成,因此Docker Image是只读的。

当我们将其运行起来,就相当于在只读的Image外包裹了一层读写层变成了容器。

当你删除容器之后,使用这个镜像重新创建一个容器,此时的镜像的只读层还和原来的一样,但是你在读写层的修改全部都会丢失。

那么问题就来了,如果想要持久化在读写层的数据,该怎么利用docker做到呢?

docker使用volume实现数据的持久化,不仅如此volume还能帮助容器和容器之间,容器和host之间共享数据。

如果想要持久化数据,就必须将读写层的数据暂存在host machine,因此当你使用:

以上两种指令都可以帮助你创建一个Volume,其实是在host machine上创建一个directory。

常在mac上用docker的人应该知道,和Linux不同,在mac上用docker会在mac上启动一个虚拟机运行docker,因此volume创建的directory并不在你的machine上,而是在虚拟机中。

你可以看到上图中有volume有一些是自己命名的有一些是一串数字:
- 自己命名的是使用 docker volume create --name ** 创建的,比如haha 是使用 docker volume create --name haha
- 数字Volume是在创建container的同时创建的

使用 --volumes-from , docker run -it -h NEWCONTAINER --volumes-from container-test debian /bin/bash 意思是,将container-test这个container中的volume挂载到当前将要运行起来的容器中。前提是container-test中的volumes必须被创建过,但是container-test可以是被stop的,原因是volume只能被手动删除,不是随着容器停止而被删除

准备一个容器专门用来做数据容器,比如 docker run -d -v /dbdata --name dbdata postgres echo Data-only container for postgres 运行postgres这个容器然后给他配置一个volume,其他容器都可以 --volumes-from 到这个volume,数据容器可以不在运行状态甚至可以被删除,只要volume创建了即可。

虽然有很多方式创建volume但是感觉最方便的还是用 docker-compose

docker-compose可以很方便的实现容器和容器间,容器和主机间数据共享。

就是你创建的,然后 volumes: - mydata:/data 就是将你创建的volume挂载到容器中,此时web和web1共享pezhang_mydata volume

Docker Volume

Docker的数据持久化主要有两种方式:

  • bind mount
  • volume

Docker的数据持久化即使数据不随着container的结束而结束,数据存在于host机器上——要么存在于host的某个指定目录中(使用bind mount),要么使用docker自己管理的volume(/var/lib/docker/volumes下)。

bind mount

bind mount自docker早期便开始为人们使用了,用于将host机器的目录mount到container中。但是bind mount在不同的宿主机系统时不可移植的,比如Windows和Linux的目录结构是不一样的,bind mount所指向的host目录也不能一样。这也是为什么bind mount不能出现在Dockerfile中的原因,因为这样Dockerfile就不可移植了。

将host机器上当前目录下的host-data目录mount到container中的/container-data目录:

docker run -it -v $(pwd)/host-dava:/container-data alpine sh

有几点需要注意:

  • host机器的目录路径必须为全路径(准确的说需要以/~/开始的路径),不然docker会将其当做volume而不是volume处理
  • 如果host机器上的目录不存在,docker会自动创建该目录
  • 如果container中的目录不存在,docker会自动创建该目录
  • 如果container中的目录已经有内容,那么docker会使用host上的目录将其覆盖掉

使用volume

volume也是绕过container的文件系统,直接将数据写到host机器上,只是volume是被docker管理的,docker下所有的volume都在host机器上的指定目录下/var/lib/docker/volumes。

将my-volume挂载到container中的/mydata目录:

docker run -it -v my-volume:/mydata alpine sh

然后可以查看到给my-volume的volume:

docker volume inspect my-volume
[
    {
        "CreatedAt": "2018-03-28T14:52:49Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/my-volume/_data",
        "Name": "my-volume",
        "Options": {},
        "Scope": "local"
    }
]

可以看到,volume在host机器的目录为/var/lib/docker/volumes/my-volume/_data。此时,如果my-volume不存在,那么docker会自动创建my-volume,然后再挂载。

也可以不指定host上的volume:

docker run -it -v /mydata alpine sh

此时docker将自动创建一个匿名的volume,并将其挂载到container中的/mydata目录。匿名volume在host机器上的目录路径类似于:/var/lib/docker/volumes/300c2264cd0acfe862507eedf156eb61c197720f69e7e9a053c87c2182b2e7d8/_data

除了让docker帮我们自动创建volume,我们也可以自行创建:

docker volume create my-volume-2

然后将这个已有的my-volume-2挂载到container中:

docker run -it -v my-volume-2:/mydata alpine sh

需要注意的是,与bind mount不同的是,如果volume是空的而container中的目录有内容,那么docker会将container目录中的内容拷贝到volume中,但是如果volume中已经有内容,则会将container中的目录覆盖。请参考这里

Dockerfile中的VOLUME

在Dockerfile中,我们也可以使用VOLUME指令来申明contaienr中的某个目录需要映射到某个volume:

#Dockerfile
VOLUME /foo

这表示,在docker运行时,docker会创建一个匿名的volume,并将此volume绑定到container的/foo目录中,如果container的/foo目录下已经有内容,则会将内容拷贝的volume中。也即,Dockerfile中的VOLUME /foodocker run -v /foo alpine的效果一样。

Dockerfile中的VOLUME使每次运行一个新的container时,都会为其自动创建一个匿名的volume,如果需要在不同container之间共享数据,那么我们依然需要通过docker run -it -v my-volume:/foo的方式将/foo中数据存放于指定的my-volume中。

因此,VOLUME /foo在某些时候会产生歧义,如果不了解的话将导致问题。




挂载主机目录

挂载一个主机目录作为数据卷

使用 --mount 标记可以指定挂载一个本地主机的目录到容器中去。

$ docker run -d -P     --name web     # -v /src/webapp:/opt/webapp     --mount type=bind,source=/src/webapp,target=/opt/webapp     training/webapp     python app.py

上面的命令加载主机的 /src/webapp 目录到容器的 /opt/webapp目录。这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,以前使用 -v 参数时如果本地目录不存在 Docker 会自动为你创建一个文件夹,现在使用 --mount 参数时如果本地目录不存在,Docker 会报错。

Docker 挂载主机目录的默认权限是 读写,用户也可以通过增加 readonly 指定为 只读

$ docker run -d -P     --name web     # -v /src/webapp:/opt/webapp:ro     --mount type=bind,source=/src/webapp,target=/opt/webapp,readonly     training/webapp     python app.py

加了 readonly 之后,就挂载为 只读 了。如果你在容器内 /opt/webapp 目录新建文件,会显示如下错误

/opt/webapp # touch new.txt
touch: new.txt: Read-only file system

查看数据卷的具体信息

在主机里使用以下命令可以查看 web 容器的信息

$ docker inspect web

挂载主机目录 的配置信息在 "Mounts" Key 下面

"Mounts": [
    {
        "Type": "bind",
        "Source": "/src/webapp",
        "Destination": "/opt/webapp",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

挂载一个本地主机文件作为数据卷

--mount 标记也可以从主机挂载单个文件到容器中

$ docker run --rm -it    # -v $HOME/.bash_history:/root/.bash_history    --mount type=bind,source=$HOME/.bash_history,target=/root/.bash_history    ubuntu:18.04    bash

root@2affd44b4667:/# history
1  ls
2  diskutil list

这样就可以记录在容器输入过的命令了。

以上是关于docker volume的主要内容,如果未能解决你的问题,请参考以下文章

docker-compose转为k8s配置文件

docker-compose转为k8s配置文件

docker-compose转为k8s配置文件

Docker——Docker基础+Docker安装

重启docker命令(docker自动重启)

Docker教程-1-学习及安装Docker