理解Docker(8):Docker 存储之卷(Volume)
<p>(1)Docker 安装及基本用法</p><p>(2)Docker 镜像</p>
<p>(3)Docker 容器的隔离性 - 使用 Linux namespace 隔离容器的运行环境</p>
<p>(4)Docker 容器的隔离性 - 使用 cgroups 限制容器使用的资源</p>
<p>(5)Docker 网络</p>
<p>(6)若干企业生产环境中的容器网络方案</p>
<p>(7)Docker 存储 - AUFS</p>
<p>(8)Docker 存储 - Volume</p>
<p> </p>
<h2>1. Docker volume 的几种形态</h2>
<p> 有状态容器都有数据持久化需求。前一篇文章中提到过,Docker 采用 AFUS 分层文件系统时,文件系统的改动都是发生在最上面的容器层。在容器的生命周期内,它是持续的,包括容器在被停止后。但是,当容器被删除后,该数据层也随之被删除了。因此,Docker 采用 volume (卷)的形式来向容器提供持久化存储。Docker volume 有如下几种形态。</p>
<h3>1.1 无 - 不使用 Docker volume</h3>
<p>默认情况下,容器不使用任何 volume,此时,容器的数据被保存在容器之内,它只在容器的生命周期内存在,会随着容器的被删除而被删除。当然,也可以使用 docker commit 命令将它持久化为一个新的镜像。</p>
<h3>1.2 Data volume (数据卷)</h3>
<p>一个 data volume 是容器中绕过 Union 文件系统的一个特定的目录。它被设计用来保存数据,而不管容器的生命周期。因此,当你删除一个容器时,Docker 肯定不会自动地删除一个volume。有如下几种方式来使用 data volume:</p>
<p>(1)使用 “-v 容器内目录” 形式</p>
<div class="cnblogs_code">
<pre>docker run -d -P --name web -v /webapp training/webapp python app.py</pre>
</div>
<p>使用 docker inspect 命令可以看出,Docker 将本地一个 _data 目录 mount 为容器内的 webapp 目录了:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Mounts</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: [
{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">f143b7f379fb6d012a08656fc950bf6df4bf5a5b90c72f310644aa997620122b</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Source</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/var/lib/docker/volumes/f143b7f379fb6d012a08656fc950bf6df4bf5a5b90c72f310644aa997620122b/_data</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Destination</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/webapp</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Driver</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">local</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Mode</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RW</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Propagation</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">
}
],</span></pre>
</div>
<p>其实,在 web 容器被删除后,/var/lib/docker/volumes/f143b7f379fb6d012a08656fc950bf6df4bf5a5b90c72f310644aa997620122b/_data 目录及其中的内容都还会保留下来,但是,新启动的容器无法再使用这个目录,也就是说,已有的数据不能自动地被重复使用了。</p>
<p>(2)使用 -v 来挂载一个主机上的目录到容器的目录</p>
<div class="cnblogs_code">
<pre>docker run -d -P --name web2 -v /src/webapp:/webapp training/webapp python app.py</pre>
</div>
<p>主机上的目录可以是一个本地目录,也可以在一个 NFS share 内,或者在一个已经格式化好了的块设备上。</p>
<p>其实这种形式和第一种没有本质的区别,容器内对 /webapp 的操作都会反映到主机上的 /src/webapp 目录内。只是,重新启动容器时,可以再次使用同样的方式来将 /src/webapp 目录挂载到新的容器内,这样就可以实现数据持久化的目标。</p>
<p>(3)使用 -v 来挂载主机上的一个文件到容器内的一个文件</p>
<div class="cnblogs_code">
<pre>docker run --rm -it -v ~/.bash_history:/root/.bash_history ubuntu /bin/bash</pre>
</div>
<h3>1.3 使用 data container</h3>
<p>如果要在容器之间共享数据,最好是使用 data container。这种 container 中不会跑应用,而只是挂载一个卷。比如:</p>
<p>创建一个 data container:</p>
<div class="cnblogs_code">
<pre>docker create -v /dbdata --name dbstore training/webapp /bin/true</pre>
</div>
<p>启动一个 app container:</p>
<div class="cnblogs_code">
<pre>docker run -d -P --name web3 --volumes-<span style="color: rgba(0, 0, 255, 1)">from</span> dbstore training/webapp python app.py</pre>
</div>
<p>其实,对 web3 这个容器来说,volume 的本质没变,它只是将 dbstore 容器的 /dbdata 目录映射的主机上的目录映射到自身的 /dbdata 目录。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Mounts</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: [
{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Source</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/var/lib/docker/volumes/5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330/_data</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Destination</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/dbdata</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Driver</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">local</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Mode</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RW</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Propagation</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">
}
],</span></pre>
</div>
<p>但是,其好处是,可以不管其目录的临时性而不断地重复使用它。</p>
<h3>1.4 使用 docker volume 命令</h3>
<p>Docker 新版本中引入了 docker volume 命令来管理 Docker volume。</p>
<p>(1)使用默认的 ‘local’ driver 创建一个 volume</p>
<div class="cnblogs_code">
<pre>root@docker1:/home/sammy# docker volume create --<span style="color: rgba(0, 0, 0, 1)">name vol1
vol1
root@docker1:</span>/home/<span style="color: rgba(0, 0, 0, 1)">sammy# docker volume inspect vol1
[
{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">vol1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Driver</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">local</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Mountpoint</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/var/lib/docker/volumes/vol1/_data</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Labels</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {},
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Scope</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">local</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
}
]</span></pre>
</div>
<p>(2)使用这个 volume</p>
<div class="cnblogs_code">
<pre>docker run -d -P --name web4 -v vol1:/volume training/webapp python app.p</pre>
</div>
<p>结果还是一样的,即将 vol1 对应的主机上的目录挂载给容器内的 /volume 目录。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Mounts</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: [
{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">vol1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Source</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/var/lib/docker/volumes/vol1/_data</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Destination</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/volume</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Driver</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">local</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Mode</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">z</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RW</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Propagation</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">rprivate</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
}
],</span></pre>
</div>
<h3>1.5 Volume 删除和孤单 volume 清理</h3>
<h4>1.5.1 在删除容器时删除 volume</h4>
<p>可以使用 docker rm -v 命令在删除容器时删除该容器的卷。</p>
<div class="cnblogs_code">
<pre>root@docker1:/home/sammy# docker run -d -P --name web5 -v /webapp training/<span style="color: rgba(0, 0, 0, 1)">webapp python app.py
69199905a74cb360935e32f4e99f7f11319f6aa36033a920aa0bae25874f5c69
root@docker1:</span>/home/<span style="color: rgba(0, 0, 0, 1)">sammy# docker volume ls
DRIVER VOLUME NAME
local 5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330
local <span style="color: rgba(0, 0, 255, 1)">838f4dd99721a9445be22a6b42d35e04cb43ad145ecf26107a9025f428587f76</span>
local vol1
root@docker1:</span>/home/sammy# docker rm <span style="color: rgba(0, 0, 255, 1)">-</span><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">v</span>f web5
web5
root@docker1:</span>/home/<span style="color: rgba(0, 0, 0, 1)">sammy# docker volume ls
DRIVER VOLUME NAME
local 5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330
local vol1</span></pre>
</div>
<h4>1.5.2 批量删除孤单 volumes</h4>
<p>从上面的介绍可以看出,使用 docker run -v 启动的容器被删除以后,在主机上会遗留下来孤单的卷。可以使用下面的简单方法来做清理:</p>
<div class="cnblogs_code">
<pre>root@docker1:/home/sammy# <span style="color: rgba(0, 0, 255, 1)">docker volume ls -qf dangling=true</span><span style="color: rgba(0, 0, 0, 1)">
244a23f3ab11f17345a68e77f96bb46a8dbaf445760dd86ab0faa07dfbd84236
c864cfac232e8728b1805abc8c363d324124b38e6297544a8cbbf61d883c7e46
f143b7f379fb6d012a08656fc950bf6df4bf5a5b90c72f310644aa997620122b
root@docker1:</span>/home/sammy# <span style="color: rgba(0, 0, 255, 1)">docker volume rm $(docker volume ls -qf dangling=true</span><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">)</span>
244a23f3ab11f17345a68e77f96bb46a8dbaf445760dd86ab0faa07dfbd84236
c864cfac232e8728b1805abc8c363d324124b38e6297544a8cbbf61d883c7e46
f143b7f379fb6d012a08656fc950bf6df4bf5a5b90c72f310644aa997620122b
root@docker1:</span>/home/<span style="color: rgba(0, 0, 0, 1)">sammy# <span style="color: rgba(0, 0, 255, 1)">docker volume ls</span>
DRIVER VOLUME NAME
local 5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330
local vol1</span></pre>
</div>
<p>github 上有很多脚本可以自动化地清理孤单卷,比如:</p>
<ul>
<li>https://github.com/chadoe/docker-cleanup-volumes/blob/master/docker-cleanup-volumes.sh</li>
<li>https://github.com/meltwater/docker-cleanup </li>
</ul>
<p>1.6 小结</p>
<p>对以上内容的两点小结:</p>
<ul>
<li>容器内的数据是临时性的,它会随着容器生命周期的结束而消失</li>
<li>默认的 Docker volume (driver = ‘loclal’)不管是哪种形式,本质上都是将容器所在的主机上的一个目录 mount 到容器内的一个目录,因此,它不具备可移植性。</li>
</ul>
<h2>2. Flocker:容器的分布式存储平台</h2>
<p>第一部分提到过,原生的 Docker volume 不具备可移植性。于是,出现了Docker 的分布式卷解决方案 Flocker。先来看看 Flocker volume 和 Docker 原生 volume 的对比:</p>
<p><img src="https://images2015.cnblogs.com/blog/697113/201610/697113-20161006134925629-1061688528.jpg" alt=""></p>
<p>启动一个使用 Flocker 卷的容器:</p>
<div class="cnblogs_code">
<pre>docker run --volume-driver flocker -v flocker-volume:/container/dir --name=container-xyz</pre>
</div>
<p>它带来的好处包括:</p>
<ul>
<li>容器的数据会被写入 Flocker 后端存储而不是主机上,因此,在主机出现故障时可以保证数据不丢失</li>
<li>在容器迁移时,Flocker 会自动地将卷从一个 host 移植到另一个 host</li>
</ul>
<p>Flocker 的结构:</p>
<p><img src="https://images2015.cnblogs.com/blog/697113/201610/697113-20161006140603692-1864846450.jpg" alt=""></p>
<h2> </h2><br><br>
来源:https://www.cnblogs.com/sammyliu/p/5932996.html 好详细的Docker Volume教程啊!感谢楼主的分享,整理得很用心~
之前对Docker Volume的了解比较零散,看完这篇终于把几种形态弄清楚了。特别是关于"孤单volume"清理的部分,之前删容器后经常发现volume还留着,不知道怎么清理,现在终于有办法了。[哈哈]
想补充几点自己的经验:
1. 关于数据持久化,个人感觉生产环境用host目录挂载更常见些,方便做备份和监控。不过像楼主说的,确实缺乏可移植性。
2. Flocker之前调研过,确实是个不错的方案,但配置起来稍微复杂些。现在Docker官方也有volume plugin生态,如果只是单机器使用其实local volume就够了。
3. 最近发现docker volume还有一个好处是可以配合docker-compose的volumes字段使用,配置起来更清晰:volumes:
- mydata:/data
不知道大家生产环境都是怎么选择存储方案的?有没有推荐的多节点共享存储方案?[好奇]
再次mark一下,楼主这个系列要收藏~[赞]
頁:
[1]