Atlantis
GitHub 切换暗/亮/自动模式 切换暗/亮/自动模式 切换暗/亮/自动模式 返回首页

8. 共享存储

Created At 2024-09-17 Updated on 2025-10-25

1. 前言

即使使用 qcow2 文件作为硬盘存储格式,按需分配实际存储空间,也无法避免 Proxmox VE 本地存储耗尽的问题,以我的一个 dev 开发机器,就是由于长期编译程序,导致根分区对应的 qcow2 文件大小已接近 300G,而实际物理设备只有 512GB,为了压缩 qcow2 文件回收存储空间,不得不先迁移到一个大量容量 HDD,整个过程比较折腾人。

计算和存储分离是常见手法,为了更好地利用现有存储,过去我尝试了 NFS、Samba、WebDAV、S3 等存储方案,这里做一个记录。

2. 测试环境

这里使用一个 Debian 12 虚拟机部署存储服务端,内网 IP 地址为 192.168.123.2,一块 HDD 和一块 SSD 分别挂载到 /mnt/external-hdd 与 /mnt/external-ssd,客户端也是一个 Debian 12 虚拟机,测试时会将远端存储挂载到本地的 /mnt/external-hdd 与 /mnt/external-ssd。

由于 HDD 是通过 USB 接入 Proxmox VE 主机然后透传给虚拟机的,为了避免使用 /etc/fstab 挂载可能导致的系统卡死问题,这里改用 systemd 执行挂载,配置如下:

➜  ~ lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda      8:0    0    64G  0 disk
├─sda1   8:1    0   512M  0 part /boot/efi
└─sda2   8:2    0  63.5G  0 part /
sdb      8:16   0 223.6G  0 disk
└─sdb1   8:17   0 223.6G  0 part /mnt/external-ssd
sdc      8:32   0   1.8T  0 disk /mnt/external-hdd
sr0     11:0    1     4M  0 rom
➜  ~ blkid /dev/sdb
/dev/sdb: PTUUID="285361c4-8e79-4773-a4da-f1e3af654b0c" PTTYPE="gpt"
➜  ~ blkid /dev/sdc
/dev/sdc: UUID="73246c75-2ce4-48ca-8bfd-cb95fdcc8afc" BLOCK_SIZE="4096" TYPE="ext4"
➜  ~ cat /lib/systemd/system/mnt-external\\x2dhdd.mount
[Unit]
Description=Mount data partition

[Mount]
What=/dev/disk/by-uuid/73246c75-2ce4-48ca-8bfd-cb95fdcc8afc
Where=/mnt/external-hdd
Type=ext4
Options=defaults

[Install]
WantedBy=multi-user.target
➜  ~ cat /lib/systemd/system/mnt-external\\x2dssd.mount
[Unit]
Description=Mount data partition

[Mount]
What=/dev/disk/by-uuid/9abbe539-05c4-456c-ab0d-bdf2a75e92ab
Where=/mnt/external-ssd
Type=btrfs
Options=defaults

[Install]
WantedBy=multi-user.target
➜  ~ systemctl enable --now mnt-external\\x2dssd.mount
➜  ~ systemctl enable --now mnt-external\\x2dhdd.mount
➜  ~ df -Th |grep /mnt/external
/dev/sdb1      btrfs     224G   12G  211G   6% /mnt/external-ssd
/dev/sdc       ext4      1.8T  1.3T  428G  76% /mnt/external-hdd
  1. HDD 对应的设备为 /dev/sdc,未分区,直接格式化为 ext4,使用 blkid 挂载设备,避免重新拔插设备、添加新设备后导致对应的 /dev/sdx 设备变化影响挂载
  2. SSD 对应的设备为 /dev/sdb,创建一个主分区,格式化为 btrfs,从使用体验看,除了需要 raid 的场景外,最好创建一个主分区再格式化为 btrfs
  3. mount 文件的命名需要与设备挂载路径一致,挂载路径中含有特殊符号时需要转义,比如将 - 改为 \\x2d
  4. 使用 systemd 挂载磁盘的另一个好处是可以配置 service 依赖关系,比如让 docker 等待两个设备挂载成功后再启动

Debian 12 虚拟机的内网 IP 为 192.168.123.2,为了方便以后迁移数据,我给他在内网解析了一个域名,若以后有专用的 NAS 存储数据,只需要做服务端的数据迁移就行。

3. NFS

3.1 配置服务端

NFS 是 Linux 上最简单的共享文件存储方案,安装与配置都不复杂。

