Docker docs Use volumes

前言
Docker Volume 是一個比較好的機制將 Docker Container 產生的資料永久保存
有稍微摸索過 Docker 的人可能會有點疑惑: 不是可以透過 -v /home/xzk:/mydata
把 host OS 上面的目錄映射進去 container 裡面嗎? 為什麼我還需要 volumes ?
的確,bind mount 某種程度上這樣很方便。不過 Docker 官方因為以下幾點原因,還是推薦使用 Docker Volume 作為永久資料保存的方式。
Docker Volume 的優勢 (對比 bind mount)
- Volume 比較好備份或整合。
- 您可以使用 Docker CLI 或者 Docker API 管理 Volume。
- Volume 可以同時運作在 Linux 與 Windows 上。
- 與多個 container 共享空間,Volume 會比較安全。
- Volume driver 能夠使用多種空間來源 例如: SSH remote host 或者 cloud provider service (公有雲儲存服務 例如: AWS S3) 並且提供額外功能 例如: 加密
- 對於 Host OS 是 Windows 或者 Mac 而言,Volume 有更好的效能。
再稍微深入一點 Docker 的人可能又會問: Container 內不是有什麼「可寫層」嗎 ? 我直接把檔案寫進去就好
直接把檔案寫入可寫層 (writable layer) 會增加 container 的大小,而且會跟 container 的生命週期 (lifecycle) 綁在一起。
另外關於可寫層 關於這個我們以後會專門做視頻為大家講解 應該是下一篇會寫的東西。 About storage drivers
另外官方有提到 如果想儲存不是要永久保存的資料 還有另外一個選項就是 tmpfs (不常用,直接丟連結)
掛載儲存空間進入容器
一般來說使用 -v or --volume
的機會會來的比 --mount
多,後者支援使用者設定更詳細、更冗長的語法。
-v
最大的優勢就是能以 2~3 個以 :
分隔的語法設定好大部分常用的功能。
bind-mount (掛載 Host OS 自身 File System 的路徑)
1
|
docker run -itd --name bind_mount -v /tmp/auto_gen:/data:rw centos:7
|
上面這行指令就是映射 (mapping) Host OS 身上的 tmp/auto_gen
目錄進去一個名為 bind_mount
的 container 裡面,掛載點路徑: /data
。
其中 tmp/auto_gen
若本來不存在,docker 會自動建立。路徑必定要是絕對路徑
最後一個欄位 :rw
其實可以省略,預設即是 rw
,如果要唯讀可以使用 :ro
。
1
2
3
|
root@eric_nginx[~] ll -d /tmp/auto_gen/
drwxr-xr-x 2 root root 4096 May 14 15:41 /tmp/auto_gen/
|
查看一下 container 裡面掛載狀況
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
root@eric_nginx[~] docker exec -it bind_mount df -Th
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 40G 3.9G 34G 11% /
tmpfs tmpfs 64M 0 64M 0% /dev
tmpfs tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
shm tmpfs 64M 0 64M 0% /dev/shm
###
/dev/sdc1 ext4 40G 3.9G 34G 11% /data
###
tmpfs tmpfs 1.9G 0 1.9G 0% /proc/acpi
tmpfs tmpfs 1.9G 0 1.9G 0% /proc/scsi
tmpfs tmpfs 1.9G 0 1.9G 0% /sys/firmware
|
Docker Volume
1
|
docker run -itd --name docker_volume -v my_vol:/data centos:7
|
上面這行指令會將名為 my_vol
的 Docker Volume 並將該空間映射 (mapping) 至 container。
若該 Docker Volume 原本不存在,則會自動建立。
使用
1
|
docker volume inspect my_vol
|
可以查看 Docker Volume 詳細內容:
1
2
3
4
5
6
7
8
9
10
11
|
[
{
"CreatedAt": "2021-05-14T15:57:45+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/my_vol/_data",
"Name": "my_vol",
"Options": null,
"Scope": "local"
}
]
|
上面有提到,Docker Volume 跟 Docker Container 的生命週期是完全分開的!
也就是說,當 container 被 rm
刪除,Docker Volume 還是會留下來 或者跟我走
Docker Volume Driver
share data among machines
在開始之前先來個情境想像~ 您為了要打造 抗單點故障、高可用性、高容錯率 的應用程式,所以架構圖會長這樣。
請大家不要踢館說這樣也在同一個 Datacenter 沒有異地備援! Docker 官方就給我這張圖 TAT

