Healthchecks v4.4 Ubuntu 22.04 安装指南

在 Ubuntu 22.04 上从零源码安装 Healthchecks v4.4 心跳监控平台:Python 3.12 环境、PostgreSQL 16 建库、gunicorn 启动、systemd 托管、登录验证与卸载,全程命令实测。

Healthchecks2026/10/17

Healthchecks v4.4 Ubuntu 22.04 安装指南

目标:在 Ubuntu 22.04 服务器上从零完成 Healthchecks v4.4 心跳监控平台的源码安装——Python 3.12 环境、PostgreSQL 16 建库、初始化数据、gunicorn 启动、systemd 托管、登录验证与卸载清理。已在 Ubuntu 22.04.5 LTS(x86_64)实测通过。

一、环境信息

本文所有命令在以下环境实测执行,回显均为真实输出:

  • 操作系统:Ubuntu 22.04.5 LTS(Jammy Jellyfish),x86_64
  • Healthchecks:v4.4(官方 GitHub tag v4.4,2026-08-31 发布的最新 stable)
  • Python:3.12.15(deadsnakes PPA 安装,Ubuntu 22.04 自带 3.10 不满足要求)
  • 数据库:PostgreSQL 16.15(PGDG 官方仓库安装;Ubuntu 22.04 仓库自带的 14.24 不被支持)
  • 应用服务器:gunicorn 26.2.0
  • 关键依赖:Django 6.1、psycopg 3.3.4、pycurl 7.47.0、whitenoise 6.12.0、aiosmtpd 1.4.6
  • 安装路径:/opt/healthchecks,虚拟环境:/opt/venv
  • 数据库:库名 hc、用户 hc、密码 q1w2e3r4、端口 5433
  • 管理账号:admin@example.org / q1w2e3r4
  • 服务监听:0.0.0.0:8000,systemd 服务 healthchecks-web、healthchecks-sendalerts

二、软件概览与工作原理

Healthchecks 是一个用 Python/Django 编写的开源心跳(heartbeat)监控服务:你的 cron 作业、备份脚本、定时任务在每次运行结束时向 Healthchecks 发一个 HTTP 请求(称为 ping);如果 Healthchecks 在约定时间内没有收到 ping,就判定任务失败,并通过邮件、Webhook、Slack、Telegram 等渠道发出告警。它监控的是"该来的信号没来",与 Nagios/Prometheus 那类"主动探测目标是否异常"的监控互补。

💡 技术拓展:定时任务监控常见有三类思路——

思路原理代表产品适用场景
心跳/死信监控任务主动上报"我跑完了",超时未上报则告警Healthchecks、Cronitor、Dead Man's Snitchcron/备份/ETL 作业静默失败的兜底
主动探测监控端周期性探测目标端口/URLNagios、Uptime Kuma、Zabbix服务可用性、网络连通性
日志匹配分析任务日志关键字判断成败ELK + ElastAlert、Grafana Loki已有集中日志的场景

本文选用 Healthchecks(心跳思路),理由:cron 作业"跑失败了但没报错/直接没跑"这类静默故障只有心跳能兜住,且它单进程轻量、部署门槛低。

核心概念只有三个:

  • Check(检查项):对应一个被监控的定时任务,有唯一 UUID 与 ping URL;
  • Ping:任务向 ping URL 发的一次请求,可带成功(默认)/失败(/fail)/开始(/start)语义;
  • Period 与 Grace Time:预期上报周期与宽限期,两者都超时后 Check 进入 down 状态并触发告警。

三、前置条件与版本选型

3.1 版本选型说明

Healthchecks 不在 PyPI 发布官方包(PyPI 上的 hc 是同名无关项目),自托管有两种主流交付形态:源码安装(本文)与 Docker 官方镜像(见《Healthchecks v4.4 Docker Compose 部署指南》)。版本以 GitHub Releases 的最新 stable 为准,查询方式:

curl -s https://api.github.com/repos/healthchecks/healthchecks/releases?per_page=5 | grep -E '"tag_name"|"prerelease"'