安装NFS服务端

apt install -y nfs-kernel-server

配置共享目录

/etc/exports 文件内容如下,这里添加了 insecure 参数允许通过客户端使用 1024 以上端口连接服务器,兼容 macOS 的使用场景。

➜  ~ cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
#		to NFS clients.  See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes       hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4        gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes  gss/krb5i(rw,sync,no_subtree_check)
#
/mnt/external-ssd *(rw,sync,insecure,no_root_squash,no_subtree_check)
/mnt/external-hdd *(rw,sync,insecure,no_root_squash,no_subtree_check)

重载配置

systemctl restart nfs-kernel-server

3.2 配置客户端

3.2.1 Linux

Linux 客户端上可以使用命令行或者 systemd 挂载 NFS 存储,我们需要先安装 NFS 客户端:apt install -y nfs-common

命令行

➜  ~ mktemp -d
/tmp/tmp.3OCVVuHu9o
➜  ~ mount -t nfs 192.168.123.2:/mnt/external-ssd /tmp/tmp.3OCVVuHu9o
➜  ~ df -Th /tmp/tmp.3OCVVuHu9o
Filesystem                      Type  Size  Used Avail Use% Mounted on
192.168.123.2:/mnt/external-ssd nfs4  224G   12G  211G   6% /tmp/tmp.3OCVVuHu9o
➜  ~ umount /tmp/tmp.3OCVVuHu9o

systemd

mount 文件的写法是通用的,这里修改 What 为远端 NFS 存储,type 为 nfs,如下:

➜  ~ cat /lib/systemd/system/mnt-external\\x2dssd.mount
[Unit]
Description=Mount NFS share
After=network-online.target

[Mount]
What=192.168.123.2:/mnt/external-ssd
Where=/mnt/external-ssd
Type=nfs
Options=defaults

[Install]
WantedBy=multi-user.target
➜  ~ systemctl enable --now mnt-external\\x2dssd.mount
➜  ~ df -Th /mnt/external-ssd
Filesystem                      Type  Size  Used Avail Use% Mounted on
192.168.123.2:/mnt/external-ssd nfs4  224G   12G  211G   6% /mnt/external-ssd

3.2.2 macOS

macOS 与 Windows 也支持挂载 NFS,默认只读,以 macOS 为例,在 Finder 中使用 Cmd + k 连接服务器即可:

alt text

alt text

NFS 只读带来的一个好处是 macOS 无法在远端存储上写入 .DS_Store 文件了(能减少垃圾文件产生),需要写入文件时可以通过网页操作,比如 filebrowser。

3.2.3 Docker

Docker 支持创建 NFS 类型的 Volume 用于挂载到容器内,如下:

docker volume create \
--driver local \
--opt type=nfs \
--opt o=addr=192.168.123.2,rw,nfsvers=4 \
--opt device=:/mnt/external-ssd \
external-ssd

创建完成后可以检查 volume 参数:

➜  ~ docker volume inspect external-ssd
[
    {
        "CreatedAt": "2024-09-18T11:25:37+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/external-ssd/_data",
        "Name": "external-ssd",
        "Options": {
            "device": ":/mnt/external-ssd",
            "o": "addr=192.168.123.2,rw,nfsvers=4",
            "type": "nfs"
        },
        "Scope": "local"
    }
]

使用 alpine:3.18 容器镜像测试挂载,如下:

➜  ~ docker run --rm -v external-ssd:/mnt/external-ssd alpine:3.18 sh -c 'mount | grep nfs'
:/mnt/external-ssd on /mnt/external-ssd type nfs4 (rw,relatime,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.123.3,local_lock=none,addr=192.168.123.2)

4. Samba

Samba 相比 NFS 有更好的兼容性和易用性,无论是 Windows、macOS 还是 Linux,都可以轻松连接 Samba 服务器获取文件共享的能力,两者的对比如下:

特性 NFS Samba
协议类型 主用于 Unix/Linux 系统 主用于 Windows 系统
操作系统支持 Linux、Unix 等 Windows、Linux、Unix
文件系统访问 直接通过文件系统调用 通过 SMB/CIFS 协议
性能 较高,特别是在 Unix/Linux 系统上 较低于 NFS,尤其在大型文件传输时
安全性 支持基本的权限控制,依赖于用户和组 提供更细粒度的安全设置,包括用户验证
配置复杂性 配置相对简单,依赖于 /etc/exports 文件 配置稍复杂,需要设置 smb.conf
跨平台支持 主要为 Unix/Linux,Windows 支持较差 跨平台,Windows 和 Linux 之间兼容性好
文件锁定 支持文件锁定 支持文件锁定
性能调优 可以通过调整参数进行优化 也支持性能优化,但相对复杂
文件共享 适合大规模文件共享 适合小型文件共享和家庭网络
使用场景 数据库、虚拟机存储等 文件共享、打印服务等

