Docker -- 數據卷的管理和卷插件

  • 爲什麼用數據卷?
  • 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						## 數據目錄也不見了
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章