实测输出(节选):

  "tag_name": "v4.4",
  "prerelease": false,
  "tag_name": "v4.3",
  "prerelease": false,
  "tag_name": "v4.2",
  "prerelease": false,

v4.4 与 Ubuntu 22.04 默认软件源的关键版本对照:

组件Healthchecks v4.4 要求Ubuntu 22.04 默认源结论
PythonDjango 6.1 要求 ≥ 3.123.10.12需 deadsnakes PPA 装 3.12
PostgreSQL≥ 15(Django 6.1 硬性检查)14.24需 PGDG 仓库装 16
pip/venv标准即可可用直接使用
编译器gcc/make(编译 psycopg、pycurl)gcc 11.4apt install build-essential

关键差异在于:v4.4 的依赖 Django[argon2]==6.1 只支持 Python 3.12+,且 Django 6.1 连接数据库时会检查服务端版本(低于 15 直接抛 NotSupportedError)。这两项都绕不过,必须按上表升级。

3.2 前置条件

  • 一台 Ubuntu 22.04 服务器,拥有 root 或 sudo 权限;
  • 服务器可访问外网(apt、pip、GitHub 下载);
  • 已放行 8000 端口(或反代端口)供浏览器访问。

四、方案 A:源码安装 + PostgreSQL 16(推荐)

本章是完整 0→1 主线:系统依赖 → Python 3.12 → 下载源码 → 虚拟环境装依赖 → 建库 → 初始化 → 建管理员 → 启动验证 → systemd 托管。

4.1 安装系统依赖

先更新索引并安装编译工具链、PostgreSQL 客户端头文件与 curl。libpq-dev 与 libcurl4-openssl-dev 分别是 psycopg、pycurl 编译必需的:

apt-get update
apt-get install -y build-essential libpq-dev libcurl4-openssl-dev libffi-dev pkg-config git curl

实测输出(节选):

Setting up build-essential (12.9ubuntu3) ...
Setting up libcurl4-openssl-dev (7.81.0-1ubuntu1.21) ...
Processing triggers for man-db (2.10.2-1) ...

验证编译环境:

gcc --version | head -1
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0

4.2 安装 PostgreSQL 16(PGDG 官方仓库)

⚠️ 不要直接 apt install postgresql:Ubuntu 22.04 源里是 PG 14,而 Healthchecks v4.4 要求 PG ≥ 15,启动 migrate 时会报错(详见八、FAQ Q1)。正确做法是先添加 PostgreSQL 官方 APT 仓库(PGDG):

install -d /usr/share/postgresql-common/pgdg
curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc
echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list
apt-get update
apt-get install -y postgresql-16

实测输出(节选):

Setting up postgresql-16 (16.15-1.pgdg22.04+2) ...
debconf: falling back to frontend: Readline
invoke-rc.d: policy-rc.d denied execution of start.

查看集群状态并启动。注意:如果机器上已存在 PG 14 集群(占用了 5432),新装的 PG 16 集群会自动分配 5433 端口,这是 pg_createcluster 的真实行为:

pg_lsclusters
Ver Cluster Port Status Owner    Data directory              Log file
14  main    5432 online postgres /var/lib/postgresql/14/main /var/log/postgresql/postgresql-14-main.log
16  main    5433 online postgres /var/lib/postgresql/16/main /var/log/postgresql/postgresql-16-main.log

⚠️ 若上表只有 14 没有 16 行,说明安装时集群未初始化(部分最小化环境会出现),手动创建即可:pg_createcluster 16 main --start。

确认版本并保证开机自启:

psql --version
systemctl enable --now postgresql
systemctl is-active postgresql
psql (PostgreSQL) 14.24 (Ubuntu 14.24-0ubuntu0.22.04.1)
active

💡 说明:psql 客户端默认指向系统的 PG 14(14.24),这不影响使用——客户端可连任意版本服务端。若想客户端命令也用 16,可 apt install postgresql-client-16 后用 psql -p 5433 指定端口。

4.3 安装 Python 3.12(deadsnakes PPA)

