S3 存储上的用户数据
S3 存储上的用户数据(Userdata on S3 Storage)
目录
Wavelog 把用户文件(QSL 卡、eQSL 卡、QSL 明信片图片)存放在 userdata 文件夹中,参见集中式用户数据。Wavelog 没有原生 S3 支持,但你可以把一个 S3 存储桶挂载为该文件夹上的文件系统。Wavelog 察觉不到其中的差别,也无需改动 config.php。
此方案适用于任何兼容 S3 的存储,例如 AWS S3、RustFS、Ceph RGW、Garage 或 Hetzner Object Storage。
信息(哪些文件夹适合放在 S3)
userdata和backup很合适:文件写入一次,很少读取。按下面为userdata所示的方式挂载backup,使用它自己的存储桶或前缀即可。其他可写文件夹(
application/config、application/logs、application/cache、uploads、updates)技术上也能在 S3 上工作,但不推荐。它们会在每次请求时被读取,或者经常被拆成小块写入,既慢又会产生大量 S3 请求。请把它们留在普通的数据卷上。
Linux 服务器
使用 rclone 挂载存储桶,并由 systemd 单元启动。示例假设系统为 Debian/Ubuntu,且 Wavelog 位于 /var/www/html,参见 Linux 安装。
1. 安装 rclone
sudo apt install rclone fuse3 ca-certificates
2. 配置 S3 远程
创建 /etc/rclone/rclone.conf,并用 sudo chmod 600 /etc/rclone/rclone.conf 使其仅对 root 可读:
[s3]
type = s3
provider = Other
endpoint = https://s3.example.com
access_key_id = YOUR_ACCESS_KEY
secret_access_key = YOUR_SECRET_KEY
把 provider 设置为与你的存储相匹配(AWS、Ceph、Other 等)。全部选项见 rclone S3 文档。
3. 创建 systemd 单元
创建 /etc/systemd/system/wavelog-userdata.service:
[Unit]
Description=Wavelog userdata on S3
Wants=network-online.target
After=network-online.target
[Service]
Type=notify
ExecStart=/usr/bin/rclone mount s3:wavelog-userdata /var/www/html/userdata \
--config /etc/rclone/rclone.conf \
--allow-other --uid 33 --gid 33 \
--vfs-cache-mode writes
Restart=on-failure
[Install]
WantedBy=multi-user.target
在 Debian/Ubuntu 上,uid/gid 33 就是 www-data。其他发行版请用 id <user> 检查 Web 服务器用户。
4. 启动
sudo systemctl daemon-reload
sudo systemctl enable --now wavelog-userdata.service
findmnt /var/www/html/userdata
如果 userdata 中已经有文件,请在启动该单元之前运行 sudo mv /var/www/html/userdata /var/www/html/userdata.old && sudo mkdir /var/www/html/userdata,然后按迁移现有数据中所述把它们复制回来。
Docker
使用 rclone Docker 数据卷插件。它把存储桶挂载为 Docker 数据卷,因此容器配置保持不变。该插件只适用于 Linux 上的 Docker Engine,不适用于 Docker Desktop。
1. 安装插件
sudo mkdir -p /var/lib/docker-plugins/rclone/config /var/lib/docker-plugins/rclone/cache
docker plugin install rclone/docker-volume-rclone:amd64 args="-v" --alias rclone --grant-all-permissions
在 ARM 主机上请使用 :arm64 而不是 :amd64。
2. 配置 S3 远程
创建 /var/lib/docker-plugins/rclone/config/rclone.conf。这样凭据就不会出现在你的 compose 文件中。
[s3]
type = s3
# RustFS has no own provider in rclone, use Other
provider = Other
endpoint = https://s3.example.com
access_key_id = YOUR_ACCESS_KEY
secret_access_key = YOUR_SECRET_KEY
把 provider 设置为与你的存储相匹配(AWS、Ceph、Other 等)。全部选项见 rclone S3 文档。
3. 在 docker-compose.yml 中使用该数据卷
替换你的 docker-compose.yml 中 volumes: 部分的 wavelog-userdata 条目:
volumes:
wavelog-userdata:
driver: rclone
driver_opts:
remote: 's3:wavelog-userdata' # <- remote name from rclone.conf : bucket name
allow_other: 'true'
uid: '33'
gid: '33'
vfs_cache_mode: writes
在 Wavelog 容器内,uid/gid 33 就是 www-data。然后用 docker compose up -d 重建整个栈。
Kubernetes(实验性)
危险:实验性
在 Kubernetes 上使用 S3 是可行的,但有一个在生产环境中很要紧的缺点:如果 CSI 驱动 Pod 重启,Wavelog 就会失去对文件的访问,直到 Wavelog 的 Pod 也重启为止。见下文的驱动重启。生产环境中,普通的
ReadWriteMany数据卷(CephFS、NFS 等)是更稳妥的选择。
使用瑞士数据科学中心(Swiss Data Science Center)提供的 CSI 驱动 csi-rclone 来替代块存储 PVC。它和上面的方案一样使用 rclone。额外的好处是数据卷为 ReadWriteMany,因此多个 Wavelog 副本可以共享同一份 userdata。
1. 安装驱动
helm repo add renku https://swissdatasciencecenter.github.io/helm-charts
helm install csi-rclone renku/csi-rclone --namespace csi-rclone --create-namespace
这会创建 StorageClass csi-rclone。全部选项见 chart 参数值。
驱动 Pod 以特权模式运行(FUSE)。如果你的集群强制执行 Pod Security Standards,请为命名空间打上 pod-security.kubernetes.io/enforce=privileged 标签。如果 NetworkPolicies 限制了出站流量,驱动 Pod 需要能访问 Kubernetes API 和你的 S3 端点。
2. 创建 Secret
驱动会从同一命名空间中与 PVC 同名的 Secret 读取 rclone 配置:
apiVersion: v1
kind: Secret
metadata:
name: wavelog-userdata # must match the PVC name
namespace: wavelog
type: Opaque
stringData:
remote: s3
remotePath: wavelog-userdata # bucket name, optionally with a prefix: bucket/prefix
configData: |
[s3]
type = s3
provider = Other
endpoint = https://s3.example.com
access_key_id = YOUR_ACCESS_KEY
secret_access_key = YOUR_SECRET_KEY
vfsOpt: '{"UID": 33, "GID": 33, "DirCacheTime": "5s", "CacheMode": "writes"}'
remotePath按原样挂载。如果你删除并重建 PVC,它会重新找到自己的数据。驱动从不会删除存储桶中的数据。vfsOpt以 JSON 形式接收 rclone VFS 选项。UID/GID33 是 Wavelog 镜像内的www-data。DirCacheTime是 rclone 缓存目录列表的时长(默认 1 分钟)。在有多个 Wavelog 副本时,刚上传的 QSL 卡在这段时间内对其他副本不可见,并会返回 404。几秒钟是在延迟与 S3 请求数量之间不错的折中。- 这些选项在挂载数据卷时读取。要修改它们,请编辑 Secret 并重启 Wavelog 的 Pod。
3. 创建并挂载 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wavelog-userdata
namespace: wavelog
spec:
accessModes:
- ReadWriteMany
storageClassName: csi-rclone
resources:
requests:
storage: 10Gi # ignored by S3, but required by Kubernetes
在你的 Wavelog Deployment 中:
spec:
template:
spec:
containers:
- name: wavelog
volumeMounts:
- name: userdata
mountPath: /var/www/html/userdata
volumes:
- name: userdata
persistentVolumeClaim:
claimName: wavelog-userdata
警告:多个副本
即使
DirCacheTime很短,新文件也需要几秒钟才会出现在其他副本上,因为 rclone 是在文件写入之后稍晚才上传它。Ingress 控制器上的会话保持(sticky sessions)会把每个用户送到同一个副本,因此上传者能立刻看到该文件。注意(Mountpoint for Amazon S3)
不推荐使用 AWS 的 CSI 驱动「Mountpoint for Amazon S3」。默认情况下它无法覆盖或删除文件,而这正是 Wavelog 需要的功能。
驱动重启
rclone 运行在驱动的节点 Pod(csi-rclone-nodeplugin)内。如果该 Pod 重启,该节点上的所有挂载都会消失。该节点上的 Wavelog Pod 仍在运行,但 userdata 会以 Transport endpoint is not connected 报错:QSL 图片返回错误,上传也会失败。Kubernetes 不会重新挂载正在运行的 Pod 的数据卷,只重启容器同样无济于事。整个 Pod 必须重建。这是基于 FUSE 的 CSI 驱动的普遍限制。
出现这种情况的时机:
- 驱动升级: DaemonSet 会立即替换所有驱动 Pod,运行着 Wavelog Pod 的节点也不例外。这是最主要的原因。
- 驱动崩溃: 比较少见,例如 rclone 守护进程未通过存活探针时。
- 节点重启或排空: 影响不大,Wavelog Pod 本来就会被重启或迁移。
驱动重启后,请重建 Wavelog 的 Pod:
kubectl -n wavelog rollout restart deployment/wavelog
为避免升级时意外重启,可把驱动 DaemonSet 的更新策略设为 OnDelete。这样新的驱动版本只会在其 Pod 被删除时才推出,例如在下一次节点排空时——那时 Wavelog 的 Pod 本来就要迁移。chart 中没有对应的参数值,请使用 Helm post-renderer 或直接给 DaemonSet 打补丁:
kubectl -n csi-rclone patch daemonset csi-rclone-nodeplugin -p '{"spec":{"updateStrategy":{"type":"OnDelete"}}}'
helm upgrade 会重置手动打的补丁,因此 post-renderer 才是永久的解决方案。
注意事项
警告
- 延迟: 每次文件访问都要经过网络。对 QSL 图片来说这没问题,请保持缓存选项开启。
- 权限: Web 服务器以
www-data(uid/gid 33)运行。若uid/gid不正确,Wavelog 无法写入该文件夹。- PUID/PGID: 如果你在 Wavelog 容器上设置了
PUID/PGID,请在挂载时使用相同的uid/gid值。否则容器会在启动时尝试对挂载的文件夹执行chown,这在 S3 上会失败,容器也就无法启动。- 保持存储桶私有: 文件由 Wavelog 自己提供。存储桶不需要任何公开访问权限。
迁移现有数据
- 启用维护模式。
- 备份当前的
userdata文件夹。 - 用新的 S3 数据卷启动 Wavelog,并把旧数据复制进去:
- Linux 服务器:
sudo cp -r /var/www/html/userdata.old/. /var/www/html/userdata/ - Docker:
docker cp ./userdata/. wavelog-main:/var/www/html/userdata/ - Kubernetes:
kubectl cp ./userdata/. <wavelog-pod>:/var/www/html/userdata/
- Linux 服务器:
- 进入管理 > 调试,确认
userdata显示为可写。 - 在 Web 界面中打开几张 QSL 卡和 eQSL 卡,上传一张测试 QSL 卡,然后再删除它。
- 关闭维护模式。