🔄 卡若AI 同步 2026-02-22 14:59 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 8 个

This commit is contained in:
2026-02-22 14:59:39 +08:00
parent 275712c8b1
commit 34f04372a8
8 changed files with 450 additions and 2 deletions

View File

@@ -166,7 +166,9 @@ bash scripts/存客宝_lytiao_Docker部署.sh
### 5. kr宝塔 运行堵塞 + Node 深度修复
- **运行堵塞**(负载 100%、CPU 98%):结束异常 node 进程、停 Node、修复 site.db、查日志、批量启动。
- **负载诊断**`./scripts/.venv_tx/bin/python scripts/腾讯云_TAT_kr宝塔_负载诊断.py`(只读,输出负载/CPU/Node 进程)
- **负载分析文档**`references/kr宝塔_负载100_原因分析与处理.md`
- **运行堵塞修复**:结束异常 node、停 Node、修复 site.db、批量启动。
- **TAT**`./scripts/.venv_tx/bin/python scripts/腾讯云_TAT_kr宝塔_运行堵塞与Node深度修复.py`
- **宝塔终端**(推荐):上传 `scripts/kr宝塔_运行堵塞与Node深度修复_宝塔终端执行.sh``bash` 执行。

View File

@@ -237,10 +237,19 @@ ssh -p 22022 -i "服务器管理/Steam/id_ed25519" root@43.139.27.93 "nginx -s r
---
## 七、本次诊断结果摘要2026-02-20
## 七、负载 100% 详细分析
见独立文档:`references/kr宝塔_负载100_原因分析与处理.md`
**要点**:负载由 Node 项目 MODULE_NOT_FOUND 崩溃循环导致;修正 site.db 启动命令后可批量启动。TAT 诊断:`scripts/腾讯云_TAT_kr宝塔_负载诊断.py`
---
## 八、本次诊断结果摘要2026-02-20
- **本机 → kr宝塔**ping 正常22022 端口可达。
- **SSH**:曾出现 Connection closed by remote host建议用宝塔面板终端执行上述命令。
- **负载**TAT 诊断显示负载已降至 0.5Node 进程数为 0全停根因是 Node 崩溃循环。
- **带宽**:近 24h 公网出带宽最大 5.1 Mbps已顶满 5M是「带宽卡」的主要原因已提供腾讯云脚本与宝塔终端处理步骤。
---

View File