4.1 配置服务端

对于家庭用户来说,Samba 的配置也不复杂,具体操作如下:

安装服务端

apt install -y samba

配置共享目录

编辑 /etc/samba/smb.conf,使用 ; 注释掉 [homes]、[printers]、[printe$] 等配置,然后在末尾添加以下内容:

[external-hdd]
   comment = external-hdd
   path = /mnt/external-hdd
   public = yes
   guest ok = yes
   browseable = yes
   read only = no
   force user = root
   force group = root

[external-ssd]
   comment = external-ssd
   path = /mnt/external-ssd
   public = yes
   guest ok = yes
   browseable = yes
   read only = no
   force user = root
   force group = root

重载配置

systemctl restart smbd

上述配置中,设置了 external-ssd 与 external-hdd 两个共享目录,并允许公开访问和读写,以 root 用户的权限操作文件。

4.2 配置客户端

4.2.1 Linux

Linux 客户端上可以使用命令行或者 systemd 挂载 NFS 存储,我们需要先安装 Samba 客户端:apt install -y smbclient

命令行

挂载路径与 NFS 有一些差异,远端存储的格式为 //服务端地址/共享目录

➜  ~ mount -t cifs -o username=none,password=none //192.168.123.2/external-ssd /tmp/tmp.yDPru6ot52
➜  ~ df -Th /tmp/tmp.yDPru6ot52
Filesystem                   Type  Size  Used Avail Use% Mounted on
//192.168.123.2/external-ssd cifs  224G   14G  211G   6% /tmp/tmp.yDPru6ot52
➜  ~ umount /tmp/tmp.yDPru6ot52

systemd

这里修改 What 为远端 Samba 存储,type 为 cifs,Options 中可以配置挂载参数,并设置了用户名密码为 none。

➜  ~ cat /lib/systemd/system/mnt-external\\x2dssd.mount
[Unit]
Description=Mount Samba share
After=network-online.target

[Mount]
What=//192.168.123.2/external-ssd
Where=/mnt/external-ssd
Type=cifs
Options=username=none,password=none

[Install]
WantedBy=multi-user.target
➜  ~ systemctl enable --now mnt-external\\x2dssd.mount
➜  ~ df -Th /mnt/external-ssd
Filesystem                   Type  Size  Used Avail Use% Mounted on
//192.168.123.2/external-ssd cifs  224G   14G  211G   6% /mnt/external-ssd

4.2.2 macOS

启动 Samba 服务端后,macOS 和 Windows 客户端就可以自动发现局域网内网的 Samba 存储:

alt text

4.2.3 Docker

Docker 也支持创建 CIFS 类型的 Volume 用于挂载到容器内,如下:

docker volume create \
--driver local \
--opt type=cifs \
--opt device=//192.168.123.2/external-ssd \
--opt o=addr=192.168.123.2,username=none,password=none,file_mode=0777,dir_mode=0777 \
--name external-ssd

创建完成后可以检查 volume 参数:

➜  ~ docker volume inspect external-ssd
[
    {
        "CreatedAt": "2024-09-18T11:37:40+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/external-ssd/_data",
        "Name": "external-ssd",
        "Options": {
            "device": "//192.168.123.2/external-ssd",
            "o": "addr=192.168.123.2,username=none,password=none,file_mode=0777,dir_mode=0777",
            "type": "cifs"
        },
        "Scope": "local"
    }
]

使用 alpine:3.18 容器镜像测试挂载,如下:

➜  ~ docker run --rm -v external-ssd:/mnt/external-ssd alpine:3.18 sh -c 'mount | grep cifs'
//192.168.123.2/external-ssd on /mnt/external-ssd type cifs (rw,relatime,vers=3.1.1,cache=strict,username=none,uid=0,noforceuid,gid=0,noforcegid,addr=192.168.123.2,file_mode=0777,dir_mode=0777,soft,nounix,serverino,mapposix,rsize=4194304,wsize=4194304,bsize=1048576,retrans=1,echo_interval=60,actimeo=1,closetimeo=1)

4.3 用户认证

若需要用户认证,最简单的办法是添加一个 Linux 用户,然后为共享目录绑定用户,下面创建一个 debian 用户,密码为 debian,用于访问 /debian 共享目录,如下:

# 创建用户
➜  ~ useradd debian
# 为用户配置samba密码
➜  ~ smbpasswd -a debian
New SMB password:
Retype new SMB password:
Added user debian.
# 创建目录并配置权限为debian用户
➜  ~ mkdir /debian
➜  ~ chown debian:debian /debian

然后编辑 /etc/samba/smb.conf,添加以下配置:

[debian]
   comment = Debian
   path = /debian
   public = no
   guest ok = no
   browseable = yes
   read only = no
   write list = debian
   valid users = debian

重启 samba 服务后,在客户端配置用户密码连接 Samba,macOS 上使用如下,在服务器连接中添加用户名:

alt text

点击连接会提示输入账号密码:

alt text

挂载成功后可以右键中可看到新建文件夹的选项,说明具备写入权限:

alt text

如果需要删除用户,操作如下:

➜  ~ smbpasswd -d debian
Disabled user debian.
➜  ~ smbpasswd -x debian
Deleted user debian.
➜  ~ userdel debian

5. WebDav与S3

WebDAV 与 S3 都是基于 HTTP 协议的,更适合应用层,而挂载到本地使用时需要 FUSE(Filesystem in Userspace)提供用户空间的文件系统支持,性能相比 NFS 和 Samba 会有比较大的折扣,这里有几个工具可以支持 WebDAV 和 S3 存储。

5.1 MinIO

参考 CNCF笔记 / 使用K3s部署容器化应用 / 2. 基础配置 / 2.3 公共服务

Minio 是一个开源的 S3 存储服务,支持多用户和高可用部署,复杂的应用场景可以考虑使用 MinIO。

5.2 Caddy

参考 探索频道 / 3. Caddy使用手册

集成 github.com/mholt/caddy-webdav 模块后,可以来为 caddy 添加 WebDAV 支持。

macOS 和 Windows 上可以直接挂载 WebDAV 存储,Linux 需要安装 davfs2 来支持挂载 WebDAV 存储,如下:

