Ubuntu 22.04 安装 Libravatar 2.0 指南

在真实 Ubuntu 22.04 LTS 服务器从零安装 ivatar 2.0 的实测指南:在线源码与离线 wheelhouse 双路径,交付 gunicorn + systemd + nginx 生产形态,命令与回显逐行照抄。

LibravatarLibravatar头像服务2026/10/28

Ubuntu 22.04 安装 Libravatar 2.0 指南

目标:在一台真实的 Ubuntu 22.04 LTS 服务器上,从零安装并跑通 Libravatar 服务端 ivatar 2.0(libravatar.org 官网同款实现),覆盖在线源码安装(方案 A)与离线 wheelhouse 安装(方案 B)两条实测路径,交付 gunicorn + systemd + nginx 的生产形态。全部命令与回显均来自真实部署过程,逐行照抄。

一、环境信息

项目取值
操作系统Ubuntu 22.04.5 LTS(x86_64,最小化安装)
服务端实现ivatar 2.0(pyproject.toml 包名 libravatar,源码 commit 6f4b7e0,2026-09-25)
部署路径/opt/ivatar(虚拟环境 /opt/ivatar/.virtualenv)
Python3.10.12(Ubuntu 22.04 自带)
Django5.2.18(requirements 声明 >= 4.2.16,实测装到 5.2.18)
数据库PostgreSQL 14.24(psycopg 3.3.6)
应用服务gunicorn 26.2.0(WSGI,127.0.0.1:8000,systemd 单元 ivatar.service)
反向代理nginx 1.18.0(Ubuntu),监听 8080,静态文件直接服务
缓存memcached 1.6.14
部署账号root(系统服务账户见 4.8)

二、前置条件

2.1 资源与网络

  • 磁盘 ≥ 2 GB 空闲(源码约 25 MB、虚拟环境约 400 MB、静态文件约 40 MB、wheelhouse 约 140 MB);
  • 内存 ≥ 1 GB(3 个 gunicorn worker + PostgreSQL + memcached 实测充裕);
  • 服务器需要能访问 git.linux-kernel.at 与 pypi.org(方案 A);完全离线机器走方案 B;
  • 如需联邦发现,提前规划对外域名(本文示例 avatars.example.com,DNS SRV 配置见《Libravatar 2.0 介绍与选型指南》三章)。

2.2 系统依赖包(apt)

一条命令装齐编译工具链、Python 头文件、图像/数据库/缓存依赖:

# apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y \
  git python3 python3-venv python3-pip python3-dev build-essential \
  libpq-dev libldap2-dev libsasl2-dev libmemcached-dev \
  libxml2-dev libxslt1-dev libmagic1 libjpeg-dev zlib1g-dev libfreetype6-dev \
  postgresql postgresql-contrib nginx memcached dnsutils curl
(安装过程输出略)
0 upgraded, 212 newly installed, 0 to remove and 2 not upgraded.

实测装了 212 个包。关键项用途:build-essential/python3-dev(argon2-cffi 等带 C 扩展的包)、libldap2-dev/libsasl2-dev(django-auth-ldap 编译依赖)、libxml2-dev/libxslt1-dev(lxml)、libmagic1(python-magic 做上传文件魔数校验的运行库)、libjpeg-dev/zlib1g-dev/libfreetype6-dev(Pillow)、dnsutils(dig 验证 SRV)。

坑:在最小化安装的系统里(未配置 policy-rc.d 放行服务自启时),apt 装完 postgresql 与 nginx 可能不会自动启动。实测 systemctl is-active postgresql 返回 inactive,需手动 systemctl start postgresql(见 4.4)。

2.3 版本核查

# python3 --version
Python 3.10.12
# git --version
git version 2.34.1
# psql --version
psql (PostgreSQL) 14.24 (Ubuntu 14.24-0ubuntu0.22.04.1)

三、安装方式选型

ivatar 官方没有发布 PyPI 包(PyPI 上不存在可安装的 libravatar 分发包),因此只有源码安装一条路,源码获取分两种:

方式命令适用
git clone(方案 A)git clone https://git.linux-kernel.at/oliver/ivatar.git联网机器,后续可 git pull 升级
tarball(方案 B)https://git.linux-kernel.at/oliver/ivatar/-/archive/master/ivatar-master.tar.gz离线机器/审计留档

本文方案 A = git clone + pip 在线装依赖;方案 B = tarball + 预构建 wheelhouse + pip --no-index 离线装依赖。两条路的 systemd/nginx/数据库部分完全一致(4.5–4.10)。

四、方案 A:在线源码安装(推荐)

4.1 获取源码

# git -c http.proxy=http://192.168.1.62:7897 -c https.proxy=http://192.168.1.62:7897 \
  clone https://git.linux-kernel.at/oliver/ivatar.git /opt/ivatar
