WsgiDAV Docker 与 WSGI 服务器部署指南

用 Docker 与主流 WSGI 服务器(gunicorn/uvicorn)编排部署 WsgiDAV 4.3.5 WebDAV 服务器,覆盖镜像选择、WSGI 应用工厂、docker-compose 编排、数据卷持久化与验证。

WsgiDAVWsgiDAV2026/10/06

WsgiDAV Docker 与 WSGI 服务器部署指南

目标:用 Docker 容器化部署 WsgiDAV 4.3.5 WebDAV 服务器,并演示如何把 WsgiDAV 作为 WSGI 应用挂载到 gunicorn(同步/多进程)与 uvicorn(ASGI)之上,实现端口映射、数据卷持久化与多用户认证。

WsgiDAV 本质是一个 WSGI 应用(wsgidav.wsgidav_app.WsgiDAVApp),既可作为独立服务器运行(默认 cheroot),也完全可以交给 gunicorn、uvicorn 等生产级 WSGI/ASGI 服务器托管。Docker 场景下,常见两种方式:

  1. 直接在容器内跑 wsgidav -c(cheroot 兜底);
  2. 用 gunicorn / uvicorn 加载 wsgidav_app:WsgiDAVApp(或自建 application 工厂),便于多 worker、优雅重启与负载。

本文以 Ubuntu 22.04 + Python 3.10 镜像演示。

1. 镜像选型

WsgiDAV 官方没有单一官方 Docker 镜像仓库,最稳妥的方式是 python:3.10-slim(或 python:3.11-slim)系统镜像上 pip install wsgidav。本文使用 python:3.10-slim。

docker pull python:3.10-slim

也可用 python:3.11、python:3.10-alpine(注意 alpine 需额外编译依赖,生产建议 -slim)。

2. 目录规划

宿主机编排目录 /opt/data/wsgidav/:

mkdir -p /opt/data/wsgidav/data
cd /opt/data/wsgidav
  • ./data:WebDAV 共享根,容器内挂载 /data
  • ./config.yml、./Dockerfile、./docker-compose.yml:编排文件

3. WSGI 应用工厂(方式一:gunicorn 读取 application)

编写应用工厂 /opt/data/wsgidav/app.py,供 gunicorn 加载(wsgidav.wsgidav_app.WsgiDAVApp 实例即 WSGI 可调用对象):

# /opt/data/wsgidav/app.py
from wsgidav.wsgidav_app import WsgiDAVApp

_config = {
    "host": "0.0.0.0",
    "port": 8080,
    "provider_mapping": {"/": {"root": "/data"}},
    "simple_dc": {
        "user_mapping": {
            "/": {"admin": {"password": "admin123", "roles": ["admin"]}}
        }
    },
}

application = WsgiDAVApp(_config)

结构要点:application 是模块级变量,gunicorn 用 app:application 引用。provider_mapping 的 / 挂到容器内 /data(与 compose 的 volume 挂载点一致)。simple_dc.user_mapping 键为 realm /。

4. Dockerfile

# /opt/data/wsgidav/Dockerfile
FROM python:3.10-slim

RUN pip install --no-cache-dir wsgidav cheroot gunicorn lxml

WORKDIR /opt/app
COPY app.py /opt/app/app.py

EXPOSE 8080
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "2", "app:application"]

--workers 2 起两个 worker;若用方式二(wsgidav 直接跑)则不需要 gunicorn/lxml,CMD ["wsgidav","-c","/etc/wsgidav.yaml"]。

构建:

docker build -t wsgidav:4.3.5 .

5. docker-compose.yml(gunicorn)

# /opt/data/wsgidav/docker-compose.yml
services:
  wsgidav:
    build: .
    image: wsgidav:4.3.5
    container_name: wsgidav
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - ./data:/data
    command: ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "2", "app:application"]
  • ports: "8080:8080":宿主 8080 映射容器 8080
  • volumes: ./data:/data:共享根持久化到宿主 ./data
  • command:覆盖 Dockerfile 默认 CMD 的 worker 数量等

启动:

docker compose up -d
docker compose ps
# NAME      IMAGE          STATUS       PORTS
# wsgidav   wsgidav:4.3.5  Up 5 sec     0.0.0.0:8080->8080/tcp

查看日志:

docker compose logs -f wsgidav

验证:

# 未认证 → 401
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/
# 401

# OPTIONS → DAV 头
curl -s -u admin:admin123 -X OPTIONS -i http://127.0.0.1:8080/
# HTTP/1.1 200 OK
# DAV: 1,2
# Allow: OPTIONS, HEAD, GET, PROPFIND, DELETE, COPY, MOVE, PROPPATCH, LOCK, UNLOCK

# PROPFIND 列目录(先在 data 放个文件)
echo test > data/hello.txt
curl -s -u admin:admin123 -X PROPFIND -H "Depth: 1" http://127.0.0.1:8080/ | grep -o "<D:href>[^<]*</D:href>"
# <D:href>/</D:href>
# <D:href>/hello.txt</D:href>

# PUT / GET / DELETE
curl -s -u admin:admin123 -o /dev/null -w "PUT -> %{http_code}\n" -T /etc/hostname http://127.0.0.1:8080/u.txt
# PUT -> 201
curl -s -u admin:admin123 http://127.0.0.1:8080/u.txt
# <你的主机名>
curl -s -u admin:admin123 -o /dev/null -w "DELETE -> %{http_code}\n" -X DELETE http://127.0.0.1:8080/u.txt
# DELETE -> 204

6. 方式二:直接跑 wsgidav(无 app.py)

不写 app.py,用 wsgidav 自带的命令行 + 配置文件 + cheroot,Docker 内即可:

# docker-compose.yml(方式二)
services:
  wsgidav:
    image: python:3.10-slim
    container_name: wsgidav
    restart: unless-stopped
    command: sh -c "pip install --no-cache-dir wsgidav cheroot lxml && wsgidav -c /etc/wsgidav.yaml"
    ports:
      - "8080:8080"
    volumes:
      - ./wsgidav.yaml:/etc/wsgidav.yaml:ro
      - ./data:/data

wsgidav.yaml 指向容器内 /data:

host: 0.0.0.0
port: 8080
provider_mapping:
  "/":
    root: /data
simple_dc:
  user_mapping:
    "/":
      admin:
        password: admin123
        roles: ["admin"]

方式二每次启动都会 pip install(把镜像当一次性的话需先 commit 或写 Dockerfile)。生产建议把安装固化进 Dockerfile(见第 4 节)。

7. 方式三:uvicorn 托管(ASGI)

WsgiDAV 4.3.5 也提供 ASGI 网关接口。安装 uvicorn 后,用相关网关适配器:

pip install uvicorn

将 app.py 顶层替换为 ASGI 适配(示例,需安装对应 worker/adapter):

from wsgidav.fs_dav_provider import FilesystemProvider
from wsgidav.wsgidav_app import WsgiDAVApp

_config = {
    "provider_mapping": {"/": FilesystemProvider("/data")},
    "simple_dc": {
        "user_mapping": {"/": {"admin": {"password": "admin123", "roles": ["admin"]}}}
    },
    "http_authenticator": {"domain_controller": None},
}
app_wsgi = WsgiDAVApp(_config)
# 通过 uvicorn 的 WSGIMiddleware 桥接
from uvicorn.middleware.wsgi import WSGIMiddleware
application = WSGIMiddleware(app_wsgi)

启动:

uvicorn --host 0.0.0.0 --port 8080 --app-dir /opt "app:application"

ASGI/WSGI 桥接会引入中间件开销;若仅需同步 WebDAV,gunicorn(第 5 节)更直接、性能更好。uvicorn 适合已采用 ASGI 技术栈、希望与其他 ASGI 服务共存的场景。

8. 数据持久化与重启验证

# 写入容器共享根
echo hi > data/persist.txt
docker compose down
docker compose up -d
# 数据仍在
cat data/persist.txt
# hi

./data:/data 保证容器销毁/重建不丢数据。

9. 日常运维

docker compose ps
docker compose logs -f wsgidav
docker compose restart wsgidav   # 改配置后
docker compose stop wsgidav
docker compose down              # 停止并移除容器(数据保留)
docker exec wsgidav ls -la /data # 进容器查共享目录

10. 常见问题

Q1:gunicorn 报 Worker failed to boot? 多半是 app.py 里有语法/导入错误,或 --chdir/--app-dir 指向不对。docker compose logs 看 Traceback。

Q2:simple_dc.user_mapping 键? 必须等于 provider realm(容器内挂 /data 时 realm 仍是 /,键写 "/")。

Q3:容器内端口连不上? 确认 ports 映射、容器内 host: 0.0.0.0 且 port 一致;docker compose ps 看 PORTS。

Q4:想固定 Python 版本? 用 python:3.11-slim 或在 Dockerfile FROM python:3.11-slim。

Q5:多 worker 下锁/属性状态? gunicorn 多进程时内存态 LockStorageDict/属性不去共享,多 worker 场景建议使用数据库类 lock/property store 或单 worker,详见配置篇。

11. 参考链接

  • WsgiDAV 仓库:https://github.com/mar10/wsgidav
  • Gunicorn 文档:https://docs.gunicorn.org
  • Uvicorn 文档:https://www.uvicorn.org
  • WsgiDAV 配置:https://wsgidav.readthedocs.io/en/latest/run-configure.html