容器中運行的軟體可能會產生資料,例如 log 檔。而這些資料都是存放於容器中,若刪除容器,這些資料也會連帶消失。為了做到資料的持久化,需對容器做「掛載」(mount)的動作。掛載能夠將主機的檔案空間,直通容器之內,方便我們管理當中的資料。
本文的目的是讓讀者認識什麼是掛載,以及使用起來的效果,並介紹相關的指令參數。而下一篇才會介紹「Volume 掛載」。
此篇亦轉載到個人部落格。
以筆者的工作經驗為例,Spring Boot 後端程式每天都會將當日的 log,獨立儲存成一份 log 檔。如果是發生 exception,或 ERROR 等級的 log,還會另外存一份。
這些檔案會隨著容器的刪除而消失。如果因為上版或其他原因,需重新建立容器,結果忘記備份重要資料,那就糟了!而備份時會透過指令,從容器內部複製資料出來,也比較不方便。
另一方面,使用 Spring Boot 時可能會由外部(獨立於 JAR 檔之外)提供如「application.properties」的 config 配置檔。若配置檔內容需要調整,也是要特地進入容器中進行修正。
基於這些列舉的情境,為了便於存取重要資料,並做到持久化,因此我們需要進行掛載。這樣就能在主機的檔案系統,管理容器中的資料。
附帶一提,MySQL 資料庫的容器,本身會主動用「Volume」的功能進行掛載(於下一篇介紹)。因此 DB 的資料並不會隨容器刪除而直接消失。
為了示範,本文以 Day 32 提到的「docker/getting-started」映像檔作為練習對象。這會是個 hello world 性質的範例。
docker image pull docker/getting-started:latest
筆者將以 Windows 系統的電腦當作主機來示範。本節最終想要做到的,是在 Windows 上,能夠對容器中的資料進行移入或移出。
在建立容器的指令中,可添加掛載相關的參數,指令寫法為:docker container run -v {主機絕對路徑}:{容器路徑} {其他參數}
。
範例指令如下:
docker container run -d -v "C:\host-test-folder":"/container-test-folder" --name MyDockerTutorial -p 80:80 docker/getting-started:latest
此處會將主機的「C:\host-test-folder」資料夾,掛載到容器的「container-test-folder」資料夾。若雙方資料夾不存在,則 Docker 將自動建立。要注意的是,若容器在該路徑下,已存在相同名稱的資料夾,則會以主機資料夾覆蓋。
接著讀者可以在「host-test-folder」資料夾中隨意放一個檔案,例如文字檔。此時進入容器查看,會發現該檔案已經在容器中了。請參考下圖操作指令。
docker-bind-mount-folder-confirmation.png
在使用 -v
參數進行掛載時,除了主機路徑與容器路徑,還能提供「唯讀」或「可讀寫」的參數值。
再次以 Spring Boot 後端程式為例。假設掛載了兩個資料夾,一個存放產生的 log 檔,另一個存放讓程式讀取的 config 檔。那麼我們並不預期容器中運行的軟體,會對後者的資料夾新增檔案,甚至修改原資料。
掛載時,可以額外設定該資料夾是否為唯讀。參數寫法為 -v {主機絕對路徑}:{容器路徑}:{ro | rw}
。其中 ro
代表唯讀(read only),rw
代表可讀寫(read write)。
建立容器的範例指令如下:
docker container run -d -v "C:\host-test-folder":"/container-test-folder":ro --name MyDockerTutorial -p 80:80 docker/getting-started:latest
讀者可進入容器,試著在「container-test-folder」路徑下新增資料夾、新增檔案或使用 vi 編輯器修改檔案,會得到「Read-only file system」的訊息。如此一來,我們只能單方面從主機端異動該資料夾了,達到了保護的效果。
docker-fail-to-write-to-read-only-mounted-folder.png
除了使用 -v
參數進行掛載,也能使用 --mount
參數。前者較簡潔,而後者的可讀性更好。
該參數的寫法為 --mount type={掛載方式},source={主機絕對路徑},destination={容器路徑},readonly
。
建立容器的範例指令如下:
docker container run -d --mount type=bind,source="C:\host-test-folder",destination="/container-test-folder",readonly --name MyDockerTutorial -p 80:80 docker/getting-started:latest
可以看到 --mount
參數會使用「key=value」的格式提供參數。
其中的 type
參數,由於此處是示範掛載主機資料夾,所以值固定傳入 bind
。這種使用主機資料夾的做法,同時也被稱作本文標題的「綁定掛載」。另外,若省略 readonly
參數,則視為「可讀寫」。
上面示範如何掛載整個資料夾到容器中,而檔案當然也行。
舉例來說,Redis 資料庫的配置檔叫做 redis.conf
;而全文檢索引擎 Elasticsearch 的配置檔叫做 elasticsearch.yml
。我們可以在主機準備好軟體所需的檔案,再單獨掛載到容器中。
指令的寫法與掛載資料夾並無不同,範例寫法如下。
docker container run -d -v "C:\test-config.json":"/container-app-config.json" --name MyDockerTutorial -p 80:80 docker/getting-started:latest
或
docker container run -d --mount type=bind,source="C:\test-config.json",destination="/container-app-config.json" --name MyDockerTutorial -p 80:80 docker/getting-started:latest
要注意的是,若容器的目的地資料夾已存在相同名稱的檔案,則會以主機的檔案進行覆蓋。而容器資料夾的其他檔案或子資料夾,則不受影響。
本文基於練習目的,掛載時是使用主機的檔案系統。因此在下指令時,掛載來源路徑的寫法,將依賴於主機的作業系統。若換成在 MacOS 或 Linux 系統上使用 Docker,則參數值就得隨之調整了。為了在操作上能保持一致,我們可改用 Docker 提供的「Volume」,於下一篇介紹。
今日文章到此結束!
最後推廣一下自己的部落格,我是「新手工程師的程式教室」的作者,請多指教