Website logo

Robert Chang

技術部落格

Docker - 實際使用 Volume 來儲存資料

昨天都還只是紙上談兵,根本還沒有真的見到 volume 本人,別急,讓我們把 mysql 給運行起來:

$ docker container run --detach --name mysql --env MYSQL_ROOT_PASSWORD=whatever mysql # 不換行
cc94cfe86706e585f36d60e7dec2a7ffdfcf9ca5ff83f697d8

接著一樣,確認這個容器是真的有在運作,而不是進入停止的狀態。

$ docker container list
CONTAINER ID  IMAGE COMMAND    CREATED STATUS PORTS          NAMES
cc94cfe86706  mysql "docke..." Abou... Up     3306/tcp, 33.. mysql

確認過後,再次使用 inspect 這個指令來看看容器有什麼不一樣!

$ docker container inspect mysql
[
 {
  ...
  "Mounts":[
    {
      "Type": "volume",
      "Name": "2c1a7d85be3f0c1079ab0d73b92fd9aadb82...",
      "Source": "/var/lib/docker/volumes/2c1a7d../_data",
      "Destination": "/var/lib/mysql",
      "Driver": "local",
      "Mode": "",
      "RW": true,
      "Propagation": ""
    }
  ]
  ...
 }
]

礙於獨特的 SHA 值太長,所以我用了 的方式來縮短篇幅,但這個並不影響,因為每個人運行後的 SHA 值都不一樣。

可以觀察到這個容器掛載了一個 volume 在裡面,可以看到 Destination 的部分,是指容器內部路徑,而 Source 則是指外部的 volume,而這個 volume 的路徑,若是使用 Linux 作業系統的人,可以直接透過 cd 的指令進入到這個資料夾中。

而 macOS 以及 Windows 的使用者,還記得我們在之前的章節提過,macOS 以及 Windows 都是透過迷你的虛擬機在運行 Docker,所以直接 cd 到這個路徑是行不通的,因為資料是存放在迷你的虛擬機中。

這邊可以透過以下指令來列出存活的 volume:

$ docker volume list
DRIVER    VOLUME NAME
local     2c1a7d85be3f0c1079ab0d73b92fd9aadb8204c2....

仔細對比 VOLUME NAME 會發現跟連接到容器的 SHA 值是一模一樣的,代表寫在映像檔中的 VOLUME 指令,會在沒有指定 volume 的情況下自行建立一個以隨機 SHA 值命名的 volume。

我們一樣可以透過 inspect 的指令來看到 volume 的詳細資訊,可以透過 docker volume inspect + TAB 的方式,來做到篩選現有的 volume,並且檢查其詳細資訊:

$ docker volume inspect 2c1a7d85be3f0c1079ab0d73....
[
 {
  "CreatedAt": "2022-09-17T10:46:59Z",
  "Driver": "local",
  "Labels": null,
  "Mountpoint": "/var/lib/docker/volumes/2c1a7d85b...",
  "Name": "2c1a7d85be3f0c10...",
  "Options": null,
  "Scope": "local"
 }
]

怎麼知道哪個 volume 接到哪個容器?

從剛剛的結尾,我們會發現一件事,可以透過容器看到現在連接的是哪一個 volume,但卻沒辦法從 volume 的角度去看到這個 volume 現在連接哪一個容器!

若是開啟兩個 MySQL 的服務呢?

$ docker container run --detach --name mysql2 --env MYSQL_ROOT_PASSWORD=whatever mysql # 不換行
35fd131389c0343f0faadc4fbe74b976d216c1fc65cd84b

接著列出所有的 volume 看看:

$ docker volume list
DRIVER    VOLUME NAME
local     2c1a7d85be3f0c1079ab0d73b92fd9aadb8204c2....
local     35fd131389c0343f0faadc4fbe74b976d216c1fc....

應該會開始意識到 VOLUME NAME 所帶來的問題,對吧?

從列出 volume 這件事情,我們沒有辦法得到任何有用的資訊,沒辦法知道這是接到哪一個。

而如果今天容器被刪除了呢?我們也知道刪除容器再次重啟是一件稀鬆平常的事情。

$ docker container rm --force mysql mysql2
mysql
mysql2

接著確認所有的容器都被刪除地乾乾淨淨了。

$ docker container list --all
CONTAINER ID   IMAGE COMMAND CREATED STATUS PORTS   NAMES

接著再列出 volume,它們並沒有被刪除,就如同前面提到的,volume 正常情況下會需要透過手動來刪除。

$ docker volume list
DRIVER    VOLUME NAME
local     2c1a7d85be3f0c1079ab0d73b92fd9aadb8204c2....
local     35fd131389c0343f0faadc4fbe74b976d216c1fc....

但現在好像完蛋了,哪一個 volume 是對應到哪一個服務呢?雖然資料都還在,難不成我要像在玩抽抽樂一樣嗎?這個 volume 去對應那個容器試試看好了,不行再換下一個?

為你的 volume 命名

顯然抽抽樂去對應容器不是一個有效率且聰明的作法,所以我們可以透過替 volume 命名來讓我們人類可以透過肉眼輕易的理解出區別。

回到最一開始啟動容器時的指令,可以透過加入 --volume 的指令,來告訴 Docker 要對應的 volume 名字是什麼:

$ docker container run --detach --name mysql --env MYSQL_ROOT_PASSWORD=whatever --volume /var/lib/mysql mysql # 不要輸入這段指令,是錯誤的

請你先別急著下指令,上面雖然加入了 --volume 的指令並且目的地也是 mysql 這個映像檔本身資料庫檔案存放的位置,但上述的寫法其實和沒寫是一樣的,因為 VOLUME /var/lib/mysql 早就被定義在 Dockerfile 裡面了不是嗎?

所以我們應該在目的地的前方加上 volume 的名字,像是這樣:

$ docker container run --detach --name mysql --env MYSQL_ROOT_PASSWORD=whatever --volume mysql-data:/var/lib/mysql mysql # 不換行
827b118d0aa7c8f83415e66d2a49106....

注意到了嗎?我們只需要在目的地的前方加上我們要取的名字,並且用冒號進行連接,就能夠建立一個有名字的 volume 並且連接到 /var/lib/mysql 這個路徑。

接著列出所有的 volume 看看,是不是真的有效:

$ docker volume list
DRIVER    VOLUME NAME
local     2c1a7d85be3f0c1079ab0d73b92fd9aadb8204c2....
local     35fd131389c0343f0faadc4fbe74b976d216c1fc....
local     mysql-data

接著 inspect 這個 volume 看看,是不是一切都變得易讀多了呢?

也可以輕鬆地透過名字來分辨這個 volume 是哪一個容器要用的:

$ docker volume inspect mysql-data
[
 {
  "CreatedAt": "2022-09-17T14:13:56Z",
  "Driver": "local",
  "Labels": null,
  "Mountpoint": "/var/lib/docker/volumes/mysql-data/_data",<-易讀
  "Name": "mysql-data", <-易讀
  "Options": null,
  "Scope": "local"
 }
]

除了在容器啟動時直接輸入 volume 的名字,如剛剛範例中的 mysql-data:/var/lib/mysql 之外,也可以提前建立好 volume,並連接到容器上。

$ docker volume create whatever
whatever

結語

希望大家有因為這次的鐵人賽而對 Docker 稍微有一些概念,進而自己去挖掘更多的功能!

上一篇文章Docker - 介紹 Volume

下一篇文章Elasticsearch - 這是什麼?用在哪?