知搜监测服务

独立 Linux 部署手册 · Python 3.11+ · 默认 SQLite / 可选 MySQL 8

不要覆盖现有网站或 Nginx 配置。后端 4188 只允许回环或容器内部访问,公网入口使用现有 Nginx HTTPS。内置 HTTP Basic:必须设置用户名和至少 12 位、非示例值的独立密码;空配置不能启动。唯一免认证接口为 /healthz,响应 JSON {"ok":true}。Docker 探活检查 HTTP 200 且 JSON 的 ok 字段严格为布尔值 true,不比较纯文本。不要在 HTTP 上发送认证凭据。

包根应含 app.py、monitoring.py、storage.py、config.py、geo_update.py、monitor-static/、requirements.txt 及本部署文件。安装器和 Docker 均显式复制 geo_update.py,由 app.main 启动每周调度。此包不携带生产域名、凭据、访问数据或 IP 数据库,不安装 cron 或 systemd timer。requirements.txt 包含 tzdata,供 Windows 和缺少系统时区数据库的 Linux 环境作为 ZoneInfo 后备。

1. 原生 systemd 安装

推荐 Debian 12+、Ubuntu 24.04+ 或提供 Python 3.11+ 的 Rocky/Alma。旧版系统请使用官方仓库中可用的新版 Python,或选 Docker;不要替换系统 Python。需 root、systemd、网络访问 PyPI。

# Debian / Ubuntu(先确认 python3 --version 至少 3.11)
sudo apt-get update
sudo apt-get install -y python3 python3-venv python3-pip acl ca-certificates

# Rocky / Alma 9 系列:按系统仓库实际可用版本安装
sudo dnf install -y python3.11 python3.11-pip acl ca-certificates

# 在解压后的包根执行;无需 chmod,脚本必须保持 LF 换行
sudo bash deploy/install.sh
# 若默认 python3 过旧,改用:
sudo env PYTHON=python3.11 bash deploy/install.sh

sudoedit /etc/zhisou-monitor.env
sudo systemctl restart zhisou-monitor
sudo systemctl status zhisou-monitor --no-pager
curl --fail http://127.0.0.1:4188/healthz
sudo journalctl -u zhisou-monitor -n 100 --no-pager

安装器创建无登录专用用户 zhisou-monitor、独立 venv 和版本目录,代码由 root 所有;数据写入 /var/lib/zhisou-monitor。配置默认 root:root 0600,由 systemd 读取。0600 不允许组读;确需组读时仅授权受信任运维组并改 0640,不能开放 other 权限。再次安装会保留配置并恢复 0600。服务账户不需要直接读取配置文件。

安装器不会修改、重载 Nginx,也不会自动启动或重启应用,避免未配置认证或日志权限时意外上线。完成配置后上述 restart 即启动。资源限额为 512 MiB 内存、1 CPU、128 任务、4096 文件描述符;通过 systemctl edit zhisou-monitor 覆盖参数,不直接改发布单元。ProtectSystem=strict 仅允许数据目录写入,改数据路径时必须同步修改 ReadWritePaths。

sudo systemctl stop zhisou-monitor
sudo systemctl start zhisou-monitor
sudo systemctl restart zhisou-monitor
sudo systemctl disable --now zhisou-monitor

2. Docker Compose

使用 Docker Engine 与 Compose v2 插件。SQLite 默认只有一个服务,无 Nginx 容器。先在主机建立实际日志目录,再从包根运行。容器 UID/GID 固定为 10001,必须按下一节授权日志读取。容器根文件系统只读,数据卷可写。

cp .env.example .env
chmod 600 .env
# 编辑 .env:必须填写 YMING_ADMIN_USER / YMING_ADMIN_PASSWORD
# YMING_HOST_LOG_DIR 指向主机网站 JSON 日志目录
docker compose build
docker compose up -d
docker compose ps
docker compose logs --tail=100 monitor
curl --fail http://127.0.0.1:4188/healthz
docker compose stop
docker compose start

Compose 强制容器监听 0.0.0.0:4188,但只映射主机 127.0.0.1:4188;数据、备份、IP 库分别为 /data、/data/backups、/data/ip_geo.sqlite3,日志模式 /logs/access.json*。这些值覆盖 .env 中原生路径。自定义日志文件名须修改 compose 的 YMING_LOG_PATTERN。不要使用 host 网络或公网端口映射。若 Nginx 自身是容器,需由管理员接入共同私有网络并代理 monitor:4188,不能把其容器回环地址当成本服务。

可选独立 MySQL 配置

以下命令使用 deploy/compose.mysql.yaml,不与默认文件叠加。先停止 SQLite 实例以释放 4188。MySQL 8.4.8 使用独立持久卷,不暴露 3306;数据库用户 SELECT 1 健康检查成功后才启动应用。设置不同的强 MYSQL_PASSWORD 与 MYSQL_ROOT_PASSWORD,业务不能使用 root。

docker compose stop
# 编辑 .env 中的 MYSQL_USER、MYSQL_PASSWORD、MYSQL_DATABASE、MYSQL_ROOT_PASSWORD
docker compose --project-directory . -f deploy/compose.mysql.yaml up -d --build
docker compose --project-directory . -f deploy/compose.mysql.yaml ps
docker compose --project-directory . -f deploy/compose.mysql.yaml logs --tail=100
docker compose --project-directory . -f deploy/compose.mysql.yaml stop

两个项目各有独立数据卷,切换不会自动复制数据。已初始化的 MySQL 卷不会因更改 .env 自动修改账户密码,应在数据库中执行账户变更再同步配置。不要执行 down -v,除非明确要销毁数据。固定镜像标签 python:3.11.16-slim-bookworm、mysql:8.4.8;生产发布建议记录实际 image digest,并定期审核安全补丁,固定 tag 不保证内容永久不变。

3. Nginx、HTTPS 与日志格式

参考 deploy/nginx.conf.example,将 log_format 放入 http {} 层级,将 access_log 和 location 合并到实际网站的现有 HTTPS server {}。不要整份覆盖 nginx.conf;一个网站多个 access_log 会产生多份日志,应确认业务需求。

# http {} 级:
log_format zhisou_json escape=json
    '{"time":"$time_iso8601","remote_addr":"$remote_addr",'
    '"request_id":"$request_id","method":"$request_method",'
    '"uri":"$request_uri","status":$status,"bytes":$body_bytes_sent,'
    '"request_time":$request_time,"referer":"$http_referer",'
    '"user_agent":"$http_user_agent","host":"$host"}';

# 网站现有 HTTPS server {} 内:
access_log /var/log/nginx/access.json zhisou_json;
location = /monitor { return 308 /monitor/; }
location ^~ /monitor/ {
    proxy_pass http://127.0.0.1:4188/;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Authorization $http_authorization;
    proxy_connect_timeout 5s;
    proxy_read_timeout 60s;
    proxy_hide_header Cache-Control;
    add_header Cache-Control "no-store" always;
}

proxy_pass 的尾斜杠不可省略,它将 /monitor/ 前缀剥离。^~ 防止网站正则 location 抢走静态资源请求。不添加第二套 auth_basic;认证由应用负责。保留现有证书及 TLS 配置,在 80 端口将请求重定向 HTTPS,不在那里代理监测服务;认证页面不应被 CDN 或共享缓存缓存。完成后执行 sudo nginx -t,通过后再 sudo systemctl reload nginx。

通过 HTTPS 访问 https://example.com/monitor/(替换为自己的域名)。curl -I https://example.com/monitor/ 应返回 401;curl -u 用户名 https://example.com/monitor/ 将交互询问密码,勿把密码放命令行。/monitor/healthz 可免认证访问且仅用于存活检查,不代表日志采集或数据库业务完全正常。

真实字段示例(每个请求一行 JSON,文档 IP 为保留示例地址):

{"time":"2026-10-19T10:00:00+08:00","remote_addr":"192.0.2.10","request_id":"demo-unique-001","method":"GET","uri":"/products?q=demo","status":200,"bytes":1024,"request_time":0.013,"referer":"https://example.com/","user_agent":"ExampleBrowser/1.0","host":"example.com"}

字段名必须保持 time、remote_addr、request_id、method、uri、status、bytes、request_time、referer、user_agent、host。time 为带时区 ISO 8601;status/bytes 为整数、request_time 为秒;request_id 应唯一,缺失时应用按原始行哈希去重。标准 combined 文本格式不能直接导入。可增加自定义字段,但不要将 uri 改名为 request_uri,或将 user_agent 改为 http_user_agent。避免在 URL 中记录令牌、密码和个人敏感数据。

CDN/负载均衡环境下,仅对可信代理网段配置 Nginx real_ip_header 与 set_real_ip_from,让 $remote_addr 成为经过校验的客户端地址;不能直接信任任何客户端提供的 X-Forwarded-For。修改 real-IP 规则前应核对可信代理官方网段。访问日志含个人信息,请按所在地区规则控制保存期与访问权限。

4. 日志读取、轮换与 SELinux

只授权目标网站日志,不用 chmod 777,也不把服务加入能读取所有系统日志的管理员组。文件需要 r,所有父目录需要 x;glob 枚举所在目录还需 r。下面示例针对现有 /var/log/nginx,请先核对实际路径。

# 原生服务
sudo setfacl -m u:zhisou-monitor:--x /var/log
sudo setfacl -m u:zhisou-monitor:r-x /var/log/nginx
sudo setfacl -m u:zhisou-monitor:r-- /var/log/nginx/access.json
# 继承 ACL:会影响此目录以后创建的所有文件;共享目录请改用专用日志目录
sudo setfacl -m d:u:zhisou-monitor:r-x /var/log/nginx
sudo -u zhisou-monitor head -n 1 /var/log/nginx/access.json

# Docker 对应主机 UID;rootless / userns-remap 须换成映射后的主机 UID
sudo setfacl -m u:10001:r-x /var/log/nginx
sudo setfacl -m u:10001:r-- /var/log/nginx/access.json
sudo setfacl -m d:u:10001:r-x /var/log/nginx
getfacl /var/log/nginx/access.json

轮换后新文件必须继续可读,建议在客户现有 logrotate 策略中审核 create 的组/权限与 ACL mask,保持 Nginx 的 reopen/postrotate;不要新建重复规则竞争同一日志。已有历史文件需逐个授权。不要依赖 copytruncate 保证无遗漏。轮换一次后再次运行读取测试,并核对采集状态。默认不导入 .gz 历史,确有需要才设置 YMING_IMPORT_GZIP=1;历史回灌会增加 IO,应监测容量。

Rocky/Alma 上 Unix ACL 之外还有 SELinux。遇到拒绝先查 sudo ausearch -m AVC -ts recent;由管理员制定最小目录标签/策略。Nginx 代理本地服务可能需要 sudo setsebool -P httpd_can_network_connect 1(会放宽 httpd 网络连接,先审核)。不要禁用 SELinux,也不要对整个现有 /var/log/nginx 盲目加 :Z,避免破坏宿主 Nginx 的访问;容器日志挂载需审核共享标签策略。

5. 环境变量

应用读取环境变量,不应假设自动加载工作目录 .env。原生由 systemd EnvironmentFile 加载,Docker 由 Compose 加载。密码含 $、#、空格时使用单引号,避免插值;建议使用密码管理器生成至少 24 位随机密码,不使用换行。不要 source 配置文件,也不要把 docker compose config 的完整输出贴到公开渠道。

变量默认值 / 用途
YMING_ADMIN_USER / YMING_ADMIN_PASSWORD无默认凭据,用户名非空且不能含冒号;密码至少 12 位且不是示例值
YMING_BIND / YMING_PORT原生 127.0.0.1 / 4188;容器内部 0.0.0.0
YMING_DATA_DIR原生 /var/lib/zhisou-monitor;Docker /data
YMING_BACKUP_DIR / YMING_GEO_DATABASE数据目录下 backups / ip_geo.sqlite3;GEO_DATABASE 是应用内部路径名,配置键为 YMING_GEO_DATABASE
YMING_GEO_SOURCE_URL默认空,禁用网络更新;填写有合法使用授权的 HTTPS 数据源,格式见 IP-DATA.html
YMING_GEO_SHA256 / YMING_GEO_SHA256_URL默认空;可选固定 64 位十六进制 SHA256 或提供摘要的 HTTPS 地址,固定摘要优先。均为空时不做预期摘要校验,生产建议配置可信摘要
YMING_GEO_MIN_ROWS可选正整数,未设置时默认 1000;示例中保持注释,不可显式设置为空,否则整数解析失败
YMING_SITE_HOSTSexample.com,www.example.com;逗号分隔,自行替换站点主机名
YMING_LOG_PATTERN原生 /var/log/nginx/access.json*;Docker /logs/access.json*
YMING_DB_BACKENDsqlite 或 mysql,默认 sqlite
MYSQL_HOST / PORT / USER / PASSWORD / DATABASE数据库专用 MYSQL_ 前缀;默认端口 3306,不使用业务 root 账户
MYSQL_SSL_CA可选 CA PEM 文件绝对路径;远程 MySQL 使用 TLS,容器需额外挂载只读证书文件
YMING_RETENTION_DAYS / YMING_WORKERS / YMING_CACHE_SECONDS30 / 4 / 30
YMING_BASE_PATH / YMING_TIMEZONE/monitor/ / Asia/Shanghai
YMING_IMPORT_GZIP0;设置 1 才导入压缩历史日志

IP 数据库应为应用支持的 SQLite 格式,不是直接将 .mmdb 改名;更新过程需能写入所在目录。每周更新只由应用内部调度。本部署不增设 OS 定时任务。PyMySQL 精确固定为 1.2.3,并启用 rsa extra 安装 cryptography 支持 MySQL 8 caching_sha2_password;参见 PyMySQL 官方 PyPI。传递依赖未全量锁定,严格复现应在已验证的目标平台保存 pip freeze 和镜像 digest。

更新器由 app.main 在服务内部启动:启动时检查一次,之后每天检查;成功后间隔一周更新,失败后一天重试。不要重复配置 systemd timer 或 cron。修改环境变量后重启服务。下载源和摘要源必须来自可信且已获授权的提供方;签名 URL 等敏感信息只保存在受限配置中。完整数据格式、授权要求与离线导入说明见 IP 数据说明。

6. 备份、恢复与迁移

备份包括数据库、IP 库、配置、版本号,以及需要再处理的原始日志。备份含敏感数据,应加密并离机保存,定期演练恢复。本地 backups 目录不等于异机容灾。SQLite 不能在写入期间只复制 analytics.sqlite3 而遗漏 WAL;最简单可靠的方法为短暂停服复制整个数据目录。

# 原生 SQLite 冷备;先确保 /root/backup 不含同名备份
sudo install -d -m 0700 /root/backup
sudo systemctl stop zhisou-monitor
sudo tar -C /var/lib -czf /root/backup/monitor-data.tgz zhisou-monitor
sudo cp -p /etc/zhisou-monitor.env /root/backup/
sudo systemctl start zhisou-monitor

# Docker SQLite 冷备(包根)
mkdir -m 700 backup
docker compose stop monitor
docker compose run --rm --no-deps -v "$PWD/backup:/backup" monitor \
  python -c "import tarfile; t=tarfile.open('/backup/data.tar','w'); t.add('/data',arcname='data'); t.close()"
docker compose start monitor

Docker 备份目录需先授权 UID 10001 写入,例如 sudo chown 10001:10001 backup;不要放宽到全员可写。恢复时停服,将备份解压到新的空数据目录/卷,恢复 UID/GID(原生专用用户、容器 10001),保留旧卷作为回退。禁止向运行中的数据库解压覆盖。原生跨主机迁移可解压到 /var/lib,重新安装代码并恢复 /etc/zhisou-monitor.env;确认不覆盖目标已有数据。Docker 可用受控临时容器将 data.tar 恢复到新空卷,再挂给 monitor。

MySQL 使用逻辑备份,不使用 SQLite 文件代替。停 monitor 避免应用状态变动,数据库保持运行。以下在 root 专用目录或权限 0700 的备份目录执行,主机 shell 的 umask 应为 077:

umask 077
docker compose --project-directory . -f deploy/compose.mysql.yaml stop monitor
docker compose --project-directory . -f deploy/compose.mysql.yaml exec -T mysql \
  sh -c 'MYSQL_PWD="$MYSQL_PASSWORD" exec mysqldump -u "$MYSQL_USER" --single-transaction --no-tablespaces --set-gtid-purged=OFF "$MYSQL_DATABASE"' > monitor.sql
docker compose --project-directory . -f deploy/compose.mysql.yaml start monitor

# 恢复到已经初始化、确认可覆盖的目标数据库;monitor 必须停止
docker compose --project-directory . -f deploy/compose.mysql.yaml exec -T mysql \
  sh -c 'MYSQL_PWD="$MYSQL_PASSWORD" exec mysql -u "$MYSQL_USER" "$MYSQL_DATABASE"' < monitor.sql

另行备份 MySQL 模式 monitor 的 /data 和 .env。远程原生 MySQL 使用受限客户端配置文件和 mysqldump,不把密码放入参数。切换 YMING_DB_BACKEND 不会迁移历史数据:SQLite→MySQL 需经验证的字段映射/迁移工具或在空库中重新导入留存原始日志;先核对条数、时间范围、去重键和采集偏移,禁止直接将 SQLite SQL dump 导入 MySQL。

7. 升级、回滚与验收

升级前停采集并备份数据及配置,记录旧版本与当前链接路径。将新包解压到独立目录,再执行安装器;它会新建版本和 venv,保留旧目录,current 原子切换。重新启动后检查 /healthz、认证、静态页面、采集时间、数据库和日志权限。不要在未备份时升级数据库结构。

readlink -f /opt/zhisou-monitor/current
sudo systemctl stop zhisou-monitor
# 完成上述备份,再从新包执行
sudo bash deploy/install.sh
sudo systemctl restart zhisou-monitor

# 代码回滚:将下面路径替换成已记录的真实旧版本
sudo systemctl stop zhisou-monitor
sudo ln -s /opt/zhisou-monitor/releases/OLD_RELEASE /opt/zhisou-monitor/rollback.new
sudo mv -Tf /opt/zhisou-monitor/rollback.new /opt/zhisou-monitor/current
sudo systemctl start zhisou-monitor

若新版改动数据库结构,代码回滚前还必须恢复对应旧版数据和配置,不能假定向后兼容。原生单元的旧副本也应随发布归档。Docker 升级前给旧镜像打独立归档标签并保存 compose/.env;新版本修改应用 image tag 后 build、up -d。回滚恢复旧 compose 和镜像并使用 up -d --no-build,必要时恢复旧数据卷。不要删除旧镜像/卷直到验收完成。

上线检查:HTTPS 证书有效;不带认证的业务接口返回 401;/healthz 返回 HTTP 200 和 JSON {"ok":true};ss -lntp 确认主机 4188 仅回环;实际网站产生一条请求后能看到新增记录;日志轮换后仍能采集;服务重启后数据保留;MySQL 不公开端口;配置不进入版本控制。内存耗尽时检查 journalctl 或容器 OOM 状态,按实际日志量调整资源。健康探活不代替业务数据验收。

本手册提供部署配置,不承诺尚未在目标 Linux / Docker 环境验证的运行结果。首次发布应在隔离测试机完成上述验收后再迁入生产。