【Docker】透過 Volume 來處理掛載(以 MySQL 為例)


https://unsplash.com/photos/dyu466BfWj8

上一篇認識了掛載,讓我們能夠將主機的資料夾連通到容器中。雖然單人在本地使用很方便,但缺點是下指令時,主機的路徑寫法會依賴於作業系統,導致寫法不能完全統一。例如 Windows 的絕對路徑表示方式,與 MacOS / Linux 就有很大的不同。

本文將介紹官方建議的「Volume」,它也是使用主機的空間來掛載,且儲存空間是被 Docker 管理的。所以在 Docker 指令中,能直接使用 volume 的名字作為參數值,不會受限於主機作業系統的差異。最後將以 MySQL 為例,簡單示範掛載的應用。

此篇內容轉載自本人在 iThome 的文章


一、Volume 的操作

(一)建立

讓我們建立一個 volume,指令寫法為 docker volume create --name {名稱}

假設取名為「my_space」,則範例指令如下:

docker volume create --name my_space

(二)查看

使用 docker volume ls 指令,可列出主機上現有的 volume。除了上面建立的「my_space」,讀者也許還會看到其他亂數名稱的 volume,那些是由其他 container 自行建立的。

例如 MySQL 資料庫的映像檔,會要求 Docker 在建立容器時,主動將儲存資料的路徑與 volume 進行掛載。

若要查看 volume 的詳細資訊,請使用 docker volume inspect {名稱} 指令。範例寫法如下。

docker volume inspect my_space

指令會回傳 JSON 格式的資料,範例結果如下。

[
    {
        "CreatedAt": "2023-11-30T07:55:45Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/my_space/_data",
        "Name": "my_space",
        "Options": null,
        "Scope": "local"
    }
]

其中「Mountpoint」是 volume 對應主機的儲存位置。雖然筆者使用 Windows 系統,但 Docker Desktop 會安裝 Linux 子系統。該 volume 實際的空間便是在該 Linux 系統中。

(三)刪除

當 volume 不再需要了,可以將它們刪除。指令寫法為 docker volume rm {名稱}。範例指令如下:

docker volume rm my_space

要注意的是,當沒有任何容器在使用此 volume 時,才可以刪除。


二、使用 Volume 掛載

為了示範,本文以先前「【Docker】映像檔與容器的操作」文章提到的 docker/getting-started 映像檔作為練習對象。這會是個 hello world 性質的範例。

docker image pull docker/getting-started:latest

將 volume 掛載到容器的方式,與前面介紹的主機資料夾大同小異,只是把路徑換成 volume 名稱罷了。

docker container run -d -v my_space:"/container-test-folder" --name MyDockerTutorial -p 80:80 docker/getting-started:latest

docker container run -d -v my_space:"/container-test-folder" --name MyDockerTutorial -p 80:80 docker/getting-started:latest

docker container run -d --mount type=volume,source="my_space",destination="/container-volume-space" --name MyDockerTutorial -p 80:80 docker/getting-started:latest

要注意的是,如果掛載時選擇使用 --mount 參數,則 `type` 的值應給予「volume」。


三、搬遷資料

如果想要在 volume 進行資料的移入或移出,該怎麼做呢?其實 Docker 並沒有提供相關的指令。相對地,我們可使用「從容器複製資料到主機」及「從主機複製資料到容器」的指令,因為與 volume 是連通的。

將資料從主機複製到容器的指令寫法為 docker container cp {主機路徑} {容器名稱:容器路徑}。範例指令如下:

docker container cp ./test-host-image.png MyDockerTutorial:/test-container-image.png

若是要將資料從容器複製出來到主機,則先寫容器路徑,再寫主機路徑。

docker container cp MyDockerTutorial:/test-folder/test-image.png "C:\"

主機的路徑可使用相對路徑或絕對路徑。而容器即便未啟動,也可進行複製資料的動作。


四、在 MySQL 容器的應用

最後看看如何將掛載應用於 MySQL。本節使用以下的 MySQL 映像檔。

docker image pull mysql:8.2.0

(一)初始化資料庫

利用掛載,可以在建立資料庫容器後,做一些初始化。比方說我們希望資料庫中能夠預先準備好資料表(table)。

以下是一組範例 SQL 指令,可將其存為「.sql」檔。用途是在叫做「demo」的資料庫下,建立名為「student」的資料表。

CREATE TABLE `demo`.`student` (
  `id` VARCHAR(20) NOT NULL,
  `studentId` VARCHAR(20) NOT NULL,
  `name` VARCHAR(20) NOT NULL,
  `birthday` DATE NOT NULL,
  PRIMARY KEY (`id`)
);

在 MySQL 的容器中,有個叫做「docker-entrypoint-initdb.d」的資料夾。MySQL 會在容器初次啟動時,執行裡面的「.sql」檔。那麼要如何利用掛載,將想執行的 SQL 檔放入容器中的該資料夾呢?

首先示範綁定掛載。假設主機已準備好一個資料夾叫「db-init-scripts」,裡面有一至多個 SQL 檔。則建立 MySQL 容器時,可透過指令,將其掛載到容器的「docker-entrypoint-initdb.d」資料夾中。

docker container run -d -p 3306:3306 --name TestMySQL -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=demo -v "C:\db-init-scripts":"/docker-entrypoint-initdb.d" mysql:8.2.0

再來介紹檔案移入的做法。先把容器建立起來,但先不要立即啟動。

docker container create -p 3306:3306 --name TestMySQL -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=demo mysql:8.2.0

接著只要簡單地把要執行的檔案,都複製到容器中,即可啟動。

docker container cp . TestMySQL:"/docker-entrypoint-initdb.d"

(二)資料的備份

資料庫的初始化工作完成後,使用一陣子便會累積一些資料。本節使用的 MySQL 版本是 8.2.0,若我們想改用其他版,或者單純搬遷資料,則 DB 裡的資料就得備份出來,移動到新的容器。

MySQL 的容器會主動建立 volume,並掛載到「/var/lib/mysql」路徑。我們可以透過第一節第二段介紹的指令確認掛載資訊。

docker container inspect TestMySQL

在指令回傳的結果中,找到「Mounts」欄位,能看到容器的哪些資料夾被掛載到什麼地方。此處得知被掛載到叫做「bb8846...」的 volume。

[
    {
        "...": "...",
        "Mounts": [
            {
                "Type": "volume",
                "Name": "bb88460fef7dd96deae1692d8503add05f584ed3348c9e6c1c0dbe3fde340130",
                "Source": "/var/lib/docker/volumes/bb88460fef7dd96deae1692d8503add05f584ed3348c9e6c1c0dbe3fde340130/_data",
                "Destination": "/var/lib/mysql",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
        "...": "..."
    }
]

備份資料時,正規的做法是採用「mysqldump」指令,它位於容器的「/usr/bin」路徑下。

mysqldump -h localhost -u root -p demo > /var/lib/mysql/demo_backup_20231205.sql

總之備份完成後,會得到一個 SQL 檔。我們可以選擇將其移動到上述的 volume 保存,或者起初就建立一個專門存放的 volume 掛載到容器。

待有需要使用還原時,再依照本節第一段的做法,將其複製到新容器的「docker-entrypoint-initdb.d」資料夾,達到初始化的目的。

那如果忘記備份資料就刪除容器怎麼辦?有一個比較 workaround 的做法,就是隨便建立一個容器,把 volume 掛載上去。再從容器中被掛載的資料夾,複製想要的資料出來。


上一篇:【Docker】透過綁定掛載讓主機連通容器

下一篇:【Docker】將容器打包為映像檔與人分享

留言