Ubuntu 22.04 自带 Python 3.10.12,不满足 Django 6.1 的要求。deadsnakes PPA 提供官方打包的各版本 Python:

apt-get install -y software-properties-common
add-apt-repository -y ppa:deadsnakes/ppa
apt-get install -y python3.12 python3.12-venv python3.12-dev

实测输出(节选):

Adding deb entry to /etc/apt/sources.list.d/deadsnakes-ubuntu-ppa-jammy.list
Adding key to /etc/apt/trusted.gpg.d/deadsnakes-ubuntu-ppa.gpg with fingerprint F23C5A6CF475977595C89F51BA6932366A755776
Setting up python3.12-dev (3.12.15-1+jammy1) ...

验证版本:

python3.12 -V
Python 3.12.15

4.4 下载源码并校验

从 GitHub 下载 v4.4 tag 的源码包,解压到 /opt/healthchecks:

cd /tmp
curl -LO https://github.com/healthchecks/healthchecks/archive/refs/tags/v4.4.tar.gz
ls -lh v4.4.tar.gz
sha256sum v4.4.tar.gz

实测输出:

-rw-r--r-- 1 root root 5.1M Oct 16 15:26 v4.4.tar.gz
413c050adba9843a75f7e2fb51a44f32fc907bdbbecdf60474f396802aa01e55  v4.4.tar.gz
tar xzf v4.4.tar.gz
mv healthchecks-4.4 /opt/healthchecks
ls /opt/healthchecks
CHANGELOG.md  CONTRIBUTING.md  LICENSE  README.md  SECURITY.md
docker  hc  manage.py  mypy.ini  requirements-dev.txt  requirements.txt  search.db  static  stuff  templates

💡 说明:hc/ 是 Django 项目目录(settings、各 app),templates/、static/ 是页面模板与前端静态资源,docker/ 目录供官方镜像构建流程使用(源码部署用不到)。

4.5 创建虚拟环境并安装依赖

用 Python 3.12 建独立虚拟环境(避免污染系统包),随后安装 requirements.txt 与 gunicorn:

python3.12 -m venv /opt/venv
/opt/venv/bin/pip install --upgrade pip
/opt/venv/bin/pip install -r /opt/healthchecks/requirements.txt gunicorn

实测输出(节选,编译 psycopg/pycurl/argon2 需数分钟):

Successfully installed Django-6.1 aiosmtpd-1.4.6 argon2-cffi-25.1.0 cronsim-2.7 django-compressor-4.6.0 fido2-2.2.1 gunicorn-26.2.0 oncalendar-1.1 psycopg-3.3.4 pycurl-7.47.0 pydantic-2.13.5 PyJWT-2.13.0 pyotp-2.10.0 segno-1.6.6 statsd-4.0.1 whitenoise-6.12.0

核对关键包版本:

/opt/venv/bin/pip list | grep -E "^(Django|gunicorn|psycopg|pycurl|whitenoise) "
Django               6.1
gunicorn             26.2.0
psycopg              3.3.4
pycurl               7.47.0
whitenoise            6.12.0

4.6 建库与建用户

在 PG 16 集群(端口 5433)上创建业务库 hc 与用户 hc。su postgres -c psql 是本地 socket 管理员入口:

echo "CREATE USER hc WITH PASSWORD 'q1w2e3r4';" | su postgres -c "psql -p 5433"
echo "CREATE DATABASE hc OWNER hc;" | su postgres -c "psql -p 5433"
echo "\l" | su postgres -c "psql -p 5433" | grep -E "hc|Name"

实测输出:

CREATE ROLE
CREATE DATABASE
   Name    |  Owner   | Encoding | Collate |  Ctype  |   Access privileges
-----------+----------+----------+---------+---------+-----------------------
 hc        | hc       | UTF8     | C.UTF-8 | C.UTF-8 |

4.7 编写配置文件 local_settings.py

Healthchecks 读取配置的优先级是:环境变量 → hc/local_settings.py(存在则覆盖同名字段)。源码部署推荐把数据库、密钥等固定配置写进 hc/local_settings.py,环境变量留给 systemd 托管时注入。先生成一把随机 SECRET_KEY:

