上一篇認識了掛載,讓我們能夠將主機的資料夾連通到容器中。雖然單人在本地使用很方便,但缺點是下指令時,主機的路徑寫法會依賴於作業系統,導致寫法不能完全統一。例如 Windows 的絕對路徑表示方式,與 MacOS / Linux 就有很大的不同。
本文將介紹官方建議的「Volume」,它也是使用主機的空間來掛載,且儲存空間是被 Docker 管理的。所以在 Docker 指令中,能直接使用 volume 的名字作為參數值,不會受限於主機作業系統的差異。最後將以 MySQL 為例,簡單示範掛載的應用。
此篇亦轉載到個人部落格。
讓我們建立一個 volume,指令寫法為 docker volume create --name {名稱}
。
假設取名為「my_space」,則範例指令如下:
docker volume create --name my_space
使用 docker volume ls
指令,可列出主機上現有的 volume。除了上面建立的 my_space
,讀者也許還會看到其他亂數名稱的 volume,那些是由其他 container 自行建立的。
例如 MySQL 資料庫的映像檔,會要求 Docker 在建立容器時,主動將儲存資料的路徑與 volume 進行掛載。
docker-volume-list.png
若要查看 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 時,才可以刪除。
為了示範,本文以 Day 32 提到的 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 --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 映像檔。
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"
docker-copy-sql-file-to-container.png
資料庫的初始化工作完成後,使用一陣子便會累積一些資料。本節使用的 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 掛載上去。再從容器中被掛載的資料夾,複製想要的資料出來。
今日文章到此結束!
最後推廣一下自己的部落格,我是「新手工程師的程式教室」的作者,請多指教