溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Docker目錄掛載及文件共享的方法

發(fā)布時(shí)間:2022-03-19 15:25:33 來(lái)源:億速云 閱讀:3493 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“Docker目錄掛載及文件共享的方法”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Docker目錄掛載及文件共享的方法”吧!

前言

Docker中的數(shù)據(jù)可以存儲(chǔ)在類似于虛擬機(jī)磁盤的介質(zhì)中,在Docker中稱為數(shù)據(jù)卷(Data Volume)。數(shù)據(jù)卷可以用來(lái)存儲(chǔ)Docker應(yīng)用的數(shù)據(jù),也可以用來(lái)在Docker容器間進(jìn)行數(shù)據(jù)共享。數(shù)據(jù)卷呈現(xiàn)給Docker容器的形式就是一個(gè)目錄,支持多個(gè)容器間共享,修改也不會(huì)影響鏡像。使用Docker的數(shù)據(jù)卷,類似在系統(tǒng)中使用 mount 掛載一個(gè)文件系統(tǒng)。

Docker文件系統(tǒng)工作情況

想要了解Docker Volume,首先我們需要知道Docker的文件系統(tǒng)是如何工作的。Docker鏡像是由多個(gè)文件系統(tǒng)(只讀層)疊加而成。當(dāng)我們啟動(dòng)一個(gè)容器的時(shí)候,Docker會(huì)加載只讀鏡像層并在其上(鏡像棧頂部)添加一個(gè)讀寫層。如果運(yùn)行中的容器修改了現(xiàn)有的一個(gè)已經(jīng)存在的文件,那該文件將會(huì)從讀寫層下面的只讀層復(fù)制到讀寫層,該文件的只讀版本仍然存在,只是已經(jīng)被讀寫層中該文件的副本所隱藏。當(dāng)刪除Docker容器,并通過(guò)該鏡像重新啟動(dòng)時(shí),之前的更改將會(huì)丟失。在Docker中,只讀層及在頂部的讀寫層的組合被稱為Union File System(聯(lián)合文件系統(tǒng))。

為了能夠保存(持久化)數(shù)據(jù)以及共享容器間的數(shù)據(jù),Docker提出了Volume的概念。簡(jiǎn)單來(lái)說(shuō),Volume就是目錄或者文件,它可以繞過(guò)默認(rèn)的聯(lián)合文件系統(tǒng),而以正常的文件或者目錄的形式存在于宿主機(jī)上。
最常用的其實(shí)就是目錄掛載文件共享的使用。

掛載本地目錄

Docker容器啟動(dòng)的時(shí)候,如果要掛載宿主機(jī)的一個(gè)目錄,可以用-v參數(shù)指定,這個(gè)其實(shí)也是創(chuàng)建一個(gè)數(shù)據(jù)卷,只不過(guò)是把一個(gè)本地主機(jī)的目錄當(dāng)做數(shù)據(jù)卷掛載在容器上。
譬如我要啟動(dòng)一個(gè) CentOS 容器,宿主機(jī)的 /hostTest 目錄掛載到容器的 /conainterTest 目錄,可通過(guò)以下方式指定:

1
docker run -it  -v /hostTest:/conainterTest --name centos-demo-1  centos

這樣在容器啟動(dòng)后,容器內(nèi)會(huì)自動(dòng)創(chuàng)建 /conainterTest 的目錄。通過(guò)這種方式,我們可以明確一點(diǎn),即-v參數(shù)中,冒號(hào)”:”前面的目錄是宿主機(jī)目錄,后面的目錄是容器內(nèi)目錄。

基礎(chǔ)操作

下面我們來(lái)驗(yàn)證一下: 我服務(wù)器上的docker 版本都是 18.09.2

1
2
3
[root@VM_156_200_centos ~]# docker version
Client:
 Version:           18.09.2

首先執(zhí)行命令:

1
2
3
4
5
6
7
[root@VM_156_200_centos /]# docker run -it  -v /hostTest:/conainterTest --name centos-demo-1  centos 
[root@a8e50a72519e /]# cd conainterTest/
[root@a8e50a72519e conainterTest]# echo "123" > 123
[root@a8e50a72519e conainterTest]# ls
123
[root@a8e50a72519e conainterTest]# exit
exit

run 一個(gè) centos 的鏡像,如果本地沒(méi)有的話,會(huì)去執(zhí)行docker pull centos下載,并生成一個(gè) centos-demo-1 的容器,并進(jìn)入交互模式
這時(shí)候就可以看到容器里面已經(jīng)有生成/conainterTest這個(gè)目錄了,接下來(lái)我們?cè)谶@個(gè)目錄下創(chuàng)建了一個(gè) 123 的文件,然后退出容器。
回到宿主機(jī)之后,可以看到宿主機(jī)對(duì)應(yīng)的目錄已經(jīng)有/hostTest這個(gè)目錄了,并且也有 123 這個(gè)文件。

1
2
3
4
5
6
[root@VM_156_200_centos /]# cd /hostTest/
[root@VM_156_200_centos hostTest]# ls
123
[root@VM_156_200_centos hostTest]# echo "456" > 456
[root@VM_156_200_centos hostTest]# ls
123  456

