跳至主要内容

5.5 另一種方式:Bind Mount

除了前面談到的 volume 之外,bind mount 也是另外一種把不屬於映像檔本身的檔案放入容器中的方法。

這個功能本身的設計非常的酷,第一次使用的時候真的有一種『 啊! 』的聲音出現,完全讓人理解要如何在本地端使用 Docker 進行開發。

而實際上 Bind Mount 就是單純的把本機的檔案如同字面上的意思,掛載到容器內,而這個指令沒有辦法寫在 Dockerfile 裡面,因為必須把一個真實存在的資料夾或是檔案掛載到容器內,所以只能在 docker container run 的時候加入。

而在背後執行的原理,就是本機的路徑和容器內的路徑指向本機的同一個檔案,而使用起來的方式也很像 volume,容器的刪除並不會連帶地刪除掉本機的檔案,兩個之間的優先度,當然是本機獲勝!

接下來一樣使用 nginx 來示範一下,bind mount 的使用情境是如何,不要覺得為什麼又是 nginx,因為它最輕鬆就可以把畫面體現在瀏覽器上,可以很快地看到差異。

--mount 或是 --volume 都可以

這個小節的標題可能會有點疑惑,--volume 剛剛不是介紹過了嗎?

這是因為要使用 bind mount 這個功能,不論是用 --volume 或是 --mount 的指令都可以,但就是有一些細節需要注意。

本次的範例放在本書 GitHub 儲存庫中 ch-05 的 nginx-example 中。

進入到這個資料夾後,可以透過標題提到的兩種方式,達到一模一樣的目的。

$ docker container run --detach --name nginx-volume --publish 80:80 --volume $(pwd):/usr/share/nginx/html nginx # 不換行
6cb48a848c2373ff65b...

接著打開瀏覽器輸入 http://localhost,會看到『 Hi, 你成功的使用了 Bind Mount 功能了!』的字樣,我們利用了資料夾內本地的 index.html 去替換掉了 nginx 原先設定好的 index.html。

這是第一種方式,一樣使用 --volume 的指令,但是搭配的是本地端的絕對路徑而非上一小節的 volume 名稱,而 $( ) 的寫法,則是在 Docker 的指令中穿插終端機指令的作法,這邊的 pwd 在 Linux 以及 macOS 代表的是此處的意思。

而在 Windows 系統中的使用者,則可能需要將指令更改為下方所示 ( %cd% ),這單純就只是作業系統不同導致指令不相同,但實際上要傳達給 Docker 的意思是一樣的。

$ docker container run --detach --name nginx-volume --publish 80:80 --volume %cd%:/usr/share/nginx/html nginx 
# 不換行

換句話說,--volume 可以使用 Volume 的方式 ( 可能有點饒口 ),也就是上一個小節中替一個 volume 命名並且連接到容器上;也可以使用 Bind Mount 的方式將本機的檔案掛載至容器內。

接著來試試第二種方式實現 bind mount 的功能。

$ docker container run --detach --name nginx-bind-mount --mount type=bind,source=$(pwd),target=/usr/share/nginx/html --publish 8080:80 nginx # 不換行
55a3af9c6969d425e64c304015b1...

打開瀏覽器輸入 http://localhost:8080,一樣可以看到相同的字樣 『 Hi, 你成功的使用了 Bind Mount 功能了!』來證明確實取代了 nginx 預設的畫面。

這個寫法確實會囉唆一些,但我覺得對於新手使用 Bind Mount 功能來說,是非常清晰易懂的,--mount 後方要傳遞三個參數給 Docker,分別是 type、source、target,而 type 就是使用 bind 的方式,source 則是一樣的 $(pwd) / %cd% ( Windows 使用者 ) 代表此處的絕對路徑,target 則是容器內的絕對路徑。

這邊實現了兩種指令都將本地的資料掛載至容器內,進去容器看看長什麼樣子吧,現在有兩個容器都在背景執行,想去哪一個都可以。

$ docker container exec --interactive --tty nginx-bind-mount bash
root@55a3af9c6969:/# cd /usr/share/nginx/html
root@55a3af9c6969:/# ls
Dockerfile LICENSE README.md index.html

