自己架設Docker的共享儲存空間

Docker很好用,在單機環境下真的很好用。Docker原本的設計,是為了快速迭代而設計成Image的。在一般設定下,每次新建或重建container,都會根據Image重設一下各方面的環境,包括儲存空間。重設CPU,Memory,大家都很易理解,但重設儲存空間,真的不是每一個使用情況都可以這樣。

又或者說,未必所有使用情況都會有一個第三方的儲存空間可以用。所以良心的Docker在單機環境下也有提供bind mount或是docker named volume,作為可以長期保存,不受container生死的影響,以達到長期存在Data的存在。

單機-儲存空間

單機情況下很簡單,就用一個docker compose做例子

services:
  nginx:
    image: nginx:1.23
    restart: always
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./html/:/usr/share/nginx/html/
      - nginxlogs:/var/log/nginx/

volumes:
  nginxlogs:

其中html就是一個bind mount,而nginxlogs就是一個docker named volume,兩者都可以長期保存data,除非各位自己手動刪除,否則不會因為container的興亡而不見了。

但有兩個很重要的分別

  • bind mount,直接跟host os連接,實際上是每次folder有更新,docker都要同步host和container之間的資料。
    • bind mount在linux下很暢順,因為大部份docker image/container原本就是linux engine,所以folder mount真的可以互通。
    • bind mount在windows / mac下,就會不斷抄資料。面對大量檔案,例如node_module,就會有速度上的問題
  • docker named volume,就是docker 分離一些獨立空間,然後再綁到container上
    • 相對bind mount,即使在windows / mac下,都沒有那個速度上的問題。筆者猜測,即使是獨立空間,其實本身都已經限定在linux enginx下,所以沒有需要抄資料。
    • 但在windows / mac下,因應docker 底層建立Linux VM的技術不同,你可能沒法在windows / mac預設環境下直接讀取docker named volume。
    • 若要讀取docker named volume,最好的做法,還是連上docker container,然後用docker cp 來抄回資料。一但抄資料,其實都會有速度上問題,不過docker cp是手動決定何時做的,不做docker cp,其實container也是可以用。

Cluster (Docker Swarm) - 儲存空間

雖然良心的bind mount和named volume解決了單機上的儲存問題,但到了cluster環境,就沒有可以跨機同步儲存空間的做法,要做就自己建立。

筆者也稍為研究了一下同步的問題,不過對技術真的很有要求。所以退而求其次,筆者還是選擇簡單的第三方儲存空間。就是做一個可以分享存取的NAS。

建立nfs

linux下要安裝nfs其實很簡單,不過要注意資料夾和防火牆權限。

以下安裝教學以ubunut 22.04為例,記得把下面的YOUR_DOCKER_NODE_ADDRESS_RANGE轉為你的真實IP段落

apt update && apt install nfs-kernel-server ufw -y

mkdir -p /mnt/nfs_share
chown -R nobody:nogroup /mnt/nfs_share/
chmod 777 /mnt/nfs_share/

echo "/mnt/nfs_share YOUR_DOCKER_NODE_ADDRESS_RANGE/24(rw,sync,no_subtree_check)" >> /etc/exports

exportfs -a

systemctl restart nfs-kernel-server

ufw allow from YOUR_DOCKER_NODE_ADDRESS_RANGE/24 to any port nfs
ufw allow ssh
ufw enable

修改docker compose

最後,你在原來的docker-compose的docker volume上加driver_opts就大功告成。

記得把下面的YOUR_NFS_IP轉為你的真實IP

volumes:
  nginxlogs:
    driver_opts:
      type: "nfs"
      o: "nfsvers=4,addr=YOUR_NFS_IP,nolock,soft,rw"
      device: ":/mnt/nfs_share/nginxlogs"