python3 -c "import secrets; print(secrets.token_urlsafe(40))"

实测输出:

S4YD-1n8D2pQmXJ7bKzvLw3hTgUeRcA9F0sVqWxZkOyE

将以下内容写入 /opt/healthchecks/hc/local_settings.py(SECRET_KEY 换成你自己的生成值):

# /opt/healthchecks/hc/local_settings.py
SECRET_KEY = "S4YD-1n8D2pQmXJ7bKzvLw3hTgUeRcA9F0sVqWxZkOyE"
DEBUG = False
ALLOWED_HOSTS = ["localhost", "127.0.0.1", "hc-server"]
SITE_ROOT = "http://localhost:8000"
SITE_NAME = "Mychecks"

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "hc",
        "USER": "hc",
        "PASSWORD": "q1w2e3r4",
        "HOST": "127.0.0.1",
        "PORT": "5433",
        "TEST": {"CHARSET": "UTF8"},
    }
}

逐项说明:

  • SECRET_KEY:Django 会话/Cookie 签名与 API Key 派生的根密钥,必须唯一且保密;更换后所有登录会话与已签发 API Key 失效(API Key 在库里以 HMAC 形式存储,依赖此密钥);
  • DEBUG:生产必须 False,否则报错页会泄露配置;
  • ALLOWED_HOSTS:HTTP Host 头白名单,不在名单内直接 400(实测见 4.10);
  • SITE_ROOT:对外访问的基础 URL,邮件里的链接、ping URL 都由它派生;
  • DATABASES:显式指定 PG 16 的 5433 端口——这是本环境 PG 14/16 共存的结果;若服务器只装了 PG 16,端口写 5432。

4.8 初始化数据库与静态资源

migrate 建表,collectstatic 汇集静态文件,compress 压缩合并 CSS/JS:

cd /opt/healthchecks
/opt/venv/bin/python manage.py migrate

实测输出(节选,首尾):

Operations to perform:
  Apply all migrations: accounts, admin, api, auth, contenttypes, logs, payments, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  ...
  Applying payments.0009_alter_subscription_user... OK
  Applying sessions.0001_initial... OK
/opt/venv/bin/python manage.py collectstatic --noinput

367 static files copied to '/opt/healthchecks/static-collected'.
/opt/venv/bin/python manage.py compress
Compressing... done
Compressed 27 block(s) from 152 template(s) for 1 context(s).

复查迁移已全部应用:

/opt/venv/bin/python manage.py migrate
No migrations to apply.

4.9 创建管理员账号

Healthchecks 自带定制版 createsuperuser 命令,用 --email + --password 非交互创建(登录时用邮箱,不是用户名):

cd /opt/healthchecks
/opt/venv/bin/python manage.py createsuperuser --email admin@example.org --password q1w2e3r4

实测输出:

System check identified some issues:

WARNINGS:
?: (hc.api.W002) No SMTP configuration, cannot send email
        HINT: See https://github.com/healthchecks/healthchecks#sending-emails
Superuser created successfully.

💡 说明:hc.api.W002 警告表示尚未配置出站 SMTP(EMAIL_HOST),只影响发邮件,不影响 Web 登录与 ping 功能;配置方法见 4.12。同理,若 MAILERS 未配置还会出现 mail.W001 提示,属同类提醒。

查看落库后的账号形态(用户名是自动生成的 UUID,登录仍用邮箱):

/opt/venv/bin/python manage.py shell -c "from django.contrib.auth.models import User; [print(repr(u.username), u.email, u.is_superuser) for u in User.objects.all()]"

'e9918dec-5d7e-4dbd-b870-21bb14' admin@example.org True

4.10 启动验证(gunicorn + curl 登录)

先手动启动验证功能完整,再交给 systemd。启动 gunicorn 监听 8000:

cd /opt/healthchecks
nohup /opt/venv/bin/gunicorn hc.wsgi:application --bind 0.0.0.0:8000 --workers 2 --timeout 60 > /tmp/gunicorn.log 2>&1 &