可以看到連這個資料夾內的 Dockerfile 以及 README.md 甚至是 LICENSE 都掛載進來了,是因為這邊用一整個檔案目錄去取代掉另一個檔案目錄;要注意的地方是我們不能夠使用一個檔案去取代掉檔案目錄,後面會做一個錯誤示範來驗證這件事。

而現在要做的是 Bind Mount 最讓人驚艷的功能,現在我們還停留在 nginx-bind-mount 這個容器內,接著打開一個終端機的視窗,並且進入 docker-volume-nginx-example 這個資料夾,並且隨意新增一個檔案。

# 另一個終端機視窗
$ cd docker-volume-nginx-example
$ touch test.txt

# 原本的終端機視窗,nginx-bind-mount 的容器內
root@55a3af9c6969:/# ls
Dockerfile LICENSE README.md index.html test.txt

非常有趣,剛剛新增的 test.txt 這個檔案就這樣即時的更新到了容器內,而且完全不需要重新啟動容器,也不必做任何的指令,這也讓使用 Docker 在本機開發變成一件輕鬆寫意的事情。

只需要啟動一個符合應用程式環境的容器,並且把開發的檔案掛載到容器內,一切就搞定了,也不必在容器內去做複雜的設定,因為需要的檔案還是存放在本機中。

而前面有提過背後的原理就是 本機的路徑和容器內的路徑指向本機的同一個檔案,所以不論是在容器內做編輯,刪除,都會相同的影響到本機的檔案。

接著是剛剛提到的錯誤示範,來驗證 不能夠使用一個檔案去取代掉檔案目錄 這句話,這邊的 source 只有 index.html 一個檔案,但是 target 卻是一整個檔案目錄。

$ docker container run --detach --name nginx-bind-mount-wrong --mount type=bind,source=$(pwd)/index.html,target=/usr/share/nginx/html --publish 8081:80 nginx # 不換行

566580d89326987432d87d7c434ed23875728f29aa54673e2e831d208e76733a
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/host_mnt/Users/RobertChang/docker-volume-nginx-example/index.html" to rootfs at "/usr/share/nginx/html": mount /host_mnt/Users/RobertChang/docker-volume-nginx-example/index.html:/usr/share/nginx/html (via /proc/self/fd/14), flags: 0x5000: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type.

Docker 很聰明地會提示你,source 本身並不是一個 directory,這是在掛載時需要注意的小地方。

小小測驗

這並不是實際的練習題,而是因為 Volume 以及 Bind Mount 是兩個不同的概念,但做到的事情很像,所以在這邊有幾個小小的問題希望大家可以作答,問問自己,不知道可以直接看答案並思考一下。

第一題:哪一種方式可以將存在於本機的檔案連接到容器內?

第二題:當我們在使用 bind mount 的方式時,$(pwd) 代表的是什麼意思呢?

第三題:今天運行了一個需要儲存空間的服務 ( MySQL, PostgreSQL, Redis, Elasticsearch ... ),我要如何知道容器內存放資料的路徑呢?

解答

第一題: bind mount 的方式才能實踐這一個功能;volume 的方式並不能將存在本機的檔案放入容器內,volume 更像是替容器開了一個外部儲存空間的概念。

第二題: 代表這裡的意思,英文的原意是 print working directory,印出現在正在工作的資料夾 ( 絕對路徑 )

第三題: 仔細的閱讀 DockerHub 上面的說明,這類型需要儲存空間的服務一定都會寫在說明中告訴你容器內部存放資料的路徑;再者就是前面有示範過的方式,先拉下要使用的映像檔,並且使用 docker image inspect 的方式去查看存放資料的路徑。

不知道大家是不是都有答對呢?並真的理解了 Volume 以及 Bind Mount 的差別,至於實際應用的場景則會在後面的章節一一出現。

升級資料庫版本練習

每一個章節都會提供一些小小的練習來熟悉本章的內容,通常會比較難一點;若是想不出來也不需要灰心,有些時候可能是指令不熟悉導致,可以往前翻閱來加強記憶喔,亦或是翻到後面的解答章節也可以幫助你解惑喔,記得 --help 以及 docs.docker.com 會是你最好的夥伴。