@@ -0,0 +1,179 @@
# kr宝塔 负载 100% · 原因分析与处理
> 适用43.139.27.93kr宝塔2核4G41 站点)。当宝塔首页显示「运行堵塞」、负载 100%、CPU 98% 时参考。
---
## 一、负载原因分析(按可能性排序)
### 1. Node 项目反复崩溃重启(最常见)
**现象**:多个 Node 项目配置错误(如 MODULE_NOT_FOUND、启动命令 `node /path` 把目录当入口),启动即失败,宝塔/pm2 自动重启,形成死循环。
**占用**:每个 `npm start` / `node` 进程 3050% CPU2 核下 23 个即可占满。
**判断**
```bash
ps aux | grep -E 'node|npm|pnpm' # 看是否有大量 node 进程
# 宝塔 Node 项目 → 查看启动日志,是否有 MODULE_NOT_FOUND
```
**处理**
1. 停止全部 Node 项目(宝塔 Node 项目 → 批量停止)
2. 修正启动命令为 `cd /项目根目录 && (pnpm start || npm run start)`
3. 逐一启动,确认无报错后再启动下一个
4. 脚本:`腾讯云_TAT_kr宝塔_运行堵塞与Node深度修复.py``kr宝塔_运行堵塞与Node深度修复_宝塔终端执行.sh`
---
### 2. 多站并发 + 2核瓶颈
**现象**41 个站点、10+ Node 应用,在 2 核 CPU 上高并发时易顶满。
**占用**Nginx + 多个 next-server/node 进程,单核处理能力有限。
**判断**
```bash
ps aux --sort=-%cpu | head -15 # 看 TOP 进程
ss -ant state established | wc -l # 连接数
```
**处理**
- 短期:停用或合并低优先级 Node 项目
- 长期:升级为 4 核(腾讯云控制台 → 实例 → 调整配置)
---
### 3. 带宽打满导致请求堆积
**现象**:出带宽 5M 顶满请求排队Nginx/上游响应变慢连接数堆积CPU 处理堆积请求升高。
**判断**
```bash
# 腾讯云监控或脚本
./scripts/.venv_tx/bin/python scripts/kr宝塔_腾讯云带宽与CPU近24h.py
```
**处理**升级带宽、Nginx 限速、优化大流量站点CDN、压缩、缓存
---
### 4. 异常进程 / 挖矿 / 攻击
**现象**:未知进程占满 CPU或单 IP 连接数异常多。
**判断**
```bash
ps aux --sort=-%cpu | head -20
ss -antn state established | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -10
```
**处理**:结束异常进程;对异常 IP 限连接数或封禁(宝塔安全/防火墙)。
---
### 5. 磁盘 I/O 满或磁盘满
**现象**`df -h` 显示 90%+ 占用,或 `iostat` 显示高 I/O 等待。
**判断**
```bash
df -h / /www
du -sh /www/wwwlogs /var/log /tmp 2>/dev/null
```
**处理**:清理日志(见下文「六、一键清理」)、扩容磁盘。
---
## 二、诊断命令速查(宝塔终端执行)
```bash
# 一键诊断
echo "=== 负载 ===" && uptime
echo "=== 内存 ===" && free -m
echo "=== 磁盘 ===" && df -h / /www
echo "=== 连接数 ===" && ss -ant state established | wc -l
echo "=== CPU TOP10 ===" && ps aux --sort=-%cpu | head -11
echo "=== Node 进程 ===" && ps aux | grep -E 'node|npm' | grep -v grep
```
---
## 三、TAT 远程诊断(无需 SSH
```bash
./scripts/.venv_tx/bin/python scripts/腾讯云_TAT_kr宝塔_负载诊断.py
```
输出负载、内存、磁盘、连接数、TOP 进程、Node 进程详情。
---
## 四、处理流程(优先级)
| 优先级 | 动作 | 说明 |
|--------|------|------|
| 1 | 停止全部 Node | 先降压,避免反复崩溃重启 |
| 2 | 修正 Node 启动命令 | 解决 MODULE_NOT_FOUND避免死循环 |
| 3 | 结束高 CPU 进程 | `kill -9 PID` 异常 node/npm |
| 4 | 清理磁盘/日志 | 释放空间,减轻 I/O |
| 5 | 逐一启动 Node | 确认无报错再启下一个 |
| 6 | 长期:升级配置 | 4 核、升带宽 |
---
## 五、Node 反复崩溃的根因与修复
**根因**:宝塔 Node 项目配置的启动命令为 `node /www/wwwroot/self/wanzhi/玩值大屏`Node 把**目录路径**当模块加载,报 MODULE_NOT_FOUND进程退出宝塔自动重启循环往复。
**正确启动命令**
```
cd /www/wwwroot/self/wanzhi/玩值大屏 && (pnpm start 2>/dev/null || npm run start)
```
**修复**:运行 `kr宝塔_运行堵塞与Node深度修复_宝塔终端执行.sh`,脚本会自动修正 site.db 中所有 Node 项目的 `project_script`
---
## 六、一键清理(磁盘 / 日志)
在宝塔终端执行:
```bash
# 清理网站日志7 天前、>50M 截断)
find /www/wwwlogs -name '*.log' -mtime +7 -type f -delete
find /www/wwwlogs -name '*.log' -type f -size +50M -exec truncate -s 0 {} \;
# 清理 /tmp
find /tmp -type f -mtime +7 -delete
# 清理系统日志7 天前)
find /var/log -name '*.log' -mtime +7 -type f -delete
```
---
## 七、带宽相关(已有结论)
- **近 24h**:公网出带宽最大 5.1 Mbps顶满 5M
- **建议**升级带宽、Nginx 限速limit_conn、limit_rate
---
---
## 八、本次诊断结果2026-02-20 TAT inv-i08m1g0e3t
| 指标 | 数值 | 结论 |
|------|------|------|
| 负载 | 0.00, 0.05, 0.52 | ✅ 已恢复(此前 100% 已解除) |
| 内存 | 2777/7578 MB 已用4537 可用 | ✅ 正常 |
| 磁盘 | 66G/79G87%11G 可用 | ⚠️ 偏高,建议清理日志 |
| 连接数 | 8 | ✅ 正常 |
| CPU TOP | nginx 0.2%、systemd 0.1% | ✅ 无高 CPU 进程 |
| Node 进程 | 0 | ⚠️ **全部 Node 已停止** |
**根因结论**:此前负载 100% 主要来自 **Node 项目 MODULE_NOT_FOUND 崩溃→宝塔自动重启→再崩溃** 的死循环。当前 Node 已全部停止,负载自然回落。
**下一步**:运行 `kr宝塔_运行堵塞与Node深度修复_宝塔终端执行.sh` 修正启动命令并批量启动 Node。

