Docker 存儲
Docker 為容器提供了兩種存放數(shù)據(jù)的資源:
1、由 storage driver 管理的鏡像層和容器層。
2、Data Volume。
storage driver
Docker 鏡像的分層結(jié)構(gòu),簡單回顧一下:

容器由最上面一個可寫的容器層,以及若干只讀的鏡像層組成,容器的數(shù)據(jù)就存放在這些層中。這樣的分層結(jié)構(gòu)最大的特性是 Copy-on-Write:
1、新數(shù)據(jù)會直接存放在最上面的容器層。
2、修改現(xiàn)有數(shù)據(jù)會先從鏡像層將數(shù)據(jù)復(fù)制到容器層,修改后的數(shù)據(jù)直接保存在容器層中,鏡像層保持不變。
3、如果多個層中有命名相同的文件,用戶只能看到最上面那層中的文件。
分層結(jié)構(gòu)使鏡像和容器的創(chuàng)建、共享以及分發(fā)變得非常高效,而這些都要歸功于 Docker storage driver。正是 storage driver 實現(xiàn)了多層數(shù)據(jù)的堆疊并為用戶提供一個單一的合并之后的統(tǒng)一視圖。
Docker 支持多種 storage driver,有 AUFS、Device Mapper、Btrfs、OverlayFS、VFS 和 ZFS。它們都能實現(xiàn)分層的架構(gòu),同時又有各自的特性。對于 Docker 用戶來說,具體選擇使用哪個 storage driver 是一個難題,因為:
1、沒有哪個 driver 能夠適應(yīng)所有的場景。
2、driver 本身在快速發(fā)展和迭代。
不過 Docker 官方給出了一個簡單的答案:
優(yōu)先使用 Linux 發(fā)行版默認(rèn)的 storage driver。Docker 安裝時會根據(jù)當(dāng)前系統(tǒng)的配置選擇默認(rèn)的 driver。默認(rèn) driver 具有最好的穩(wěn)定性,因為默認(rèn) driver 在發(fā)行版上經(jīng)過了嚴(yán)格的測試。
運(yùn)行docker info可以查看默認(rèn) driver。
Ubuntu 用的 AUFS,底層文件系統(tǒng)是 extfs,各層數(shù)據(jù)存放在 /var/lib/docker/aufs。
Redhat/CentOS 的默認(rèn) driver 是 Device Mapper,SUSE 則是 Btrfs。
對于某些容器,直接將數(shù)據(jù)放在由 storage driver 維護(hù)的層中是很好的選擇,比如那些無狀態(tài)的應(yīng)用。無狀態(tài)意味著容器沒有需要持久化的數(shù)據(jù),隨時可以從鏡像直接創(chuàng)建。
Data Volume
有持久化數(shù)據(jù)的需求,容器啟動時需要加載已有的數(shù)據(jù),容器銷毀時希望保留產(chǎn)生的新數(shù)據(jù),也就是說,這類容器是有狀態(tài)的。這就要用到 Docker 的另一種存儲機(jī)制:Data Volume。
Data Volume 本質(zhì)上是 Docker Host 文件系統(tǒng)中的目錄或文件,能夠直接被 mount 到容器的文件系統(tǒng)中。Data Volume 有以下特點:
1、Data Volume 是目錄或文件,而非沒有格式化的磁盤(塊設(shè)備)。
2、容器可以讀寫 volume 中的數(shù)據(jù)。
3、volume 數(shù)據(jù)可以被永久的保存,即使使用它的容器已經(jīng)銷毀。
現(xiàn)在我們有數(shù)據(jù)層(鏡像層和容器層)和 volume 都可以用來存放數(shù)據(jù),具體使用的時候要怎樣選擇呢?考慮下面幾個場景:
1、Database 軟件 vs Database 數(shù)據(jù)
2、Web 應(yīng)用 vs 應(yīng)用產(chǎn)生的日志
3、數(shù)據(jù)分析軟件 vs input/output 數(shù)據(jù)
4、Apache Server vs 靜態(tài) HTML 文件
相信大家會做出這樣的選擇:
1、前者放在數(shù)據(jù)層中。因為這部分內(nèi)容是無狀態(tài)的,應(yīng)該作為鏡像的一部分。
2、后者放在 Data Volume 中。這是需要持久化的數(shù)據(jù),并且應(yīng)該與鏡像分開存放
docker 提供了兩種類型的 volume:bind mount 和 docker managed volume。
- bind mount
bind mount 是將 host 上已存在的目錄或文件 mount 到容器。
通過 -v 將其 mount 到容器,-v 的格式為 <host path>:<container path>。
bind mount 時還可以指定數(shù)據(jù)的讀寫權(quán)限,默認(rèn)是可讀可寫,可指定為只讀。
除了 bind mount 目錄,還可以單獨指定一個文件。使用 bind mount 單個文件的場景是:只需要向容器添加文件,不希望覆蓋整個目錄。
bind mount 的使用直觀高效,易于理解,但它也有不足的地方:bind mount 需要指定 host 文件系統(tǒng)的特定路徑,這就限制了容器的可移植性,當(dāng)需要將容器遷移到其他 host,而該 host 沒有要 mount 的數(shù)據(jù)或者數(shù)據(jù)不在相同的路徑時,操作會失敗。移植性更好的方式是 docker managed volume。
- docker managed volume
docker managed volume 與 bind mount 在使用上的最大區(qū)別是不需要指定 mount 源,指明 mount point 就行了。
docker managed volume 的創(chuàng)建過程:
1、容器啟動時,簡單的告訴 docker "我需要一個 volume 存放數(shù)據(jù),幫我 mount 到目錄 /abc"。
2、docker 在 /var/lib/docker/volumes 中生成一個隨機(jī)目錄作為 mount 源。
3、如果 /abc 已經(jīng)存在,則將數(shù)據(jù)復(fù)制到 mount 源,
4、將 volume mount 到 /abc
除了通過 docker inspect 查看 volume,我們也可以用 docker volume 命令
兩種 data volume 的原理和基本使用方法,下面做個對比:
1、相同點:兩者都是 host 文件系統(tǒng)中的某個路徑。
2、不同點:

共享數(shù)據(jù)
- 容器與 host 共享數(shù)據(jù)
我們有兩種類型的 data volume,它們均可實現(xiàn)在容器與 host 之間共享數(shù)據(jù),但方式有所區(qū)別。
對于 bind mount 是非常明確的:直接將要共享的目錄 mount 到容器。
docker managed volume 就要麻煩點。由于 volume 位于 host 中的目錄,是在容器啟動時才生成,所以需要將共享數(shù)據(jù)拷貝到 volume 中。
docker cp 可以在容器和 host 之間拷貝數(shù)據(jù),當(dāng)然我們也可以直接通過 Linux 的 cp 命令復(fù)制到 /var/lib/docker/volumes/xxx。
- 容器之間共享數(shù)據(jù)
1、用bind mount共享數(shù)據(jù)
第一種方法是將共享數(shù)據(jù)放在 bind mount 中,然后將其 mount 到多個容器。
2、用volume container共享數(shù)據(jù)
另一種在容器之間共享數(shù)據(jù)的方式是使用 volume container。volume container 是專門為其他容器提供 volume 的容器。它提供的卷可以是 bind mount,也可以是 docker managed volume。
下面我們創(chuàng)建一個 volume container:
docker create --name vc_data -v ~/htdocs:/usr/local/apache2/htdocs -v /other/useful/tools busybox
我們將容器命名為 vc_data(vc 是 volume container 的縮寫)。注意這里執(zhí)行的是 docker create 命令,這是因為 volume container 的作用只是提供數(shù)據(jù),它本身不需要處于運(yùn)行狀態(tài)。容器 mount 了兩個 volume:
1、bind mount,存放 web server 的靜態(tài)文件。
2、docker managed volume,存放一些實用工具(當(dāng)然現(xiàn)在是空的,這里只是做個示例)。
通過 docker inspect 可以查看到這兩個 volume
其他容器可以通過 --volumes-from 使用 vc_data 這個 volume container:
docker run --name web1 -d -p 80 --volumes-from vc_data httpd
容器已經(jīng)成功共享了 volume container 中的 volume。
下面我們討論一下 volume container 的特點:
1、與 bind mount 相比,不必為每一個容器指定 host path,所有 path 都在 volume container 中定義好了,容器只需與 volume container 關(guān)聯(lián),實現(xiàn)了容器與 host 的解耦。
2、使用 volume container 的容器其 mount point 是一致的,有利于配置的規(guī)范和標(biāo)準(zhǔn)化,但也帶來一定的局限,使用時需要綜合考慮。
3、用data-packed volume container共享數(shù)據(jù)
volume container 的數(shù)據(jù)歸根到底還是在 host 里,有沒有辦法將數(shù)據(jù)完全放到 volume container 中,同時又能與其他容器共享呢?
當(dāng)然可以,通常我們稱這種容器為 data-packed volume container。其原理是將數(shù)據(jù)打包到鏡像中,然后通過 docker managed volume 共享。
volume 生命周期管理
Data Volume 中存放的是重要的應(yīng)用數(shù)據(jù),如何管理 volume 對應(yīng)用至關(guān)重要。
- 備份
因為 volume 實際上是 host 文件系統(tǒng)中的目錄和文件,所以 volume 的備份實際上是對文件系統(tǒng)的備份。
- 恢復(fù)
volume 的恢復(fù)也很簡單,如果數(shù)據(jù)損壞了,直接用之前備份的數(shù)據(jù)拷貝到 /myregistry 就可以了。
- 遷移
如果我們想使用更新版本的 Registry,這就涉及到數(shù)據(jù)遷移,方法是:
1、docker stop 當(dāng)前 Registry 容器。
2、啟動新版本容器并 mount 原有 volume。
docker run -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:latest
當(dāng)然,在啟用新容器前要確保新版本的默認(rèn)數(shù)據(jù)路徑是否發(fā)生變化。
- 銷毀
docker 不會銷毀 bind mount,刪除數(shù)據(jù)的工作只能由 host 負(fù)責(zé)。對于 docker managed volume,在執(zhí)行 docker rm 刪除容器時可以帶上 -v 參數(shù),docker 會將容器使用到的 volume 一并刪除,但前提是沒有其他容器 mount 該 volume,目的是保護(hù)數(shù)據(jù),非常合理。
刪除容器時沒有帶 -v 呢?這樣就會產(chǎn)生孤兒 volume,可以用 docker volume rm 刪除。如果想批量刪除孤兒 volume,可以執(zhí)行:
docker volume rm $(docker volume ls -q)