而且我們還再新建了一個(gè) 456 文件,看看會(huì)不會(huì)同步到容器中,接下來(lái)啟動(dòng)容器查看一下:

1
2
3
4
5
6
[root@VM_156_200_centos hostTest]# docker start -ai  centos-demo-1
[root@a8e50a72519e /]# cd conainterTest/
[root@a8e50a72519e conainterTest]# ls
123  456
[root@a8e50a72519e conainterTest]# cat 456
456

可以看到這個(gè)是有的,所以這兩個(gè)目錄其實(shí)就是共享的。接下來(lái)具體分析一下這個(gè)命令的幾種情況。

容器目錄不可以為相對(duì)路徑

具體指令如下:

1
2
3
[root@VM_156_200_centos /]# docker run -it  -v /hostTest:conainterTest --name centos-demo-1  centos 
docker: Error response from daemon: invalid volume specification: '/hostTest:conainterTest': invalid mount config for type "bind": invalid mount path: 'conainterTest' mount path must be absolute.
See 'docker run --help'.

直接報(bào)錯(cuò),提示 conainterTest 不是一個(gè)絕對(duì)路徑,所謂的絕對(duì)路徑,必須以下斜線“/”開頭。

宿主機(jī)目錄如果不存在,則會(huì)自動(dòng)生成

先把宿主機(jī)的目錄刪掉:然后在run容器:
ps: 因?yàn)橛袨槿萜髅?,但是因?yàn)橄嗤值娜萜鲃?chuàng)建會(huì)報(bào)錯(cuò),所以這邊我有一個(gè)隱形操作,就是每次操作,都會(huì)先執(zhí)行:

1
2
[root@VM_156_200_centos /]# docker rm centos-demo-1 
centos-demo-1

當(dāng)然這個(gè)不是重點(diǎn),所以后面我不會(huì)再把這個(gè)操作再說(shuō)明,默認(rèn)每次run的時(shí)候,如果名字相同,那么我就會(huì)先刪掉同名的容器。

1
2
3
4
[root@VM_156_200_centos /]# rm -rf  hostTest
[root@VM_156_200_centos /]# docker run -it  -v /hostTest:/conainterTest --name centos-demo-1  centos 
[root@e57a25c8f8e5 /]# ls
anaconda-post.log  bin  conainterTest  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

查看宿主機(jī),發(fā)現(xiàn)新增了 /hostTest 這個(gè)目錄

1
2
3
[root@VM_156_200_centos /]# cd hostTest/
[root@VM_156_200_centos hostTest]# pwd
/hostTest

宿主機(jī)的目錄如果為相對(duì)路徑呢?

這次,我們換個(gè)目錄名 hostTest1 試試:

1
[root@VM_156_200_centos /]# docker run -it  -v hostTest1:/conainterTest --name centos-demo-1  centos

接下來(lái)去 / 查找有沒(méi)有增加 hostTest1 目錄:

1
[root@VM_156_200_centos /]# ll / | grep 'hostTest1'

發(fā)現(xiàn)找不到?? 那么 hostTest1 在哪里創(chuàng)建呢? 通過(guò)docker inspect命令,查看容器“Mounts”那一部分,我們可以得到這個(gè)問(wèn)題的答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@VM_156_200_centos hostTest]# docker inspect centos-demo-1
[
    {
    ...
        "Mounts": [
            {
                "Type": "volume",
                "Name": "hostTest1",
                "Source": "/var/lib/docker/volumes/hostTest1/_data",
                "Destination": "/conainterTest",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ],
    ...
    }
]

可以看出,容器內(nèi)的 /conainterTest 目錄掛載的是宿主機(jī)上的 /var/lib/docker/volumes/hostTest1/_data 目錄。
原來(lái),所謂的相對(duì)路徑指的是 /var/lib/docker/volumes/ ,與宿主機(jī)的當(dāng)前目錄無(wú)關(guān)。

只是-v指定一個(gè)目錄的情況?

先啟動(dòng)一個(gè)容器:

1
2
3
[root@VM_156_200_centos volumes]# docker run -it  -v /test2 --name centos-demo-1  centos 
[root@64e9d535b48c /]# ls
anaconda-post.log  bin  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  test2  tmp  usr  var

發(fā)現(xiàn) /test2 在容器中有存在,那么宿主機(jī)的目錄在哪里呢?? 用 docker inspect 查看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@VM_156_200_centos hostTest]# docker inspect centos-demo-1
[
    {
    ...
        "Mounts": [
            {
                "Type": "volume",
                "Name": "43c602bf16cf87452499988cd4dbfad834e40e2f01e93d960ec01a557f40bc58",
                "Source": "/var/lib/docker/volumes/43c602bf16cf87452499988cd4dbfad834e40e2f01e93d960ec01a557f40bc58/_data",
                "Destination": "/test2",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
    ...
    }
]

可以看出,同上例中的結(jié)果類似,只不過(guò),它不是相對(duì)路徑的目錄名,而是隨機(jī)生成的一個(gè)目錄名。

如果在容器內(nèi)修改了目錄的屬主和屬組,那么對(duì)應(yīng)的掛載點(diǎn)是否會(huì)修改呢?

首先開啟一個(gè)容器,并查看容器內(nèi) /conainterTest/ 的屬性

1
2
3
[root@VM_156_200_centos volumes]# docker run -it  -v /hostTest:/conainterTest --name centos-demo-1  centos 
[root@25d6613bcb81 /]# ll -d /conainterTest/
drwxr-xr-x 2 root root 4096 Feb 26 03:24 /conainterTest/

接下來(lái)查看宿主機(jī)內(nèi) /hostTest 目錄的屬性:

1
2
[root@VM_156_200_centos volumes]# ll -d /hostTest/
drwxr-xr-x 2 root root 4096 Feb 26 11:24 /hostTest/

可以看到都是 root。接下來(lái)我們?cè)谌萜鲀?nèi)新建用戶,修改 /conainterTest 的屬主和屬組:

1
2
3
4
5
[root@VM_156_200_centos volumes]# docker start -ai  centos-demo-1
[root@25d6613bcb81 /]# useradd kbz
[root@25d6613bcb81 /]# chown -R kbz.kbz /conainterTest/
[root@25d6613bcb81 /]# ll -d /conainterTest/
drwxr-xr-x 2 kbz kbz 4096 Feb 26 03:24 /conainterTest/

可以看到已經(jīng)將屬主和屬組都變成 kbz 了。接下來(lái)查看宿主機(jī) /hostTest 的屬主和屬組是否會(huì)改變??

1
2
[root@VM_156_200_centos volumes]# ll -d /hostTest/
drwxr-xr-x 2 privoxy privoxy 4096 Feb 26 11:24 /hostTest/

發(fā)現(xiàn)改變了,但是并不是 kbz,而是一個(gè) privoxy ??
原來(lái),這個(gè)與UID有關(guān)系,UID,即“用戶標(biāo)識(shí)號(hào)”,是一個(gè)整數(shù),系統(tǒng)內(nèi)部用它來(lái)標(biāo)識(shí)用戶。一般情況下它與用戶名是一一對(duì)應(yīng)的。首先查看容器內(nèi)victor對(duì)應(yīng)的UID是多少:

1
2
3
[root@VM_156_200_centos volumes]# docker start -ai  centos-demo-1
[root@25d6613bcb81 /]# cat /etc/passwd | grep kbz
kbz:x:1000:1000::/home/kbz:/bin/bash

kbz 的 UID 為 1000,那么宿主機(jī)內(nèi) 1000 對(duì)應(yīng)的用戶是誰(shuí)呢?

1
2
[root@VM_156_200_centos volumes]# cat /etc/passwd | grep 1000
privoxy:x:1000:1000::/home/privoxy:/bin/bash

果然是 privoxy, 那么就說(shuō)的通了。

容器銷毀了,在宿主機(jī)上新建的掛載目錄是否會(huì)消失

在這里,主要驗(yàn)證兩種情況:

  • 指定了宿主機(jī)目錄,即 -v /hostTest:/conainterTest。

  • 沒(méi)有指定宿主機(jī)目錄,即 -v /conainterTest

首先的第一種情況:

1
2
3
4
5
6
7
8
9
[root@VM_156_200_centos /]# rm -rf /hostTest/  
[root@VM_156_200_centos /]# ll | grep hostTest
[root@VM_156_200_centos /]# docker run -it  -v /hostTest:/conainterTest --name centos-demo-1  centos 
[root@a225da0f4576 /]# exit
exit
[root@VM_156_200_centos /]# docker rm centos-demo-1 
centos-demo-1
[root@VM_156_200_centos /]# ll | grep hostTest
drwxr-xr-x    2 root root     4096 Feb 26 14:10 hostTest

我們先把宿主機(jī)的 hostTest 目錄刪掉,然后進(jìn)行掛載,再把容器刪掉,最后發(fā)現(xiàn)掛載時(shí)候建的 hostTest 還存在。
可以看出,即便容器銷毀了,新建的掛載目錄不會(huì)消失。進(jìn)一步也可驗(yàn)證,如果宿主機(jī)目錄的屬主和屬組發(fā)生了變化,容器銷毀后,宿主機(jī)目錄的屬主和屬組不會(huì)恢復(fù)到掛載之前的狀態(tài)。

第二種情況:
通過(guò)上面的驗(yàn)證知道,如果沒(méi)有指定宿主機(jī)的目錄,則容器會(huì)在 /var/lib/docker/volumes/ 隨機(jī)配置一個(gè)目錄,那么我們看看這種情況下的容器銷毀是否會(huì)導(dǎo)致相應(yīng)目錄的刪除:
首先啟動(dòng)容器:

1
[root@VM_156_200_centos /]# docker run -it  -v conainterTest --name centos-demo-1  centos

