Docker資料持久化

谢友海發表於2024-09-08

本章將和大家分享Docker中如何實現資料的持久化。廢話不多說,下面我們直接進入主題。

一、什麼是資料卷

我們都知道在Docker中,容器的資料讀寫預設發生在容器的儲存層,當容器被刪除時其上的資料將會丟失。如果想實現資料的持久化,就需要將容器和宿主機建立聯絡(將資料從宿主機掛載到容器內),通俗的說,資料卷就是在容器和宿主機之間實現資料共享。

資料卷是一個位於宿主機上的目錄或檔案,可以被Docker容器掛載使用。它允許容器和宿主機之間,或者容器和容器之間共享資料。資料卷的設計目的是為了資料的持久化,即資料不會隨著容器的刪除而丟失,完全獨立於容器的生存週期。

資料卷是由 Docker 管理的持久化資料儲存區域,用於在容器之間共享和持久化資料。Docker 會在主機的特定位置(通常是 /var/lib/docker/volumes/ 目錄下)建立資料卷目錄。

1、使用方式

資料卷可以透過Docker命令列工具進行建立、掛載、檢視和管理。常用的命令包括:

  • docker run -v /宿主機路徑:/容器內路徑 ...:在建立容器時掛載資料卷。
  • docker volume create 資料卷名稱:顯式建立一個資料卷。
  • docker volume ls:列出所有資料卷。
  • docker volume inspect 資料卷名稱:檢視資料卷的詳細資訊。
  • docker volume rm 資料卷名稱:刪除資料卷。

2、匿名掛載和具名掛載

  • 匿名掛載:沒有指定資料卷名稱,只指定了容器內路徑。
  • 具名掛載:在-v或--mount標誌中指定了資料卷的名稱和容器內路徑。

二、Docker支援的三種資料掛載方式

Docker提供了三種不同的方式將資料從宿主機掛載到容器中:卷掛載(Volume Mounts)、繫結掛載(Bind Mounts)和臨時檔案系統掛載(tmpfs Mounts)。

  • volume:Docker管理宿主機檔案系統的一部分(/var/lib/docker/volumes),是Docker預設儲存資料方式。
  • bind mounts:可以儲存在宿主機系統的任意位置。將宿主機的任意位置的檔案或者目錄掛載到容器中。
  • tmpfs mounts:掛載儲存在宿主機系統的記憶體中,不會寫入宿主機的檔案系統。

三、卷掛載(Volume Mounts)

卷掛載是Docker提供的一種持久化儲存方式,它將容器內的資料儲存在宿主機上的一個目錄裡。這個目錄由Docker管理,通常位於 /var/lib/docker/volumes/ 這個路徑下。當容器被刪除後,資料卷中的資料不會被刪除,實現了資料的持久化。卷掛載支援具名資料卷掛載(Named Volumes)和匿名資料卷掛載(Anonymous Volumes)這兩種方式。

1、具名資料卷掛載

特點:具名資料卷在建立時會被分配一個使用者指定的名稱,這個名稱在Docker主機上是唯一的。這使得資料卷的管理變得更為直觀和方便。

建立方式:可以透過docker volume create命令顯式建立具名資料卷,或者在docker run命令中透過-v或--mount選項並指定一個名稱(而非路徑)來隱式建立。

示例:

  • 顯式建立:docker volume create my-named-volume
  • 隱式建立並掛載:docker run -d --name my-container -v my-named-volume:/app/data nginx 或 docker run -d --name my-container --mount source=my-named-volume,target=/app/data nginx

優勢:具名資料卷獨立於容器的生命週期,即使容器被刪除,資料卷也會保留在Docker主機上,除非顯式刪除資料卷。這使得資料持久化和容器遷移變得更加容易。

語法示例:

# 建立一個名為nginx_volume的資料卷(命名卷)
docker volume create nginx_volume

# 檢視當前所有資料卷
docker volume ls

# 檢視資料卷的詳細資訊
docker volume inspect nginx_volume