Cloning into '/opt/ivatar'...
(进度行略)
# cd /opt/ivatar && git log -1 --format="%h %ad" --date=short
6f4b7e0 2026-09-25

坑:git.linux-kernel.at 在部分网络下直连 clone 长时间无响应(实测挂起超时)。给 git 显式挂 HTTP 代理(git -c http.proxy=...)即可稳定拉取;彻底不通的网络直接用方案 B 的 tarball。

4.2 创建虚拟环境

# cd /opt/ivatar && python3 -m venv .virtualenv
# .virtualenv/bin/pip --version
pip 22.0.2 from /opt/ivatar/.virtualenv/lib/python3.10/site-packages/pip (python 3.10)

4.3 安装 Python 依赖(127 个安装产物)

# cd /opt/ivatar
# export http_proxy=http://192.168.1.62:7897 https_proxy=http://192.168.1.62:7897
# .virtualenv/bin/pip install -r requirements.txt
Collecting argon2-cffi>=21.3.0
(约 240 行 Collecting/Downloading 输出略)
Successfully installed Django-5.2.18 Identicon-0.1.1 Pillow-12.3.0 PyJWT-2.15.1 PyMySQL-1.2.3 PyYAML-6.0.3 argon2-cffi-2 …(约 125 个包,一次装齐)

requirements.txt 内含 7 个 git+https:// 依赖(pagan、pydenticon5、python3-openid、django-openid-auth、identicon、monsterid、Robohash 的 fork),pip 会现场克隆构建,网络要通;装完核心版本核查:

# .virtualenv/bin/python -c "import django; print(django.__version__)"
5.2.18
# .virtualenv/bin/pip show gunicorn psycopg | grep -E "^(Name|Version)"
Name: gunicorn
Version: 26.2.0
Name: psycopg
Version: 3.3.6

4.4 初始化 PostgreSQL

# systemctl start postgresql && systemctl is-active postgresql
active
# su - postgres -c "psql -c \"CREATE USER ivatar_user WITH PASSWORD 'q1w2e3r4';\""
CREATE ROLE
# su - postgres -c "psql -c \"CREATE DATABASE ivatar OWNER ivatar_user;\""
CREATE DATABASE

4.5 写入部署配置 config_local.py

# cd /opt/ivatar && cat > config_local.py <<'EOF'
# -*- coding: utf-8 -*-
# Production overrides for this deployment
DEBUG = False
SECRET_KEY = "x3LXrkzXd4IeKx3SukQ/T1ugzyHZm16heNpiAHPHwfwtV98zZ0lFO8KWlVxILqB6"
ALLOWED_HOSTS = ["*"]

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "ivatar",
        "USER": "ivatar_user",
        "PASSWORD": "q1w2e3r4",
        "HOST": "127.0.0.1",
        "PORT": "5432",
    }
}
EOF
  • SECRET_KEY 用 manage.py generate_secret_key 现生成(命令见《Libravatar 2.0 常用 CLI 命令与工具详解》);
  • ALLOWED_HOSTS = ["*"] 便于先跑通,生产建议收紧为实际域名(误配后果与验证见《Libravatar 2.0 配置参数详解》四章);
  • 内网无公网出口的机器,建议追加一行 DEFAULT_GRAVATARPROXY = False,否则未命中头像会走到 Gravatar 代理并 500(原因与日志见配置篇排障 5.4)。

配置加载顺序(settings.py → config.py → config_local.py)与全部参数含义见《Libravatar 2.0 配置参数详解》。

4.6 迁移数据库

# cd /opt/ivatar && .virtualenv/bin/python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, django_openid_auth, ivataraccount, sessions, social_django
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
(中间约 60 行 Applying … OK 略)
  Applying social_django.0021_repair_twitch_id_keys... OK
  Applying social_django.0022_nonce_timestamp_index... OK

实测 68 个迁移全部 OK。

4.7 收集静态文件

# .virtualenv/bin/python manage.py collectstatic --noinput
1264 static files copied to '/opt/ivatar/static'.

4.8 创建管理员账号

# DJANGO_SUPERUSER_PASSWORD=q1w2e3r4 .virtualenv/bin/python manage.py createsuperuser --noinput --username admin --email admin@example.com
Superuser created successfully.