然后通過(guò) docker inspect 來(lái)查看掛載的宿主機(jī)的目錄:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@VM_156_200_centos ~]# docker inspect centos-demo-1
[
    {
    ...
        "Mounts": [
            {
                "Type": "volume",
                "Name": "225e07eb178cc49ee6a4bd95d82430fdf77af717fc4924cb0d201a3f2f162683",
                "Source": "/var/lib/docker/volumes/225e07eb178cc49ee6a4bd95d82430fdf77af717fc4924cb0d201a3f2f162683/_data",
                "Destination": "conainterTest",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
    ...
    }
]

對(duì)應(yīng)的是/var/lib/docker/volumes/22…83/_data目錄, 去查看一下這個(gè)目錄是否存在:

1
2
3
[root@VM_156_200_centos /]# ll /var/lib/docker/volumes/225e07eb178cc49ee6a4bd95d82430fdf77af717fc4924cb0d201a3f2f162683/
total 4
drwxr-xr-x 2 root root 4096 Feb 26 14:14 _data

發(fā)現(xiàn)該目錄依然存在。而且即使重啟了docker服務(wù),該目錄依舊存在。

掛載宿主機(jī)已存在目錄后,在容器內(nèi)對(duì)其進(jìn)行操作,報(bào)“Permission denied”

可通過(guò)兩種方式解決:

  • 關(guān)閉selinux。
    臨時(shí)關(guān)閉:# setenforce 0
    永久關(guān)閉:修改/etc/sysconfig/selinux文件,將SELINUX的值設(shè)置為disabled。

  • 以特權(quán)方式啟動(dòng)容器
    指定–privileged參數(shù)

    1
    docker run -it --privileged=true -v /test:/soft centos /bin/bash

將掛載的路徑的權(quán)限設(shè)置為只讀

掛載的路徑權(quán)限默認(rèn)為讀寫。如果指定為只讀可以用:ro

1
2
3
4
[root@VM_156_200_centos /]# docker run -it  -v /hostTest:/conainterTest:ro --name centos-demo-1  centos 
[root@c9c2a58bbfef /]# cd conainterTest/
[root@c9c2a58bbfef conainterTest]# echo "12" > 12
bash: 12: Read-only file system

這時(shí)候,如果在容器的掛載目錄下進(jìn)行讀寫操作的話,就會(huì)報(bào)錯(cuò)。

掛載文件

不僅可以掛載目錄,還可以掛載文件。如果掛載目錄可以理解為系統(tǒng)的目錄映射的話,那么掛載文件,也可以理解為文件映射。
語(yǔ)法跟掛載目錄差不多。不過(guò)有一點(diǎn)要注意的是,掛載宿主機(jī)文件的時(shí)候,該文件一定要存在,如果該文件不存在,就會(huì)跟上面例子說(shuō)的一樣,docker會(huì)自動(dòng)幫你創(chuàng)建,但是這時(shí)候它創(chuàng)建的時(shí)候,就是目錄了,而不是文件了。
接下來(lái)嘗試一下:

1
2
3
4
[root@VM_156_200_centos ~]# vim web.list
[root@VM_156_200_centos ~]# cat web.list
1111111111
2222222222

我創(chuàng)建了一個(gè) web.list 的文件,并且寫入了兩行,這時(shí)候就開始掛載這個(gè)文件了:

1
2
3
4
5
6
7
8
9
10
11
[root@VM_156_200_centos ~]# docker run -it  -v /root/web.list:/root/web.list --name centos-demo-1  centos 
[root@6c91b4f6b765 /]# cat /root/web.list 
1111111111
2222222222
[root@6c91b4f6b765 /]# echo "333333" >> /root/web.list 
[root@6c91b4f6b765 /]# cat /root/web.list 
1111111111
2222222222
333333
[root@6c91b4f6b765 /]# exit
exit

可以看到掛載成功了,并且在容器內(nèi)又在 web.list 寫入新的一行,并退出容器

1
2
3
4
5
6
7
8
9
10
11
[root@VM_156_200_centos ~]# cat /root/web.list 
1111111111
2222222222
333333
[root@VM_156_200_centos ~]# echo "444444" >> /root/web.list 
[root@VM_156_200_centos ~]# docker start -ai  centos-demo-1
[root@6c91b4f6b765 /]# cat /root/web.list 
1111111111
2222222222
333333
444444

可以看到在宿主機(jī),之前容器新寫的一行也同步過(guò)來(lái)了,而且之后宿主機(jī)也新加了一行,之后進(jìn)入容器也一樣有。
掛載文件跟上述掛載目錄一樣,也是默認(rèn)為讀寫的,如果設(shè)置為只讀的話,那么在容器里面就不能修改了,只能在宿主機(jī)修改:

1
2
3
4
5
6
7
8
9
10
11
12
[root@VM_156_200_centos ~]# docker run -it  -v /root/web.list:/root/web.list:ro --name centos-demo-1  centos 
[root@2a7e3a4ed710 /]# echo "5555" >> /root/web.list 
bash: /root/web.list: Read-only file system
[root@2a7e3a4ed710 /]# exit
exit
[root@VM_156_200_centos ~]# echo "666" >> /root/web.list 
[root@VM_156_200_centos ~]# cat web.list 
1111111111
2222222222
333333
444444
666

可以看到在容器內(nèi)編輯是會(huì)報(bào)錯(cuò)的,而在宿主機(jī)內(nèi)就不會(huì)。

掛載數(shù)據(jù)卷容器

上述的掛載目錄和掛載文件,只是 Docker Volume 的使用方式。
而我們用的方式就是在docker run命令后面跟上-v參數(shù)即可創(chuàng)建一個(gè)數(shù)據(jù)卷,然后把本地目錄或者文件當(dāng)做數(shù)據(jù)卷掛載到容器中。當(dāng)然也可以跟多個(gè)-v參數(shù)來(lái)創(chuàng)建多個(gè)數(shù)據(jù)卷。
因此我們完全可以創(chuàng)建一個(gè)數(shù)據(jù)卷的容器,后面專門用來(lái)提供其他容器來(lái)掛載。
首先先創(chuàng)建一個(gè)專門用來(lái)提供數(shù)據(jù)的本地目錄,然后掛載到一個(gè)普通的容器,而這個(gè)容器就是其他容器要掛載的數(shù)據(jù)卷容器,這種方式主要就是用來(lái)做多個(gè)容器的共享數(shù)據(jù)存在的。

1
2
3
4
[root@VM_156_200_centos ~]# mkdir go-data
[root@VM_156_200_centos go-data]# cp -r  /root/go/src/goworker/ ./
[root@VM_156_200_centos go-data]# ls
goworker

go-data 這個(gè)目錄存放一些 golang 語(yǔ)言的項(xiàng)目,然后我們把它做成一個(gè)數(shù)據(jù)卷容器,并指向容器的 /go/src 目錄:

1
2
3
4
5
6
[root@VM_156_200_centos go-data]# docker run -it  -v /root/go-data/:/go/src/ --name centos-go-data  centos 
[root@10d5c4f11e77 /]# cd /go/src
[root@10d5c4f11e77 src]# ls
goworker
[root@10d5c4f11e77 src]# exit
exit

這樣這個(gè)就生成了一個(gè)名為 centos-go-data 的數(shù)據(jù)卷容器了,容器里面 /go/src 目錄含有一個(gè)叫做 goworker 的 go 程序。剛好我服務(wù)器上之前有一個(gè) go-1.10 環(huán)境的鏡像。
接下來(lái)我們將這個(gè)鏡像 run 起來(lái),并且掛載 centos-go-data 這個(gè)數(shù)據(jù)卷容器(通過(guò) –volumes-from 可以掛載數(shù)據(jù)卷容器),看看原來(lái) /go/src 里面應(yīng)該為空的目錄,會(huì)不會(huì)有這個(gè)數(shù)據(jù)卷容器里面的go程序代碼?

1
2
3
4
5
6
7
[root@VM_156_200_centos go-data]# docker run -it --volumes-from centos-go-data --name golang-volume-demo kbz/golang-1.10
root@412ab80da79e:/app# cd /go/src 
root@412ab80da79e:/go/src# ls
goworker
root@412ab80da79e:/go/src# cd goworker/
root@412ab80da79e:/go/src/goworker# ls
hello_worker.go  worker  worker.go

可以看到原本應(yīng)該有空的 src 目錄,竟然有代碼目錄存在了,說(shuō)明這個(gè)數(shù)據(jù)卷掛載成功了。接下來(lái)將這個(gè) goworker 添加一個(gè)文件,看會(huì)不會(huì)同步到掛載的那個(gè)本地目錄:

1
2
3
4
5
6
7
8
root@412ab80da79e:/go/src/goworker# echo "123" > 123
root@412ab80da79e:/go/src/goworker# ls
123  hello_worker.go  worker  worker.go
root@412ab80da79e:/go/src/goworker# exit
exit
[root@VM_156_200_centos go-data]# cd /root/go-data/goworker/
[root@VM_156_200_centos goworker]# ls
123  hello_worker.go  worker  worker.go

發(fā)現(xiàn)本地目錄也出現(xiàn)剛才在容器中出現(xiàn)的 123 文件了,說(shuō)明還是共享成功了。 這時(shí)候我們?cè)?run 一個(gè)新的 golang 環(huán)境的容器,然后也掛載這個(gè)數(shù)據(jù)卷看看數(shù)據(jù)會(huì)不會(huì)也有這個(gè) 123 文件?

1
2
3
4
[root@VM_156_200_centos goworker]# docker run -it --volumes-from centos-go-data --name golang-volume-demo-2 kbz/golang-1.10
root@25d7a8fd5da1:/app# cd /go/src/goworker/
root@25d7a8fd5da1:/go/src/goworker# ls
123  hello_worker.go  worker  worker.go

發(fā)現(xiàn)新的容器也有這個(gè) 123 文件。
其實(shí)很多時(shí)候這種數(shù)據(jù)卷容器都是用來(lái)做持久化的,舉個(gè)例子,比如我有一些golang的測(cè)試代碼,做成了一個(gè)數(shù)據(jù)卷容器,然后我本地有好幾個(gè)不同go環(huán)境的容器,每一個(gè)容器在 run 的時(shí)候,都會(huì)掛載這個(gè)數(shù)據(jù)卷容器。但是我們不希望在go環(huán)境的容器里面去修改這個(gè)數(shù)據(jù)卷容器里面的數(shù)據(jù)。所以剛開始我們?cè)谏蛇@個(gè)數(shù)據(jù)卷容器的時(shí)候,就要把權(quán)限設(shè)置為只讀才行,這樣這個(gè)數(shù)據(jù)卷就變成一個(gè)公共的掛載數(shù)據(jù)卷,別的容器只能掛載使用里面的數(shù)據(jù),但是卻不能做修改:

1
2
3
4
5
6
7
8
9
10
11
[root@VM_156_200_centos goworker]# docker rm centos-go-data
centos-go-data
[root@VM_156_200_centos goworker]# docker run -it  -v /root/go-data/:/go/src/:ro --name centos-go-data  centos 
[root@851a09a40cb8 /]# exit
exit
[root@VM_156_200_centos goworker]# docker run -it --volumes-from centos-go-data --name golang-volume-demo-3 kbz/golang-1.10
root@c986955f5ee0:/app# cd /go/src/goworker/
root@c986955f5ee0:/go/src/goworker# ls
123  hello_worker.go  worker  worker.go
root@c986955f5ee0:/go/src/goworker# echo "456" > 456
bash: 456: Read-only file system

一旦進(jìn)行修改,就會(huì)報(bào)錯(cuò)。
而且這種數(shù)據(jù)卷容器可以掛好幾個(gè)的,就跟用 -v 掛載多個(gè)本地目錄一樣:

1
docker run --name data -v /opt/data1:/var/www/data1 -v /opt/data2:/var/www/data2:ro -it docker.io/ubuntu

這邊稍微說(shuō)一下,沒(méi)有加-t和-i參數(shù),所以這個(gè)容器創(chuàng)建好之后是沒(méi)有進(jìn)行交互模式的。其中 -i 表示以“交互模式”運(yùn)行容器。 -t 表示打開一個(gè)連接到容器的終端。