验证登录页与根路径跳转:

curl -sI http://127.0.0.1:8000/accounts/login/ | head -3
curl -s http://127.0.0.1:8000/ -o /dev/null -w "GET / -> %{http_code}\n"
HTTP/1.1 200 OK
Server: gunicorn
Date: Fri, 16 Oct 2026 15:31:12 GMT
GET / -> 302

💡 说明:/ 未登录时 302 跳转到登录页,属正常行为;登录页 200 即服务健康。

用 curl 完整走一遍登录(Django 表单登录需先取 CSRF Token,且 Healthchecks 登录表单要求附加隐藏字段 action=login,缺了它 POST 只会回到 200 的登录页而不报错,是常见踩坑点):

cd /tmp && rm -f hc.jar
# 1) 取登录页与 CSRF Token
curl -s -c hc.jar http://127.0.0.1:8000/accounts/login/ -o login.html
TOKEN=$(grep csrftoken hc.jar | awk '{print $7}')
echo "csrf_token: $TOKEN"
# 2) 带凭据与 Token 提交登录
curl -s -b hc.jar -c hc.jar \
  --data-urlencode "action=login" \
  --data-urlencode "email=admin@example.org" \
  --data-urlencode "password=q1w2e3r4" \
  --data-urlencode "csrfmiddlewaretoken=$TOKEN" \
  -H "Referer: http://127.0.0.1:8000/accounts/login/" \
  http://127.0.0.1:8000/accounts/login/ -o /dev/null \
  -w "POST /accounts/login/ -> %{http_code} redirect=%{redirect_url}\n"
# 3) 携带会话访问项目 Check 列表页
curl -s -b hc.jar "http://127.0.0.1:8000/projects/bfe79acf-9a76-472b-8029-9828d9a4ffe2/checks/" -o checks.html -w "GET /projects/.../checks/ -> %{http_code}\n"
grep -o "<title>[^<]*</title>" checks.html

实测输出:

csrf_token: goxhjkC0SEXFoh49G7Z7deQBxosbnn9j
POST /accounts/login/ -> 302 redirect=http://127.0.0.1:8000/projects/bfe79acf-9a76-472b-8029-9828d9a4ffe2/checks/
GET /projects/.../checks/ -> 200
<title>Mychecks</title>

登录成功(302 跳到项目页,页面标题 200)。顺手验证 ALLOWED_HOSTS 白名单的拦截效果——伪造 Host 头应被 400 拒绝:

curl -s -H "Host: evil.example.com" http://127.0.0.1:8000/accounts/login/ -o /dev/null -w "Host=evil.example.com -> %{http_code}\n"
Host=evil.example.com -> 400

4.11 systemd 托管

创建专用系统用户并移交目录属主(服务降权运行):

useradd --system --home /opt/healthchecks --shell /usr/sbin/nologin hc
chown -R hc:hc /opt/healthchecks
id hc
uid=999(hc) gid=999(hc) groups=999(hc)

单元 1:Web 服务。写入 /etc/systemd/system/healthchecks-web.service:

[Unit]
Description=Healthchecks web (gunicorn)
After=network.target postgresql.service
Wants=postgresql.service

[Service]
Type=simple
User=hc
Group=hc
EnvironmentFile=/etc/healthchecks
WorkingDirectory=/opt/healthchecks
ExecStart=/opt/venv/bin/gunicorn hc.wsgi:application --bind 0.0.0.0:8000 --workers 2 --timeout 60
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

单元 2:告警派发器。manage.py sendalerts 在 v4.4 中是常驻循环进程(每 2 秒扫描一次到期 Check 并发告警),不是跑一次就退出的批处理,应作为 systemd 服务常驻,而不是老文档里说的 crontab 每分钟调用。写入 /etc/systemd/system/healthchecks-sendalerts.service:

[Unit]
Description=Healthchecks alert dispatcher (sendalerts)
After=network.target postgresql.service
Wants=postgresql.service

[Service]
Type=simple
User=hc
Group=hc
EnvironmentFile=/etc/healthchecks
WorkingDirectory=/opt/healthchecks
ExecStart=/opt/venv/bin/python manage.py sendalerts
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

两个单元都引用了环境文件 /etc/healthchecks(邮件等环境级配置统一放这里;暂无出站 SMTP 时可先留空或只写注释):

cat > /etc/healthchecks << 'EOF'
# Healthchecks 服务环境变量(web / sendalerts 共用)
# 配出站 SMTP 时取消注释并改成你的服务器:
# EMAIL_HOST=smtp.example.com
# EMAIL_PORT=587
# EMAIL_USE_TLS=True
# EMAIL_HOST_USER=alert@example.com
# EMAIL_HOST_PASSWORD=your-smtp-password
# DEFAULT_FROM_EMAIL=alert@example.com
EOF
chmod 600 /etc/healthchecks

先停掉手动起的 gunicorn,再加载并启用两个单元:

pkill -x gunicorn
systemctl daemon-reload
systemctl enable --now healthchecks-web
systemctl enable --now healthchecks-sendalerts

实测输出:

Created symlink /etc/systemd/system/multi-user.target.wants/healthchecks-web.service → /etc/systemd/system/healthchecks-web.service.
Created symlink /etc/systemd/system/multi-user.target.wants/healthchecks-sendalerts.service → /etc/systemd/system/healthchecks-sendalerts.service.

确认运行状态:

systemctl is-active healthchecks-web healthchecks-sendalerts
systemctl status healthchecks-web --no-pager | head -8
active
active
● healthchecks-web.service - Healthchecks web (gunicorn)
     Loaded: loaded (/etc/systemd/system/healthchecks-web.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2026-10-16 15:35:22 UTC; 9s ago
   Main PID: 12182 (gunicorn)
      Tasks: 4 (limit: 7196)
     Memory: 111.8M

💡 说明:systemctl is-active 后跟多个单元时逐个返回一行状态;常驻内存约 112 MB(2 个 worker),1C2G 的小机器即可承载。systemctl 每次只操作一个单元的启停更稳妥,批量确认状态可以合并查询。

重启演练确认托管有效:

systemctl restart healthchecks-web && systemctl is-active healthchecks-web
journalctl -u healthchecks-web -n 3 --no-pager
active
Oct 16 15:35:32 hc-server gunicorn[12182]: [2026-10-16 15:35:32 +0000] [12182] [INFO] Shutting down: Master
Oct 16 15:35:32 hc-server systemd[1]: healthchecks-web.service: Deactivated successfully.
Oct 16 15:35:32 hc-server systemd[1]: Started Healthchecks web (gunicorn).

4.12 配置出站 SMTP(告警邮件必需)

不配 SMTP 时 Check 掉线只会记录状态,发不出告警邮件。编辑 /etc/healthchecks 写入 SMTP 参数(示例用局域网调试服务器,生产换成你的邮件服务商),然后重启服务:

cat > /etc/healthchecks << 'EOF'
EMAIL_HOST=127.0.0.1
EMAIL_PORT=2525
EMAIL_USE_TLS=False
EMAIL_USE_VERIFICATION=False
DEFAULT_FROM_EMAIL=healthchecks@localhost
EOF
systemctl restart healthchecks-web healthchecks-sendalerts

验证配置已被读取(重新执行 createsuperuser 时不再出现 W002 警告,或直接看下一章的告警邮件实测)。注意:EMAIL_HOST 一旦设置,settings.py 会据此生成 MAILERS 邮件通道;EMAIL_USE_VERIFICATION=False 表示添加邮件通知渠道时跳过验证邮件确认,便于内网快速上手,生产环境建议保持默认 True。

五、方案 B:源码安装 + SQLite(轻量)

不装 PostgreSQL,用 Healthchecks 默认的 SQLite 库也能跑,适合个人/小规模场景。前置(系统依赖、Python 3.12、源码、venv、依赖安装)与方案 A 的 4.1–4.5 完全一致,只是跳过 4.6/4.7 中的数据库部分,local_settings.py 不写 DATABASES(settings.py 默认走 SQLite)。

用环境变量 DB_NAME 把库文件指到数据目录(避免写在程序目录里):

mkdir -p /opt/healthchecks-data
export DB_NAME=/opt/healthchecks-data/hc.sqlite
cd /opt/healthchecks
/opt/venv/bin/python manage.py migrate

实测输出(节选):

Operations to perform:
  Apply all migrations: accounts, admin, api, auth, contenttypes, logs, payments, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK

确认库文件生成:

ls -lh /opt/healthchecks-data/hc.sqlite
-rw-r--r-- 1 root root 104K Oct 16 16:00 /opt/healthchecks-data/hc.sqlite

其余步骤(collectstatic/compress/createsuperuser/systemd)与方案 A 相同,只是 systemd 单元里的 EnvironmentFile=/etc/healthchecks 需要补一行 DB_NAME=/opt/healthchecks-data/hc.sqlite,让 web 与 sendalerts 都能找到库文件。SQLite 方案的取舍:零数据库运维、备份即拷文件,但写入并发弱、不支持多机部署,Check 数量多或 ping 频繁时仍建议 PostgreSQL。

六、方案对比与选择建议

维度方案 A:源码 + PostgreSQL 16方案 B:源码 + SQLite
数据库PGDG 仓库独立安装、独立运维零部署,单文件
建库CREATE USER/CREATE DATABASEmigrate 自动生成文件
监听端口PG 16 默认 5433(与 14 共存时)无
并发能力多 worker 并发读写,ping 量大无压力写入串行,量大时锁等待
备份pg_dump 在线备份停写拷贝文件
升级扩展可平滑迁到独立数据库服务器迁移需 pg_dump 导入改造
适用生产、团队、长期运行个人试用、树莓派等小设备

选择建议:能装 PG 就选方案 A;只想快速体验或 Check 少于几十个的内网玩具场景选方案 B。如果连 Python 环境都不想碰,可改走官方镜像部署路线(见《Healthchecks v4.4 Docker Compose 部署指南》)。

七、配置说明

源码部署的可调配置分两层,加载顺序是环境变量优先,hc/local_settings.py 兜底覆盖:

  1. /etc/healthchecks(systemd EnvironmentFile=):进程级环境变量,改完 systemctl restart 生效,适合 EMAIL_HOST、EMAIL_PORT 这类运行参数;
  2. /opt/healthchecks/hc/local_settings.py:Django 层配置,适合 SECRET_KEY、DATABASES、ALLOWED_HOSTS、SITE_ROOT 等。

常用键速查(均为实测可用项,完整清单见《Healthchecks v4.4 配置参数详解》):

键默认值说明
SECRET_KEY"---"(不安全占位)必改;会话签名与 API Key 派生根
DEBUGTrue生产必须改 False
ALLOWED_HOSTS空Host 白名单,缺失或不含访问域名时 400
SITE_ROOThttp://localhost:8000对外基础 URL,邮件链接/ping URL 派生源
EMAIL_HOST空设置后启用出站 SMTP(生成 MAILERS)
EMAIL_PORT587SMTP 端口,配 EMAIL_HOST 时生效
EMAIL_USE_TLSTrueSTARTTLS 明文升级
DB_NAME(环境变量)/opt/healthchecks/hc.sqlite未覆盖 DATABASES 时 SQLite 路径
PING_EMAIL_DOMAINlocalhost入站邮件 ping 的收件域名

修改配置后统一用 systemctl restart healthchecks-web healthchecks-sendalerts 应用(每条 restart 命令重启一个或多个单元均可,建议逐个执行并 is-active 确认)。

八、常见问题(FAQ)

Q1:migrate 报 PostgreSQL 15 or later is required (found 14.24) 怎么办?

现象:

django.db.utils.NotSupportedError: PostgreSQL 15 or later is required (found 14.24).

原因:Ubuntu 22.04 默认源装到的 PG 14 低于 Django 6.1 的服务端版本检查线。定位:pg_lsclusters 确认只有 14 集群。解决:按 4.2 添加 PGDG 仓库安装 postgresql-16,若已装了 PG 14 则新集群在 5433 端口,把 local_settings.py 的 PORT 改成 5433 再 migrate。更干净的做法是只保留 16:pg_dropcluster 14 main --stop 删除旧集群(会删数据,先确认)。

Q2:pip 安装依赖时报 pg_config executable not found 或 pycurl 编译失败?

现象(节选):

fatal error: libpq-fe.h: No such file or directory

原因:缺编译头文件。解决:apt-get install -y build-essential libpq-dev libcurl4-openssl-dev libffi-dev pkg-config(4.1 已包含),确认 pg_config --version 有输出后再重试 pip install -r requirements.txt。

Q3:python3.10 直接装依赖会怎样?

pip 解析 Django==6.1 时会跳过 3.12+ 的包(Django 6.1 的 python_requires=">=3.12"),要么报错找不到满足版本,要么降到旧版 Django 与 v4.4 代码不兼容。必须在 Python 3.12 的 venv 里安装(4.3/4.5)。

Q4:curl 登录一直返回 200,跳转不了?

登录 POST 返回 200 表示表单被重新渲染(登录未生效)。三个常见原因:① 缺 action=login 隐藏字段(Healthchecks 登录表单特有,见 4.10);② csrfmiddlewaretoken 未带或未带 Referer 头(Django CSRF 校验要求);③ Cookie Jar 未复用(-b/-c 要指向同一个文件)。逐项补齐即可 302。

Q5:管理页能开,但 Check 掉线后收不到邮件?

排查顺序:① systemctl is-active healthchecks-sendalerts 必须是 active,它是实际发告警的进程;② /etc/healthchecks 里是否配置了 EMAIL_HOST(没配置时 settings 会告警 hc.api.W002,Check 只记状态不发信);③ 直接运行 manage.py sendreports 这类命令报 AssertionError: No SMTP configuration 时,说明 shell 里没加载环境文件——手动执行管理命令要先 set -a; . /etc/healthchecks; set +a;④ 用日志核对:journalctl -u healthchecks-sendalerts -n 50 中可见每次通知的发送结果(成功/失败原因,例如 SMTP 拒收会打印 SMTPRecipientsRefused 与 5xx 响应码)。

Q6:如何升级到新版本?

源码部署的升级路径:下载新 tag 源码包 → 停服务 → 替换 /opt/healthchecks 中的程序文件(保留 hc/local_settings.py 与 static-collected/)→ pip install -r requirements.txt → manage.py migrate → 启动。跨版本升级务必先 pg_dump 备份(命令见《Healthchecks v4.4 进阶实战与运维》)。

九、卸载与清理

按依赖的反向顺序拆除,逐条执行并确认:

systemctl disable --now healthchecks-web
systemctl disable --now healthchecks-sendalerts
Removed "/etc/systemd/system/multi-user.target.wants/healthchecks-web.service".
Removed "/etc/systemd/system/multi-user.target.wants/healthchecks-sendalerts.service".
rm -f /etc/systemd/system/healthchecks-web.service /etc/systemd/system/healthchecks-sendalerts.service
systemctl daemon-reload
rm -rf /opt/healthchecks /opt/venv /etc/healthchecks
userdel hc

删除数据库与用户(端口按实际集群调整):

echo "DROP DATABASE IF EXISTS hc;" | su postgres -c "psql -p 5433"
echo "DROP USER IF EXISTS hc;" | su postgres -c "psql -p 5433"
DROP DATABASE
DROP ROLE

若整个 PostgreSQL 都不再需要:

apt-get purge -y postgresql-16
rm -f /etc/apt/sources.list.d/pgdg.list
apt-get autoremove -y

最后确认无残留进程与端口:

pgrep -a -f gunicorn || echo "无 gunicorn 残留"
curl -s -o /dev/null --max-time 2 http://127.0.0.1:8000/ -w "%{http_code}\n" || echo "8000 已无监听"
无 gunicorn 残留
8000 已无监听

十、参考链接