- 爲什麼用數據卷?
- docker 分層文件系統性能差,生命週期與容器相同(容器刪除,文件也丟失)而 docker 數據卷是 mount
到宿主機中,繞開了分層文件系統,和主機磁盤性能相同,容器刪除後依然保留,但是僅限本地磁盤,不能隨容器遷移
docker 官方提供兩種卷:
1.bind mount
2.docker managed volume
bind mount
bind mount 卷直接將將主機上的目錄或文件直接 mount 到容器裏,使用直接,高效
掛載目錄
[root@server1 ~]# docker run -d --name web1 -p 80:80 -v /opt/website:/usr/share/nginx/html nginx
## 使用-V 參數將server1上的/opt/website 掛載到容器中的 nginx默認發佈目錄,就是/usr/share/nginx/html
[root@server1 ~]# curl localhost
<html>
<head><title>403 Forbidden</title></head>
#訪問時被拒絕的,因爲我們裏面還沒有資源
[root@server1 ~]# docker exec web1 mount
#這條命令是在容器中執行mount命令,但是不進入容器。
/dev/mapper/rhel-root on /usr/share/nginx/html type xfs (rw,relatime,attr2,inode64,noquota)
# 可以看到dev/mapper/rhel-root,也就是我們的根目錄,掛載到容器的/usr/share/nginx/html
現在在server1上寫一個資源
[root@server1 ~]# vim /opt/website/index.html
www.westos.org
[root@server1 website]# curl localhost # 訪問
www.westos.org
[root@server1 website]# echo woaini >> index.html #追加
[root@server1 website]# curl localhost
www.westos.org
woaini
還可以在容器中進行更改:
[root@server1 website]# docker container attach web1
## 這樣的方式是登陸不進去的,因爲nginx是一個應用容器,但是之前的 ubuntu 爲什麼可以?
## 查看構建歷史
^C[root@server1 website]# docker history nginx:latest
IMAGE CREATED CREATED BY SIZE COMMENT
9beeba249f3e 10 days ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
<missing> 10 days ago /bin/sh -c #(nop) STOPSIGNAL SIGTERM 0B
<missing> 10 days ago /bin/sh -c #(nop) EXPOSE 80 0B
<missing> 10 days ago /bin/sh -c ln -sf /dev/stdout /var/log/nginx… 22B
<missing> 10 days ago /bin/sh -c set -x && addgroup --system -… 57.6MB
<missing> 10 days ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~buster 0B
<missing> 10 days ago /bin/sh -c #(nop) ENV NJS_VERSION=0.3.9 0B
<missing> 10 days ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.17.10 0B
<missing> 10 days ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
<missing> 11 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 11 days ago /bin/sh -c #(nop) ADD file:7780c81c33e6cc5b6… 69.2MB
[root@server1 website]# docker history ubuntu:latest
IMAGE CREATED CREATED BY SIZE COMMENT
07c86167cdc4 4 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 4 years ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$… 1.9kB
<missing> 4 years ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/poli… 195kB
<missing> 4 years ago /bin/sh -c #(nop) ADD file:b9504126dc5590898… 188MB
# 發現 nginx 鏡像只最後只運行了 nginx,沒有交互式進入 shell,而 ubuntu 有 /bin/bash
所以我們:
[root@server1 website]# docker start web1
doc web1
[root@server1 website]# docker exec -it web1 bash
##-it 是 exec 的選項,bash 表示在容器內執行的操作,打開一個 bash
root@cbdb166d282c:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@cbdb166d282c:/#
root@cbdb166d282c:/# cd /usr/share/nginx/html/
root@cbdb166d282c:/usr/share/nginx/html# echo wobuaini >> index.html
root@cbdb166d282c:/usr/share/nginx/html# cat index.html
www.westos.org
woaini
wobuaini
[root@server1 website]# curl localhost
www.westos.org
woaini
wobuaini # 已經更改了。
[root@server1 website]# cat index.html
www.westos.org
woaini
wobuaini
## 而且容器上的數據其實只是更改了宿主機的數據。
- ‘這種方式,不存在的目錄會自動新建(無論容器還是宿主機),但是會以宿主機爲準,綁定掛載到容器後,容器中的對應目錄如果有數據,就會被覆蓋’
- ‘bind mount 方式掛載時默認權限是 rw,可以在掛載時指定只讀(ro)’
[root@server1 website]# ls /opt/
containerd data website # 此時宿主機時沒有 /opt/data1 和 /opt/data2 的,而且容器中沒有/data12的目錄
[root@server1 website]# docker run -it --name vm1 -v /opt/data1:/data1 -v /opt/data2:/data2:ro ubuntu
root@5ac1d4a95e41:/# ls
bin data1 dev home lib64 mnt proc run srv tmp var
boot data2 etc lib media opt root sbin sys usr # 自動創建了data1 和2的目錄
root@5ac1d4a95e41:/# cd data1
root@5ac1d4a95e41:/data1# touch file1 # data1中可以創建文件
root@5ac1d4a95e41:/data1# touch ../data2/file2 #data2中不可以,因爲我們剛纔設置了只讀掛載
touch: cannot touch '../data2/file2': Read-only file system
[root@server1 website]# cd /opt/data2
[root@server1 data2]# touch file2 #但是我們在宿主機中卻可以創建
[root@server1 data2]# docker attach vm1
root@5ac1d4a95e41:/data1# cd /data2
root@5ac1d4a95e41:/data2# ls
file2
掛載yum源
[root@server1 data2]# docker run -it --name vm2 -v /etc/yum.repos.d/westos.repo:/etc/yum.repos.d/westos.repo rhel7 bash
bash-4.2# yum repolist
Skipping unreadable repository '///etc/yum.repos.d/rhel7.repo'
westos | 4.3 kB 00:00:00
(1/2): westos/group_gz | 146 kB 00:00:00
(2/2): westos/primary_db | 4.2 MB 00:00:00
repo id repo name status
westos westos 5152
repolist: 5152
##這裏我們也可以加上只讀選項,讓 yum 源不能被更改
[root@server1 ~]# docker run -it --name vm2 -v /etc/yum.repos.d/dvd.repo:/etc/yum.repos.d/dvd.repo:ro rhel7 bash
[root@server1 data2]# docker rm -f vm1 vm2 web1
vm1
vm2
web1
docker managed volume(docker 管理卷)
- bind mount 必須指定宿主機文件系統路徑,移植性差(如果容器在 A 主機上 crash 掉,B主機上如果沒有對應路徑,無法進行移植)
- 而 docker managed volume 不需要指定,docker自動爲容器創建,默認數據卷目錄都在/var/lib/docker/volumes
- 如果掛載時指向容器內已有的目錄,原數據會被複制到 volumes 中
[root@server1 ~]# docker run -d --name web1 -p 80:80 -v /usr/share/nginx/html nginx
## 這李我們指向了容器中的 /usr/share/nginx/html 目錄,並沒有指定宿主機的目錄,
[root@server1 ~]# curl localhost ##發現可以訪問,看到 nginx 主頁
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
。。。
[root@server1 data2]# docker inspect web1
...
"Mounts": [
{
"Type": "volume",
"Name": "26b2ff67433a363a7c9ec4c650f6c068c66f3bc31bc79b6da1794b0568edb5f7",
"Source": "/var/lib/docker/volumes/26b2ff67433a363a7c9ec4c650f6c068c66f3bc31bc79b6da1794b0568edb5f7/_data",
## 數據就存放在這裏。
[root@server1 data2]# cd /var/lib/docker/volumes/26b2ff67433a363a7c9ec4c650f6c068c66f3bc31bc79b6da1794b0568edb5f7/_data
[root@server1 _data]# ls ## 進入這個目錄查看
50x.html index.html
[root@server1 _data]# cat index.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
。。。
## 進行頁面更改
[root@server1 _data]# echo woainginx > index.html
[root@server1 _data]# curl localhost
woainginx
## 這樣就比較方便了
不過上面它數據自動掛載的目錄名字太長,我們還可以自定義。
[root@server1 _data]# docker rm -f web1 # 它佔用了80 端口
dweb1
[root@server1 _data]# docker run -d --name web2 -p 80:80 -v website2:/usr/share/nginx/html nginx
5b04432c4ce0cc52a50d277f836a1383ae88034807adb400b5cbc3d6cf6e3eb0
[root@server1 _data]# cd /var/lib/docker/volumes/website2/
[root@server1 website2]# ls
_data
[root@server1 website2]# vim _data/index.html
[root@server1 website2]# echo website2.com > _data/index.html
[root@server1 website2]# curl localhost
website2.com
[root@server1 website2]# docker inspect web2
"Mounts": [
{
"Type": "volume",
"Name": "website2",
"Source": "/var/lib/docker/volumes/website2/_data",
## 就是我們指定的目錄了
[root@server1 website2]# docker volume ls
DRIVER VOLUME NAME
local website2
[root@server1 website2]# docker rm -f web2
web2
[root@server1 website2]# docker volume ls
DRIVER VOLUME NAME
local website2
# 可以看出,即使容器刪除了,數據卷還在
docker volume prune
## 刪除所有沒有被容器使用的卷,如果容器較多,一個一個刪除麻煩
我們先不刪除,下面要用。
另外一種用法
[root@server1 website2]# docker volume create webdata
webdata # 創建一個卷
[root@server1 website2]# docker run -d --name web1 -p 80:80 -v webdata:/usr/share/nginx/html nginx
7c10b34c4eccd7dedfaae52ed160bd26182d306aec9a46f068ab4281597e2067
[root@server1 website2]# cd /var/lib/docker/volumes/webdata/_data/
[root@server1 _data]# ls
50x.html index.html
[root@server1 _data]# echo 111111 > index.html
[root@server1 _data]# curl localhost
111111 # 運行一次
[root@server1 website2]# docker rm -f web1
web1 ## 然後刪除容器
[root@server1 website2]# docker volume ls
DRIVER VOLUME NAME
local webdata ## 卷還在
local website2
[root@server1 _data]# docker run -d --name web2 -p 80:80 -v webdata:/usr/share/nginx/html nginx
c357f10080cb436170a053468644144c377e2a2e80efa181f4f30f1444445254
## 重新創建一個容器依然可以使用剛纔web1的數據
[root@server1 _data]# curl localhost
111111 ##看到了吧
還可以讓多個容器掛載同一個目錄。
[root@server1 _data]# docker run -d --name web3 -v webdata:/usr/share/nginx/html nginx
f5f2bc305557b2cfa3c1410fc5a3a5e4238e5e2b9f9fc6640db2cc511f51cc69
##新建一個容器,去掉端口映射。
[root@server1 _data]# docker inspect web3
......
"Mounts": [
{
"Type": "volume",
"Name": "webdata",
"Source": "/var/lib/docker/volumes/webdata/_data",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3",
[root@server1 _data]# curl localhost
111111
[root@server1 _data]# curl 172.17.0.3
111111
## 這就是docker 管理卷的用法
這樣的 數據卷管理方式就保證了,即便我們的容器掛掉了,但是它的數據依然是保留的,我們想要恢復容器的話,只需要一條開啓命令就可以依然使用之前的數據目錄,這樣在我們多容器之間,我們可以用NFS 把這些數據共享,這樣就可以遠程使用另外一臺主機使容器重啓了。
卷插件
docker 卷默認使用的是 local 類型的驅動,只能存在於宿主機,跨主機的 volume 就需要第三方驅動
[root@server1 ~]# docker volume ls
DRIVER VOLUME NAME
local webdata # local的驅動
local website2
https://docs.docker.com/engine/extend/legacy_plugins/
docker 官方只提供了插件 api,開發者可以根據實際需求定製插件驅動
卷插件的工作流程:
docker engine(daemon) --> volume plugin --> storage platform; docker引擎調用插件,插件操作存儲
- docker plugin 是以 web 服務的方式運行在 docker 主機上,是獨立的
- plugin 的啓動和停止,並不依賴於 docker,docker daemon ,它依靠在默認路徑下查找 unix socket 文件來自動發現插件
- 當客戶端與 daemon 交互並使用插件創建數據卷時,daemon 會在後端找到插件對應的 socket 文件,建立連接併發起相應的 API 請求,最終結合 daemon 自身的處理完成客戶端請求
convoy 卷插件實踐
convey底層存儲支持三種模式:devicemapper、NFS、EBS(亞馬遜的彈性化存儲)
需要在所有 docker 節點提前掛載 NFS 存儲.
nfs掛載
#server1 和 server2 都安裝 nfs:
[root@server1 ~]# yum install -y nfs-utils.x86_64
[root@server2 ~]# yum install -y nfs-utils.x86_64 # 安裝nfs
[root@server1 ~]# systemctl start rpcbind.service
[root@server2 ~]# systemctl start rpcbind.service # 開啓這個服務纔可以看到共享資源
[root@server1 ~]# vim /etc/exports
[root@server1 ~]# cat /etc/exports ## 編輯共享目錄文件
/mnt/nfs *(rw,no_root_squash)
可讀可寫,root 在操作時不切換身份,默認會切換到 nfs 用戶
[root@server1 ~]# mkdir /mnt/nfs # 建立共享目錄
[root@server1 ~]# systemctl start nfs
[root@server1 ~]# exportfs -rv #輸出共享目錄
exporting *:/mnt/nf
#在 server2 上
[root@server2 ~]# mkdir /mnt/nfs
[root@server2 ~]# showmount -e 172.25.254.1 # 查看server1的輸出目錄
Export list for 172.25.254.1:
/mnt/nfs *
[root@server2 ~]# mount 172.25.254.1:/mnt/nfs /mnt/nfs # 掛載
df[root@server2 ~]# df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/rhel-root 17811456 2044816 15766640 12% /
devtmpfs 495420 0 495420 0% /dev
tmpfs 507512 0 507512 0% /dev/shm
tmpfs 507512 13208 494304 3% /run
tmpfs 507512 0 507512 0% /sys/fs/cgroup
/dev/sda1 1038336 135224 903112 14% /boot
tmpfs 101504 0 101504 0% /run/user/0
172.25.254.1:/mnt/nfs 17811456 2748288 15063168 16% /mnt/nfs
[root@server1 ~]# touch /mnt/nfs/file1
[root@server2 ~]# ls /mnt/nfs/file1
/mnt/nfs/file1 # 出現了說明掛載沒有問題。
配置convoy
server1 和 server2 都獲取 convoy 包並解壓
[root@server1 ~]# tar zxf convoy.tar.gz
[root@server1 ~]# cd convoy/
[root@server1 convoy]# ls
convoy convoy-pdata_tools SHA1SUMS
[root@server1 convoy]# cp convoy convoy-pdata_tools /usr/local/bin/ # 放到環境變量下方便使用
[root@server1 convoy]# conv
convertquota convoy convoy-pdata_tools
啓動convey進程:
[root@server1 convoy]# convoy --help
USAGE:
convoy [global options] command [command options] [arguments...]
COMMANDS:
daemon start convoy daemon
info information about convoy
create create a new volume: create [volume_name] [options]
delete delete a volume: delete <volume> [options]
mount mount a volume: mount <volume> [options]
umount umount a volume: umount <volume> [options]
list list all managed volumes
inspect inspect a certain volume: inspect <volume>
snapshot snapshot related operations
backup backup related operations
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--socket, -s "/var/run/convoy/convoy.sock" Specify unix domain socket for communication between server and client
--debug, -d Enable debug level log with client or not
--verbose Verbose level output for client, for create volume/snapshot etc
--help, -h show help
--version, -v print the version
[root@server1 convoy]# convoy daemon --drivers vfs --driver-opts vfs.path=/mnt/nfs &
##使用插件的驅動 #文件存儲路徑
##插件目錄,docker 程序默認會在這個目錄下查找 convoy 套接字
[root@server1 convoy]# mkdir /etc/docker/plugin
[root@server1 plugins]# echo "unix:///var/run/convoy/convoy.sock" > convoy.spec
[root@server1 plugins]# ls
convoy.spec
##把 convoy.sock 文件路徑寫入 convoy.spec,必須以 spec 結尾,convoy 是插件名
## 'server2 上也做上面操作'
root@server2 ~]# tar zxf convoy.tar.gz
cd[root@server2 ~]# cd convoy/
[root@server2 convoy]# ls
convoy convoy-pdata_tools SHA1SUMS
[root@server2 convoy]# cp convoy convoy-pdata_tools /usr/local/bin/
[root@server2 convoy]# conv
convertquota convoy convoy-pdata_tools
[root@server2 convoy]# convoy daemon --drivers vfs --driver-opts vfs.path=/mnt/nfs &
[1] 4071
[root@server2 convoy]# DEBU[0000] Creating config at /var/lib/rancher/convoy pkg=daemon
[root@server2 convoy]# mkdir /etc/docker/plugins
[root@server2 convoy]# cd /etc/docker/plugins/
[root@server2 plugins]# echo "unix:///var/run/convoy/convoy.sock" > convoy.spec
[root@server2 plugins]# ls
convoy.spec
就可以創建捲了:
[root@server1 plugins]# convoy create vol1
DEBU[10515] Calling: POST, /volumes/create, request: POST, /v1/volumes/create pkg=daemon
DEBU[10515] event=create object=volume opts=map[VolumeDriverID: VolumeType: VolumeIOPS:0 PrepareForVM:false Size:0 BackupURL: VolumeName:vol1] pkg=daemon reason=prepare volume=vol1
DEBU[10515] Created volume event=create object=volume pkg=daemon reason=complete volume=vol1
DEBU[10515] Response: vol1 pkg=daemon
vol1
[root@server1 plugins]# convoy list
{
"vol1": {
"Name": "vol1",
"Driver": "vfs",
"MountPoint": "",
"CreatedTime": "Wed May 27 15:50:15 +0800 2020",
"DriverInfo": {
"Driver": "vfs",
"MountPoint": "",
"Path": "/mnt/nfs/vol1", # 可以阿看到卷路徑爲/mnt/nfs/vol1
"PrepareForVM": "false",
"Size": "0",
"VolumeCreatedAt": "Wed May 27 15:50:15 +0800 2020",
"VolumeName": "vol1"
},
"Snapshots": {}
}
}
[root@server1 plugins]# ls /mnt/nfs/
config file1 vol1 ## 路徑下面還多出了一個config目錄,裏面時客戶端的一些配置。
[root@server1 vol1]# docker run -it --name vm1 -v vol1:/data ubuntu
## 打開容器,掛載到容器的/data目錄
root@64962c47da46:/# cd /data/
root@64962c47da46:/data# ls
root@64962c47da46:/data# touch file{1..10}
root@64962c47da46:/data# ls
file1 file10 file2 file3 file4 file5 file6 file7 file8 file9
[root@server1 plugins]# cd /mnt/nfs/vol1/
[root@server1 vol1]# ls
file1 file10 file2 file3 file4 file5 file6 file7 file8 file9
## 可以看到創建的文件在本地
[root@server2 plugins]# cd /mnt/nfs/vol1/
[root@server2 vol1]# ls
file1 file10 file2 file3 file4 file5 file6 file7 file8 file9
## 而且server2也能看見了
這個時候容器就算 crash 掉,數據也已經保存在了本地,可以隨時遷移容器
[root@server1 vol1]# docker rm -f vm1 # 掛掉VM1 容器
[root@server2 ~]# docker run -it --name vm1 -v vol1:/data ubuntu
##在 server2 上恢復,這裏不用再次去新建 convoy 是因爲插件是通過 nfs 同步的,一個節點新建,其他節點都能看到
root@4bfc57f68d52:/# cd data/
root@4bfc57f68d52:/data# ls
file1 file10 file2 file3 file4 file5 file6 file7 file8 file9
清理convoy存儲
先刪除所有佔用存儲的容器。
[root@server2 ~]# umount /mnt/nfs/
[root@server2 ~]# docker rm -f vm1
dvm1
[root@server2 ~]# convoy list
{}
## server2上取消掛載後就不見這個捲了
[root@server1 vol1]# docker rm -f vm1
[root@server1 ~]# convoy delete vol1
DEBU[12302] Calling: DELETE, /volumes/, request: DELETE, /v1/volumes/ pkg=daemon
DEBU[12302] event=delete object=volume pkg=daemon reason=prepare volume=vol1
DEBU[12302] Cleaning up /mnt/nfs/vol1 for volume vol1 pkg=vfs
DEBU[12302] event=delete object=volume pkg=daemon reason=complete volume=vol1
[root@server1 ~]# convoy list
{}
## server1上刪除容器後 執行第二部刪除操作,就看不到了
[root@server1 ~]# cd /mnt/nfs/
[root@server1 nfs]# ls
config file1 ## 數據目錄也不見了