# -*- coding: utf-8 -*- r'''把本地的 monitor/rescue 脚本装到 Lightsail(第二版:https 版)。 == 为什么单独写一个,而不是继续远端打补丁 == 前面几轮我一直在用"SSH 过去、用嵌套 heredoc 现场改 monitor.sh"的做法, 结果连续踩了三次: 1. PowerShell 的 `$(...)` 在**本机**被求值(报"systemctl 不是 cmdlet") 2. 引号被吃掉 -> Python 报 unterminated string literal 3. 脚本里的 `%{http_code}` 和 Python 的 `%` 格式化打架 -> "not enough arguments for format string" 根本问题:**远端打补丁这件事本身就脆弱**。 本地写完整文件、整份上传,就没有这些问题 —— 而且改了什么一目了然, 还能进 git 做版本管理。 所以现在改成:relay/monitor-lightsail.sh 和 relay/rescue-lightsail.sh 是**唯一事实来源**,本地编辑 -> 整份上传 -> 重装。 == 本地文件名 vs 远端文件名 == 本地叫 monitor-lightsail.sh(和那台旧的 monitor.sh 区分开, 免得看混),远端统一叫 monitor.sh / rescue.sh, 因为 systemd unit 和监测脚本里写的是这个名字。 ''' from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "_build")) from lssh import run, upload, HOST as LS_HOST # noqa: E402 RELAY_DIR = "/opt/ykxmz-relay" # (本地文件, 远端文件名) PAIRS = [ ("relay/monitor-lightsail.sh", "monitor.sh"), ("relay/rescue-lightsail.sh", "rescue.sh"), ] def main() -> int: print("=" * 62) print(" 装 monitor / rescue 到 Lightsail") print("=" * 62) print() print(" ── 1. 上传 ──") for local_name, remote_name in PAIRS: local = ROOT / local_name if not local.is_file(): print(" ✗ 找不到 %s" % local_name) return 1 upload(str(local), "%s/%s" % (RELAY_DIR, remote_name), mode=0o755) print() print(" ── 2. 语法检查 ──") out = run("cd %s && for f in monitor.sh rescue.sh; do " "bash -n $f || exit 1; done && echo SH_OK" % RELAY_DIR, timeout=120, quiet=True) print(" " + out[1].strip()) print() print(" ── 3. 重置状态 + 跑一次监测 ──") out = run("printf 'fails=0\\nlast_rescue=0\\n' > " "%s/monitor_state.txt; " "bash %s/monitor.sh; echo ' --- 状态 ---'; " "cat %s/monitor_state.txt | sed 's/^/ /'; " "echo ' --- 日志尾 ---'; " "tail -3 %s/monitor.log | sed 's/^/ /'" % (RELAY_DIR, RELAY_DIR, RELAY_DIR, RELAY_DIR), timeout=180, quiet=True) for ln in out[1].strip().splitlines(): print(ln) print() print(" ── 4. 探测各层现在什么状态 ──") probe = ( 'printf " %-36s %s\\n" "PRIMARY https://域名" ' '"$(curl -sL -o /dev/null -w \'%{http_code}\' --max-time 15 ' 'https://yk.xxmxxs.top/v1/health)"; ' 'printf " %-36s %s\\n" "FALLBACK 127.0.0.1:13245" ' '"$(curl -s -o /dev/null -w \'%{http_code}\' --max-time 8 ' 'http://127.0.0.1:13245/v1/health)"; ' 'printf " %-36s %s\\n" "nginx :80(301 到 https)" ' '"$(curl -s -o /dev/null -w \'%{http_code}\' --max-time 8 ' 'http://127.0.0.1:80/v1/health)"; ' 'printf " %-36s %s\\n" "nginx :443(本机带 SNI)" ' '"$(curl -sk -o /dev/null -w \'%{http_code}\' --max-time 8 ' '--resolve yk.xxmxxs.top:443:127.0.0.1 ' 'https://yk.xxmxxs.top/v1/health)"; ' 'printf " %-36s %s\\n" "公网 443(Lightsail 防火墙)" ' '"$(curl -sk -o /dev/null -w \'%{http_code}\' --max-time 8 ' 'https://LS_HOST/v1/health 2>/dev/null || echo 不通)"' ) out = run(probe, timeout=180, quiet=True) for ln in out[1].strip().splitlines(): print(ln) print() print(" 说明:PRIMARY 现在是 522 —— 因为公网 443 还没开。") print(" 这不影响 FALLBACK 正常,所以监测**不会**误报成") print(" '中转挂了';它会把层次标成 edge(问题在边缘层),") print(" 等 443 开了 PRIMARY 自然就 200 了。") return 0 if __name__ == "__main__": sys.exit(main())