這個練習是模擬一個真實的情境,原先使用了 mariadb:10.3 版本的映像檔作為資料庫的服務,但因為某些原因,需要將服務升級到 mariadb:10.8 的映像檔作為服務,而這次的練習目的就是透過已命名的 volume 來保存資料,並且升級服務的同時資料不會遺失。

在非 Docker 環境的作法下,我們會直接用套件管理工作 ( brew, apt ... ) 去升級 mysql 的版本,而關於資料儲存的部分,這些升級都會在背後幫助你完成一切的手續,只需要 brew upgrade mariadb 就完工了。

  1. 以 mariadb:10.3 的映像檔建立容器,並且把 volume 命名為 mariadb-data 且連接到容器上,並確認容器的 logs 一切正常。
  2. 透過閱讀 DockerHub 上的使用說明來得到啟動容器所需要的參數以及容器內儲存資料的路徑。
  3. 確認一切都沒問題後,暫停掉以 mariadb:10.3 的映像檔所建立容器
  4. 改以 mariadb:10.8 的映像檔來建立新的容器,並且將 mariadb-data 這個 volume 連接到容器上,並確認容器的 logs 一切正常。

升級資料庫版本練習解答

  1. 以 mariadb:10.3 的映像檔建立容器,並且把 volume 命名為 mariadb-data 且連接到容器上,並確認容器的 logs 一切正常,並透過閱讀 DockerHub 上的使用說明來得到啟動容器所需要的參數以及容器內儲存資料的路徑。

來到 mariadb 的 DockerHub 頁面,可以看到使用說明上關於啟動服務需要的環境變數。

啟動 mariadb 需要的參數

接著需要找到容器內資料庫的儲存路徑,往下滑,會看到官方說明也有明確地標示出要如何儲存資料。

mariadb 容器內的檔案儲存路徑

接著可以正式的啟動 maridb:10.3 的服務了。

$ docker run --detach --name maria-db --env MARIADB_USER=user --env MARIADB_PASSWORD=password --env MARIADB_ROOT_PASSWORD=password --volume mariadb-data:/var/lib/mysql mariadb:10.3 # 不換行
Unable to find image 'mariadb:10.3' locally
10.3: Pulling from library/mariadb
675920708c8b: Pull complete
8cd439041170: Pull complete
566a6a9b01f3: Pull complete
033be70909e9: Pull complete
e62d5ac9f0cc: Pull complete
482b55ad0b73: Pull complete
73f5e77b89d0: Pull complete
5bc9da099573: Pull complete
59fd6ada2b2c: Pull complete
eaaa8398274c: Pull complete
0fdd4bd4ac7c: Pull complete
938a2e87683b: Pull complete
Digest: sha256:c769235db77fff4b6dc1eccb32cfc51b40b686a450bf12e6eecf...
Status: Downloaded newer image for mariadb:10.3
8d1f095437b3fcfc1f535d97723924780ae494a1f4a0f03caee47132d17fded7

成功啟動後,先檢查一下 volume 是不是有正確的建立。

$ docker volume list
DRIVER VOLUME NAME
local mariadb-data

接著再來確認一下服務的 logs 有沒有什麼錯誤的訊息。

$ docker container logs --follow maria-db
2022-09-18 09:57:31+00:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:10.3.36+maria~ubu2004 started.
2022-09-18 09:57:31+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2022-09-18 09:57:31+00:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:10.3.36+maria~ubu2004 started.
2022-09-18 09:57:31+00:00 [Note] [Entrypoint]: MariaDB upgrade not required
2022-09-18 9:57:31 0 [Note] mysqld (mysqld 10.3.36-MariaDB-1:10.3.36+maria~ubu2004) starting as process 1 ...
2022-09-18 9:57:31 0 [Note] InnoDB: Using Linux native AIO
2022-09-18 9:57:31 0 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2022-09-18 9:57:31 0 [Note] InnoDB: Uses event mutexes
2022-09-18 9:57:31 0 [Note] InnoDB: Compressed tables use zlib 1.2.11
2022-09-18 9:57:31 0 [Note] InnoDB: Number of pools: 1
2022-09-18 9:57:31 0 [Note] InnoDB: Using SSE2 crc32 instructions
2022-09-18 9:57:31 0 [Note] InnoDB: Initializing buffer pool, total size = 256M, instances = 1, chunk size = 128M
2022-09-18 9:57:31 0 [Note] InnoDB: Completed initialization of buffer pool
2022-09-18 9:57:31 0 [Note] InnoDB: If the mysqld execution user is authorized, page cleaner thread priority can be changed. See the man page of setpriority().
2022-09-18 9:57:31 0 [Note] InnoDB: Starting crash recovery from checkpoint LSN=1625452
2022-09-18 9:57:31 0 [Note] InnoDB: 128 out of 128 rollback segments are active.
2022-09-18 9:57:31 0 [Note] InnoDB: Removed temporary tablespace data file: "ibtmp1"
2022-09-18 9:57:31 0 [Note] InnoDB: Creating shared tablespace for temporary tables
2022-09-18 9:57:31 0 [Note] InnoDB: Setting file './ibtmp1' size to 12 MB. Physically writing the file full; Please wait ...
2022-09-18 9:57:31 0 [Note] InnoDB: File './ibtmp1' size is now 12 MB.
2022-09-18 9:57:31 0 [Note] InnoDB: 10.3.36 started; log sequence number 1625461; transaction id 20
2022-09-18 9:57:31 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
2022-09-18 9:57:31 0 [Note] Plugin 'FEEDBACK' is disabled.
2022-09-18 9:57:31 0 [Note] Recovering after a crash using tc.log
2022-09-18 9:57:31 0 [Note] Starting crash recovery...
2022-09-18 9:57:31 0 [Note] Crash recovery finished.
2022-09-18 9:57:31 0 [Note] InnoDB: Buffer pool(s) load completed at 220918 9:57:31
2022-09-18 9:57:31 0 [Note] Server socket created on IP: '::'.
2022-09-18 9:57:31 0 [Note] Reading of all Master_info entries succeeded
2022-09-18 9:57:31 0 [Note] Added new Master_info '' to hash table
2022-09-18 9:57:31 0 [Note] mysqld: ready for connections. <- 等待連接代表啟動是正常的

透過 logs 也確認到 MariaDB 已經正確地啟動。

  1. 確認一切都沒問題後,暫停掉以 mariadb:10.3 的映像檔所建立容器
$ docker container stop maria-db
maria-db
  1. 改以 mariadb:10.8 的映像檔來建立新的容器,並且將 mariadb-data 這個 volume 連接到容器上,並確認容器的 logs 一切正常。
$ docker run --detach --name maria-db-new --env MARIADB_USER=user --env MARIADB_PASSWORD=password --env MARIADB_ROOT_PASSWORD=password --volume mariadb-data:/var/lib/mysql mariadb:10.8 # 不換行
Unable to find image 'mariadb:10.8' locally
10.3: Pulling from library/mariadb
675920708c8b: Pull complete
8cd439041170: Pull complete
566a6a9b01f3: Pull complete
033be70909e9: Pull complete
e62d5ac9f0cc: Pull complete
482b55ad0b73: Pull complete
73f5e77b89d0: Pull complete
5bc9da099573: Pull complete
59fd6ada2b2c: Pull complete
eaaa8398274c: Pull complete
0fdd4bd4ac7c: Pull complete
938a2e87683b: Pull complete
Digest: sha256:c8f817ab07d329fc9ff888fd19b0c5718d177411132c4d7b3e907...
Status: Downloaded newer image for mariadb:10.8
77bac6edd4011ba726adbfa0043b51111039ac10845c1a10b4b7e3a15a35ff05

接著再次列出 volume,確認沒有因為新的服務而啟動新的 volume。

$ docker volume list
DRIVER VOLUME NAME
local mariadb-data

一樣透過 docker container logs 的方式確認新版本的 MariaDB 是正確地啟動。

