天天看點

掌握Docker:綁定挂載、匿名卷、命名卷

掌握Docker:綁定挂載、匿名卷、命名卷

容器是短暫的

記住容器是臨時的、短暫的。每當容器完成執行指令時,它就完全“關閉”了。

=> docker run node ls

bin
boot
dev
etc
home
lib
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
           

它使用鏡像node啟動一個新容器并在容器内執行指令(列出檔案和目錄)。

該指令給出一個輸出并完成。指令完成後,容器将關閉并且其所有資料都将丢失。

執行 Javascript 程式

假設我們有一個包含非常基本的 Javascript 程式的檔案:

helloWorld.js

greeting = (message) => {
  console.log(message)
}

greeting("Hello, world")
           

我們如何使用容器運作這個程式?可以嘗試做類似的事情:

docker run node node helloWorld.js
           

得到錯誤:

Error: Cannot find module '/app/helloWorld.js'
...
           

那是因為容器是隔離的,不能共享同一個主機檔案系統。

綁定挂載:在主機和容器之間同步資料

我們必須将檔案helloWorld.js與容器同步。Docker 提供了一種挂載卷的方法,這意味着:

從Host 挂載一個目錄或檔案到 Container,這樣在 Host 中所做的每一個更改都會被鏡像到 Container,反之亦然。

您更改主機中的檔案/目錄,容器将看到更改。您更改容器中的檔案/目錄,主機将看到更改。

讓我們與容器同步:

docker run 
  -v $(pwd)/docker-101/helloWorld.js:/app/helloWorld.js 
  node 
  node /app/helloWorld.js
           
Hello, world
           

解釋:

  • pwd是指目前目錄的完整路徑名
  • -v {hostPath}:{containerPath}将檔案/目錄挂載到容器
  • node是鏡像
  • node /app/helloWorld.js使用容器内定義的path執行指令

一個真實的項目

假設我們有一個這樣的項目目錄:

/docker-101
  /src
    /components
    components.js
  index.js
           

從docker-101目錄内部,執行node

docker run 
  -v $(pwd):/app
  node
  node /app/index.js
           

目前目錄中的所有檔案都将挂載到容器中的/app。

如果我們想進入容器并從那裡操作檔案怎麼辦?

docker run 
  -it
  -v $(pwd):/app
  node
  bash
           

.這裡:

  • -it訓示 Docker 保持容器終端(bash/shell)打開
  • bash在容器内打開一個新的 bash/shell

執行這個docker run指令,保持容器終端打開,在容器内部執行更多指令并建立新檔案:

root@8dcbfa6d777c:/# cd /app

root@8dcbfa6d777c:/app# ls
index.js  src

root@8dcbfa6d777c:/app# touch new-file.js

root@8dcbfa6d777c:/app# ls
index.js  new-file.js src

root@8dcbfa6d777c:/app# exit
exit
           

現在,退出容器後,在主機執行ls,我們會發現,相應的新建立的檔案都同步在host上了:

index.js    new-file.js src           

這種類型的卷稱為Path Volume。

使用mount參數

另一種挂載卷的方法是使用選項--mount,它指定挂載類型、源和目标。

docker run 
  --mount type=bind,source=$(pwd),target=/app
  node
  node /app/helloWorld.js
           

mount更明确,但在大多數情況下使用-v已經足夠了。

命名卷與匿名卷

綁定加載是由程式員自己維護的,而資料卷是由 Docker 引擎維護的存儲方式,使用 docker volume create 指令建立,可以利用卷驅動支援多種存儲方案。其預設的驅動為 local,也就是本地卷驅動。本地驅動支援命名卷和匿名卷。

docker volume create my-volume

docker volume inspect my-volume
           
[
    {
        "CreatedAt": "2022-02-05T00:47:59Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-volume/_data",
        "Name": "my-volume",
        "Options": {},
        "Scope": "local"
    }
]
           

Mountpoint訓示的路徑是主機中的确切路徑。是以,每個使用此卷的容器都會将其資料直接同步到此挂載點。

而匿名卷就是沒名字的卷,一般是 docker run -v /data 這種不指定卷名的時候所産生,或者 Dockerfile 裡面的定義直接使用的。

如果容器啟動時使用了 --rm 選項,容器停止時,容器被自動删除,匿名卷也會自動被删除。

反之,如果沒有--rm選項,即使使用命名 docker container rm my_container 删除了容器,匿名卷不會被自動删除。每次建立新的容器,都會重新建立新的匿名卷。 重新開機容器則使用已有附加的匿名卷。

要删除匿名卷,使用指令 docker volume rm 或者 prune

是以,一般而言,匿名卷隻存放無關緊要的臨時資料,随着容器消亡,這些資料将失去存在的意義。

在 Dockerfile 中定義的挂載,是指匿名資料卷。無法在Dockerfile 内建立命名卷。

最佳實踐

不應該在容器存儲層内進行資料寫入操作,所有寫入應該使用卷。如果定制鏡像的時候,就可以确定某些目錄會發生頻繁大量的讀寫操作,那麼為了避免在運作時由于使用者疏忽而忘記指定卷,導緻容器發生存儲層寫入的問題,就可以在 Dockerfile 中使用 VOLUME 來指定某些目錄為匿名卷。這樣即使使用者忘記了指定卷,也不會産生不良的後果。

比如,Dockerfile 中說 VOLUME /data,那麼如果直接 docker run,其 /data 就會被挂載為匿名卷,向 /data 寫入的操作不會寫入到容器存儲層,而是寫入到了匿名卷中。但是如果運作時 docker run -v mydata:/data,這就覆寫了 /data 的挂載設定,要求将 /data 挂載到名為 mydata 的命名卷中。是以說 Dockerfile 中的 VOLUME 實際上是一層保險,確定鏡像運作可以更好的遵循最佳實踐,不向容器存儲層内進行寫入操作。