➜  ~ apt install -y davfs2
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
davfs2 is already the newest version (1.6.1-1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
➜  ~ mount -t davfs http://192.168.123.2:8080/mnt/external-ssd /tmp/tmp.WoamaAg85F
Please enter the username to authenticate with server
http://192.168.123.2:8080/mnt/external-ssd or hit enter for none.
  Username:
Please enter the password to authenticate user  with server
http://192.168.123.2:8080/mnt/external-ssd or hit enter for none.
  Password:
➜  ~ df -Th /tmp/tmp.WoamaAg85F
Filesystem                                 Type  Size  Used Avail Use% Mounted on
http://192.168.123.2:8080/mnt/external-ssd fuse  1.3T  763G  509G  61% /tmp/tmp.WoamaAg85F

5.3 Rclone

Rclone 是一个用于管理云存储上文件的命令行程序,它既可以作为客户端使用,也可以提供简单的服务端功能,Debian 12 上可以直接安装:

apt install -y rclone

官网上可以下载最新版本:Downdloads

WebDAV

服务端使用命令行启动

rclone serve webdav /mnt/external-ssd --addr 192.168.123.2:8080 --baseurl /mnt/external-ssd

客户端创建配置文件后可以使用命令行浏览远端存储:

➜  ~ cat .config/rclone/rclone.conf
[webdav]
type = webdav
url = http://192.168.123.2:8080
vendor = other

➜  ~ rclone ls webdav:/mnt/external-ssd/
420872192 libvirt/iso/Fedora-Cloud-Base-Generic.aarch64-40-1.14.qcow2
222572544 libvirt/iso/alpine-standard-3.20.2-arm64.iso
 72740864 libvirt/iso/alpine-virt-3.20.2-arm64.iso
551858176 libvirt/iso/debian-12.6.0-arm64-netinst.iso
222429184 libvirt/iso/generic_alpine-3.20.2-aarch64-uefi-cloudinit-r0.qcow2
626851840 libvirt/iso/ubuntu-22.04-arm64-tpl.img
2490609664 libvirt/iso/ubuntu-24.04-live-server-arm64-64k.iso
7048935424 libvirt/iso/uos-server-20-1070e-arm64.iso
136445952 libvirt/images/fedora.qcow2
 62914560 libvirt/images/ubuntu22.04-2024-8-2.qcow2

或者挂载到本地目录:

➜  ~ mktemp -d
/tmp/tmp.YD9zbVQK2v
➜  ~ rclone mount webdav:/mnt/external-ssd/ /tmp/tmp.YD9zbVQK2v
2024/09/18 12:55:00 NOTICE: webdav root 'mnt/external-ssd': --vfs-cache-mode writes or full is recommended for this remote as it can't stream

挂载操作会前台运行,我们需要新开一个终端浏览文件:

➜  ~ df -Th /tmp/tmp.YD9zbVQK2v/
Filesystem              Type         Size  Used Avail Use% Mounted on
webdav:mnt/external-ssd fuse.rclone  1.0P     0  1.0P   0% /tmp/tmp.YD9zbVQK2v
➜  ~ tree /tmp/tmp.YD9zbVQK2v
/tmp/tmp.YD9zbVQK2v
└── libvirt
    ├── images
    │   ├── fedora.qcow2
    │   └── ubuntu22.04-2024-8-2.qcow2
    └── iso
        ├── alpine-standard-3.20.2-arm64.iso
        ├── alpine-virt-3.20.2-arm64.iso
        ├── debian-12.6.0-arm64-netinst.iso
        ├── Fedora-Cloud-Base-Generic.aarch64-40-1.14.qcow2
        ├── generic_alpine-3.20.2-aarch64-uefi-cloudinit-r0.qcow2
        ├── ubuntu-22.04-arm64-tpl.img
        ├── ubuntu-24.04-live-server-arm64-64k.iso
        └── uos-server-20-1070e-arm64.iso

4 directories, 10 files

S3

服务端使用命令行启动:

rclone serve s3 /mnt/external-ssd --auth-key ACCESS_KEY_ID,SECRET_ACCESS_KEY --addr :8080

客户端创建配置文件后可以使用命令行浏览远端存储:

➜  ~ cat .config/rclone/rclone.conf
[s3]
type = s3
provider = Rclone
endpoint = http://192.168.123.2:8080/
access_key_id = ACCESS_KEY_ID
secret_access_key = SECRET_ACCESS_KEY
use_multipart_uploads = false
➜  ~ rclone ls s3:/
626851840 libvirt/iso/ubuntu-22.04-arm64-tpl.img
 62914560 libvirt/images/ubuntu22.04-2024-8-2.qcow2
222572544 libvirt/iso/alpine-standard-3.20.2-arm64.iso
551858176 libvirt/iso/debian-12.6.0-arm64-netinst.iso
7048935424 libvirt/iso/uos-server-20-1070e-arm64.iso
2490609664 libvirt/iso/ubuntu-24.04-live-server-arm64-64k.iso
222429184 libvirt/iso/generic_alpine-3.20.2-aarch64-uefi-cloudinit-r0.qcow2
 72740864 libvirt/iso/alpine-virt-3.20.2-arm64.iso
420872192 libvirt/iso/Fedora-Cloud-Base-Generic.aarch64-40-1.14.qcow2
136445952 libvirt/images/fedora.qcow2

也可以挂载到本地,每个子目录就是一个 bucket:

➜  ~ mktemp -d
/tmp/tmp.rSJm1joU1g
➜  ~ rclone mount s3:/libvirt /tmp/tmp.rSJm1joU1g

新开一个终端浏览文件:

➜  ~ df -Th /tmp/tmp.rSJm1joU1g
Filesystem     Type         Size  Used Avail Use% Mounted on
s3:libvirt     fuse.rclone  1.0P     0  1.0P   0% /tmp/tmp.rSJm1joU1g
➜  ~ tree /tmp/tmp.rSJm1joU1g
/tmp/tmp.rSJm1joU1g
├── images
│   ├── fedora.qcow2
│   └── ubuntu22.04-2024-8-2.qcow2
└── iso
    ├── alpine-standard-3.20.2-arm64.iso
    ├── alpine-virt-3.20.2-arm64.iso
    ├── debian-12.6.0-arm64-netinst.iso
    ├── Fedora-Cloud-Base-Generic.aarch64-40-1.14.qcow2
    ├── generic_alpine-3.20.2-aarch64-uefi-cloudinit-r0.qcow2
    ├── ubuntu-22.04-arm64-tpl.img
    ├── ubuntu-24.04-live-server-arm64-64k.iso
    └── uos-server-20-1070e-arm64.iso

3 directories, 10 files