Docker 數(shù)據(jù)持久化的三種方案,你總能用到
容器中的數(shù)據(jù)可以存儲(chǔ)在容器層。但是將數(shù)據(jù)存放在容器層存在以下問(wèn)題:
- 數(shù)據(jù)不是持久化。意思是如果容器刪除了,這些數(shù)據(jù)也就沒(méi)了
- 主機(jī)上的其它進(jìn)程不方便訪問(wèn)這些數(shù)據(jù)
- 對(duì)這些數(shù)據(jù)的I/O會(huì)經(jīng)過(guò)存儲(chǔ)驅(qū)動(dòng),然后到達(dá)主機(jī),引入了一層間接層,因此性能會(huì)有所下降
Docker 提供了3種持久化數(shù)據(jù)的方式:
- volumes:存于主機(jī)文件系統(tǒng)中的某個(gè)區(qū)域,由Docker管理(/var/lib/docker/volumes/ on linux)。非Docker進(jìn)程不應(yīng)該修改這些數(shù)據(jù)。卷是Docker中持久化數(shù)據(jù)的最好方式
- bind mount:存于主機(jī)文件系統(tǒng)中的任意位置。非Docker進(jìn)程可以修改這些數(shù)據(jù)
- tmpfs mount(Linux中):存于內(nèi)存中(注意,并不是持久化到磁盤(pán))。在容器的生命周期中,它能被容器用來(lái)存放非持久化的狀態(tài)或敏感信息
volumes
如果沒(méi)有顯式創(chuàng)建,一個(gè)卷會(huì)在最開(kāi)始掛載時(shí)被創(chuàng)建。當(dāng)容器停止時(shí),卷仍然存在。多個(gè)容器可以通過(guò)read-write或read-only的方式使用同一個(gè)卷。
只有在顯式刪除時(shí),卷才會(huì)被刪除。如果將一個(gè)空卷掛載到容器中一個(gè)存有文件或目錄的目錄中,這些文件或目錄會(huì)被拷貝到空卷中;如果將一個(gè)非空卷掛載到容器中一個(gè)存有文件或目錄的目錄中,這些文件或目錄會(huì)被隱藏。
使用
- 創(chuàng)建:
docker volume create
- 刪除某個(gè)卷:
docker volume rm 卷名
- 刪除所有未使用的卷:
docker volume prune
- 列出所有卷:
docker volume ls
- 查看某個(gè)卷的信息:
docker volume inspect 卷名
- 掛載到容器:
-v
或--volume
。如果是Docker17.06或更高:推薦使用--mount
。(同 bind mount)
- 掛載類型:key為type,value為bind、volume或tmpfs
- 掛載源:key為source或src,對(duì)于命名卷,value為卷名,對(duì)于匿名卷,則忽略
- 容器中的掛載點(diǎn):key為destination、dst或target,value為容器中的路徑
- 讀寫(xiě)類型:value為readonly,沒(méi)有key
- volume-opt選項(xiàng),可以出現(xiàn)多次。比如volume-driver=local,volume-opt=type=nfs,…
第一個(gè)域:對(duì)于命名卷,為卷名;匿名卷,則忽略,此時(shí)會(huì)創(chuàng)建匿名卷
第二個(gè)域:容器中的掛載點(diǎn)
第三個(gè)域:可選參數(shù),由','
隔開(kāi),如ro
-v或—volume:由3個(gè)域組成,’:’分隔
—mount:由多個(gè)’,’隔開(kāi)的鍵值對(duì)
=組成:
當(dāng)使用docker service create 啟動(dòng)Docker服務(wù)時(shí),只支持--mount,不支持-v和--volume。并且每個(gè)服務(wù)容器使用它們各自的本地卷,因此如果使用本地(local)卷驅(qū)動(dòng),容器無(wú)法通過(guò)卷共享數(shù)據(jù),但是一些卷驅(qū)動(dòng)支持共享存儲(chǔ)。Docker for AWS和Doocker for Azure都使用Cloundstor plugin支持持久存儲(chǔ)
場(chǎng)景
- 多個(gè)運(yùn)行容器間共享數(shù)據(jù)
- 當(dāng)Docker主機(jī)不確保具有給定的目錄或文件時(shí)。卷可以將容器運(yùn)行時(shí)與Docker主機(jī)的配置解耦合
- 備份、恢復(fù)、或?qū)?shù)據(jù)從一個(gè)Docker主機(jī)遷移到另一個(gè)Docker主機(jī)時(shí)
bind mount
主機(jī)中的文件或目錄通過(guò)全路徑被引用。在使用綁定掛載時(shí),這些目錄或文件不一定要已經(jīng)存在。
如果使用這種方式將一個(gè)目錄掛載到容器中一個(gè)存有文件或目錄的目錄中,這些文件或目錄會(huì)被隱藏;如果主機(jī)中的文件或目錄不存在,當(dāng)使用--mount
掛載時(shí),Docker會(huì)報(bào)錯(cuò),當(dāng)使用-v
或--volume
時(shí),會(huì)在主機(jī)上創(chuàng)建目錄
使用
掛載到容器:-v或—volume。如果是Docker17.06或更高:推薦使用—mount。(同 volumes)
-v
或--volume
:由3個(gè)域組成,':'
分隔
- 第一個(gè)域:對(duì)于命名卷,為卷名;匿名卷,則忽略,此時(shí)會(huì)創(chuàng)建匿名卷
- 第二個(gè)域:容器中的掛載點(diǎn)
- 第三個(gè)域:可選參數(shù),由
','
隔開(kāi),如ro
--mount
:由多個(gè)','
隔開(kāi)的鍵值對(duì)<key>=<value>
組成:
- 掛載類型:key為type,value為bind、volume或tmpfs
- 掛載源:key為source或src,value為主機(jī)中文件或目錄的路徑
- 容器中的掛載點(diǎn):key為destination、dst或target,value為容器中的路徑
- 讀寫(xiě)類型:value為readonly,沒(méi)有key
- bind-propagation選項(xiàng):key為bind-propagation,value為rprivate、private、rshared、shared、rslave或slave
- 一致性選項(xiàng):value為consistent、delegated、cached。這個(gè)選項(xiàng)僅僅適用于Docker for Mac
--mount
不支持z和Z(這個(gè)不同于-v和—volume)
場(chǎng)景
大體上來(lái)說(shuō),只要可能,最好使用volumes
- 主機(jī)與容器共享配置文件(Docker默認(rèn)情況下通過(guò)這種方式為容器提供DNS解析,通過(guò)將/etc/resolv.conf掛載到容器中)
- 共享源代碼或build artifacts(比如將Maven的target/目錄掛載到容器中,每次在Docker主機(jī)中build Maven工程時(shí),容器能夠訪問(wèn)到那些rebuilt artifacts)
- 當(dāng) docker主機(jī)中的文件或目錄結(jié)構(gòu)和容器需要的一致時(shí)
bind propagation
對(duì)于bind mount和volumes,默認(rèn)都是rprivate。只有在使用bind mount時(shí)可配置,且必須在linux下。bind propagation是個(gè)超前主題,對(duì)于大多數(shù)用戶來(lái)說(shuō),并不需要配置
對(duì)于一個(gè)掛載點(diǎn)/mnt
,假設(shè)它同時(shí)也被掛載到/tmp
。bind propagation控制 whether a mount on /tmp/a would also be available on /mnt/a
在設(shè)置bind propagation之前,主機(jī)文件系統(tǒng)需要支持bind propagation
下面的例子將主機(jī)中的target/
掛載到容器中2次:
- docker run -d
- -it
- --name devtest
- --mount type=bind,source="$(pwd)"/target,target=/app
- --mount type=bind,source="$(pwd)"/target,target=/app2,readonly,bind-propagation=rslave
- nginx:latest
此時(shí)如果創(chuàng)建/app/foo/
,/app2/foo
也會(huì)存在
selinux label
你能添加z
或Z
選項(xiàng)來(lái)修改掛載到容器中的主機(jī)文件或目錄的selinux label:
z
選項(xiàng)指明bind mount的內(nèi)容在多個(gè)容器間是共享的Z
選項(xiàng)指明bind mount的內(nèi)容是私有不共享的
要特別小心的使用這兩個(gè)選項(xiàng)。”Bind-mounting a system directory such as /home
or /usr
with the Z
option renders your host machine inoperable and you may need to relabel the host machine files by hand”
tmpfs mount
只在linux中支持
相對(duì)于volumes和bind mount,tmpfs mount是臨時(shí)的,只在主機(jī)內(nèi)存中持久化。當(dāng)容器停止,tmpfs mount會(huì)被移除。對(duì)于臨時(shí)存放敏感文件很有用
不同于volumes和bind mount,多個(gè)容器無(wú)法共享tmpfs mount
使用
- 掛載到容器:—tmpfs。如果是Docker17.06或更高:推薦使用—mount
- 掛載類型:key為type,value為bind、volume或tmpfs
- 容器中的掛載點(diǎn):key為destination、dst或target,value為容器中的路徑
- tmpfs-size和tmpfs-mode選項(xiàng)
- —tmpfs:直接指定容器中的掛載點(diǎn)。不允許指定任何配置選項(xiàng)
- —mount:由多個(gè)’,’隔開(kāi)的鍵值對(duì)
=組成:
場(chǎng)景
- 最好的使用場(chǎng)景是你既不想將數(shù)據(jù)存于主機(jī),又不想存于容器中時(shí)。這可以是出于安全的考慮,或當(dāng)應(yīng)用需要寫(xiě)大量非持久性的狀態(tài)數(shù)據(jù)時(shí)為了保護(hù)容器的性能
volume drivers
機(jī)器間共享數(shù)據(jù)
當(dāng)構(gòu)建錯(cuò)誤容忍應(yīng)用時(shí),可能需要配置同一個(gè)服務(wù)的多個(gè)副本來(lái)訪問(wèn)相同的文件:
有多種方法來(lái)實(shí)現(xiàn)這個(gè)目的:
- 為應(yīng)用添加邏輯,將文件存儲(chǔ)到一個(gè)云對(duì)象存儲(chǔ)系統(tǒng)(如Amazon S3)中
- 使用一個(gè)支持將文件寫(xiě)入外部存儲(chǔ)系統(tǒng)(如NFS或Amazon S3)的driver來(lái)創(chuàng)建卷
volume drivers可以將底層存儲(chǔ)系統(tǒng)從應(yīng)用邏輯中抽象出來(lái)。比如,如果你的服務(wù)使用一個(gè)具有NFS driver的卷,你能更新你的服務(wù)使用不同的driver,作為在云中存儲(chǔ)數(shù)據(jù)的示例,而不更改應(yīng)用程序邏輯
使用
在使用docker volume create
或驅(qū)動(dòng)容器創(chuàng)建匿名卷時(shí),可以指定一個(gè)volume drivers。下面的例子使用vieux/sshfs作為volume drivers
假設(shè)有2個(gè)節(jié)點(diǎn),第一個(gè)節(jié)點(diǎn)是Docker主機(jī),它能SSH到第二個(gè)節(jié)點(diǎn)
1、在Docker主機(jī)中,安裝vieux/sshfs插件
- docker plugin install --grant-all-permissions vieux/sshfs
2、使用卷驅(qū)動(dòng)創(chuàng)建卷
1)創(chuàng)建命名卷
- docker volume create --driver vieux/sshfs
- -o sshcmd=test@node2:/home/test
- -o password=testpassword
- sshvolume
2)啟動(dòng)容器時(shí)使用卷驅(qū)動(dòng)創(chuàng)建匿名卷
- docker run -d
- --name sshfs-container
- --volume-driver vieux/sshfs
- --mount src=sshvolume,target=/app,volume-opt=sshcmd=test@node2:/home/test,volume-opt=password=testpassword
- nginx:latest
3、備份、恢復(fù)、遷移數(shù)據(jù)卷
1)備份一個(gè)容器
- docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
- 啟動(dòng)一個(gè)新容器,掛載dbstore容器中的卷
- 掛載一個(gè)本地主機(jī)目錄到容器/backup
- 使用tar將dbdata卷中的數(shù)據(jù)打包成backup.tar
2)用備份恢復(fù)容器
使用剛剛創(chuàng)建的備份來(lái)恢復(fù)容器:
- docker run -v /dbdata --name dbstore2 ubuntu /bin/bash
然后,在新創(chuàng)建的容器的卷中使用tar
解包備份的數(shù)據(jù):
- docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"