1
2
3
4
5
[root@VM_156_200_centos js-data]# docker run -it --volumes-from centos-go-data --volumes-from centos-js-data  --name golang-volume-demo-4 kbz/golang-1.10
root@94e9d5868711:/app# cd /go/src/ && ls
goworker
root@94e9d5868711:/go/src# cd /js && ls
test.js

這樣就掛載了兩個(gè)數(shù)據(jù)卷容器。
使用數(shù)據(jù)容器的兩個(gè)注意點(diǎn):

  • 不要運(yùn)行數(shù)據(jù)容器,這純粹是在浪費(fèi)資源。

  • 不要為了數(shù)據(jù)容器而使用“最小的鏡像”,如busybox或scratch,只使用數(shù)據(jù)庫(kù)鏡像本身就可以了。你已經(jīng)擁有該鏡像,所以并不需要占用額外的空間。

創(chuàng)建數(shù)據(jù)卷的其他方式

除了以上用 -v 來(lái)創(chuàng)建掛載數(shù)據(jù)卷之外,docker 還可以用以下方式來(lái)創(chuàng)建數(shù)據(jù)卷:

直接用docker volume 來(lái)管理

Docker 新版本中引入了 docker volume 命令來(lái)管理 Docker volume:
比如我創(chuàng)建一個(gè)名為 js-data 的數(shù)據(jù)卷:

1
2
[root@VM_156_200_centos js-data]# docker volume create --name js-data
js-data

這樣就創(chuàng)建成功了,可以通過(guò) docker volume ls 來(lái)查看當(dāng)前的數(shù)據(jù)卷:

1
2
3
4
5
[root@VM_156_200_centos js-data]# docker volume ls
DRIVER              VOLUME NAME
local               0e01f8abe89c9956609eed063c623536112525fb32cf4c0363255a953aa2d35d
...
local               js-data

然后我們可以通過(guò)docker volume inspect xxx來(lái)查看這個(gè)數(shù)據(jù)卷所對(duì)應(yīng)的本地目錄是哪個(gè)?

1
2
3
4
5
6
7
8
9
10
11
12
[root@VM_156_200_centos js-data]# docker volume inspect js-data
[
    {
        "CreatedAt": "2019-02-26T16:12:28+08:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/js-data/_data",
        "Name": "js-data",
        "Options": {},
        "Scope": "local"
    }
]

可以看到對(duì)應(yīng)的本地目錄就是 /var/lib/docker/volumes/js-data/_data 這個(gè)目錄。當(dāng)然目前這個(gè)目錄是空的:

1
2
[root@VM_156_200_centos js-data]# ll /var/lib/docker/volumes/js-data/_data
total 0

接下來(lái)就可以將這個(gè)數(shù)據(jù)卷掛載上去:

1
2
3
4
5
6
7
8
[root@VM_156_200_centos js-data]# docker run -it -v js-data:/js  --name golang-volume-demo-3 kbz/golang-1.10
root@c55671066dec:/app# cd /js
root@c55671066dec:/js# ls
root@c55671066dec:/js# echo "123" > 123
root@c55671066dec:/js# ls
123
root@c55671066dec:/js# exit
exit

語(yǔ)法差不了多少,也是 -v 來(lái)指定,不過(guò)這時(shí)候“:”前面已經(jīng)不是宿主機(jī)的目錄了,而是換成 js-data 這個(gè)數(shù)據(jù)卷了,當(dāng)然這個(gè)目錄是空的。
然后我們給他添加了一個(gè)新的文件 123,看看宿主機(jī)目錄會(huì)不會(huì)也有?

1
2
3
4
5
[root@VM_156_200_centos js-data]# ll /var/lib/docker/volumes/js-data/_data
total 4
-rw-r--r-- 1 root root 4 Feb 26 16:29 123
[root@VM_156_200_centos js-data]# cat /var/lib/docker/volumes/js-data/_data/123
123