$ docker container logs --follow maria-db-new
2022-09-18 10:07:01+00:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:10.8.4+maria~ubu2204 started.
2022-09-18 10:07:01+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2022-09-18 10:07:01+00:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:10.8.4+maria~ubu2204 started.
2022-09-18 10:07:01+00:00 [Note] [Entrypoint]: MariaDB upgrade (mariadb-upgrade) required, but skipped due to $MARIADB_AUTO_UPGRADE setting
2022-09-18 10:07:01 0 [Note] mariadbd (server 10.8.4-MariaDB-1:10.8.4+maria~ubu2204) starting as process 1 ...
2022-09-18 10:07:01 0 [Note] InnoDB: Compressed tables use zlib 1.2.11
2022-09-18 10:07:01 0 [Note] InnoDB: Number of transaction pools: 1
2022-09-18 10:07:01 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
2022-09-18 10:07:01 0 [Note] mariadbd: O_TMPFILE is not supported on /tmp (disabling future attempts)
2022-09-18 10:07:01 0 [Warning] mariadbd: io_uring_queue_init() failed with ENOMEM: try larger memory locked limit, ulimit -l, or https://mariadb.com/kb/en/systemd/#configuring-limitmemlock under systemd (262144 bytes required)
2022-09-18 10:07:01 0 [Warning] InnoDB: liburing disabled: falling back to innodb_use_native_aio=OFF
2022-09-18 10:07:01 0 [Note] InnoDB: Initializing buffer pool, total size = 128.000MiB, chunk size = 2.000MiB
2022-09-18 10:07:01 0 [Note] InnoDB: Completed initialization of buffer pool
2022-09-18 10:07:01 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
2022-09-18 10:07:01 0 [Note] InnoDB: Upgrading redo log: 96.000MiB; LSN=1625479
2022-09-18 10:07:01 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
2022-09-18 10:07:01 0 [Note] InnoDB: 128 rollback segments are active.
2022-09-18 10:07:01 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
2022-09-18 10:07:01 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
2022-09-18 10:07:01 0 [Note] InnoDB: log sequence number 1625479; transaction id 20
2022-09-18 10:07:01 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
2022-09-18 10:07:01 0 [Note] Plugin 'FEEDBACK' is disabled.
2022-09-18 10:07:01 0 [Warning] You need to use --log-bin to make --expire-logs-days or --binlog-expire-logs-seconds work.
2022-09-18 10:07:01 0 [Note] Server socket created on IP: '0.0.0.0'.
2022-09-18 10:07:01 0 [Note] Server socket created on IP: '::'.
2022-09-18 10:07:01 0 [Note] InnoDB: Buffer pool(s) load completed at 220918 10:07:01
2022-09-18 10:07:01 0 [ERROR] Incorrect definition of table mysql.event: expected column 'definer' at position 3 to have type varchar(, found type char(141).
2022-09-18 10:07:01 0 [ERROR] mariadbd: Event Scheduler: An error occurred when initializing system tables. Disabling the Event Scheduler.
2022-09-18 10:07:01 0 [Note] mariadbd: ready for connections.<- 等待連接代表啟動是正常的

再透過 docker container inspect 去確認一下新版本的 MariaDB 是否連接到 mariadb-data 這個 volume。

$ docker container inspect maria-db-new
[
...
{
"Mounts": [
{
"Type": "volume",
"Name": "mariadb-data",
"Source": "/var/lib/docker/volumes/mariadb-data/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
]
}
...
]

這樣就成功地升級了 MariaDB 的版本,且使用了同一個 volume 來避免資料的流失。

在運行的容器中修改程式碼練習

每一個章節都會提供一些小小的練習來熟悉本章的內容,通常會比較難一點;若是想不出來也不需要灰心,有些時候可能是指令不熟悉導致,可以往前翻閱來加強記憶喔,亦或是翻到後面的解答章節也可以幫助你解惑喔,記得 --help 以及 docs.docker.com 會是你最好的夥伴。

這個練習則是將 Bind Mount 實際應用到開發場景上,正如之前所提過的,Bind Mount 的方式讓你能夠輕鬆地在本地端開發,可以透過編輯本地的檔案同時影響到正在運作的容器。

而這次將使用 Static Site Generator 來作為練習,Static Site Generator 顧名思義是一種靜態頁面的產生器,有非常多知名的框架,如使用 Ruby 開發的 Jekyll,或是 Golang 開發的 Hugo 等等。

而這次將透過 Hugo 這個靜態頁面產生器在本地運行一個開發的伺服器,而這樣的練習並不需要了解 Golang 這個程式語言,而是透過 Bind Mount 的方式來建立本機和運行中的容器之間的橋樑;所以這個練習只是單純的修改文件來啟發您對於 Bind Mount 的使用。

  1. 首先要拿到這次練習的檔案,ㄧ樣放在本書的 GitHub 儲存庫內 ch-05 的 bind-amount-practice 中。
  2. 確認進入到檔案目錄後,這次是根據 robeeerto/hugo:latest 這個映像檔作為容器的基底,並且打開 1313 的 port,同時把當前目錄的 content 資料夾用 Bind Mount 的方式放到容器中的 /app/content 的位置,接著打開瀏覽器輸入 http://localhost:1313,確認服務有正式啟動,可以看到畫面。
  3. 接著編輯當前目錄中 content/posts 內的 practice.md 這個檔案的內文,看看瀏覽器的刷新以及變化。

在運行的容器中修改程式碼練習解答

  1. 確認進入到檔案目錄後,這次是根據 robeeerto/hugo:latest 這個映像檔作為容器的基底,並且打開 1313 的 port,同時把當前目錄的 content 資料夾用 Bind Mount 的方式放到容器中的 /app/content 的位置,接著打開瀏覽器輸入 http://localhost:1313,確認服務有正式啟動,可以看到畫面。

根據該章節,知道可以透過兩種方式來達到一樣的目的,分別是 --volume 以及 --mount

使用 --volume 的方式

$ docker container run --publish 1313:1313 --name hugo --volume $(pwd)/content:/app/content robeeerto/hugo # 不換行
Start building sites …
hugo v0.92.2+extended linux/amd64 BuildDate=2022-02-23T16:47:50Z VendorInfo=ubuntu:0.92.2-1

| EN
-------------------+-----
Pages | 10
Paginator pages | 0
Non-page files | 0
Static files | 1
Processed images | 0
Aliases | 1
Sitemaps | 1
Cleaned | 0

Built in 99 ms
Watching for changes in /app/{content,themes}
Watching for config changes in /app/config.toml, /app/themes/ananke/config.yaml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address 0.0.0.0)
Press Ctrl+C to stop

使用 --mount 的方式

$ docker container run --publish 1313:1313 --name hugo --mount type=bind,source=$(pwd)/content,target=/app/content robeeerto/hugo # 不換行
Start building sites …
hugo v0.92.2+extended linux/amd64 BuildDate=2022-02-23T16:47:50Z VendorInfo=ubuntu:0.92.2-1

| EN
-------------------+-----
Pages | 10
Paginator pages | 0
Non-page files | 0
Static files | 1
Processed images | 0
Aliases | 1
Sitemaps | 1
Cleaned | 0

Built in 99 ms
Watching for changes in /app/{content,themes}
Watching for config changes in /app/config.toml, /app/themes/ananke/config.yaml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address 0.0.0.0)
Press Ctrl+C to stop

不論採用哪種方式,打開瀏覽器輸入 http://localhost:1313 都會看到下方的畫面。

Hugo 運行成功畫面

  1. 接著編輯當前目錄中 content/posts 內的 practice.md 這個檔案的內文,看看瀏覽器的刷新以及變化。

透過編輯器修改了 practice.md 的內文,此時重新回到瀏覽器就會發現內容已經更動了,有沒有突然覺得 Bind Mount 真的超級好用呢?

更新的文章內容

您也能試試看按照相同的格式新增文章,看看會發生什麼事!

恭喜您完成了 Volume 這個章節,目前的我們掌握了基本的容器使用技能、基礎映像檔的建置以及使用、到現在如何儲存應用程式外的資料也學會了,還能夠透過 Bind Mount 的方式在本地端運行著容器進行開發。

而下個章節我們將進入 Docker Compose,這將會打開全新的視野。