背景

本文记录的是国庆节期间在家做的一个研究和探索的过程,之所以没有提 CloudDrive,是老苏最终想实现的是一个通用的 WebDAV 客户端,而不仅仅是挂载阿里云盘。

关于 CloudDrive 可以看老苏之前写的 『 适合国内网盘的免费挂载工具CloudDrive

提出问题

老苏的无损音乐放在了阿里云盘,阿里云盘又通过 Docker 支持了 WebDAV,现在的问题是怎么挂载到群晖,使之成为一个能被 Airsonic 等流媒体软件直接使用的音乐库?

关于阿里云盘支持 WebDAV,可以看老苏之前写的

一开始老苏选择了 远程连接

选择 WebDAV

因为没设置 WebDAV 账号、密码,所以只需要填 IP 和端口

应用之后就可以像访问群晖的本地盘一样使用了

但是老苏发现这种方式不能被 docker 识别,因为在 添加文件夹 中找不到我们刚才挂载成功的 WebDAV 目录

解决方案

老苏找到了 davfs2,这是一个 Linux 文件系统驱动程序,允许您把 WebDAV 资源挂载到您的 Linux 文件系统中,就像它们是本地磁盘一样。但是唯一的问题是需要下源代码自己编译,这对大部分人来说还是比较麻烦的。

当然也有现成的 WebDAV Client,比如 efrecon/webdav-client

老苏抱着折(学习)腾(研究)的目的,还是想自己尝试基于 davfs2 构建一个 docker 版的 WebDAV Client

构建镜像

如果你不想自己构建,可以跳过,直接阅读下一章节

老苏参考了很多的案例,形成了最终的 Dockerfile,有两个特点记录一下

  • 采用了 tini 做进程管理
  • 采用了死循环来防止脚本退出
FROM ubuntu:16.04
MAINTAINER laosu<wbsu2003@gmail.com>

RUN apt-get update \
  && apt-get install -y davfs2 \
  && mkdir -p /mnt/webdrive \
  && apt-get clean \
  && rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/*

VOLUME /mnt/webdrive

COPY ./start.sh /usr/local/bin
RUN chmod +x /usr/local/bin/start.sh

# Add Tini
ENV TINI_VERSION v0.19.0
# ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
ADD https://hub.fastgit.org/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]

# Run your program under Tini
CMD [ "/usr/local/bin/start.sh" ]

下面👇是 start.sh 文件,最后增加了一个死循环,目的是不要让这个脚本退出,否则拉起的进程也退出了

#!/bin/bash

# Set defaults
USER=${WEBDRIVE_USER}
PASSWORD=${WEBDRIVE_PASSWORD}
URL=${WEBDRIVE_URL}
FOLDER_USER=${PUID:-0}

echo "$URL $USER $PASSWORD" >> /etc/davfs2/secrets

# Create user
if [ $FOLDER_USER -gt 0 ]; then
  useradd webdrive -u $FOLDER_USER -N -G users
fi

# Mount the webdav drive 
echo "--Mount begin--"
mount -t davfs $URL /mnt/webdrive -o uid=$FOLDER_USER,gid=users,dir_mode=755,file_mode=755
echo "--Mount end--"

# Just keep this script running 
while [[ true ]]; do 
   sleep 1 
   echo "--loop--"
done

构建镜像和容器运行的基本命令如下👇

# 将 Dockerfile 和 start.sh 放在同一个目录

# 构建镜像
docker build -t wbsu2003/webdav-client:v1 .

# 生成容器
docker run -d \
--name webdav-client
--privileged \
--cap-add=SYS_ADMIN \
--device /dev/fuse \
-e WEBDRIVE_USER=<username> \
-e WEBDRIVE_PASSWORD=<password> \
-e WEBDRIVE_URL=http://url/webdav/ \
-e PUID=1000 \
-v <host/path/to/folder>:/mnt/webdrive:shared \
wbsu2003/webdav-client:v1

举个栗子,下面是直接在编译 DockerCentOS 上挂载 webdav-aliyundriver 映射的阿里云盘

# 创建共享挂载点
mount --bind /mnt /mnt
mount --make-shared /mnt

docker run -d \
--name webdav-client \
--privileged \
--device /dev/fuse \
-e WEBDRIVE_USER=admin \
-e WEBDRIVE_PASSWORD=123456 \
-e WEBDRIVE_URL=http://192.168.0.199:8123/ \
-e PUID=1000 \
-v /mnt/webdav:/mnt/webdrive:shared \
wbsu2003/webdav-client

群晖验证

安装方法和 RcloneBrowser 比较类似,如果你还不了解,可以看看老苏之前写的 『 群晖上通过RcloneBrowser挂载云盘

第一步、建目录

docker 文件夹中,创建一个新文件夹,并将其命名为 webdav

第二步、共享挂载

如果你已经安装过 RcloneBrowser ,这一步可以跳过

因为老苏映射的目录在 volume1 上,如果你的目录是其他的卷上,记得修改

# 共享挂载
mount --make-shared /volume1

不然后面 mount 的时候可能会遇到下面👇这样的错误

docker: Error response from daemon: linux mounts: path /volume1/docker/webdav is mounted on /volume1 but it is not a shared mount.

这条命令在群晖重启后需要重新执行,所以我们可以把这句命令,通过 任务计划 加到开机脚本中

这是一个触发任务,事件是开机

邮件发不发看个人需要,主要是运行脚本

第三步、启动容器

ssh 客户端中执行下面👇的命令即可

除了挂载的卷,其他跟在 CentOS 上是一模一样的

docker run -d \
--name webdav-client \
--privileged \
--device /dev/fuse \
-e WEBDRIVE_USER=admin \
-e WEBDRIVE_PASSWORD=123456 \
-e WEBDRIVE_URL=http://192.168.0.199:8123/ \
-e PUID=1000 \
-v /volume1/docker/webdav:/mnt/webdrive:shared \
wbsu2003/webdav-client

容器运行成功后,在 FileStation 中查看,已经获取到了云盘的目录

可以像本地资源一样使用,现在来试试 Airsonic

开始扫描,记得勾选 快速模式

但是播放就不行了,就算是预览个图片感觉都很慢,可能跟 WebDAV 的机制有关系,反正对老苏来说没有什么实际意义

遗留问题

  1. RcloneBrowserCloudDrive 一样,在复制的时候也会报错,看来基于容器的 fuse 映射在群晖的 FileStation 上是无解的

  1. 不支持密码和账号为空的 WebDAV 挂载,返回错误 /sbin/mount.davfs:/etc/davfs2/secrets:69: malformed line,但是因为问题 1 的缘故,老苏已经不想再继续下去

  2. 虽然没测坚果云,但老苏知道肯定不行,因为坚果云的 WebDAV 服务器不支持 Class 1,需要在 davfs2.conf 中改为 ignore_dav_header 1,老苏压根没做处理

小结

如果你需要一个通用的 WebDAV Client,建议去试试 efrecon/webdav-client,比老苏写的严谨,在容器停止的时候,做了 unmount 处理,规避了很多问题,比如导致 FileStation 不能列出文件

# 生成容器
docker run -it --rm \
    --device /dev/fuse \
    --cap-add SYS_ADMIN \
    --security-opt "apparmor=unconfined" \
    --env "WEBDRIVE_USERNAME=<YourUserName>" \
    --env "WEBDRIVE_PASSWORD=<SuperSecretPassword>" \
    --env "WEBDRIVE_URL=https://dav.box.com/dav" \
    --env "DAVFS2_ASK_AUTH=0" \
    -v /mnt/tmp:/mnt/webdrive:rshared \
    efrecon/webdav-client

不过都是基于同样的技术实现的,所以遗留 问题 1 也同样存在,速度各方面也没太大的差异

虽然不算达成了目标,但是在折腾的过程中还是学到了很多东西

参考文档

volga629/davfs2: davfs2 is a Linux tool for connecting to WebDAV shares as though they were local disks.
地址:https://github.com/volga629/davfs2

davfs2.conf: Configuration file for mount.davfs - Linux Man Pages (5)
地址:https://www.systutorials.com/docs/linux/man/5-davfs2.conf/

如何在一个Docker中同时运行多个程序进程?_dianfu2892的博客-程序员宅基地 - 程序员宅基地
地址:https://www.cxyzjd.com/article/dianfu2892/101466594

richardregeer/docker-davfs-webdisk: Use docker to mount a davfs webdisk
地址:https://github.com/richardregeer/docker-davfs-webdisk

efrecon/docker-webdav-client: WebDAV client for Docker with easy access to all davfs2 options!
地址:https://github.com/efrecon/docker-webdav-client

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