事實(shí)上肯定是有的。 而且這種方式其實(shí)就是上面那種當(dāng)宿主機(jī)的目錄是相對(duì)目錄的處理方式,其實(shí)在docker處理過(guò)程中,如果宿主機(jī)目錄是相對(duì)的,這時(shí)候就會(huì)去判斷本地是否存在這個(gè)volume,如果不存在就創(chuàng)建一個(gè)(所以其實(shí)我們不太需要去顯示的創(chuàng)建一個(gè) volume),所以我們還可以看到文章前面當(dāng)當(dāng)宿主機(jī)的目錄是相對(duì)目錄的處理方式那時(shí)候的相對(duì)目錄的 hostTest1,其實(shí)已經(jīng)是一個(gè) volume了:

1
2
3
4
5
6
7
8
9
10
11
12
[root@VM_156_200_centos js-data]# docker volume inspect hostTest1
[
    {
        "CreatedAt": "2019-02-26T11:30:34+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/hostTest1/_data",
        "Name": "hostTest1",
        "Options": null,
        "Scope": "local"
    }
]

但是有一點(diǎn)要注意的話,如果要指定具體的宿主機(jī)目錄的話,那就不能用這種方式,“:” 前面的宿主機(jī)目錄路徑還是得用絕對(duì)路徑。

通過(guò)dockerfile創(chuàng)建數(shù)據(jù)卷

除了用docker volume命令顯示或者用 -v 宿主機(jī)相對(duì)目錄隱示的創(chuàng)建數(shù)據(jù)卷之外,還可以在dockerfile設(shè)置volume數(shù)據(jù)卷。
ps: 注意上述[5 掛載數(shù)據(jù)卷容器]創(chuàng)建的數(shù)據(jù)卷容器,其實(shí)本質(zhì)上是一個(gè)容器,即 Docker Container,而不是 Docker Volume。 Container 是可以在 docker ps -a 可以找到,而在 docker volume ls 這里面是找不到的:

1
2
3
4
[root@VM_156_200_centos js-data]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                           PORTS                                                                        NAMES
a559685d18ae        centos              "/bin/bash"              About an hour ago   Exited (0) About an hour ago                                                                                  centos-js-data
851a09a40cb8        centos              "/bin/bash"              About an hour ago   Exited (0) About an hour ago                                                                                  centos-go-data

在 dockerfile 設(shè)置 volume 數(shù)據(jù)卷,舉個(gè)例子:

1
2
3
4
5
6
[root@VM_156_200_centos ~]# mkdir docker-volume-centos
[root@VM_156_200_centos ~]# cd docker-volume-centos/
[root@VM_156_200_centos docker-volume-centos]# vim dockerfile
[root@VM_156_200_centos docker-volume-centos]# cat dockerfile 
FROM centos
VOLUME /js-data

可以看到這個(gè) dockerfile 掛載了一個(gè)容器內(nèi)的 js-data。 接下來(lái) build 一下:

1
[root@VM_156_200_centos docker-volume-centos]# docker build --rm -t volume-centos .

然后 run 起來(lái):

1
2
3
4
5
6
7
8
[root@VM_156_200_centos docker-volume-centos]# docker run -it --name volume-centos-demo  volume-centos
[root@eeb532993285 /]# cd /js-data
[root@eeb532993285 js-data]# ls
[root@eeb532993285 js-data]# echo "456" > 456
[root@eeb532993285 js-data]# ls
456
[root@eeb532993285 js-data]# exit
exit