# 啟動容器時將資料卷掛載到容器內的 /usr/share/nginx/html 目錄
docker run -d -p 8090:80 --mount type=volume,source=nginx_volume,target=/usr/share/nginx/html --name nginx-container nginx:latest
或
docker run -d -p 8090:80 -v nginx_volume:/usr/share/nginx/html --name nginx-container nginx:latest

# 檢視docker容器列表
docker ps -a  #所有容器列表(包含存活和退出容器)
docker ps -l  #列出當前執行的最後一個容器

# 檢視容器具體資訊
docker inspect nginx-container  #容器ID或名稱

演示:

[root@localhost ~]# docker volume create nginx_volume
nginx_volume
[root@localhost ~]# docker volume ls
DRIVER    VOLUME NAME
local     nginx_volume
[root@localhost ~]# docker volume inspect nginx_volume
[
    {
        "CreatedAt": "2024-09-06T23:28:09+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/nginx_volume/_data",
        "Name": "nginx_volume",
        "Options": null,
        "Scope": "local"
    }
]
[root@localhost ~]# docker run -d -p 8090:80 -v nginx_volume:/usr/share/nginx/html --name nginx-container nginx:latest
eab6771135908196d70df10b604c5566b14571b52d806b205c8607737a85b370
[root@localhost ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                   CREATED          STATUS          PORTS                                   NAMES
eab677113590   nginx:latest   "/docker-entrypoint.…"   12 seconds ago   Up 11 seconds   0.0.0.0:8090->80/tcp, :::8090->80/tcp   nginx-container
[root@localhost ~]# docker inspect nginx-container
......

其中 "Mountpoint": "/var/lib/docker/volumes/nginx_volume/_data" 表示這個卷在宿主機上的掛載點(即:資料卷的工作目錄)。Docker會將這個目錄下的資料掛載到容器內的指定位置,供容器使用。這裡的 _data 目錄是Docker在建立卷時自動建立的,用於儲存實際的資料。

驗證資料卷是否掛載成功:

[root@localhost ~]# cd /var/lib/docker/volumes/nginx_volume/_data
[root@localhost _data]# echo "hello world" >> test.html
[root@localhost _data]# ll
總用量 12
-rw-r--r--. 1 root root 497 8月  12 22:21 50x.html
-rw-r--r--. 1 root root 615 8月  12 22:21 index.html
-rw-r--r--. 1 root root  12 9月   6 23:42 test.html
[root@localhost _data]# curl 192.168.4.250:8090/test.html
hello world
[root@localhost _data]# docker ps -l
CONTAINER ID   IMAGE          COMMAND                   CREATED          STATUS          PORTS                                   NAMES
eab677113590   nginx:latest   "/docker-entrypoint.…"   19 minutes ago   Up 19 minutes   0.0.0.0:8090->80/tcp, :::8090->80/tcp   nginx-container
[root@localhost _data]# docker exec -it eab677113590 /bin/bash
root@eab677113590:/# cd /usr/share/nginx/html
root@eab677113590:/usr/share/nginx/html# ls
50x.html  index.html  test.html

我們在宿主機資料卷裡新增了一個test.html檔案,進入到Docker容器內部的指定目錄下也可以看到該檔案,並且透過瀏覽器也能正常訪問到該檔案,證明我們的資料卷掛載成功了。

命名卷是資料卷的一種特殊形式,具有明確的名稱,可以更容易地管理和引用。

2、匿名資料卷掛載

特點:匿名資料卷在建立時沒有明確的名稱,Docker會自動為其分配一個隨機ID作為標識。這使得它們的管理相對複雜,因為需要透過其他方式(如docker volume ls)來查詢和識別。

建立方式:通常是在docker run命令中使用-v或--mount選項時,只指定容器內的掛載點,而不指定資料卷的名稱或主機路徑。Docker會自動為這樣的掛載點建立一個匿名資料卷。

示例:docker run -d --name my-container -v /app/data nginx 或 docker run -d --name my-container --mount type=volume,target=/app/data nginx(注意,這裡雖然使用了--mount但沒有指定source,因此會建立匿名卷)

注意:由於匿名資料卷沒有明確的名稱,因此在管理和維護上可能會更加困難。特別是當需要刪除不再使用的資料卷時,可能需要額外的步驟來識別和刪除它們。因此非必要不推薦使用該方式掛載。

語法示例:

# 使用匿名資料卷掛載啟動一個nginx容器,Docker會自動建立一個匿名資料卷並將其掛載到容器內的 /usr/share/nginx/html 這個路徑上
docker run -d -p 8091:80 --mount type=volume,target=/usr/share/nginx/html --name nginx-container-2 nginx:latest
或
docker run -d -p 8091:80 -v /usr/share/nginx/html --name nginx-container-2 nginx:latest

演示:

[root@localhost ~]# docker volume ls
DRIVER    VOLUME NAME
local     nginx_volume
[root@localhost ~]# docker run -d -p 8091:80 -v /usr/share/nginx/html --name nginx-container-2 nginx:latest
dfbe28444ded76b621e4cf61df67336b2ce3d837445e5244ed0de3b9e928de5f
[root@localhost ~]# docker volume ls
DRIVER    VOLUME NAME
local     161adf8452b212bbd9a41a2715f6152bd0d9963c011d4d505311b4beddee102b
local     nginx_volume
[root@localhost ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                   CREATED          STATUS                     PORTS                                   NAMES
dfbe28444ded   nginx:latest   "/docker-entrypoint.…"   29 seconds ago   Up 28 seconds              0.0.0.0:8091->80/tcp, :::8091->80/tcp   nginx-container-2
eab677113590   nginx:latest   "/docker-entrypoint.…"   20 hours ago     Exited (255) 2 hours ago   0.0.0.0:8090->80/tcp, :::8090->80/tcp   nginx-container

上面那個名字很長的資料卷就是匿名資料卷。

四、繫結掛載(Bind Mounts)

Docker繫結掛載(Bind Mounts)允許你將宿主機上任意的一個目錄或檔案掛載到容器內部的一個目錄或檔案上。這種方式非常適合在開發環境中使用,因為它允許你在容器外編輯檔案,同時容器內可以看到檔案的變化。

語法示例:

# 啟動nginx容器,將宿主機的 /opt/nginx 目錄掛載到容器內的 /usr/share/nginx/html 目錄
docker run -d -p 8090:80 --mount type=bind,source=/opt/nginx,target=/usr/share/nginx/html --name nginx-container nginx:latest
或
docker run -d -p 8090:80 -v /opt/nginx:/usr/share/nginx/html --name nginx-container nginx:latest

演示:

在宿主機 /opt 目錄下建立一個 nginx 資料夾,並在 nginx 資料夾裡面新增一個 test.html 檔案:

[root@localhost ~]# cd /opt
[root@localhost opt]# mkdir nginx
[root@localhost opt]# cd ./nginx/
[root@localhost nginx]# echo "hello world" >> test.html
[root@localhost nginx]# ll
總用量 4
-rw-r--r--. 1 root root 12 9月   7 23:48 test.html

啟動nginx容器,將宿主機的 /opt/nginx 目錄掛載到容器內的 /usr/share/nginx/html 目錄:

[root@localhost nginx]# docker run -d -p 8092:80 --mount type=bind,source=/opt/nginx,target=/usr/share/nginx/html --name nginx-container-2 nginx:latest
468e94b76c55b4df2ccf4d0039940e1aa03e74e9d031dfcc0b1e6f03471024c1

進入Docker容器內部,檢視容器內 /usr/share/nginx/html 目錄下的檔案列表:

[root@localhost nginx]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                   CREATED         STATUS                     PORTS                                   NAMES
468e94b76c55   nginx:latest   "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes               0.0.0.0:8092->80/tcp, :::8092->80/tcp   nginx-container-2
eab677113590   nginx:latest   "/docker-entrypoint.…"   25 hours ago    Exited (255) 6 hours ago   0.0.0.0:8090->80/tcp, :::8090->80/tcp   nginx-container
[root@localhost nginx]# docker exec -it nginx-container-2 /bin/bash
root@468e94b76c55:/# cd /usr/share/nginx/html
root@468e94b76c55:/usr/share/nginx/html# ls
test.html

可以發現並沒有 nginx 預設的 index.html 和 50x.html 檔案,只有來自宿主機上面的 test.html 檔案。

可以使用 exit 命令退出容器:

root@468e94b76c55:/usr/share/nginx/html# exit
exit
[root@localhost nginx]#

注意:

1、如果你使用 Bind mounts 掛載宿主機目錄到一個容器中的非空目錄,那麼此容器中的非空目錄中的檔案會被隱藏,容器訪問這個目錄時能夠訪問到的檔案均來自於宿主機目錄下的檔案。

2、-v 宿主機目錄路徑必須以 / 或 ~/ 開頭,否則Docker會將其當成是 Volume Mounts 而不是 Bind Mounts 。

五、臨時檔案系統掛載(tmpfs Mounts)

tmpfs掛載(也稱為記憶體檔案系統掛載)是一種特殊的掛載型別,它將資料儲存在容器的記憶體中而不是磁碟上。tmpfs主要用於需要快速讀寫操作的場景,或者是那些需要在容器啟動時清空資料的場景。

tmpfs mount只在Linux主機記憶體中持久化,是臨時性的。當容器停止,tmpfs mount會被移除,通常用於臨時存放敏感檔案。

語法示例:

# 執行容器並繫結臨時資料卷
docker run -d -p 8090:80 --mount type=tmpfs,target=/usr/share/nginx/html --name nginx-container nginx:latest

演示:

[root@localhost ~]# docker run -d -p 8093:80 --mount type=tmpfs,target=/usr/share/nginx/html --name nginx-container-3 nginx:latest
cb971be9d9316c300e74155f3069de5a7639ece74d5cb63638c0f018b4b9f0bd
[root@localhost ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                   CREATED         STATUS                      PORTS                                   NAMES
cb971be9d931   nginx:latest   "/docker-entrypoint.…"   6 seconds ago   Up 5 seconds                0.0.0.0:8093->80/tcp, :::8093->80/tcp   nginx-container-3
eab677113590   nginx:latest   "/docker-entrypoint.…"   38 hours ago    Exited (255) 19 hours ago   0.0.0.0:8090->80/tcp, :::8090->80/tcp   nginx-container
[root@localhost ~]# docker exec -it nginx-container-3 /bin/bash
root@cb971be9d931:/# cd /usr/share/nginx/html
root@cb971be9d931:/usr/share/nginx/html# ls
root@cb971be9d931:/usr/share/nginx/html# echo "hello world" >> test.html
root@cb971be9d931:/usr/share/nginx/html# ls
test.html
root@cb971be9d931:/usr/share/nginx/html# exit
exit
[root@localhost ~]# curl 192.168.4.250:8093/test.html
hello world
[root@localhost ~]#

注意事項:

  • tmpfs 中的資料在容器停止或重啟時會被清除。
  • tmpfs 的大小受到宿主機可用記憶體的限制。
  • tmpfs 適用於需要高速讀寫操作的場景,如快取或臨時檔案儲存。
  • 由於 tmpfs 儲存在記憶體中,它有助於提高安全性,因為資料不會永久駐留在磁碟上。
  • 如果 tmpfs 中的資料量很大,可能會導致容器啟動時載入較慢。

六、三種儲存方式適用場景

volume mount:

  • 多個容器間共享資料。
  • 宿主機不保證存在固定目錄結構。
  • 持久化資料到遠端主機或者雲端儲存而非本地。
  • 需要備份、遷移、合併資料時。停止container,將volume整體複製,用於備份、遷移、合併等。

bind mount:

  • 主機與容器共享配置檔案,如docker將宿主機檔案/etc/resov.conf檔案bind mount到容器上,兩者會使用相同的DNS伺服器。
  • 共享原始碼或build artifacts(比如將Maven的target/目錄掛載到容器中,每次在Docker主機中build Maven工程時,容器能夠訪問到那些rebuilt artifacts)。
  • 當docker主機中的檔案或目錄結構和容器需要一致時。

tmpfs mount:

  • 既不想將資料存於主機,又不想存於容器中時(這可以是出於安全的考慮,或當應用需要寫大量非永續性的狀態資料時為了保護容器的效能)。

至此本文就全部介紹完了,如果覺得對您有所啟發請記得點個贊哦!!!

相關文章