Container 本身有他自己的 File System,當有檔案建立的時候,都會放在這個 File System 裡,這個 File System 是跟著 Container 存活的。所以當 Container 在被刪除後, Container 裡面的狀態或者是資料都會消失。 而 Docker 能透過建立 Volumes 來解決這個問題。

Volumes 介紹

Volumes 能夠將資料放在 Container 之外,意思是說你能夠更新 Container 的時候資料不會遺失。 請看下圖, Volumes 是在 Container 之外的一個 File System。

from docker documentfrom docker document

應用程式中有兩種需要儲存檔案的方式,第一種是當應用程式執行的時候,該應用程式會產生的檔案,例如 Log 之類的。 第二種是應用程式要執行的條件是一定要有檔案存在,例如執行資料庫。 這兩種情況在使用 Volumes 的時候會不太一樣。 今天主是講第一種使用 Volumes 的方式。

Container 本身的 File System

範例的 Image 我使用 alpine,這個是由社群開發的 Linux 作業系統
請看下方的 dockerfile,我會在 Container 建立起來的時候檢查 /data/message.txt 這個檔案是否存在,如果存在我會 echo File Exists,反之會 echo Creating File,並且建立檔案。

Dockerfile
  • dockerfile
1
2
3
4
5
FROM alpine
WORKDIR /data
ENTRYPOINT (test -e message.txt && echo "File Exists" \
|| (echo "Creating File..." \
&& echo Hello, Docker $(date '+%X') > message.txt)) && cat message.txt

接下來請執行下方兩行指令,把 Container 執行起來

run container
  • bash
1
2
docker build . -t apress/vtest
docker run --name vtest apress/vtest

執行後會看到 Creating File… 的訊息,如下圖。 同時 Container 也會停止執行,因為 ENTRYPOINT 的指令只是要讓 Container 建立檔案而已,建立完檔案後 Container 就會停止執行。

成功建立檔案成功建立檔案

接下來我們要再一次啟動同一個 Container,請執行下方指令

start container
  • bash
1
docker start -a vtest

-a 是 attach 的意思,是讓 container 的 log 能呈現出來。 (docker start)

因為 Container 還沒被刪除,所以 Container File System 裡面的檔案還會存在,如下圖所示。

檔案已存在檔案已存在

隨著 Container 移除後,檔案消失了

接下來我要把 Container 刪除,再跑一個新的 Container 起來,請執行以下指令

delete and run container
  • bash
1
2
docker rm -f vtest
docker run --name vtest apress/vtest

-f 是 force 的意思,能夠強制刪除執行中的 Container。 (docker rm)

你會發現,剛剛建立的檔案 Hello, Docker 15:46:43 不見了,而新的檔案被建立 Hello, Docker 16:01:42,如下圖。

重新建立同樣的 Container重新建立同樣的 Container

使用 Volumes 保存檔案

將 Dockerfile 修改一下,只要在在 Dockerfile 加入 VOLUME /data,這個命令是在告訴 Docker 任何檔案只要存在 /data 路徑底下,應該把檔案存在 volume 裡面,放在
volume 的檔案,並不會放在原本的 Container File System 裡面。

Dockerfile
  • dockerfile
1
2
3
4
5
6
FROM alpine
VOLUME /data
WORKDIR /data
ENTRYPOINT (test -e message.txt && echo "File Exists" \
|| (echo "Creating File..." \
&& echo Hello, Docker $(date '+%X') > message.txt)) && cat message.txt

讓我們使用上方的 Dockerfile 更新原本的 apress/vtest 。 請執行下方指令

build image
  • bash
1
docker build . -t apress/vtest

第二步驟就是建立 Volume ,等等會讓檔案儲存在該 Volume 裡。 請執行下方指令

create volume
  • bash
1
docker volume create --name testdata

指令執行完後,可以輸入 docker volume ls 檢查 volume 是否成功建立。 如下圖。

建立 volume建立 volume

接下來請執行下方指令,把 Container 在次執行起來。

run container
  • bash
1
docker run --name vtest2 -v testdata:/data apress/vtest

-v testdata:/data 是在告訴 Docker 任何檔案只要存在 Container 裡面的 /data 路徑就要被存在 testdata volume 裡面

因為是全新的 Container,所以你會看檔案再次被建立,如下圖。

建立檔案在 Volume建立檔案在 Volume

接下來把 Container 刪除後再執行一次。 請執行下方指令。

recreate container
  • bash
1
2
docker rm -f vtest2
docker run --name vtest2 -v testdata:/data apress/vtest

你會發現,檔案還是存在的,因為檔案是存在 Volume 裡面。

檔案存在 Volume 裡檔案存在 Volume 裡

小結

在使用 Docker 的時候,懂得使用 Volume 是非常重要的事情。 這能夠讓你的檔案能夠一直保存著,不隨著 Container 消失而檔案也跟著消失。

延伸閱讀

[Docker - use volumes]