登录 Web 界面(http://服务器IP:8080/accounts/login/)用 admin / q1w2e3r4。

4.9 systemd 托管 gunicorn

# mkdir -p /var/log/ivatar
# cat > /etc/systemd/system/ivatar.service <<'EOF'
[Unit]
Description=ivatar (Libravatar server) WSGI service
After=network.target postgresql.service
Wants=postgresql.service

[Service]
Type=simple
WorkingDirectory=/opt/ivatar
Environment="PATH=/opt/ivatar/.virtualenv/bin:/usr/local/bin:/usr/bin:/bin"
ExecStart=/opt/ivatar/.virtualenv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 ivatar.wsgi:application
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
# systemctl daemon-reload
# systemctl enable --now ivatar
Created symlink /etc/systemd/system/multi-user.target.wants/ivatar.service → /etc/systemd/system/ivatar.service.
# systemctl status ivatar --no-pager | head -5
● ivatar.service - ivatar (Libravatar server) WSGI service
     Loaded: loaded (/etc/systemd/system/ivatar.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2026-10-28 13:59:54 UTC; 2s ago
   Main PID: 7636 (gunicorn)
      Tasks: 5 (limit: 2932)

4.10 nginx 反向代理与静态文件

# cat > /etc/nginx/sites-available/ivatar <<'EOF'
server {
    listen 8080;
    server_name _;

    access_log /var/log/ivatar/nginx-access.log;
    error_log  /var/log/ivatar/nginx-error.log;

    location /static/ {
        alias /opt/ivatar/static/;
        expires 7d;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
EOF
# ln -sf /etc/nginx/sites-available/ivatar /etc/nginx/sites-enabled/ivatar
# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# systemctl start nginx && systemctl is-active nginx
active

4.11 端到端验证

# curl -s -o /dev/null -w "gunicorn直连 /version/: %{http_code}\n" http://127.0.0.1:8000/version/
gunicorn直连 /version/: 200
# curl -s -o /dev/null -w "nginx反代  /version/: %{http_code}\n" http://127.0.0.1:8080/version/
nginx反代  /version/: 200
# curl -s -o /dev/null -w "nginx静态  /static/css/bootstrap.min.css: %{http_code}\n" http://127.0.0.1:8080/static/css/bootstrap.min.css
nginx静态  /static/css/bootstrap.min.css: 200
# curl -s http://127.0.0.1:8080/version/
{"commit_hash": "6f4b7e01cb2a6be57fea63aeb9fc95414b2d2037", "short_hash": "6f4b7e0", "branch": "master", "commit_date": "2026-10-28 13:53:26 ", "deployment_date": "…", "deployment_status": "active", "version": "master-6f4b7e0", "application_version": "2.0"}

四个 200 即部署成功:gunicorn 直连、nginx 反代、nginx 静态、版本端点。application_version: "2.0" 确认跑的是 ivatar 2.0。头像 API 的完整验证(尺寸/默认头像/强制默认/无效哈希/DNS SRV)见《Libravatar 2.0 常用 CLI 命令与工具详解》。

五、方案 B:离线安装(tarball + wheelhouse)

适用于无法访问外网的机器:在一台能联网的同架构机器上准备物料,再拷贝到目标机安装。

5.1 下载源码 tarball 并校验

$ wget https://git.linux-kernel.at/oliver/ivatar/-/archive/master/ivatar-master.tar.gz
$ echo "0862c87dfee20acc7a7c5ec6b55b3acc38965f51e5b60096ac6f83fa5f441e68  ivatar-master.tar.gz" | sha256sum -c
ivatar-master.tar.gz: OK

(校验值为 2026-10-28 从官方仓库下载的 master 快照实测值;后续 master 演进后哈希会变,以你下载当日的 sha256sum 输出为准留档。)

5.2 联网机构建 wheelhouse

$ pip download -r requirements.txt -d /opt/wheelhouse
(含 7 个 git+ 依赖的现场克隆构建,输出略)
$ ls /opt/wheelhouse | wc -l
127

wheelhouse 内含 pagan-0.4.4.zip、Robohash、django-openid-auth 等全部产物,约 140 MB,连同 tarball 一起拷贝到目标机。

5.3 生成离线 requirements

坑:直接拿原 requirements.txt 离线安装必然失败——里面有 7 行 git+https://…,pip 会尝试现场 git clone(离线机器直接报错)。解法是剔除 git 行、补上这 7 个包名(wheelhouse 里已有它们构建好的产物):

$ cd ivatar-master
$ grep -vE "^git\+" requirements.txt > requirements-offline.txt
$ printf "pagan\npydenticon5\npython3-openid\ndjango-openid-auth\nIdenticon\nmonsterid\nRobohash\n" >> requirements-offline.txt

5.4 离线安装

$ python3 -m venv .virtualenv
$ .virtualenv/bin/pip install --no-index --find-links /opt/wheelhouse -r requirements-offline.txt
Successfully installed Django-5.2.18 Identicon-0.1.1 Pillow-12.3.0 …(127 个安装产物,与方案 A 同版本)

--no-index 禁用 PyPI、--find-links 指向本地 wheelhouse,全程零外联。实测同一份 requirements 下方案 A/B 装出的版本完全一致(Django 5.2.18、gunicorn 26.2.0、psycopg 3.3.6)。

5.5 后续步骤

与方案 A 的 4.4–4.11 完全一致(数据库、config_local.py、migrate、collectstatic、superuser、systemd、nginx、验证),不再重复。

六、两方案对比

维度方案 A(在线源码)方案 B(离线 wheelhouse)
网络要求需访问 GitLab + PyPI(约 150 MB 下载)目标机零外联;准备机需联网
升级git pull + 重装依赖,增量方便重打 wheelhouse,适合变更管控
版本确定性pip 按范围解析,随时间漂移(如 Django 5.2.18)wheelhouse 固化,100% 可复现
审计合规依赖链动态获取物料(tarball + wheelhouse)可留档送审
操作成本低(三条命令)中(要先备料)

建议:常规服务器用方案 A;等保/内网/批量部署用方案 B。

七、部署后配置要点

  1. 必改:ALLOWED_HOSTS 收紧到实际域名、SECRET_KEY 确认已随机生成(本文学例仅作演示);
  2. 建议:内网机器加 DEFAULT_GRAVATARPROXY = False;对外域名头像链接用 SECURE_BASE_URL/BASE_URL 声明成自己的域名;
  3. 可选:WebP、缓存 TTL、预生成档位等业务参数逐项实测见《Libravatar 2.0 配置参数详解》;
  4. 每次改 config_local.py 后必须 systemctl restart ivatar。

八、常见问题(真实踩坑)

Q1:git clone 长时间卡住不动? git.linux-kernel.at 部分网络直连不通。git -c http.proxy=http://代理:端口 clone …,或改用 5.1 的 tarball。

Q2:pip install 报 error in … setup command: 'python_ldap' / 编译错误? 缺 LDAP 编译头。apt 清单中的 libldap2-dev libsasl2-dev python3-dev build-essential 一并装上即可(2.2 已含);不需要 LDAP 登录也不能省——requirements.txt 里 django-auth-ldap 是硬依赖。

Q3:启动报 ImportError: libmagic.so.1 或上传头像被拒? apt-get install libmagic1——python-magic 的运行时库,2.2 清单已含,漏装会在首次上传文件时暴露。

Q4:装完 postgresql/nginx 后 systemctl start 前服务是 inactive? 最小化环境 apt 不会拉起服务,手动 systemctl start postgresql nginx 并 systemctl enable --now 设自启。

Q5:访问头像接口报 502/Connection refused? ivatar.service 没起来。journalctl -u ivatar -n 20 看原因;高频根因是 memcached 未装(config.py 默认缓存后端指向 127.0.0.1:11211):apt-get install memcached && systemctl enable --now memcached。

Q6:静态文件(登录页 CSS)404? 先 manage.py collectstatic(4.7),再确认 nginx /static/ 的 alias 指向 /opt/ivatar/static/(STATIC_ROOT 实际值可用 manage.py diffsettings | grep STATIC_ROOT 核对)。

Q7:migrate 报 password authentication failed for user "ivatar_user"? config_local.py 的 DATABASES 没生效(Typo/层级写错)或密码不一致。manage.py diffsettings | grep ^DATABASES 看实际生效值——diffsettings 显示 sqlite3 就说明你的覆盖没写对位置。

Q8:manage.py 每次都打印 No metric readers configured - metrics will not be exported? OpenTelemetry 未配导出器的正常提示,不是错误,忽略(详见配置篇 FAQ)。

九、卸载与清理

# systemctl disable --now ivatar nginx
Removed "/etc/systemd/system/multi-user.target.wants/ivatar.service".
# rm /etc/nginx/sites-enabled/ivatar /etc/nginx/sites-available/ivatar /etc/systemd/system/ivatar.service
# su - postgres -c "psql -c \"DROP DATABASE ivatar;\" -c \"DROP USER ivatar_user;\""
DROP DATABASE
DROP ROLE
# rm -rf /opt/ivatar /opt/wheelhouse /var/log/ivatar /var/tmp/ivatar_cache

如需彻底移除系统包:apt-get purge postgresql nginx memcached && apt-get autoremove。

十、参考链接

  1. ivatar 官方仓库与 INSTALL.md:https://git.linux-kernel.at/oliver/ivatar
  2. 源码 tarball:https://git.linux-kernel.at/oliver/ivatar/-/archive/master/ivatar-master.tar.gz
  3. Libravatar Wiki《Running your own instance》:https://wiki.libravatar.org/running_your_own/
  4. 本专栏认知篇:《Libravatar 2.0 介绍与选型指南》
  5. 本专栏配置篇:《Libravatar 2.0 配置参数详解》
  6. 本专栏命令篇:《Libravatar 2.0 常用 CLI 命令与工具详解》