Apache STeVe 安装指南
在 Ubuntu 22.04 真实服务器上从零安装 Apache STeVe v3 投票系统,涵盖系统依赖、uv 双引导安装、虚拟环境同步、SQLite 建库、管理员账号写入、选举创建、投票与计票验证、systemd 托管及常见故障排障,全部命令与回显均实测
Apache STeVe 安装指南
目标:在真实 Ubuntu 22.04 服务器上从零安装 Apache STeVe v3(Python 实现的 STV 单一可转移投票系统),覆盖系统依赖、uv 环境搭建、SQLite 数据库初始化、管理员账号写入、选举创建、投票与计票验证及 systemd 托管。以下命令与回显均在 Ubuntu 22.04.5 真实环境逐步实测。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- Apache STeVe:v3(pyproject 版本 3.0.0,源码 trunk 提交 3222df7)
- Python:3.12.15(uv 独立 Python,venv 内置 SQLite 3.53.1)
- 包管理器:uv 0.13.0
- Git:2.34.1;系统 SQLite:3.37.2
- 源码目录:
/opt/steve;数据库:/opt/steve/v3/server/steve.db - 服务端口:58383(HTTP 与 HTTPS 同端口,由 config.yaml 的证书项决定)
- 关键依赖:asfquart 0.1.12、hypercorn 0.18.0、quart 0.22.0
- 示例管理账号:admin(演示口令 q1w2e3r4,仅作团队演示记忆;STeVe v3 无本地口令登录,认证经 asfquart 对接 Apache OAuth,见 8.5)
二、前置条件
- 一台 Ubuntu 22.04 服务器,root 权限。
- 系统依赖包:
python3 python3-pip python3-venv git sqlite3 net-tools libldap2-dev libsasl2-dev(后两项为依赖组 python-ldap 编译所需头文件)。 - 可访问 GitHub(拉取源码)与 PyPI(拉取依赖包)。
- STeVe v3 使用 SQLite 存储全部数据,无需额外数据库服务。
三、版本选型说明
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| Apache STeVe | v3(3.0.0,trunk) | 官方当前开发主线,以源码方式安装;GitHub 无 release tarball、无打包容 |
| Python | 3.12+(uv 独立 Python 3.12.15) | v3 要求 >=3.10,但 Ubuntu 22.04 系统 Python 3.10 链接的 SQLite 3.37 不含 unixepoch(),开选举会报错;uv 独立 Python 自带 SQLite 3.53.1,规避该问题 |
| uv | 0.13.0 | 官方 quickstart 指定的项目/环境管理器 |
| SQLite | ≥3.38(venv 内 3.53.1) | schema.sql 使用 unixepoch() 函数 |
| asfquart | 0.1.12 | STeVe 依赖的 Web 框架(Quart/ASGI) |
⚠️ STeVe 的发行渠道只有 GitHub 源码:
https://archive.apache.org/dist/steve/返回 404,PyPI 上的steve是无关同名包(richard 导入工具)。请勿从 PyPI 直接安装。
四、安装方案 A(官方 quickstart:uv 引导安装)
4.1 安装系统依赖
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y \
python3 python3-pip python3-venv curl wget git sqlite3 net-tools \
libldap2-dev libsasl2-dev
回显(末行):
Setting up sqlite3 (3.37.2-2ubuntu0.8) ...
3.37.2 2022-01-06 13:25:41 872ba256cbf61d9290b571c0e6d82a20c224ca3ad82971edc46b29818d5dalt1
⚠️
libldap2-dev、libsasl2-dev必须安装:依赖组中的 python-ldap 编译需要lber.h头文件,缺失时报fatal error: lber.h: No such file or directory(见 8.1)。
4.2 安装 uv
curl -LsSf https://astral.sh/uv/install.sh | sh
source $HOME/.local/bin/env
uv --version
回显:
source $HOME/.local/bin/env (sh, bash, zsh)
source $HOME/.local/bin/env.fish (fish)
uv 0.13.0 (x86_64-unknown-linux-gnu)
4.3 克隆仓库
git clone --depth 1 https://github.com/apache/steve.git /opt/steve
cd /opt/steve && git log --oneline -1
回显:
3222df7 Rebuild the website with Docusaurus (#110)
v3 全部代码在仓库的 v3/ 目录,后续命令均在该目录下执行。
4.4 创建虚拟环境并同步依赖
cd /opt/steve/v3
uv venv --python 3.12
uv sync --python 3.12
回显:
Using CPython 3.12.15
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate
... (依赖安装输出,末尾)
+ watchfiles==1.3.0
+ werkzeug==3.1.9
+ wsproto==1.3.2
+ yarl==1.25.1
核对 venv 内 Python 与 SQLite 版本:
/opt/steve/v3/.venv/bin/python --version
/opt/steve/v3/.venv/bin/python -c "import sqlite3;print('venv sqlite3:',sqlite3.sqlite_version)"
回显:
Python 3.12.15
venv sqlite3: 3.53.1
4.5 初始化数据库
cd /opt/steve/v3/server
sqlite3 steve.db < ../schema.sql
sqlite3 steve.db ".tables"
回显:
election issue mayvote person vote
五张表:election(选举)、issue(议题)、person(人员)、mayvote(投票资格)、vote(选票,落库即加密)。
4.6 写入人员与管理员账号
STeVe v3 的权限锚点是 person 表中的 pid(选举 owner、可投票人都在此表登记)。官方 quickstart 通过 ASF LDAP 导入(bin/asf-load-ldap.py,需 LDAP 凭据);自建环境可直接 INSERT 写入管理员 admin 与测试选民:
sqlite3 steve.db "INSERT INTO person (pid, name, email) VALUES ('admin', 'Steve Admin', 'admin@steve.apache.org'), ('alice', 'Alice', 'alice@steve.apache.org'), ('bob', 'Bob', 'bob@steve.apache.org'), ('carlos', 'Carlos', 'carlos@steve.apache.org'), ('david', 'David', 'david@steve.apache.org'), ('eve', 'Eve', 'eve@steve.apache.org');"
sqlite3 steve.db "SELECT pid, name, email FROM person;"
回显:
admin|Steve Admin|admin@steve.apache.org
alice|Alice|alice@steve.apache.org
bob|Bob|bob@steve.apache.org
carlos|Carlos|carlos@steve.apache.org
david|David|david@steve.apache.org
eve|Eve|eve@steve.apache.org
sqlite3 steve.db "SELECT count(*) FROM person;"
回显:
6
4.7 创建首个选举
编写 election.yaml(放在 /opt/steve/v3/server/ 下,字段含义见《Apache STeVe 配置参数详解》):
election:
title: "Demo Election 2026"
owner_pid: "admin"
open_at: 1760000000
close_at: 1880000000
issues:
- title: "Approve Budget"
description: "Vote yes/no/abstain on the proposed budget."
vtype: "yna"
- title: "Elect Board Members"
description: "Rank candidates for board seats using STV."
vtype: "stv"
kv:
version: 2
labelmap:
a: [alice, "Alice"]
b: [bob, "Bob"]
c: [carlos, "Carlos"]
d: [david, "David"]
seats: 2
record:
- admin
- alice
- bob
- carlos
- david
- eve
执行创建:
cd /opt/steve/v3/server
uv run bin/create-election.py election.yaml
回显:
INFO:steve.election:Created election[E:0948e72d25]
INFO:__main__:Created election[E:0948e72d25]: "Demo Election 2026" by owner "admin"
INFO:steve.election:Created issue[I:c7e4b8e7ba] in election[E:0948e72d25]
INFO:__main__:Added issue[I:c7e4b8e7ba] to election[E:0948e72d25]
INFO:steve.election:Created issue[I:c156373ef0] in election[E:0948e72d25]
INFO:__main__:Added issue[I:c156373ef0] to election[E:0948e72d25]
INFO:__main__:Added 6 voters to election[E:0948e72d25]
INFO:__main__:Election[E:0948e72d25] fully created from election.yaml
选举 ID(10 位十六进制,如 0948e72d25)由随机生成,每次创建不同。
4.8 启动服务
cd /opt/steve/v3/server
uv run main.py
回显(节选):
[10/12 05:14|INFO|asyncio] <Server sockets=(<asyncio.TransportSocket fd=12, family=2, type=1, proto=0, laddr=('0.0.0.0', 58383)>,)> is serving
[10/12 05:14|INFO|steve] Running on http://0.0.0.0:58383 (CTRL + C to quit)
首次启动前需复制配置:cp config.yaml.example config.yaml(默认带 TLS 证书项,HTTP 明文启动需置空 certfile:/keyfile:,见 7.1)。
4.9 验证服务
端口监听:
ss -tlnp | grep 58383
回显:
tcp LISTEN 0 0 0.0.0.0:58383 0.0.0.0:* users:(("python",pid=5980,fd=12))
首页回显:
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>
投票与计票功能验证(开启选举、6 人投票、关闭并计票):
uv run bin/tally.py --election-id 0948e72d25 --output text
回显(选举经 Python API 开启、10 张选票投出并关闭后):
INFO:__main__:Tallying 2 issues ...
Issue c156373ef0: Elect Board Members (stv)
Result: Alice elected
Bob elected
Carlos not elected
David not elected
Details: {'candidates': {'a': True, 'b': True, 'c': False, 'd': False}, 'version': 2, 'labelmap': {'a': ['alice', 'Alice'], 'b': ['bob', 'Bob'], 'c': ['carlos', 'Carlos'], 'd': ['david', 'David']}, 'seats': 2, 'votestrings': ['b,c,a', 'b,a,d', 'd,c,b', 'a,c,b', 'a,b,c']}
----------------------------------------
Issue c7e4b8e7ba: Approve Budget (yna)
Result: Yes: 3
No: 1
Abstain: 1
Details: {'y': 3, 'n': 1, 'a': 1}
----------------------------------------
STV 2 席位 Alice、Bob 当选,yna 议题 3 赞成/1 反对/1 弃权,与投放选票一致。
4.10 systemd 托管
创建 /etc/systemd/system/steve.service:
[Unit]
Description=Apache STeVe v3 voting server
Documentation=https://github.com/apache/steve
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/steve/v3/server
ExecStart=/opt/steve/v3/.venv/bin/python main.py
Restart=on-failure
RestartSec=5
Environment=PYTHONUNBUFFERED=1
[Install]
WantedBy=multi-user.target
启用并验证:
systemctl daemon-reload
systemctl enable --now steve.service
systemctl is-active steve.service
回显:
Created symlink /etc/systemd/system/multi-user.target.wants/steve.service → /etc/systemd/system/steve.service.
active
状态详情:
systemctl --no-pager -l status steve.service
回显:
● steve.service - Apache STeVe v3 voting server
Loaded: loaded (/etc/systemd/system/steve.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2026-10-12 05:16:50 UTC; 8s ago
Docs: https://github.com/apache/steve
Main PID: 6997 (python)
Tasks: 5 (limit: 5753)
Memory: 53.7M
CPU: 1.274s
CGroup: /system.slice/steve.service
└─6997 /opt/steve/v3/.venv/bin/python main.py
Oct 12 05:16:51 dfb8320a08ff python[6997]: [10/12 05:16|INFO|asfpy.twatcher] Watching: /opt/steve/v3/server/templates/settings.ezt
五、安装方案 B(备用:pip 引导 uv)
适用于不便执行远程安装脚本的场景,用 pip 安装 uv 后复用方案 A 的全部步骤。
5.1 安装系统依赖
与方案 A 的 4.1 完全一致(同一条 apt 命令与回显)。
5.2 pip 安装 uv
pip3 install uv
uv --version
回显(节选):
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
uv 0.13.0 (x86_64-unknown-linux-gnu)
pip3 show uv | head -3
回显:
Name: uv
Version: 0.13.0
Summary: An extremely fast Python package and project manager, written in Rust.
5.3 后续步骤
从 4.3(克隆仓库)起与方案 A 完全一致,直至 4.10(systemd 托管)。
六、多方案对比与选择建议
| 维度 | 方案 A(uv 脚本引导) | 方案 B(pip 引导 uv) |
|---|---|---|
| 版本 | uv 0.13.0 | uv 0.13.0 |
| 是否需联网 | 是(astral.sh + GitHub + PyPI) | 是(PyPI + GitHub) |
| 安装方式 | 官方安装脚本 | pip 包 |
| uv 安装位置 | /root/.local/bin/uv | /usr/local/bin/uv |
| 服务名 | steve.service | steve.service |
| 监听端口 | 58383 | 58383 |
| 优点 | 官方推荐路径,自更新方便 | 复用既有 pip 环境,无需访问 astral.sh |
| 缺点 | 需访问 astral.sh | pip 装进系统 Python 环境,升级随 pip |
💡 选择建议:全新机器走方案 A;受网络策略限制只能访问 PyPI 的环境走方案 B。两方案最终都生成同一套
/opt/steve目录与 venv,后续运维完全一致。
七、配置说明
7.1 config.yaml
配置文件位于 /opt/steve/v3/server/config.yaml,由模板拷贝:
cd /opt/steve/v3/server
cp config.yaml.example config.yaml
HTTP 明文模式(本指南 systemd 托管采用):
server:
port: 58383
certfile:
keyfile:
db: steve.db
7.2 TLS 证书配置
certfile/keyfile 填相对 server/certs/ 目录的文件名(或绝对路径)即启用 HTTPS。用 openssl 生成自签证书:
cd /opt/steve/v3/server/certs
openssl req -x509 -newkey rsa:2048 -sha256 -days 365 -nodes \
-keyout server.key -out server.crt -subj "/CN=localhost"
openssl x509 -in server.crt -noout -subject -dates
回显:
subject=CN = localhost
notBefore=Oct 12 05:17:19 2026 GMT
notAfter=Oct 12 05:17:19 2027 GMT
config.yaml 改为 certfile: server.crt、keyfile: server.key 并重启服务后:
curl -sk -o /dev/null -w "HTTPS %{http_code}\n" https://127.0.0.1:58383/
curl -sk https://127.0.0.1:58383/ | grep -o "<title>[^<]*</title>"
回显:
HTTPS 200
<title>Apache STeVe: Home</title>
💡 官方 certs/README 推荐 mkcert 生成本地信任证书;openssl 等任何标准工具链同样可用。
7.3 ASGI 运行形态
main.py 内置 Hypercorn ASGI 入口,生产可改为独立 ASGI 服务器托管:
cd /opt/steve/v3/server
uv run python -m hypercorn main:steve_app
回显(节选):
[10/12 05:19|INFO|main] ** Run-mode: ASGI
[2026-10-12 05:20:20 +0000] [7197] [INFO] Running on http://127.0.0.1:8000 (CTRL + C to quit)
[10/12 05:20|INFO|hypercorn.error] Running on http://127.0.0.1:8000 (CTRL + C to quit)
Hypercorn 默认绑定 127.0.0.1:8000,改绑端口用 --bind 0.0.0.0:58383。gunicorn 为 WSGI 服务器,不适用于 STeVe(asfquart 基于 Quart/asyncio,必须 ASGI)。
八、常见问题(FAQ)
8.1 uv sync 报 fatal error: lber.h: No such file or directory
错误回显(节选):
In file included from Modules/LDAPObject.c:8:
Modules/constants.h:7:10: fatal error: lber.h: No such file or directory
7 | #include "lber.h"
| ^~~~~~~~
compilation terminated.
hint: `python-ldap` (v3.0.0) was included because `apache-steve:dev` (v3.0.0) depends on `python-ldap`
定位:dev 依赖组的 python-ldap 编译需要 OpenLDAP 头文件。
解决:apt-get install -y libldap2-dev libsasl2-dev 后重新 uv sync。
8.2 开选举报 no such function: unixepoch
错误回显:
sqlite3.OperationalError: no such function: unixepoch
定位:schema.sql 的触发器使用 unixepoch(),要求 SQLite ≥3.38;Ubuntu 22.04 系统 Python 3.10 链接的 libsqlite3 为 3.37.2。
解决:用 uv 独立 Python 3.12 重建 venv(自带 SQLite 3.53.1):
cd /opt/steve/v3
rm -rf .venv
uv venv --python 3.12
uv sync --python 3.12
8.3 git clone 报 GnuTLS recv error (-54) 或 Connection refused
定位:GitHub 连接偶发抖动。
解决:重试 git clone 即可;多次失败时检查到 github.com:443 的网络策略。
8.4 端口占用
错误回显(Hypercorn 形态):
OSError: [Errno 98] Address already in use
解决:ss -tlnp | grep 58383 找到占用进程;同一台机器上避免 systemd 服务与手工进程重复启动(本指南实测中 systemd 的 steve.service 存活时再以同端口起 Hypercorn 即复现此错)。
8.5 访问受限页跳转 /auth
未登录访问 /voter、/admin 等页面时,302 跳转至 asfquart 的登录端点:
# curl -s http://127.0.0.1:58383/voter | head -c 300
<!doctype html>
<html lang=en>
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to the target URL: <a href="/auth?login=/voter?">/auth?login=/voter?</a>. If not, click the link.
说明:STeVe v3 无本地口令登录,认证经 asfquart 对接 Apache OAuth(部署到 ASF 基础设施后自动生效);自建环境通过 person 表 pid 与 Python API 完成数据层管理(选举创建、开闭、计票),Web 端登录仅在使用 ASF OAuth 时可用。本文示例管理账号 admin(演示口令 q1w2e3r4)即数据层 owner_pid 的取值。
九、卸载方式
systemctl disable --now steve.service # 停止并取消自启
rm -f /etc/systemd/system/steve.service # 删除服务单元
systemctl daemon-reload
rm -rf /opt/steve # 删除源码、venv 与数据库
apt-get remove -y libldap2-dev libsasl2-dev # 可选:清理编译头文件
十、参考链接
- Apache STeVe 官网:https://steve.apache.org/
- 源码仓库与 quickstart:https://github.com/apache/steve(`v3/docs/quickstart.md`)
- 数据库 schema 说明:https://github.com/apache/steve/blob/trunk/v3/docs/schema.md
- uv 安装文档:https://docs.astral.sh/uv/getting-started/installation/
- 专栏其他文档:《Apache STeVe 配置参数详解》《Apache STeVe 常用 CLI 命令与工具详解》