▲ 每個 node 都是一台 VM,上面都是跑 Docker。你的 Application (container) 會需要掛載一個共享儲存空間 (shared file storage)。
Docker Volume Driver 的目的就是讓使用者可以掛載除了來自 Host OS 以外的空間
透過 SSH 存取/掛載 遠端主機目錄
因為 Docker 預設的 driver 只有 local
如果要使用其他驅動必須額外安裝。
這次要使用到的是 docker-volume-sshfs
更多 Volume 相關 Plug-in #volume-plugin
安裝 sshfs for Docker
1
2
3
4
5
6
|
docker plugin install --grant-all-permissions vieux/sshfs
latest: Pulling from vieux/sshfs
Digest: sha256:1d3c3e42c12138da5ef7873b97f7f32cf99fb6edde75fa4f0bcf9ed277855811
52d435ada6a4: Complete
Installed plugin vieux/sshfs
|
其中 --grant-all-permissions
選項代表開啟所有執行這個 plugin (插件、外掛) 所需的權限。
–grant-all-permissions Grant all permissions necessary to run the plugin
建立 docker_sshfs
使用者
為了測試用途,我在 Eric_207_JMS_Nginx_1 192.168.207.75
建立了一個帳號 docker_sshfs
密碼是 123qwe
1
2
3
4
5
6
7
|
useradd docker_sshfs && passwd docker_sshfs
Changing password for user docker_sshfs.
New password:
BAD PASSWORD: The password is shorter than 7 characters
Retype new password:
passwd: all authentication tokens updated successfully.
|
建立 sshfs Docker Volume
1
2
3
4
|
docker volume create --driver vieux/sshfs \
-o sshcmd=docker_sshfs@192.168.207.75:/home/docker_sshfs \
-o password=123qwe \
myssh_vol
|
建立時會測試可連接性,從 /var/log/secure
可以看到
1
2
3
4
5
|
May 17 15:39:05 Eric_207_JMS_Nginx_1 sshd[59058]: Accepted password for docker_sshfs from 192.168.207.254 port 49169 ssh2
May 17 15:39:05 Eric_207_JMS_Nginx_1 sshd[59058]: pam_unix(sshd:session): session opened for user docker_sshfs by (uid=0)
May 17 15:39:08 Eric_207_JMS_Nginx_1 sshd[59062]: error: Received disconnect from 192.168.207.254 port 49169:0:
May 17 15:39:08 Eric_207_JMS_Nginx_1 sshd[59062]: Disconnected from 192.168.207.254 port 49169
May 17 15:39:08 Eric_207_JMS_Nginx_1 sshd[59058]: pam_unix(sshd:session): session closed for user docker_sshfs
|
sshfs 測試
1
2
3
4
5
|
docker run -itd --name sshfs_test -v myssh_vol:/data centos:7
docker exec -it sshfs_test bash
## in container
[root@9596659b0b64 /] touch /data/rw_test
|
在 Eric_207_JMS_Nginx_1
可以看到剛剛寫入的檔案
1
2
3
|
root@Eric_207_JMS_Nginx_1[~] ll /home/docker_sshfs/
total 0
-rw-r--r-- 1 docker_sshfs docker_sshfs 0 May 17 15:45 rw_test
|
掛載 CIFS/SAMBA
local
driver 自帶可以掛載 CIFS/SAMBA 的選項。
1
2
3
4
5
6
|
docker volume create \
--driver local \
--opt type=cifs \
--opt device=//uxxxxx.your-server.de/backup \
--opt o=addr=uxxxxx.your-server.de,username=uxxxxxxx,password=*****,file_mode=0777,dir_mode=0777 \
--name cif-volume
|
Volume 備份及還原 Backup and restore
Backup, restore, or migrate data volumes
備份 Backup
1
2
3
4
5
6
7
8
|
# backup_0 will store our data
docker run -itd --name backup_0 -v /data centos:7
# Create a{1..9}.txt in /data/
docker exec -it backup_0 touch /data/a{1..9}.txt
# show and check the txt in /data
docker exec -it backup_0 ls -al /data
|
▲ 產生一個 backup_0
container 並且隨機產生一個 Docker Volume 掛載到 /data
,另外產生 a1~a9.txt
。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
root@eric_nginx[~] docker run --rm --volumes-from backup_0 -v $(pwd):/backup centos:7 tar -zcvf /backup/a1_to_a9.tar.gz /data
/data/
/data/a9.txt
/data/a1.txt
/data/a2.txt
/data/a7.txt
/data/a6.txt
/data/a4.txt
/data/a3.txt
/data/a8.txt
/data/a5.txt
tar: Removing leading `/' from member names
|
▲ 建立一個使用後即刪除的 container (--rm
) 用於備份 backup_0
當中的 /data
。並映射 (mapping) 當前目錄 (pwd
) 到 /backup
裡面 (存在於拋棄式 container)
最後確認檔案是否有真正被備份出來
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
root@eric_nginx[~] ll a1_to_a9.tar.gz
-rw-r--r-- 1 root root 199 May 17 17:16 a1_to_a9.tar.gz
root@eric_nginx[~] tar -tvf a1_to_a9.tar.gz
drwxr-xr-x root/root 0 2021-05-17 17:08 data/
-rw-r--r-- root/root 0 2021-05-17 17:08 data/a9.txt
-rw-r--r-- root/root 0 2021-05-17 17:08 data/a1.txt
-rw-r--r-- root/root 0 2021-05-17 17:08 data/a2.txt
-rw-r--r-- root/root 0 2021-05-17 17:08 data/a7.txt
-rw-r--r-- root/root 0 2021-05-17 17:08 data/a6.txt
-rw-r--r-- root/root 0 2021-05-17 17:08 data/a4.txt
-rw-r--r-- root/root 0 2021-05-17 17:08 data/a3.txt
-rw-r--r-- root/root 0 2021-05-17 17:08 data/a8.txt
-rw-r--r-- root/root 0 2021-05-17 17:08 data/a5.txt
|
還原 Restore
1
2
3
|
# before restore,delete the txt file
docker exec -it backup_0 bash -c 'rm -rf /data/*'
docker exec -it backup_0 ls -al /data
|
▲ 在還原之前先把本來在 backup_0
/data/a{1..9}.txt
刪除。
這邊發生了一個需要注意的地方,一開始我是這樣下命令想把 /data
下所有檔案刪除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
docker exec -it backup_0 rm -rf /data/*
docker exec -it backup_0 ls -al /data
total 8
drwxr-xr-x 2 root root 4096 May 17 09:08 .
drwxr-xr-x 1 root root 4096 May 17 09:05 ..
-rw-r--r-- 1 root root 0 May 17 09:08 a1.txt
-rw-r--r-- 1 root root 0 May 17 09:08 a2.txt
-rw-r--r-- 1 root root 0 May 17 09:08 a3.txt
-rw-r--r-- 1 root root 0 May 17 09:08 a4.txt
-rw-r--r-- 1 root root 0 May 17 09:08 a5.txt
-rw-r--r-- 1 root root 0 May 17 09:08 a6.txt
-rw-r--r-- 1 root root 0 May 17 09:08 a7.txt
-rw-r--r-- 1 root root 0 May 17 09:08 a8.txt
-rw-r--r-- 1 root root 0 May 17 09:08 a9.txt
|
恩…. 怎麼擋案還在!!
簡單問狗一下 Docker exec rm not working 才知道原因
*
預設會被當前 shell 解釋 (也就是 Host OS 的 bash)
在解釋下面指令之前,先再去看一眼 tar -tvf a1_to_a9.tar.gz
的結構! 因為在打包的時候有包含到整個資料路徑結構
我們的目標: 把 a1~a9.txt 還原到本來 /data
底下
1
|
docker run --rm --volumes-from backup_0 -v $(pwd):/restore:ro centos:7 tar -zxvf /restore/a1_to_a9.tar.gz data/* -C /data
|
▲ 把當前工作目錄映射到 /restore
(使用後即消失的 container) 而且 read only 因為不希望當前工作目錄被意外刪除之類的。
接著從 a1_to_a9.tar.gz
指定 我們要解壓出來的檔案,也就是 data/*
;並且 切換到 /data
執行,也就是 -C /data
看一下執行結果~
1
2
3
4
5
6
7
8
9
|
data/a9.txt
data/a1.txt
data/a2.txt
data/a7.txt
data/a6.txt
data/a4.txt
data/a3.txt
data/a8.txt
data/a5.txt
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
## show data
root@eric_nginx[~] docker exec -it backup_0 ls -al /data
total 8
drwxr-xr-x 2 root root 4096 May 18 01:32 .
drwxr-xr-x 1 root root 4096 May 17 09:05 ..
-rw-r--r-- 1 root root 0 May 18 01:21 a1.txt
-rw-r--r-- 1 root root 0 May 18 01:21 a2.txt
-rw-r--r-- 1 root root 0 May 18 01:21 a3.txt
-rw-r--r-- 1 root root 0 May 18 01:21 a4.txt
-rw-r--r-- 1 root root 0 May 18 01:21 a5.txt
-rw-r--r-- 1 root root 0 May 18 01:21 a6.txt
-rw-r--r-- 1 root root 0 May 18 01:21 a7.txt
-rw-r--r-- 1 root root 0 May 18 01:21 a8.txt
-rw-r--r-- 1 root root 0 May 18 01:21 a9.txt
|
只備份檔案就好 (不包含路徑)
How do I tar a directory of files and folders without including the directory itself?
:阿我能不能備份檔案就好,不要有路徑的困擾?
答案是可以的喔~
1
|
root@eric_nginx[~] docker run --rm --volumes-from backup_0 -v $(pwd):/backup centos:7 tar -zcvf /backup/only_file_a1_to_a9.tar.gz -C /data/ ./
|
▲ 只要在壓縮之前請 tar
切換工作目錄到指定資料夾就能不考慮資料路徑結構放心解壓。
1
2
3
4
5
6
7
8
9
10
11
|
root@eric_nginx[~] tar -tvf only_file_a1_to_a9.tar.gz
drwxr-xr-x root/root 0 2021-05-18 10:14 ./
-rw-r--r-- root/root 0 2021-05-18 10:14 ./a9.txt
-rw-r--r-- root/root 0 2021-05-18 10:14 ./a1.txt
-rw-r--r-- root/root 0 2021-05-18 10:14 ./a2.txt
-rw-r--r-- root/root 0 2021-05-18 10:14 ./a7.txt
-rw-r--r-- root/root 0 2021-05-18 10:14 ./a6.txt
-rw-r--r-- root/root 0 2021-05-18 10:14 ./a4.txt
-rw-r--r-- root/root 0 2021-05-18 10:14 ./a3.txt
-rw-r--r-- root/root 0 2021-05-18 10:14 ./a8.txt
-rw-r--r-- root/root 0 2021-05-18 10:14 ./a5.txt
|
其他常用指令
查詢 container 使用的 Volume
1
|
docker inspect minio | jq .[].Mounts
|
清除所有未使用 Docker Volume
Author
LastMod
2022-07-04
(abf27d6)