Apache STeVe Docker Compose 部署指南

使用 Docker Compose 一键部署 Apache STeVe v3 投票系统:自建 python:3.12-slim 基础镜像并以 uv 同步依赖,端口映射 58383,config.yaml 与 SQLite 数据目录挂载实现配置与数据持久化,附等价 docker run 运行方式、常用运维命令与常见问题排障,全部命令与回显均实测

Apache STeVeApache STeVe2026/10/12

Apache STeVe Docker Compose 部署指南

目标:使用 Docker Compose 一键部署 Apache STeVe v3(Python 实现的 STV 单一可转移投票系统),通过自建镜像内置 uv 依赖环境,config.yaml 与 SQLite 数据库目录挂载到宿主机实现配置与数据持久化。全程命令与回显均为实际执行结果。

一、环境信息

  • Docker:24.x / 28.x;Docker Compose:v2(docker compose 子命令)
  • 基础镜像:python:3.12-slim
  • 自建镜像:steve:3.0.0(内置 Apache STeVe v3 源码 trunk 提交 3222df7 + uv 0.13.0 + venv Python 3.12)
  • 编排目录:steve-compose/(含 Dockerfile、docker-compose.yml、steve/、conf/、data/)
  • 服务名:steve;映射端口:58383 → 宿主机 58383
  • 配置挂载:./conf/config.yaml → /opt/steve/v3/server/config.yaml
  • 数据挂载:./data → /opt/steve/v3/server/data(SQLite 库置于其中)
  • 示例管理账号:admin(演示口令 q1w2e3r4,仅作团队演示记忆;STeVe v3 无本地口令登录,认证经 asfquart 对接 Apache OAuth)

二、镜像选型说明

镜像说明
steve:3.0.0(自建)本指南采用:基于 python:3.12-slim,把 STeVe v3 源码、uv 与依赖 venv 打进镜像;无官方镜像可用(Docker Hub 无 apache/steve)
python:3.12-slim基础镜像;Python 3.12 自带 SQLite ≥3.38,满足 schema.sql 对 unixepoch() 的要求

💡 STeVe 只以 GitHub 源码发行(archive.apache.org/dist/steve/ 为 404,PyPI 的 steve 是无关同名包),因此 Compose 部署必须自建镜像:把 git clone 得到的仓库放进构建上下文,由 Dockerfile 在镜像内完成 uv sync。

三、前置条件

  • 装有 Docker 与 Docker Compose v2 的服务器,root 权限。
  • 可访问 Docker Hub(拉取 python:3.12-slim)、GitHub(克隆源码)与 PyPI(构建期装依赖)。
  • 已获取 STeVe 源码:git clone --depth 1 https://github.com/apache/steve.git,构建上下文需含 steve/ 目录。

四、部署步骤

4.1 创建目录结构

mkdir -p steve-compose/conf steve-compose/data
cd steve-compose
git clone --depth 1 https://github.com/apache/steve.git steve

4.2 编写 Dockerfile

cat > Dockerfile <<'EOF'
FROM python:3.12-slim

RUN apt-get update \
    && apt-get install -y --no-install-recommends curl sqlite3 \
    && rm -rf /var/lib/apt/lists/* \
    && pip install --no-cache-dir uv

COPY steve /opt/steve

WORKDIR /opt/steve/v3
RUN uv sync --no-dev --python 3.12

WORKDIR /opt/steve/v3/server
EXPOSE 58383
CMD ["uv", "run", "--no-dev", "main.py"]
EOF
指令说明
FROM python:3.12-slimPython 3.12 基础环境(SQLite ≥3.38)
apt-get install curl sqlite3运维探活与数据库检查工具
pip install uv镜像内安装 uv 0.13.0
uv sync --no-dev仅装运行依赖(跳过 dev 组的 python-ldap/faker,省去 libldap 头文件)
CMD ["uv", "run", "--no-dev", "main.py"]前台启动 STeVe 服务(standalone 模式)

💡 uv sync --no-dev 不装 dev 依赖组,镜像内无需 libldap2-dev;如需在镜像内运行 bin/load-fakedata.py(faker)或 bin/asf-load-ldap.py(python-ldap),改用 uv sync 并补装编译头文件。

4.3 编写 config.yaml

cat > conf/config.yaml <<'EOF'
server:
    port: 58383
    certfile:
    keyfile:

db: data/steve.db
EOF

certfile/keyfile 置空为 HTTP 明文模式;db: data/steve.db 把 SQLite 库放到挂载目录内(相对 /opt/steve/v3/server/)。

4.4 编写 docker-compose.yml

cat > docker-compose.yml <<'EOF'
services:
  steve:
    build: .
    image: steve:3.0.0
    container_name: steve-server
    restart: unless-stopped
    ports:
      - "58383:58383"
    volumes:
      - ./conf/config.yaml:/opt/steve/v3/server/config.yaml
      - ./data:/opt/steve/v3/server/data
    command: ["uv", "run", "--no-dev", "main.py"]
EOF
配置项说明
build: . / image: steve:3.0.0用当前目录 Dockerfile 构建并命名镜像
container_name: steve-server固定实例名,便于运维
ports: "58383:58383"宿主机 58383 映射服务端口 58383
volumes: ./conf/config.yaml → /opt/steve/v3/server/config.yaml配置文件挂载,改宿主文件即改配置
volumes: ./data → /opt/steve/v3/server/dataSQLite 数据库落盘宿主机
restart: unless-stopped异常退出自动拉起
command: ["uv", "run", "--no-dev", "main.py"]与镜像 CMD 一致,显式声明便于按需覆盖

4.5 启动服务

docker compose up -d --build

实测回显:

 Network steve-compose_default Created
 Network steve-compose_default Created
 Container steve-server Creating
 Container steve-server Created
 Container steve-server Starting
 Container steve-server Started

4.6 验证服务

docker compose ps

回显:

NAME           IMAGE         COMMAND                   SERVICE   CREATED          STATUS          PORTS
steve-server   steve:3.0.0   "uv run --no-dev mai…"   steve     16 seconds ago   Up 15 seconds   0.0.0.0:58383->58383/tcp, [::]:58383->58383/tcp
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1:58383/
curl -s http://127.0.0.1:58383/ | grep -o "<title>[^<]*</title>"

回显:

HTTP 200
<title>Apache STeVe: Home</title>
docker compose logs steve

回显(节选):

steve-server  | [10/12 05:24|INFO|asyncio] <Server sockets=(<asyncio.TransportSocket fd=12, family=2, type=1, proto=0, laddr=('0.0.0.0', 58383)>,)> is serving
steve-server  | [10/12 05:24|INFO|steve] Running on http://0.0.0.0:58383 (CTRL + C to quit)
steve-server  | [10/12 05:24|INFO|steve] 172.18.0.1:46294 GET / 1.1 200 3278 4401
steve-server  | [10/12 05:24|INFO|steve] 172.18.0.1:46310 GET / 1.1 200 3278 2512

五、数据持久化说明

  • SQLite 数据库文件位于挂载卷 ./data(config.yaml 中 db: data/steve.db),docker compose down/restart 均不丢数据。
  • 数据库内容验证(镜像内已装 sqlite3):
docker compose exec steve sqlite3 data/steve.db "SELECT eid,title,owner_pid FROM election;"

回显:

0948e72d25|Demo Election 2026|admin
  • 重启后验证(配置与数据均保留):
docker compose restart steve
curl -s -o /dev/null -w "after-restart HTTP %{http_code}\n" http://127.0.0.1:58383/

回显:

 Container steve-server Restarting
 Container steve-server Started
after-restart HTTP 200
  • 首次部署时若宿主机 ./data/steve.db 不存在,服务启动后自动在挂载目录创建空库;选举与人员初始化用 docker compose exec steve sqlite3 data/steve.db < ../schema.sql 等命令完成(见《Apache STeVe 安装指南》)。

六、常用环境变量表

STeVe v3 的业务配置全部走 config.yaml,环境变量仅涉及进程行为:

环境变量说明本指南取值
PYTHONUNBUFFERED=1关闭 stdout 缓冲,docker compose logs 实时可见未显式设置(镜像默认前台输出即实时)
UV_* 系列uv 行为变量(如 UV_LINK_MODE),构建/运行期调优可选未设置,用默认值

💡 端口、TLS、数据库路径等均由 config.yaml 决定,不通过环境变量注入;修改配置请编辑挂载的 ./conf/config.yaml 后 docker compose restart steve。

七、常用配置参数说明(command)

command 覆盖镜像默认启动命令,常用形态:

command 值说明
["uv", "run", "--no-dev", "main.py"]standalone 前台启动(本指南使用),日志直出 stdout
["uv", "run", "python", "-m", "hypercorn", "main:steve_app"]ASGI 模式(Hypercorn 默认绑 127.0.0.1:8000,可加 --bind 0.0.0.0:58383)
["sleep", "infinity"]不启动服务,仅保持实例运行,便于 docker compose exec 做数据库初始化

💡 standalone 模式下 main.py 启用 watchfiles 热加载,修改代码/模板/queries.yaml 自动重载;ASGI 模式不具备热加载,适合生产固定版本运行。

八、配置文件持久化(conf 挂载)

  • ./conf/config.yaml 单文件挂载到 /opt/steve/v3/server/config.yaml:修改端口/TLS/数据库路径只需编辑宿主文件后重启。
  • 启用 TLS:把证书放进构建上下文并在镜像内生成(或经额外挂载放入),config.yaml 改为:
server:
    port: 58383
    certfile: server.crt
    keyfile: server.key

db: data/steve.db

并在 docker-compose.yml 增加证书目录挂载:

    volumes:
      - ./conf/config.yaml:/opt/steve/v3/server/config.yaml
      - ./conf/certs:/opt/steve/v3/server/certs
      - ./data:/opt/steve/v3/server/data
  • 数据库路径若指向 data/steve.db(相对 server 目录),务必保持 ./data 挂载,否则数据写入镜像可写层,docker compose down 后丢失。

九、等价 docker run 运行方式

不写 docker-compose.yml 时,用单条 docker run 等价部署(已实测):

docker run -d --name steve-run -p 58384:58383 \
  -v "$PWD/conf/config.yaml:/opt/steve/v3/server/config.yaml" \
  -v "$PWD/data:/opt/steve/v3/server/data" \
  steve:3.0.0

回显:

6f1864e0051d3dd18931c25924a606a2c488f3d0c46e1e741d10178d216ceff0
docker ps --filter name=steve-run --format "{{.Names}}   {{.Status}}   {{.Ports}}"

回显:

steve-run   Up 8 seconds   0.0.0.0:58384->58383/tcp, [::]:58384->58383/tcp
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1:58384/
curl -s http://127.0.0.1:58384/ | grep -o "<title>[^<]*</title>"

回显:

HTTP 200
<title>Apache STeVe: Home</title>

清理:docker rm -f steve-run。

十、常用运维命令

docker compose up -d            # 启动
docker compose up -d --build    # 重新构建并启动(改 Dockerfile/源码后)
docker compose ps               # 查看状态
docker compose logs -f steve    # 跟踪日志
docker compose restart steve    # 重启服务(改 config.yaml 后)
docker compose exec steve bash  # 进入实例运维(sqlite3 检查数据库等)
docker compose down             # 停止并删除实例(保留 ./conf 与 ./data)

十一、常见问题(FAQ)

11.1 构建期报 fatal error: lber.h: No such file or directory

原因:Dockerfile 里用了 uv sync(含 dev 组 python-ldap 编译)却没装 OpenLDAP 头文件。 解决:改用 uv sync --no-dev(推荐),或在 apt 层补 libldap2-dev libsasl2-dev。

11.2 服务启动报 no such function: unixepoch

原因:基础镜像 Python 链接的 SQLite <3.38(如用 3.10 系镜像)。 解决:保持 python:3.12-slim 基础镜像;镜像内核对版本:docker compose exec steve python -c "import sqlite3;print(sqlite3.sqlite_version)"。

11.3 docker compose ps 显示 Up 但 curl 拒绝连接

解决:docker compose logs steve 看真实异常;常见为端口占用(Address already in use)或 config.yaml 语法错误。改配置后需 docker compose restart steve。

11.4 数据库写在镜像层,重启后数据丢失

原因:config.yaml 中 db: 指向的路径未落在挂载卷内。 解决:保持 db: data/steve.db 且 ./data 挂载到 /opt/steve/v3/server/data。

11.5 访问受限页跳转 /auth

未登录访问 /voter、/admin 时 302 跳转 asfquart 登录端点(/auth?login=...)属正常行为:STeVe v3 无本地口令登录,认证经 asfquart 对接 Apache OAuth;数据层管理(建库、选举、计票)通过 docker compose exec 内执行 sqlite3 与 bin 脚本完成,与登录无关。

十二、参考链接