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