View File

@@ -0,0 +1,117 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
腾讯云 TATkr宝塔 负载诊断(只读,不杀进程)
输出uptime、内存、磁盘、连接数、CPU/内存 TOP20、node 进程详情
"""
import base64
import json
import os
import re
import sys
import time
KR_INSTANCE_ID = "ins-aw0tnqjo"
REGION = "ap-guangzhou"
DIAG_SCRIPT = r'''#!/bin/bash
echo "========== kr宝塔 负载诊断 =========="
echo ""
echo "【1】负载与运行时间"
uptime
echo ""
echo "【2】内存"
free -m
echo ""
echo "【3】磁盘"
df -h / /www 2>/dev/null
echo ""
echo "【4】连接数(ESTABLISHED)"
echo "总数:" $(ss -ant state established 2>/dev/null | wc -l)
echo ""
echo "【5】各端口连接数 TOP15"
ss -antn state established 2>/dev/null | awk '{print $4}' | cut -d: -f2 | sort | uniq -c | sort -rn | head -15
echo ""
echo "【6】单IP连接数 TOP10"
ss -antn state established 2>/dev/null | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -10
echo ""
echo "【7】CPU TOP20"
ps aux --sort=-%cpu 2>/dev/null | head -21
echo ""
echo "【8】内存 TOP20"
ps aux --sort=-%mem 2>/dev/null | head -21
echo ""
echo "【9】Node/npm/pnpm 进程"
ps aux | grep -E 'node|npm|pnpm|next-server' | grep -v grep
echo ""
echo "【10】Node 进程数"
echo "node:" $(pgrep -c node 2>/dev/null || echo 0)
echo "npm:" $(pgrep -c npm 2>/dev/null || echo 0)
echo ""
echo "========== 诊断完成 =========="
'''
def _read_creds():
d = os.path.dirname(os.path.abspath(__file__))
for _ in range(6):
if os.path.isfile(os.path.join(d, "运营中枢", "工作台", "00_账号与API索引.md")):
with open(os.path.join(d, "运营中枢", "工作台", "00_账号与API索引.md")) as f:
t = f.read()
sid = skey = None
for line in t.splitlines():
m = re.search(r"SecretId[^|]*\|\s*`([^`]+)`", line, re.I)
if m and "AKID" in m.group(1): sid = m.group(1).strip()
m = re.search(r"SecretKey\s*\|\s*`([^`]+)`", line, re.I)
if m: skey = m.group(1).strip()
return sid or os.environ.get("TENCENTCLOUD_SECRET_ID"), skey or os.environ.get("TENCENTCLOUD_SECRET_KEY")
d = os.path.dirname(d)
return None, None
def main():
sid, skey = _read_creds()
if not sid or not skey:
print("❌ 未配置腾讯云凭证"); return 1
try:
from tencentcloud.common import credential
from tencentcloud.tat.v20201028 import tat_client, models
except ImportError:
print("pip install tencentcloud-sdk-python-tat"); return 1
cred = credential.Credential(sid, skey)
client = tat_client.TatClient(cred, REGION)
req = models.RunCommandRequest()
req.Content = base64.b64encode(DIAG_SCRIPT.encode("utf-8")).decode()
req.InstanceIds = [KR_INSTANCE_ID]
req.CommandType = "SHELL"
req.Timeout = 60
req.CommandName = "kr宝塔_负载诊断"
resp = client.RunCommand(req)
inv_id = resp.InvocationId
print("✅ TAT 已下发 InvocationId:", inv_id)
print(" 等待 90s 获取诊断输出...")
time.sleep(90)
try:
req2 = models.DescribeInvocationTasksRequest()
f = models.Filter()
f.Name, f.Values = "invocation-id", [inv_id]
req2.Filters = [f]
r2 = client.DescribeInvocationTasks(req2)
for t in (r2.InvocationTaskSet or []):
print("\n状态:", getattr(t, "TaskStatus", ""))
tr = getattr(t, "TaskResult", None)
if tr:
j = json.loads(tr) if isinstance(tr, str) else {}
out = j.get("Output", "")
if out:
try: out = base64.b64decode(out).decode("utf-8", errors="replace")
except: pass
print(out)
except Exception as e:
print("查询:", e)
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,113 @@
#!/usr/bin/env python3
"""
Soul 切片优化:统一输出 1080x1920 竖屏,内容占满整个画面,无黑边
用法:
python optimize_slices_fill.py --dir "/Users/karuo/Movies/soul视频/soul 派对 106场 20260221_output"
python optimize_slices_fill.py --dir "/Users/karuo/Movies/soul视频" --recursive
"""
import argparse
import json
import re
import subprocess
import sys
from pathlib import Path
def get_video_size(path: str) -> tuple:
"""返回 (width, height)"""
cmd = [
"ffprobe", "-v", "error", "-select_streams", "v:0",
"-show_entries", "stream=width,height", "-of", "json", path
]
r = subprocess.run(cmd, capture_output=True, text=True)
if r.returncode != 0:
return 0, 0
d = json.loads(r.stdout)
s = d.get("streams", [{}])[0]
return int(s.get("width", 0)), int(s.get("height", 0))
def optimize_one(in_path: Path, out_path: Path, overwrite: bool = True) -> bool:
"""
将任意尺寸视频转为 1080x1920内容占满画面scale 覆盖 + 居中 crop
"""
import tempfile
w, h = get_video_size(str(in_path))
if w <= 0 or h <= 0:
print(f" ⚠ 无法读取: {in_path.name}")
return False
if w == 1080 and h == 1920:
print(f" ⊘ 已是 1080x1920: {in_path.name}")
return True
out_path.parent.mkdir(parents=True, exist_ok=True)
# 原地覆盖时先写临时文件,再替换
dest = out_path
if in_path.resolve() == out_path.resolve():
dest = Path(tempfile.mktemp(suffix=".mp4", dir=out_path.parent))
vf = "scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920:(iw-1080)/2:(ih-1920)/2"
cmd = [
"ffmpeg", "-y", "-i", str(in_path),
"-vf", vf, "-c:v", "libx264", "-preset", "fast", "-crf", "23",
"-c:a", "aac", "-b:a", "128k", str(dest)
]
r = subprocess.run(cmd, capture_output=True, text=True)
if r.returncode == 0:
if dest != out_path:
dest.replace(out_path)
print(f"{in_path.name} ({w}x{h} → 1080x1920)")
return True
err = (r.stderr or "").strip().split("\n")[-3:] if r.stderr else ["unknown"]
print(f"{in_path.name}: {err}")
if dest != out_path and dest.exists():
dest.unlink(missing_ok=True)
return False
def main():
ap = argparse.ArgumentParser(description="切片优化:统一 1080x1920内容占满")
ap.add_argument("--dir", "-d", required=True, help="目录路径")
ap.add_argument("--recursive", "-r", action="store_true", help="递归子目录")
ap.add_argument("--inplace", action="store_true", default=True, help="原地覆盖(默认)")
ap.add_argument("--no-inplace", action="store_false", dest="inplace", help="输出到 _filled 子目录")
args = ap.parse_args()
root = Path(args.dir)
if not root.exists():
print(f"❌ 目录不存在: {root}")
sys.exit(1)
if args.recursive:
files = list(root.rglob("*.mp4"))
else:
files = list(root.glob("*.mp4"))
# 排除源视频(未切片的长视频)
def is_source(v: Path) -> bool:
n = v.stem.lower()
if "派对" in v.name and "output" not in str(v) and "_" not in v.stem:
return True
if re.match(r"soul\s*\d+场", v.name) and "output" not in str(v):
return True
return False
files = [f for f in files if not is_source(f)]
if not files:
print(f"❌ 未找到 mp4: {root}")
sys.exit(1)
print("=" * 60)
print("📱 Soul 切片优化1080x1920内容占满")
print("=" * 60)
print(f"目录: {root} | 文件数: {len(files)}")
print("=" * 60)
ok = 0
for fp in sorted(files):
if args.inplace:
out = fp
else:
out = fp.parent / "_filled" / fp.name
if optimize_one(fp, out, overwrite=True):
ok += 1
print()
print(f"✅ 完成 {ok}/{len(files)}")
if __name__ == "__main__":
main()

View File

@@ -78,6 +78,32 @@ bash "/Users/karuo/Movies/soul视频/soul 派对 106场 20260221_output/热点
---
## [卡若复盘] 切片占满画面优化2026-02-22
**🎯 目标·结果**
目标:检查所有已完成切片,统一输出 1080×1920 竖屏,内容占满整个画面,无黑边。结果:新增 `optimize_slices_fill.py`,支持递归优化目录内所有切片。
**📌 现状**
| 目录 | 尺寸 | 说明 |
|------|------|------|
| soul 派对 106场 output | 1080×1920 | 已符合 |
| soul77/79/80/81 final、_切片 | 570×1080 | 在 9:16 手机上有左右黑边 |
| clips_enhanced | 570×1080 | 同上 |
**📌 处理**
1. **optimize_slices_fill.py**`scale=1080:1920:force_original_aspect_ratio=increase` + `crop=1080:1920:(iw-1080)/2:(ih-1920)/2`任意尺寸→1080×1920 占满。
2. **原地覆盖**:先写临时文件再替换,避免 ffmpeg 读写同文件失败。
3. **排除源视频**:自动跳过 `soul 派对 XX场.mp4` 等未切片长视频。
**📌 106 场竖屏**`crop=608:1080:(iw-608)/2:0` + `scale=1080:1920`,上下左右元素完整。
**▶ 执行命令**
```bash
python3 .../optimize_slices_fill.py --dir /Users/karuo/Movies/soul视频 --recursive
```
---
## [卡若复盘] 文档与字幕简体中文优化2026-02
**🎯 目标·结果·达成率**

View File

@@ -93,3 +93,4 @@
| 2026-02-22 14:24:16 | 🔄 卡若AI 同步 2026-02-22 14:24 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 8 个 |
| 2026-02-22 14:25:58 | 🔄 卡若AI 同步 2026-02-22 14:25 | 更新:运营中枢工作台 | 排除 >20MB: 8 个 |
| 2026-02-22 14:42:04 | 🔄 卡若AI 同步 2026-02-22 14:42 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 8 个 |
| 2026-02-22 14:44:11 | 🔄 卡若AI 同步 2026-02-22 14:43 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 8 个 |

View File

@@ -96,3 +96,4 @@
| 2026-02-22 14:24:16 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 14:24 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 8 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-22 14:25:59 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 14:25 | 更新:运营中枢工作台 | 排除 >20MB: 8 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-22 14:42:04 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 14:42 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 8 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-22 14:44:11 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 14:43 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 8 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |