Files
getDomain/docs/18_domainCheck_CentOS9一键复制部署与更新文档.md
Your Name 6ed4a99143 debug
2026-04-16 22:35:08 +08:00

1289 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 18 domainCheck CentOS9 全自动一键部署与更新文档
## 一、文档用途
这份文档只做一件事:
> 给你一套可以直接复制执行的命令,完成 `1 台国外机器 + 2 台国内机器` 的傻瓜式部署和后续更新。
文档边界:
- 这里只讲一键自动部署
- 默认你会使用仓库里的 3 个一键脚本
- 不展开手工逐条部署步骤
- 如果你想完全手工部署,请改看:
- [17_domainCheck_全流程部署实操手册.md](/www/wwwroot/getDomain/docs/17_domainCheck_全流程部署实操手册.md:1)
统一前提:
- 系统:`CentOS Stream 9`
- 代码管理:统一 `git`
- API 域名:`api.domain.com`
- 后台域名:`admin.domain`
- 分支:默认 `main`
- 脚本约定:所有一键 `.sh` 只做部署,不执行任何 `git` 命令
目标机器:
- 国外机器 `1`
- 角色:`overseas-control`
- 国内机器 `2`
- `mainland-controller-01`
- `mainland-worker-01`
一句话部署口径:
- 国外机器:`宝塔管理 Nginx / PostgreSQL / Redis + systemd 管理项目服务`
- 国内机器:`纯 systemd 执行节点,不装宝塔`
## 二、先改这里
正式执行本文命令前,先把下面这些变量替换成你自己的真实值。
```text
API_DOMAIN=api.domain.com
ADMIN_DOMAIN=admin.domain
DB_NAME=domain
DB_USER=domainuser
DB_PASSWORD=请改成你的强密码
SYNC_SHARED_TOKEN=请改成你的共享令牌
OVERSEAS_NODE_CODE=overseas-control-01
MAINLAND_CONTROLLER_NODE_CODE=mainland-controller-01
MAINLAND_WORKER_NODE_CODE=mainland-worker-01
MAINLAND_CONTROLLER_IP=国内controller机器IP
NODE_VERSION=20
NODE_NVM_DIR=
```
最少必须先替换这 5 个:
- `DB_PASSWORD`
- `SYNC_SHARED_TOKEN`
- `API_DOMAIN`
- `ADMIN_DOMAIN`
- `MAINLAND_CONTROLLER_IP`
如果你用 `root` 执行一键脚本,但 `nvm` 实际装在别的用户目录下,再额外补一个:
- `NODE_NVM_DIR`
例如:
```text
NODE_NVM_DIR=/home/www/.nvm
```
## 三、统一约定
### 1. 仓库目录
```text
/www/wwwroot/getDomain
```
### 2. 运行目录
```text
/opt/domaincheck
```
### 3. 统一更新命令
后续所有机器代码更新统一执行:
```bash
cd /www/wwwroot/getDomain
git fetch --all
git checkout main
git pull --ff-only origin main
```
### 3.1 首次全新机器先做什么
如果这台机器是第一次部署,而且本机还没有:
```text
/www/wwwroot/getDomain
```
那么要先手动拉一次代码,因为脚本文件本身也在仓库里。
首次最小步骤:
```bash
mkdir -p /www/wwwroot
cd /www/wwwroot
git clone 你的仓库地址 getDomain
cd getDomain
git checkout main
git pull --ff-only origin main
```
然后再:
1. 复制对应的 `.conf.example`
2. 编辑 `.conf`
3. 执行对应的 `.sh`
关键结论:
- 新机器第一次:必须先手动 `git clone`
- 后续每次更新:先手动 `git pull`
- 后续执行脚本:不负责 `clone`,也不负责 `pull`
- 脚本只负责“基于当前仓库代码做部署或更新”
### 3.2 这些命令用哪个用户执行
结论:
- 整套部署命令建议统一使用 `root`
- 不建议用 `www` 执行部署脚本
- 但前端 `npm install``npm run build` 会由脚本自动切到仓库属主执行
原因:
- 脚本会写:
- `/etc/default/...`
- `/etc/systemd/system/...`
- `/opt/domaincheck/...`
- 脚本会执行:
- `systemctl enable`
- `systemctl restart`
- `chown`
- 可能还有 `dnf`
这些都属于 `root` 口径,不适合 `www` 用户执行。
补充说明:
- 脚本入口仍然是 `root`
- 但国外机的一键脚本内部会自动识别仓库属主
- 然后把前端构建切到仓库属主执行
- 这样可以避免把 `node_modules``dist`、源码目录权限烧成 `root`
最稳的执行方式:
```bash
sudo -i
```
然后再继续后面的复制、编辑和执行脚本。
### 4. 统一软链接
所有机器都统一建立:
```bash
mkdir -p /opt/domaincheck
ln -sfn /www/wwwroot/getDomain/domain-api /opt/domaincheck/domain-api
ln -sfn /www/wwwroot/getDomain/domain-web /opt/domaincheck/domain-web
ln -sfn /www/wwwroot/getDomain/domainCheck /opt/domaincheck/domainCheck
```
### 5. 并发配置约定
- 默认检测并发由后台系统设置里的 `默认检测线程数` 控制
- 若某台机器需要单独并发,可在后台系统设置里增加 `节点独立线程覆盖`
- 规则是:
- 节点已单独配置:走该节点自己的线程数
- 节点未单独配置:回退到默认线程数
- 节点编码必须和该机器 `/etc/default/domaincheck-worker` 里的 `NODE_CODE` 一致
- 配置会同时写入:
- Redis`domain_tool:thread_count`
- Redis`domain_tool:node_thread_counts`
- 本地文件:`thread_count.json`
- 本地文件:`node_thread_counts.json`
### 6. 宝塔与 `nvm` 口径
- 国外主控机:
- 你如果已经长期用宝塔管理 `PostgreSQL``Redis``Nginx`,可以继续用
-`domain-api``domaincheck-worker``domaincheck-sync-agent` 仍建议走 `systemd`
- 国内机器:
- 不建议为了纯 `worker` 节点额外安装宝塔
- 原因不是不能用,而是会多一层面板进程、端口、权限和故障面
- 国内机器的最佳形态是:
- 只装 Python 运行环境
- 只装项目代码
- 只跑 `domaincheck-worker`
- 若是 controller再加 `domaincheck-sync-agent`
- Node
- 继续用 `nvm` 没问题,不需要改成系统全局安装
- 建议统一到 `Node 20`
- 生产构建时只要保证执行构建命令前已经 `nvm use 20`
### 6.1 `nvm` 安装命令
如果机器还没有安装 `nvm`,先执行:
```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
nvm -v
nvm install 20
nvm use 20
```
补充说明:
- 国外机器如果要构建前端,必须先确保 `nvm` 可用
- 一键部署脚本会调用 `nvm install 20``nvm use 20`
- 前端构建由仓库属主执行,不是由 `root` 直接执行
- 如果你用 `root` 执行脚本,而 `nvm` 实际装在别的用户目录下:
- 不需要改成 `www` 执行脚本
- 直接在 `install_overseas_quick.conf` 里填写:
- `NODE_NVM_DIR=/home/www/.nvm`
- 当前一键脚本会按这个顺序找 Node 运行环境:
- `NODE_NVM_DIR`
- 当前环境变量里的 `NVM_DIR`
- `/root/.nvm`
- `/home/www/.nvm`
- `/www/server/nvm`
- `/usr/local/nvm`
- 最后再尝试系统全局 `node``npm`
### 7. 你实际应该怎么选
- 如果国外主控机已经是宝塔机:
- 继续沿用宝塔即可
- 宝塔负责:
- `Nginx`
- `PostgreSQL`
- `Redis`
- SSL 证书
- 项目自身服务继续交给:
- `domaincheck-api.service`
- `domaincheck-worker.service`
- 如果国内机器只是跑检测:
- 不要装宝塔
- 直接按本文的纯命令部署
- 这样最轻、最稳、最容易横向复制
### 8. Python 3.11 必须单独安装
- 这里不能只依赖系统默认 `python3`
- 必须明确安装 `python3.11`
- 因为本文后续所有部署命令都直接使用:
- `python3.11 -m venv .venv`
安装命令:
```bash
dnf install -y python3.11 python3.11-devel
```
安装后先验证:
```bash
python3.11 --version
```
期望输出类似:
```text
Python 3.11.x
```
### 8.1 Linux 依赖安装特别说明
`domainCheck/requirements.txt` 是从桌面版沿用过来的,里面包含 Windows 专用依赖,例如:
- `pywin32`
- `pywin32-ctypes`
- `twisted-iocpsupport`
- `win32-setctime`
这些包在 Linux 上不能直接安装。
现在的 3 个一键部署脚本已经内置了兼容处理:
- 会自动读取 `domainCheck/requirements.txt`
- 自动生成 `requirements.linux.txt`
- 自动过滤掉 Windows-only 依赖
- 然后再执行 Linux 环境安装
所以现在在 Linux 上看到这类报错时,正确处理方式不是手工删仓库依赖,而是:
1.`git pull`
2. 确认脚本已经更新到最新版本
3. 重新执行一键部署脚本
如果你是手工部署,不走一键脚本,那就不要直接跑:
```bash
pip install -r requirements.txt
```
而应该改成:
```bash
cd /opt/domaincheck/domainCheck
python3.11 - <<'PY'
from pathlib import Path
source = Path("requirements.txt")
target = Path("requirements.linux.txt")
raw = source.read_bytes()
for encoding in ("utf-8", "utf-16", "utf-16-le", "utf-16-be", "gbk"):
try:
text = raw.decode(encoding)
break
except UnicodeDecodeError:
continue
else:
raise SystemExit("无法解析 requirements.txt 编码")
skip_prefixes = (
"pywin32==",
"pywin32-ctypes==",
"twisted-iocpsupport==",
"win32-setctime==",
)
lines = []
for line in text.splitlines():
normalized = line.strip().lstrip("\ufeff")
if normalized.lower().startswith(skip_prefixes):
continue
lines.append(normalized)
target.write_text("\n".join(lines) + "\n", encoding="utf-8")
PY
source .venv/bin/activate
pip install -r requirements.linux.txt
```
## 四、最短复制块
这一节给你三段最短命令。
用法:
- 急着部署时,先用这里
- 想看每一步解释,再看后面的完整章节
### 1. 国外主控机最短复制块
先把下面 5 个值替换掉再执行:
```text
API_DOMAIN
ADMIN_DOMAIN
DB_PASSWORD
OVERSEAS_NODE_CODE
NODE_VERSION
```
也可以直接改脚本头部变量后执行:
```bash
cp /www/wwwroot/getDomain/domain-api/deploy/multi-region/install_overseas_quick.conf.example \
/www/wwwroot/getDomain/domain-api/deploy/multi-region/install_overseas_quick.conf
# 先编辑 install_overseas_quick.conf
bash /www/wwwroot/getDomain/domain-api/deploy/multi-region/install_overseas_quick.sh
```
注意:
- 这条脚本不会执行 `git fetch`
- 这条脚本不会执行 `git pull`
- 你要先自己把 `/www/wwwroot/getDomain` 更新到目标版本,再跑脚本
- 这条脚本会自动过滤 Windows-only Python 依赖
- 这条脚本结束时会自动执行:`chown -R www:www /www/wwwroot/getDomain`
```bash
cd /www/wwwroot/getDomain
git fetch --all
git checkout GIT_BRANCH
git pull --ff-only origin GIT_BRANCH
mkdir -p /opt/domaincheck
ln -sfn /www/wwwroot/getDomain/domain-api /opt/domaincheck/domain-api
ln -sfn /www/wwwroot/getDomain/domain-web /opt/domaincheck/domain-web
ln -sfn /www/wwwroot/getDomain/domainCheck /opt/domaincheck/domainCheck
cd /opt/domaincheck/domainCheck
python3.11 -m venv .venv || true
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
/opt/domaincheck/domainCheck/.venv/bin/pip install fastapi uvicorn pydantic-settings psycopg2-binary redis openpyxl python-multipart
cat >/opt/domaincheck/domainCheck/.env <<EOF
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=domain
DB_USER=domainuser
DB_PASSWORD=DB_PASSWORD
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
EOF
/opt/domaincheck/domainCheck/.venv/bin/python /opt/domaincheck/domainCheck/init_database.py
cat >/opt/domaincheck/domain-web/.env.production <<EOF
VITE_API_BASE_URL=https://API_DOMAIN/api/v1
EOF
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
nvm install NODE_VERSION
nvm use NODE_VERSION
cd /opt/domaincheck/domain-web
npm install
npm run build
cp /opt/domaincheck/domain-api/deploy/multi-region/templates/domaincheck-api.env.example /etc/default/domaincheck-api
sed -i 's#^DOMAIN_ROOT=.*#DOMAIN_ROOT=/opt/domaincheck/domainCheck#' /etc/default/domaincheck-api
sed -i 's#^API_HOST=.*#API_HOST=0.0.0.0#' /etc/default/domaincheck-api
sed -i 's#^API_PORT=.*#API_PORT=8100#' /etc/default/domaincheck-api
sed -i 's#^NODE_CODE=.*#NODE_CODE=OVERSEAS_NODE_CODE#' /etc/default/domaincheck-api
sed -i 's#^NODE_REGION=.*#NODE_REGION=overseas#' /etc/default/domaincheck-api
sed -i 's#^NODE_ROLE=.*#NODE_ROLE=control#' /etc/default/domaincheck-api
sed -i 's#^SYNC_PUSH_ENABLED=.*#SYNC_PUSH_ENABLED=false#' /etc/default/domaincheck-api
sed -i 's#^CORS_ORIGINS=.*#CORS_ORIGINS=https://ADMIN_DOMAIN,http://127.0.0.1:3201,http://localhost:3201#' /etc/default/domaincheck-api
cp /opt/domaincheck/domain-api/deploy/systemd/domain-api.service /etc/systemd/system/domaincheck-api.service
cp /opt/domaincheck/domain-api/deploy/systemd/domain-worker.service /etc/systemd/system/domaincheck-worker.service
mkdir -p /opt/domaincheck/domain-api/runtime
touch /opt/domaincheck/domainCheck/detect_worker.log
chown -R www:www /opt/domaincheck/domain-api/runtime
chown www:www /opt/domaincheck/domainCheck/detect_worker.log
chmod 664 /opt/domaincheck/domainCheck/detect_worker.log
systemctl daemon-reload
systemctl enable domaincheck-api
systemctl enable domaincheck-worker
systemctl restart domaincheck-api
systemctl restart domaincheck-worker
curl http://127.0.0.1:8100/health
curl http://127.0.0.1:8100/api/v1/runtime/preflight
curl http://127.0.0.1:8100/api/v1/runtime/readiness
```
### 2. 国内 controller 最短复制块
先把下面 3 个值替换掉再执行:
```text
DB_PASSWORD
SYNC_SHARED_TOKEN
MAINLAND_CONTROLLER_NODE_CODE
```
也可以直接改脚本头部变量后执行:
```bash
cp /www/wwwroot/getDomain/domain-api/deploy/multi-region/install_mainland_controller_quick.conf.example \
/www/wwwroot/getDomain/domain-api/deploy/multi-region/install_mainland_controller_quick.conf
# 先编辑 install_mainland_controller_quick.conf
bash /www/wwwroot/getDomain/domain-api/deploy/multi-region/install_mainland_controller_quick.sh
```
注意:
- 这条脚本不会执行 `git fetch`
- 这条脚本不会执行 `git pull`
- 你要先自己把 `/www/wwwroot/getDomain` 更新到目标版本,再跑脚本
- 这条脚本会自动过滤 Windows-only Python 依赖
- 这条脚本结束时会自动执行:`chown -R www:www /www/wwwroot/getDomain`
```bash
cd /www/wwwroot/getDomain
git fetch --all
git checkout GIT_BRANCH
git pull --ff-only origin GIT_BRANCH
mkdir -p /opt/domaincheck
ln -sfn /www/wwwroot/getDomain/domain-api /opt/domaincheck/domain-api
ln -sfn /www/wwwroot/getDomain/domain-web /opt/domaincheck/domain-web
ln -sfn /www/wwwroot/getDomain/domainCheck /opt/domaincheck/domainCheck
cd /opt/domaincheck/domainCheck
python3.11 -m venv .venv || true
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
/opt/domaincheck/domainCheck/.venv/bin/pip install fastapi uvicorn pydantic-settings psycopg2-binary redis openpyxl python-multipart
cat >/opt/domaincheck/domainCheck/.env <<EOF
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=domain
DB_USER=domainuser
DB_PASSWORD=DB_PASSWORD
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
EOF
cd /opt/domaincheck/domain-api
bash deploy/multi-region/bootstrap_mainland.sh /opt/domaincheck controller
cat >/etc/default/domaincheck-worker <<EOF
WORKER_MODE=linux-systemd
QT_QPA_PLATFORM=offscreen
NODE_CODE=MAINLAND_CONTROLLER_NODE_CODE
NODE_REGION=mainland
NODE_ROLE=control
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=domain
DB_USER=domainuser
DB_PASSWORD=DB_PASSWORD
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
SYNC_PUSH_ENABLED=true
SYNC_SOURCE_REGION=mainland
SYNC_TARGET_REGION=overseas
SYNC_TARGET_API_BASE_URL=https://api.domain.com/api/v1
SYNC_SHARED_TOKEN=SYNC_SHARED_TOKEN
SYNC_BATCH_SIZE=200
SYNC_POLL_INTERVAL_SECONDS=30
EOF
systemctl daemon-reload
systemctl enable domaincheck-worker
systemctl enable domaincheck-sync-agent
systemctl restart domaincheck-worker
systemctl restart domaincheck-sync-agent
cd /opt/domaincheck/domain-api
bash deploy/multi-region/check_mainland_controller.sh
```
### 3. 国内 worker 最短复制块
先把下面 4 个值替换掉再执行:
```text
DB_PASSWORD
SYNC_SHARED_TOKEN
MAINLAND_WORKER_NODE_CODE
MAINLAND_CONTROLLER_IP
```
也可以直接改脚本头部变量后执行:
```bash
cp /www/wwwroot/getDomain/domain-api/deploy/multi-region/install_mainland_worker_quick.conf.example \
/www/wwwroot/getDomain/domain-api/deploy/multi-region/install_mainland_worker_quick.conf
# 先编辑 install_mainland_worker_quick.conf
bash /www/wwwroot/getDomain/domain-api/deploy/multi-region/install_mainland_worker_quick.sh
```
注意:
- 这条脚本不会执行 `git fetch`
- 这条脚本不会执行 `git pull`
- 你要先自己把 `/www/wwwroot/getDomain` 更新到目标版本,再跑脚本
- 这条脚本会自动过滤 Windows-only Python 依赖
- 这条脚本结束时会自动执行:`chown -R www:www /www/wwwroot/getDomain`
```bash
cd /www/wwwroot/getDomain
git fetch --all
git checkout GIT_BRANCH
git pull --ff-only origin GIT_BRANCH
mkdir -p /opt/domaincheck
ln -sfn /www/wwwroot/getDomain/domain-api /opt/domaincheck/domain-api
ln -sfn /www/wwwroot/getDomain/domain-web /opt/domaincheck/domain-web
ln -sfn /www/wwwroot/getDomain/domainCheck /opt/domaincheck/domainCheck
cd /opt/domaincheck/domainCheck
python3.11 -m venv .venv || true
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
/opt/domaincheck/domainCheck/.venv/bin/pip install fastapi uvicorn pydantic-settings psycopg2-binary redis openpyxl python-multipart
cat >/opt/domaincheck/domainCheck/.env <<EOF
DB_HOST=MAINLAND_CONTROLLER_IP
DB_PORT=5432
DB_DATABASE=domain
DB_USER=domainuser
DB_PASSWORD=DB_PASSWORD
REDIS_HOST=MAINLAND_CONTROLLER_IP
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
EOF
cd /opt/domaincheck/domain-api
bash deploy/multi-region/bootstrap_mainland.sh /opt/domaincheck worker
cat >/etc/default/domaincheck-worker <<EOF
WORKER_MODE=linux-systemd
QT_QPA_PLATFORM=offscreen
NODE_CODE=MAINLAND_WORKER_NODE_CODE
NODE_REGION=mainland
NODE_ROLE=worker
DB_HOST=MAINLAND_CONTROLLER_IP
DB_PORT=5432
DB_DATABASE=domain
DB_USER=domainuser
DB_PASSWORD=DB_PASSWORD
REDIS_HOST=MAINLAND_CONTROLLER_IP
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
SYNC_PUSH_ENABLED=true
SYNC_SOURCE_REGION=mainland
SYNC_TARGET_REGION=overseas
SYNC_TARGET_API_BASE_URL=https://api.domain.com/api/v1
SYNC_SHARED_TOKEN=SYNC_SHARED_TOKEN
SYNC_BATCH_SIZE=200
SYNC_POLL_INTERVAL_SECONDS=30
EOF
systemctl daemon-reload
systemctl enable domaincheck-worker
systemctl restart domaincheck-worker
```
## 五、所有机器先执行一次
### 1. 安装基础环境
```bash
dnf update -y
dnf install -y epel-release
dnf install -y git nginx redis postgresql postgresql-server postgresql-contrib gcc gcc-c++ make wget curl tar policycoreutils-python-utils
dnf install -y python3.11 python3.11-devel
```
### 2. 克隆代码
```bash
mkdir -p /www/wwwroot
cd /www/wwwroot
git clone 你的仓库地址 getDomain
cd getDomain
git checkout main
git pull --ff-only origin main
```
### 3. 建立运行目录软链接
```bash
mkdir -p /opt/domaincheck
ln -sfn /www/wwwroot/getDomain/domain-api /opt/domaincheck/domain-api
ln -sfn /www/wwwroot/getDomain/domain-web /opt/domaincheck/domain-web
ln -sfn /www/wwwroot/getDomain/domainCheck /opt/domaincheck/domainCheck
```
### 4. 创建 Python 虚拟环境
```bash
cd /opt/domaincheck/domainCheck
python3.11 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
python3.11 - <<'PY'
from pathlib import Path
source = Path("requirements.txt")
target = Path("requirements.linux.txt")
raw = source.read_bytes()
for encoding in ("utf-8", "utf-16", "utf-16-le", "utf-16-be", "gbk"):
try:
text = raw.decode(encoding)
break
except UnicodeDecodeError:
continue
else:
raise SystemExit("无法解析 requirements.txt 编码")
skip_prefixes = (
"pywin32==",
"pywin32-ctypes==",
"twisted-iocpsupport==",
"win32-setctime==",
)
lines = []
for line in text.splitlines():
normalized = line.strip().lstrip("\ufeff")
if normalized.lower().startswith(skip_prefixes):
continue
lines.append(normalized)
target.write_text("\n".join(lines) + "\n", encoding="utf-8")
PY
pip install -r requirements.linux.txt
cd /opt/domaincheck/domain-api
/opt/domaincheck/domainCheck/.venv/bin/pip install fastapi uvicorn pydantic-settings psycopg2-binary redis openpyxl python-multipart
```
## 六、国外机器一键部署
这一节默认你是:
- 国外主控机
- 可以使用宝塔管理 `Nginx / PostgreSQL / Redis`
- 项目进程仍由 `systemd` 托管
国外机器承担:
- `domain-web`
- `domain-api`
- PostgreSQL
- Redis
- `domaincheck-worker`
### 1. PostgreSQL 和 Redis 处理方式
如果你已经用宝塔装好了:
- `PostgreSQL`
- `Redis`
那这一步可以跳过,直接看后面的数据库账号和项目配置。
如果你不用宝塔,就执行下面命令。
### 2. 初始化 PostgreSQL
```bash
postgresql-setup --initdb
systemctl enable postgresql
systemctl start postgresql
```
创建数据库:
```bash
sudo -u postgres psql <<'SQL'
CREATE USER domainuser WITH PASSWORD '请改成你的强密码';
CREATE DATABASE domain OWNER domainuser;
GRANT ALL PRIVILEGES ON DATABASE domain TO domainuser;
SQL
```
### 3. 启动 Redis
```bash
systemctl enable redis
systemctl start redis
```
### 4. 配置 `domainCheck/.env`
```bash
cat >/opt/domaincheck/domainCheck/.env <<'EOF'
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=domain
DB_USER=domainuser
DB_PASSWORD=请改成你的强密码
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
EOF
```
### 5. 初始化数据库结构
```bash
cd /opt/domaincheck/domainCheck
/opt/domaincheck/domainCheck/.venv/bin/python init_database.py
```
### 6. 配置前端生产环境
```bash
cat >/opt/domaincheck/domain-web/.env.production <<'EOF'
VITE_API_BASE_URL=https://api.domain.com/api/v1
EOF
```
### 7. 用 `nvm` 构建前端
如果这台机器还没装 `nvm`,先执行:
```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
nvm -v
```
然后固定使用:
```bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
nvm install 20
nvm use 20
node -v
```
然后再构建:
```bash
cd /opt/domaincheck/domain-web
npm install
npm run build
```
### 8. 配置 API 环境变量
```bash
cp /opt/domaincheck/domain-api/deploy/multi-region/templates/domaincheck-api.env.example /etc/default/domaincheck-api
sed -i 's#^DOMAIN_ROOT=.*#DOMAIN_ROOT=/opt/domaincheck/domainCheck#' /etc/default/domaincheck-api
sed -i 's#^API_HOST=.*#API_HOST=0.0.0.0#' /etc/default/domaincheck-api
sed -i 's#^API_PORT=.*#API_PORT=8100#' /etc/default/domaincheck-api
sed -i 's#^NODE_CODE=.*#NODE_CODE=overseas-control-01#' /etc/default/domaincheck-api
sed -i 's#^NODE_REGION=.*#NODE_REGION=overseas#' /etc/default/domaincheck-api
sed -i 's#^NODE_ROLE=.*#NODE_ROLE=control#' /etc/default/domaincheck-api
sed -i 's#^SYNC_PUSH_ENABLED=.*#SYNC_PUSH_ENABLED=false#' /etc/default/domaincheck-api
sed -i 's#^CORS_ORIGINS=.*#CORS_ORIGINS=https://admin.domain,http://127.0.0.1:3201,http://localhost:3201#' /etc/default/domaincheck-api
```
### 9. 安装 systemd
```bash
cp /opt/domaincheck/domain-api/deploy/systemd/domain-api.service /etc/systemd/system/domaincheck-api.service
cp /opt/domaincheck/domain-api/deploy/systemd/domain-worker.service /etc/systemd/system/domaincheck-worker.service
```
### 10. 修正权限
```bash
mkdir -p /opt/domaincheck/domain-api/runtime
touch /opt/domaincheck/domainCheck/detect_worker.log
chown -R www:www /opt/domaincheck/domain-api/runtime
chown www:www /opt/domaincheck/domainCheck/detect_worker.log
chmod 664 /opt/domaincheck/domainCheck/detect_worker.log
chown -R www:www /www/wwwroot/getDomain
```
### 11. 启动国外服务
```bash
systemctl daemon-reload
systemctl enable domaincheck-api
systemctl enable domaincheck-worker
systemctl restart domaincheck-api
systemctl restart domaincheck-worker
```
### 12. 配置 Nginx
如果你已经用宝塔建好了:
- `admin.domain`
- `api.domain.com`
并且已经在宝塔里配好了反代和静态站点,那么这一节可以不执行。
如果你不用宝塔,就直接执行下面命令。
后台站点:
```bash
cat >/etc/nginx/conf.d/admin.domain.conf <<'EOF'
server {
listen 80;
server_name admin.domain;
root /opt/domaincheck/domain-web/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
EOF
```
API 站点:
```bash
cat >/etc/nginx/conf.d/api.domain.com.conf <<'EOF'
server {
listen 80;
server_name api.domain.com;
location / {
proxy_pass http://127.0.0.1:8100;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
EOF
```
启动 Nginx
```bash
nginx -t
systemctl enable nginx
systemctl restart nginx
```
### 13. 国外机器验收
```bash
curl http://127.0.0.1:8100/health
curl http://127.0.0.1:8100/api/v1/runtime/preflight
curl http://127.0.0.1:8100/api/v1/runtime/readiness
cd /opt/domaincheck/domain-api/deploy/linux
python3 smoke_test.py --base-url http://127.0.0.1:8100
```
## 七、国内 controller 一键部署
这一节默认你是:
- 国内 controller
- 不装宝塔
- 只跑 `domaincheck-worker + domaincheck-sync-agent`
- 本机承载 Redis / PostgreSQL 控制面
### 1. 先准备 Redis 和 PostgreSQL
```bash
postgresql-setup --initdb
systemctl enable postgresql
systemctl start postgresql
systemctl enable redis
systemctl start redis
```
创建数据库:
```bash
sudo -u postgres psql <<'SQL'
CREATE USER domainuser WITH PASSWORD '请改成你的强密码';
CREATE DATABASE domain OWNER domainuser;
GRANT ALL PRIVILEGES ON DATABASE domain TO domainuser;
SQL
```
### 2. 配置 `domainCheck/.env`
```bash
cat >/opt/domaincheck/domainCheck/.env <<'EOF'
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=domain
DB_USER=domainuser
DB_PASSWORD=请改成你的强密码
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
EOF
```
### 3. 执行 bootstrap
```bash
cd /opt/domaincheck/domain-api
bash deploy/multi-region/bootstrap_mainland.sh /opt/domaincheck controller
```
### 4. 配置 `/etc/default/domaincheck-worker`
```bash
cat >/etc/default/domaincheck-worker <<'EOF'
WORKER_MODE=linux-systemd
QT_QPA_PLATFORM=offscreen
NODE_CODE=mainland-controller-01
NODE_REGION=mainland
NODE_ROLE=control
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=domain
DB_USER=domainuser
DB_PASSWORD=请改成你的强密码
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
SYNC_PUSH_ENABLED=true
SYNC_SOURCE_REGION=mainland
SYNC_TARGET_REGION=overseas
SYNC_TARGET_API_BASE_URL=https://api.domain.com/api/v1
SYNC_SHARED_TOKEN=请改成你的共享令牌
SYNC_BATCH_SIZE=200
SYNC_POLL_INTERVAL_SECONDS=30
EOF
```
### 5. 启动 controller 服务
```bash
systemctl daemon-reload
systemctl enable domaincheck-worker
systemctl enable domaincheck-sync-agent
systemctl restart domaincheck-worker
systemctl restart domaincheck-sync-agent
```
### 6. 本机检查
```bash
cd /opt/domaincheck/domain-api
bash deploy/multi-region/check_mainland_controller.sh
```
## 八、国内 worker 一键部署
这一节默认你是:
- 国内纯 worker 节点
- 不装宝塔
- 不承载后台
- 不承载 API
- 不承载 Nginx
### 1. 配置 `domainCheck/.env`
如果它连接 controller 的同一套 Redis / PostgreSQL就改成 controller 地址:
```bash
cat >/opt/domaincheck/domainCheck/.env <<'EOF'
DB_HOST=国内controller机器IP
DB_PORT=5432
DB_DATABASE=domain
DB_USER=domainuser
DB_PASSWORD=请改成你的强密码
REDIS_HOST=国内controller机器IP
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
EOF
```
### 2. 执行 bootstrap
```bash
cd /opt/domaincheck/domain-api
bash deploy/multi-region/bootstrap_mainland.sh /opt/domaincheck worker
```
### 3. 配置 `/etc/default/domaincheck-worker`
```bash
cat >/etc/default/domaincheck-worker <<'EOF'
WORKER_MODE=linux-systemd
QT_QPA_PLATFORM=offscreen
NODE_CODE=mainland-worker-01
NODE_REGION=mainland
NODE_ROLE=worker
DB_HOST=国内controller机器IP
DB_PORT=5432
DB_DATABASE=domain
DB_USER=domainuser
DB_PASSWORD=请改成你的强密码
REDIS_HOST=国内controller机器IP
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
SYNC_PUSH_ENABLED=true
SYNC_SOURCE_REGION=mainland
SYNC_TARGET_REGION=overseas
SYNC_TARGET_API_BASE_URL=https://api.domain.com/api/v1
SYNC_SHARED_TOKEN=请改成你的共享令牌
SYNC_BATCH_SIZE=200
SYNC_POLL_INTERVAL_SECONDS=30
EOF
```
### 4. 启动 worker
```bash
systemctl daemon-reload
systemctl enable domaincheck-worker
systemctl restart domaincheck-worker
```
## 九、统一更新命令
以后你更新代码,三台机器统一执行:
```bash
cd /www/wwwroot/getDomain
git fetch --all
git checkout main
git pull --ff-only origin main
```
然后:
### 国外机器
如果国外机器前端构建使用 `nvm`,更新时建议固定这么跑:
```bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
nvm install 20
nvm use 20
cd /opt/domaincheck/domain-web
npm install
npm run build
systemctl restart domaincheck-api
systemctl restart domaincheck-worker
systemctl restart nginx
```
### 国内 controller
```bash
systemctl restart domaincheck-worker
systemctl restart domaincheck-sync-agent
```
### 国内 worker
```bash
systemctl restart domaincheck-worker
```
补充说明:
- 一键部署脚本更新后,会在脚本末尾自动执行一次:
- `chown -R www:www /www/wwwroot/getDomain`
- 如果你是完全手工更新,也建议补执行一次,避免后续 `www` 用户 `git pull` 或宝塔读写再次撞权限
## 十、最终联调检查
在国外机器执行:
```bash
curl https://api.domain.com/api/v1/runtime/readiness
curl https://api.domain.com/api/v1/runtime/cluster
curl https://api.domain.com/api/v1/runtime/sync-summary
```
期望:
- `runtime/readiness.status` 不是 `blocking`
- `runtime/cluster` 能看到:
- `overseas-control-01`
- `mainland-controller-01`
- `mainland-worker-01`
- `runtime/sync-summary` 能看到 `detect_result_batches`
## 十一、如果当前还不能上国内机器
先在国外机器演练:
```bash
cd /opt/domaincheck/domain-api
bash deploy/multi-region/rehearse_multi_region.sh http://127.0.0.1:8100
```
如果它输出:
```text
rehearsal passed
```
说明:
- 多机脚本没问题
- readiness 判断没问题
- cluster 视图没问题
- 可以开始把同样方式复制到其他机器
## 十二、最推荐的真实落地顺序
如果按你的使用习惯,我建议你就按下面走:
### 方案 A最符合你现在的习惯
- 国外主控机:
- 宝塔管理 `Nginx / PostgreSQL / Redis / SSL`
- `systemd` 管理 `domaincheck-api / domaincheck-worker`
- 前端构建使用 `nvm`
- 国内 controller
- 不装宝塔
-`PostgreSQL / Redis / domaincheck-worker / domaincheck-sync-agent`
- 国内 worker
- 不装宝塔
- 只跑 `domaincheck-worker`
### 方案 B如果以后你想进一步简化
- 国外机继续保持不变
- 国内 controller 也可以后续去掉本地 PostgreSQL只保留
- `Redis`
- `domaincheck-worker`
- `domaincheck-sync-agent`
- 国内 worker 全部连 controller 的 Redis 和数据库
### 当前阶段我建议你先不要改的
- 不要为了统一而让国内 worker 也装宝塔
- 不要为了“更标准”把 `nvm` 改成全局 Node
- 不要让项目服务交给宝塔守护进程
## 十三、最终建议
你现在最稳的部署顺序就是:
1. 先部署国外机器
2. 国外机器 smoke test 通过
3. 国外机器多机模拟演练通过
4. 再部署国内 controller
5. 再部署国内 worker
6. 最后看 `api.domain.com` 的 readiness / cluster / sync-summary
一句话总结:
> 先把国外主控机跑稳,再用同样脚本扩到 2 台国内机器;代码更新统一走 git部署执行直接复制这份文档里的命令。