Docker 入门系列(4)- Docker 数据管理(挂载目录、挂载文件、数据卷挂载、数据卷共享、数据卷删除、数据卷容器备份和恢复)
基于底層存儲實現,Docker 提供了三種適用于不同場景的文件系統掛載方式:Bind Mount、Volume 和 Tmpfs Mount。
-
Bind Mount能夠直接將宿主操作系統中的目錄和文件掛載到容器內的文件系統中,通過指定容器外的路徑和容器內的路徑,就可以形成掛載映射關系,在容器內外對文件的讀寫,都是相互可見的。 -
Volume也是從宿主操作系統中掛載目錄到容器內,只不過這個掛載的目錄由Docker進行管理,我們只需要指定容器內的目錄,不需要關心具體掛載到了宿主操作系統中的哪里。 -
Tmpfs Mount支持掛載系統內存中的一部分到容器的文件系統里,不過由于內存和容器的特征,它的存儲并不是持久的,其中的內容會隨著容器的停止而消失。
1. Bind Mount
1.1 掛載目錄到容器
要將宿主操作系統中的目錄掛載到容器之后,我們可以在容器創建的時候通過傳遞 -v 或 --volume 選項來指定內外掛載的對應目錄或文件。
docker run -d --name nginx -v /webapp/html:/usr/share/nginx/html nginx:1.12
使用 -v 或 --volume 來掛載宿主操作系統目錄的形式是 -v <host-path>:<container-path> 或 --volume <host-path>:<container-path>,其中
host-path代表宿主操作系統中的目錄;container-path代表容器中的目錄;
這里需要注意的是,為了避免混淆,
Docker這里強制定義目錄時必須使用絕對路徑,不能使用相對路徑。
在 docker inspect 的結果里,我們可以看到有關容器數據掛載相關的信息。
$ docker inspect nginx
[{
## ......"Mounts": [{"Type": "bind","Source": "/webapp/html","Destination": "/usr/share/nginx/html","Mode": "","RW": true,"Propagation": "rprivate"}],
## ......}
]
實際操作中,Docker 還支持以只讀的方式掛載,通過只讀方式掛載的目錄和文件,只能被容器中的程序讀取,但不接受容器中程序修改它們的請求。在掛載選項 -v 后再接上 :ro 就可以只讀掛載了。
docker run -d --name nginx -v /webapp/html:/usr/share/nginx/html:ro nginx:1.12
使用 -v 標記也可以指定掛載一個本地的已有目錄到容器中去作為數據卷 (推薦方式)。
wohu@iZ:~/docker/code$ docker run -d -P -i -t --name python -v /home/wohu/docker/code:/opt ubuntu:16.04
c2e114c09291abaa2c3bbd9c7318a317749ab851511d0cdd57484a7c128a19cc
wohu@iZm:~/docker/code$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c2e114c09291 ubuntu:16.04 "/bin/bash" 6 seconds ago Up 5 seconds pythonwohu@iZ:~/docker/code$ docker exec -ti c2 /bin/bash
root@c2e114c09291:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@c2e114c09291:/# pwd
/
root@c2e114c09291:/# cd /opt/
root@c2e114c09291:/opt# ls
test.py
root@c2e114c09291:/opt# python test.py
bash: python: command not found
root@c2e114c09291:/opt# cat test.py
#!/usr/bin/env python
print "this is test py file"
root@c2e114c09291:/opt# exit
exit
wohu@iZ:~/docker/code$ cat test.py
#!/usr/bin/env python
print "this is test py file"
# 主機環境下對 test.py 文件進行修改
wohu@iZ:~/docker/code$ echo "print 'new containers'" >> test.py
wohu@iZ:~/docker/code$ cat test.py
#!/usr/bin/env python
print "this is test py file"
print 'new containers'
# 進入容器查看該文件是否被修改?
wohu@iZ:~/docker/code$ docker exec -it c2 /bin/bash
root@c2e114c09291:/# cd /opt/
root@c2e114c09291:/opt# ls
test.py
root@c2e114c09291:/opt# cat test.py
#!/usr/bin/env python
print "this is test py file"
print 'new containers' # 該文件已經被修改
root@c2e114c09291:/opt# exit
exit
wohu@iZ:~/docker/code$
上面的命令加載主機的 /home/wohu/docker/code 目錄到容器的 /opt 目錄。這個功能在進行測試的時候十分方便,比如用戶可以將一些程序或數據放到本地目錄中,然后在容器內運行和使用。另外,本地目錄的路徑必須是絕對路徑,如果目錄不存在 Docker 會自動創建。
Docker 掛載數據卷的默認權限是讀寫(rw),用戶也可以通過 ro 指定為只讀:
docker run -d -P -i -t --name python -v /home/xwr/docker/code:/opt:ro ubuntu:16.04
加了 :ro 之后,容器內對所掛載數據卷內的數據就無法修改了。
1.2 掛載文件到容器
-v 標記也可以從主機掛載單個文件到容器中(不推薦) 。
docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash
這樣就可以記錄在容器輸入過的命令歷史了。注意如果直接掛載一個文件到容器,使用文件編輯工具,包括 vi 或者 sed--in-place 的時候,可能會造成文件 inode 的改變,從 Docker 1.1.0 起 這會導致報錯誤信息。
所以推薦的方式是直接掛載文件所在的目錄。
2. Tmpfs Mount
Tmpfs Mount 是一種特殊的掛載方式,它主要利用內存來存儲數據。由于內存不是持久性存儲設備,所以其帶給 Tmpfs Mount 的特征就是臨時性掛載。
與掛載宿主操作系統目錄或文件不同,掛載臨時文件目錄要通過 --tmpfs 這個選項來完成。由于內存的具體位置不需要我們來指定,這個選項里我們只需要傳遞掛載到容器內的目錄即可。
docker run -d --name webapp --tmpfs /webapp/cache webapp:latest
容器已掛載的臨時文件目錄我們也可以通過 docker inspect 命令查看。
$ docker inspect webapp
[{
## ......"Tmpfs": {"/webapp/cache": ""},
## ......}
]
掛載臨時文件首先要注意它不是持久存儲這一特性,在此基礎上,它有幾種常見的適應場景。
-
應用中使用到,但不需要進行持久保存的敏感數據,可以借助內存的非持久性和程序隔離性進行一定的安全保障。
-
讀寫速度要求較高,數據變化量大,但不需要持久保存的數據,可以借助內存的高讀寫速度減少操作的時間。
3. Volume mount
容器中管理數據主要有兩種方式:
- 數據卷 (
Data Volumes):容器內數據直接映射到本地主機環境; - 數據卷容器 (
Data Volume Containers):使用特定容器維護數據卷。
3.1 數據卷掛載
數據卷的本質其實依然是宿主操作系統上的一個目錄,只不過這個目錄存放在 Docker 內部,接受 Docker 的管理。
數據卷是一個可供容器使用的特殊目錄,它將主機操作系統目錄直接映射進容器,類似于 Linux 中的 mount 操作。
數據卷有如下特性:
- 數據卷可以在容器之間共享和重用,容器間傳遞數據將變得高效方便;
- 對數據卷內數據的修改會立馬生效,無論是容器內操作還是本地操作;
- 對數據卷的更新不會影響鏡像,解耦了應用和數據;
- 數據卷會一直存在,直到沒有容器使用,可以安全地卸載它;
在使用數據卷進行掛載時,我們不需要知道數據具體存儲在了宿主操作系統的何處,只需要給定容器中的哪個目錄會被掛載即可。
我們依然可以使用 -v 或 --volume 選項來定義數據卷的掛載。在用 docker run 命令的時候,使用 -v 標記可以在容器內創建一個數據卷。多次重復使用 -v 標記可以創建多個數據卷。
docker run -d --name webapp -v /webapp/storage webapp:latest
數據卷掛載到容器后,我們可以通過 docker inspect 看到容器中數據卷掛載的信息。
$ docker inspect webapp
[{
## ......"Mounts": [{"Type": "volume","Name": "2bbd2719b81fbe030e6f446243386d763ef25879ec82bb60c9be7ef7f3a25336","Source": "/var/lib/docker/volumes/2bbd2719b81fbe030e6f446243386d763ef25879ec82bb60c9be7ef7f3a25336/_data","Destination": "/webapp/storage","Driver": "local","Mode": "","RW": true,"Propagation": ""}],
## ......}
]
其中 Source 是 Docker 為我們分配用于掛載的宿主機目錄,其位于 Docker 的資源區域 ( 這里是默認的 /var/lib/docker ) 內。當然,我們并不需要關心這個目錄,一切對它的管理都已經在 Docker 內實現了。
為了方便識別數據卷,我們可以像命名容器一樣為數據卷命名,這里的 Name 就是數據卷的命名。在我們未給出數據卷命名的時候,Docker 會采用數據卷的 ID 命名數據卷。我們也可以通過 -v <name>:<container-path> 這種形式來命名數據卷。
docker run -d --name webapp -v appdata:/webapp/storage webapp:latest
由于 -v 選項既承載了 Bind Mount 的定義,又參與了 Volume 的定義,所以其傳參方式需要特別留意。前面提到了,-v 在定義綁定掛載時必須使用絕對路徑,其目的主要是為了避免與數據卷掛載中命名這種形式的沖突。
Volume Mount 與 Bind Mount 的區別在于數據卷 Volume Mount 可以在多個容器之間共享。
下面使用 ubuntu:16.04 鏡像創建一個 web 容器,并創建一個數據卷掛載到容器的 /webapp 目錄:
wohu@iZ:~/docker$ docker run -d -P --name python -it -v /opt ubuntu:16.04
6798b1ee4ea73622dfcdaf7b2bdab8844da4d527399229417f424e755b5be11d
wohu@iZ:~/docker$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6798b1ee4ea7 ubuntu:16.04 "/bin/bash" 7 seconds ago Up 6 seconds python
wohu@iZ:~/docker$ docker exec -it 67 /bin/bash
root@6798b1ee4ea7:/# pwd
/
root@6798b1ee4ea7:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@6798b1ee4ea7:/# exit
exit
wohu@iZm5egn5zptnov4j3oxh4fZ:~/docker$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6798b1ee4ea7 ubuntu:16.04 "/bin/bash" 52 seconds ago Up 51 seconds python
-P 是將容器服務暴露的端口,是自動映射到本地主機的臨時端口。
3.2 數據卷共享
由于數據卷的命名在 Docker 中是唯一的,所以我們很容易通過數據卷的名稱確定數據卷,這就讓我們很方便的讓多個容器掛載同一個數據卷了。
$ sudo docker run -d --name webapp -v html:/webapp/html webapp:latest
$ sudo docker run -d --name nginx -v html:/usr/share/nginx/html:ro nginx:1.12
我們使用 -v 選項掛載數據卷時,如果數據卷不存在,Docker 會為我們自動創建和分配宿主操作系統的目錄,而如果同名數據卷已經存在,則會直接引用。
如果有朋友覺得這樣對數據卷的操作方式還不夠直接和準確,我們還可以通過 docker volume 下的幾個命令專門操作數據卷。
通過 docker volume create 我們可以不依賴于容器獨立創建數據卷。
$ sudo docker volume create appdata
通過 docker volume ls 可以列出當前已創建的數據卷。
$ sudo docker volume ls
DRIVER VOLUME NAME
local html
local appdata
3.3 刪除數據卷
我們可以直接通過 docker volume rm 來刪除指定的數據卷。
docker volume rm appdata
在刪除數據卷之前,我們必須保證數據卷沒有被任何容器所使用 ( 也就是之前引用過這個數據卷的容器都已經刪除 ),否則 Docker 不會允許我們刪除這個數據卷。
在 docker rm 刪除容器的命令中,我們可以通過增加 -v 選項來刪除容器關聯的數據卷。
docker rm -v webapp
如果我們沒有隨容器刪除這些數據卷,Docker 在創建新的容器時也不會啟用它們,即使它們與新創建容器所定義的數據卷有完全一致的特征。也就是說,此時它們已經變成了孤魂野鬼,純粹的占用著硬盤空間而又不受管理。
此時我們可以通過 docker volume rm 來刪除它們,但前提時你能在一堆亂碼般的數據卷 ID 中找出哪個是沒有被容器引用的數據卷。
為此,Docker 向我們提供了 docker volume prune 這個命令,它可以刪除那些沒有被容器引用的數據卷。
$ docker volume prune -f
Deleted Volumes:
af6459286b5ce42bb5f205d0d323ac11ce8b8d9df4c65909ddc2feea7c3d1d53
0783665df434533f6b53afe3d9decfa791929570913c7aff10f302c17ed1a389
65b822e27d0be93d149304afb1515f8111344da9ea18adc3b3a34bddd2b243c7
## ......
4. 數據卷容器
如果用戶需要在多個容器之間共享一些持續更新的數據,最簡單的方式是使用數據卷容器。數據卷容器也是一個容器,但是它的目的是專門用來提供數據卷供其他容器掛載。
首先,創建一個數據卷容器 dbdata,并在其中創建一個數據卷掛載到 /dbdata (沒有該目錄會自動創建),并在其中創建一個dbdata.txt 文件。
wohu@iZm5egn5zptnov4j3oxh4fZ:~/docker/code$ docker run -ti --name dbdata -v /dbdata ubuntu:16.04
root@531dabd7eaa8:/# cd /d
dbdata/ dev/
root@531dabd7eaa8:/# cd /dbdata/
root@531dabd7eaa8:/dbdata# touch dbdata.txt
root@531dabd7eaa8:/dbdata# echo "this is dbdata.txt" > dbdata.txt
root@531dabd7eaa8:/dbdata# exit
exit
wohu@iZm5egn5zptnov4j3oxh4fZ:~/docker/code$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
531dabd7eaa8 ubuntu:16.04 "/bin/bash" 50 seconds ago Exited (0) 4 seconds ago dbdata
c2e114c09291 ubuntu:16.04 "/bin/bash" 17 minutes ago Up 17 minutes python
然后,可以在其他容器中使用 --volumes-from 來掛載 dbdata 容器中的數據卷,例如創建 db1 和 db2 兩個容器,并從 dbdata 容器掛載數據卷:
$ docker run -it --volumes-from dbdata --name db1 ubuntu
$ docker run -it --volumes-from dbdata --name db2 ubuntu
此時,容器 db1 和 db2 都掛載同一個數據卷到相同的 /dbdata 目錄。三個容器任何一方在該目錄下的寫入,其他容器都可以看到。
例如,在 dbdata 容器中創建一個 test 文 件,如下所示:
wohu@iZ:~/docker/code$ docker run -ti --name db1 --volumes-from dbdata ubuntu:16.04
root@eb16116ec6ae:/# ls
bin boot dbdata dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@eb16116ec6ae:/# cd dbdata/
root@eb16116ec6ae:/dbdata# ls
dbdata.txt
root@eb16116ec6ae:/dbdata# cat dbdata.txt
this is dbdata.txt
root@eb16116ec6ae:/dbdata# exit
exit
wohu@iZm5egn5zptnov4j3oxh4fZ:~/docker/code$ docker run -ti --name db2 --volumes-from dbdata ubuntu:16.04
root@c178579dcdb2:/# ls
bin boot dbdata dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@c178579dcdb2:/# cd dbdata/
root@c178579dcdb2:/dbdata# ls
dbdata.txt
root@c178579dcdb2:/dbdata# cat dbdata.txt
this is dbdata.txt
root@c178579dcdb2:/dbdata# exit
exit
wohu@iZm5egn5zptnov4j3oxh4fZ:~/docker/code$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c178579dcdb2 ubuntu:16.04 "/bin/bash" 25 seconds ago Exited (0) 7 seconds ago db2
eb16116ec6ae ubuntu:16.04 "/bin/bash" 57 seconds ago Exited (0) 41 seconds ago db1
531dabd7eaa8 ubuntu:16.04 "/bin/bash" 4 minutes ago Exited (0) 3 minutes ago dbdata
c2e114c09291 ubuntu:16.04 "/bin/bash" 21 minutes ago Up 20 minutes python
wohu@iZm5egn5zptnov4j3oxh4fZ:~/docker/code$
可以多次使用 --volumes-from 參數來從多個容器掛載多個數據卷。還可以從其他已經掛載了容器卷的容器來掛載數據卷。
注意:使用
--volumes-from參數所掛載數據卷的容器自身并不需要保持在運行狀態。如果刪除了掛載的容器 (包括 dbdata、db1 和 db2),數據卷并不會被自動刪除。如果要刪除一個數據卷,必須在刪除最后一個還掛載著它的容器時顯式使用docker rm -v命令來指定同時刪除關聯的容器。
5. 利用數據卷容器來遷移數據
可以利用數據卷容器對其中的數據卷進行備份、恢復、以實現數據的遷移。
5.1 備份
使用下面的命令來備份 dbdata 數據卷容器內的數據卷:
docker run --volumes-from dbdata -v (pwd):/backup --name worker ubuntu tarcvf /backup/backup.tar /dbdata
首先利用 ubuntu 鏡像創建了一個容器 worker。使用 --volumes-from dbdata 參數來讓 worker 容器掛載 dbdata 容器的數據卷(即 dbdata 數據卷)。
使用 -v$(pwd):/backup 參數來掛載本地的當前目錄到 worker 容器的 /backup 目錄。worker 容器啟動后,使用了 tar cvf /backup/backup.tar /dbdata 命令來將 /dbdata 下內容備份為容器內的 /backup/backup.tar,即宿主主機當前目錄下的 backup.tar。
$ sudo docker run --rm --volumes-from appdata -v /backup:/backup ubuntu tar cvf /backup/backup.tar /webapp/storage
通過 --rm 選項,我們可以讓容器在停止后自動刪除,而不需要我們再使用容器刪除命令來刪除它,這對于我們使用一些臨時容器很有幫助。
5.2 恢復
如果要將數據恢復到一個容器,可以按照下面的步驟操作。首先創建一個帶有數據卷的容器 dbdata2
docker run -v /dbdata --name dbdata2 ubuntu /bin/bash
然后創建另一個新的容器,掛載 dbdata2 的容器,并使用 untar 解壓備份文件到所掛載的容器卷中。
docker run --volumes-from dbdata2 -v (pwd):/backup busybox tar xvf /backup/backup.tar
或者下面命令
$ docker run --rm --volumes-from appdata -v /backup:/backup ubuntu tar xvf /backup/backup.tar -C /webapp/storage --strip
總結
以上是生活随笔為你收集整理的Docker 入门系列(4)- Docker 数据管理(挂载目录、挂载文件、数据卷挂载、数据卷共享、数据卷删除、数据卷容器备份和恢复)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Docker 入门系列(3)- Dock
- 下一篇: Docker 入门系列(5)- Dock