可以看到跑起來(lái)之后,容器里面已經(jīng)有一個(gè) js-data, 但是這個(gè)目錄是空的,我們先在這個(gè)目錄創(chuàng)建一個(gè) 456 的文件。然后退出容器。
接下來(lái)我們看看這個(gè)共享目錄對(duì)應(yīng)的宿主機(jī)的目錄是哪個(gè):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@VM_156_200_centos docker-volume-centos]# docker inspect volume-centos-demo
[
    {
    ...
        "Mounts": [
            {
                "Type": "volume",
                "Name": "b07ab9cb039ffa90cc0ea7186716aa861cb9bcda11b1925bffca7a6539ee1304",
                "Source": "/var/lib/docker/volumes/b07ab9cb039ffa90cc0ea7186716aa861cb9bcda11b1925bffca7a6539ee1304/_data",
                "Destination": "/js-data",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
    ...
    }
]

可以看到宿主目錄是這個(gè):/var/lib/docker/volumes/b07…304/_data,接下來(lái)看看這個(gè)目錄是否有 456 這個(gè)文件:

1
2
3
4
5
[root@VM_156_200_centos docker-volume-centos]# ll /var/lib/docker/volumes/b07ab9cb039ffa90cc0ea7186716aa861cb9bcda11b1925bffca7a6539ee1304/_data
total 4
-rw-r--r-- 1 root root 4 Feb 26 16:56 456
[root@VM_156_200_centos docker-volume-centos]# cat /var/lib/docker/volumes/b07ab9cb039ffa90cc0ea7186716aa861cb9bcda11b1925bffca7a6539ee1304/_data/456 
456

是有的。 通過(guò) dockerfile 設(shè)置 volume 也可以設(shè)置多個(gè),不過(guò)這種方式都是相對(duì)目錄的方式,而且文件名還隨機(jī),相當(dāng)于上述的-v 只指定一個(gè)的情況

1
[root@VM_156_200_centos volumes]# docker run -it  -v /test2 --name centos-demo-1  centos

Volume 的刪除和清理

如果是屬于 volume,那么直接調(diào)用docker volume rm xxx 就可以刪除了:

1
2
3
4
5
6
[root@VM_156_200_centos docker-volume-centos]# docker volume ls
DRIVER              VOLUME NAME
...
local               js-data
[root@VM_156_200_centos docker-volume-centos]# docker volume rm js-data
js-data

如果是掛載的數(shù)據(jù)卷容器,就調(diào)用docker rm xxx 當(dāng)做普通容器刪除即可:

1
2
[root@VM_156_200_centos docker-volume-centos]# docker rm centos-js-data 
centos-js-data

雖然容器刪除了,但是宿主機(jī)掛載的本地目錄的資料還是存在的。

dockerfile volume 的權(quán)限和許可

這是個(gè)很有意思的現(xiàn)象,就是如果dockerfile里面的volume命令前后對(duì)共享目錄進(jìn)行操作的話,是會(huì)有差別的,舉個(gè)例子:

1
2
3
4
5
6
[root@VM_156_200_centos docker-volume-centos]# cat dockerfile
FROM centos
RUN useradd foo
VOLUME /data
RUN touch /data/x
RUN chown -R foo:foo /data

這個(gè)是一個(gè)dockerfile,我們的預(yù)期目錄是,當(dāng)run起來(lái)之后,會(huì)有 /data/x 這個(gè)文件。 但是實(shí)際上沒(méi)有:

1
2
3
4
[root@VM_156_200_centos docker-volume-centos]# docker build --rm -t kbz/volume-test .
[root@VM_156_200_centos docker-volume-centos]# docker run -it --name volue-test-demo kbz/volume-test
[root@3d037af9a5ca /]# cd /data
[root@3d037af9a5ca data]# ls

可以看到,雖然有 /data 這個(gè)目錄,但是在目錄里面并沒(méi)有 x 這個(gè)文件??? 這個(gè)是怎回事呢???
這個(gè)就涉及到dockerfile的運(yùn)行規(guī)則,在Dockerfile中的每個(gè)命令都會(huì)創(chuàng)建一個(gè)新的用于運(yùn)行指定命令的容器,并將容器提交到鏡像,每一步都是在前一步的基礎(chǔ)上構(gòu)建。
因此在Dockerfile中 ENV FOO=bar等同于:

1
2
cid=$(docker run -e FOO=bar <image>)
docker commit $cid

那么針對(duì)我們本例的dockerfile,他的執(zhí)行順序應(yīng)該是(真實(shí)過(guò)程可能不是這樣的,但是應(yīng)該差不多):

1
2
3
4
5
6
7
8
cid=$(docker run centos useradd foo)
image_id=$(docker commit $cid)
cid=$(docker run -v /data centos)
image_id=$(docker commit $cid)
cid=$(docker run $image_id touch /data/x)
image_id=$(docker commit $cid)
cid=$(docker run $image_id chown -R foo:foo /data)
docker commit $(cid) volue-test-demo

那這個(gè)過(guò)程就很明顯了,因?yàn)槊恳恍卸紩?huì)啟動(dòng)一個(gè)新的容器,因此每次都會(huì)有一個(gè)新的 volume,也就是每次run的新容器, /data 都是新的。
所以其實(shí)我們的這個(gè) touch 操作,就是在當(dāng)前的 volume 的 data 目錄下操作的,而不是實(shí)際的容器或鏡像的文件系統(tǒng)內(nèi)。所以當(dāng)我們重新再run的時(shí)候,這時(shí)候新的 volume 又是一個(gè)空的 data 目錄。
所以如果要改的話,應(yīng)該要把這個(gè) touch 的操作,先存在鏡像的文件系統(tǒng)之內(nèi),然后最后在掛載目錄:

1
2
3
4
5
6
7
[root@VM_156_200_centos docker-volume-centos]# vim dockerfile
[root@VM_156_200_centos docker-volume-centos]# cat dockerfile 
FROM centos
RUN useradd foo
RUN touch /data/x
RUN chown -R foo:foo /data
VOLUME /data

這個(gè)是改完后的 dockerfile

1
2
3
4
5
[root@VM_156_200_centos docker-volume-centos]# docker build --rm -t kbz/volume-test-2 .
[root@VM_156_200_centos docker-volume-centos]# docker run -it --name volue-test-demo-2 kbz/volume-test-2
[root@2850978389ac /]# cd /data 
[root@2850978389ac data]# ls
x

這時(shí)候就有 x 文件了。 而且對(duì)應(yīng)的宿主目錄也有這個(gè) x 文件了:

1
2
3
[root@VM_156_200_centos docker-volume-centos]# cd /var/lib/docker/volumes/d4c83377d137104a13893b31e42c57e3635e8e59c14027463361b814c86c6ad7/_data
[root@VM_156_200_centos _data]# ls
x

感謝各位的閱讀,以上就是“Docker目錄掛載及文件共享的方法”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Docker目錄掛載及文件共享的方法這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI