Apache STeVe Docker Compose 部署指南
使用 Docker Compose 一键部署 Apache STeVe v3 投票系统:自建 python:3.12-slim 基础镜像并以 uv 同步依赖,端口映射 58383,config.yaml 与 SQLite 数据目录挂载实现配置与数据持久化,附等价 docker run 运行方式、常用运维命令与常见问题排障,全部命令与回显均实测
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-slim | Python 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/data | SQLite 数据库落盘宿主机 |
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 脚本完成,与登录无关。
十二、参考链接
- Apache STeVe 官网:https://steve.apache.org/
- 源码仓库:https://github.com/apache/steve
- uv 官方文档:https://docs.astral.sh/uv/
- Docker Compose 文档:https://docs.docker.com/compose/
- 专栏其他文档:《Apache STeVe 安装指南》《Apache STeVe 配置参数详解》《Apache STeVe 常用 CLI 命令与工具详解》