Files
ykxmz/_build/fix_monitor_url.py
T
xmz7891 db669e2f40 修监测单元查错地址(改监听后它开始误报网站挂了)
把中转改成只监听 127.0.0.1(公网走 nginx)之后,监测单元还在查
http://13.54.30.196:13245/v1/health —— 那个端口已经不对公网开了,
于是它连续报"公网异常",fails 涨到 2。

**差一点就是无限重启循环**:再失败一次它会启动紧急预案,
而预案的动作是"重启中转服务",重启完再查还是 000,再救……

这是改监听地址时没想到的连带影响。教训写在这里:
**改一个服务的暴露方式时,必须把所有"探测它"的地方一起看一遍。**

== 改法 ==

监测地址改成"用户实际怎么访问"为准,域名优先、IP 兜底:

    PUBLIC_URL=http://yk.xxmxxs.top          域名(经 Cloudflare + nginx)
    FALLBACK_URL=http://13.54.30.196:80      绕过 CF 直连 nginx

只要有一个通就算健康。这样域名配好前靠兜底、配好后走真实路径。

== 顺带修的 ==

  * monitor.log 属主还是 root(后来新建的),admin 写不进去
    -> 再 chown 一次
  * _build/check_lightsail.py 里的"公网"地址也要跟着改,
    并且**故意保留**一项查 :13245(预期 000)用来确认
    中转确实不再暴露公网

全量状态:三层地址、缓存头、监测、属主全部正确。
2026-10-08 22:41:33 +08:00

166 lines
6.6 KiB
Python
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.
# -*- coding: utf-8 -*-
r'''修监测单元:它现在查错了地址,在**误报网站挂了**。
== 怎么发现的 ==
把中转改成只监听 127.0.0.1(公网只能走 nginx)之后,跑状态检查看到:
公网 health 000 <- 因为 :13245 已经不对公网开了
fails=2 <- 监测单元连续两次"失败"
也就是说监测单元**误以为网站挂了**,再失败一次就会启动紧急预案 ——
而它抢救的方式是"重启中转服务",重启完它再查还是 000,再救……
**会陷入无限重启循环**。
这是我自己改监听地址时没想到的连带影响。教训:改一个服务的
暴露方式时,必须把所有"探测它"的地方一起看一遍。
== 改法 ==
监测的地址应该以"**用户实际怎么访问**"为准,现在应该是:
http://yk.xxmxxs.top/v1/health (经过 Cloudflare + nginx)
但域名还没配好(DNS 还没指过来),所以加一个**回退**:
域名不通就用 IP:80 试,只要有一个通就算健康。
这样:
* 域名配好之前 -> 走 IP:80,正常监控
* 域名配好之后 -> 走域名,监控的是用户真实路径
* 两个都不通 -> 才是真挂了
== 顺带 ==
monitor.log 的属主问题:monitor.service 以 admin 跑,
而 relay.log 之类的之前是 root 建的。已经 chown 过一次,
但 monitor.log 是后来新生成的(仍是 root)—— 这次一并修掉,
并且在 unit 里加一行让 systemd 自己保证目录属主。
'''
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 # noqa: E402
RELAY_DIR = "/opt/ykxmz-relay"
DOMAIN = "yk.xxmxxs.top"
FALLBACK = "http://13.54.30.196:80"
def main() -> int:
print("=" * 62)
print(" 修监测单元:改探测地址 + 修日志属主")
print("=" * 62)
print()
print(" ── 1. 改 monitor.sh 的健康检查(域名优先,IP 兜底)──")
# 把 PUBLIC_URL 的默认值换掉,并把 is_healthy 改成"两个都试"
old_url = 'PUBLIC_URL="${PUBLIC_URL:-http://13.54.30.196:13245}"'
new_url = (
'# 探测地址:域名优先(用户实际走的路),IP:80 兜底\n'
'# (域名 DNS 还没指过来时用它,否则监测会一直误报)\n'
'PUBLIC_URL="${PUBLIC_URL:-http://%s}"\n'
'FALLBACK_URL="${FALLBACK_URL:-%s}"' % (DOMAIN, FALLBACK))
out = run(
"grep -n 'PUBLIC_URL=' %s/monitor.sh | head -3" % RELAY_DIR,
timeout=60, quiet=True)
print(" 改前:")
for ln in out[1].strip().splitlines():
print(" " + ln)
script = (
"python3 - <<'PYEOF'\n"
"from pathlib import Path\n"
"p = Path('%s/monitor.sh')\n"
"t = p.read_text(encoding='utf-8')\n"
"old = [l for l in t.splitlines() if l.startswith('PUBLIC_URL=')]\n"
"if not old:\n"
" print(' x 找不到 PUBLIC_URL 行')\n"
"else:\n"
" t = t.replace(old[0], %r, 1)\n"
" # 健康检查改成两个都试\n"
" a = 'CODE=$(curl -s -o /dev/null -w \\'%%{http_code}\\' --max-time "
"\"$CHECK_TIMEOUT\" \\\\\\n \"$PUBLIC_URL/v1/health\" 2>/dev/null)'\n"
" b = ('PROBE=\"$PUBLIC_URL\"\\n'\n"
" 'CODE=$(curl -s -o /dev/null -w \\'%%{http_code}\\' "
"--max-time \"$CHECK_TIMEOUT\" \"$PROBE/v1/health\" 2>/dev/null)\\n'\n"
" 'if [ \"$CODE\" != \"200\" ] && [ -n \"$FALLBACK_URL\" ]; then\\n'\n"
" ' PROBE=\"$FALLBACK_URL\"\\n'\n"
" ' CODE=$(curl -s -o /dev/null -w \\'%%{http_code}\\' "
"--max-time \"$CHECK_TIMEOUT\" \"$PROBE/v1/health\" 2>/dev/null)\\n'\n"
" 'fi')\n"
" if a in t:\n"
" t = t.replace(a, b, 1)\n"
" print(' ok 健康检查改成域名优先 + IP 兜底')\n"
" else:\n"
" print(' ! 健康检查那段没匹配上,手工看')\n"
" p.write_text(t, encoding='utf-8', newline='\\n')\n"
"PYEOF" % (RELAY_DIR, new_url))
out = run(script, timeout=120, quiet=True)
for ln in out[1].strip().splitlines():
print(" " + ln)
out = run("grep -n 'PUBLIC_URL=\\|FALLBACK_URL=\\|PROBE=' %s/monitor.sh "
"| head -8" % RELAY_DIR, timeout=60, quiet=True)
print(" 改后:")
for ln in out[1].strip().splitlines():
print(" " + ln)
print()
print(" ── 2. 修日志属主 + 让 systemd 保证它 ──")
out = run(
"sudo chown -R admin:admin %s && "
"sudo sed -i '/^User=admin$/i "
"# 日志文件和状态文件都要 admin 能写(服务以 admin 跑)\\n"
"# 之前 relay.log / monitor.log 是 root 建的,导致 "
"monitor.sh 报 Permission denied\\n"
"PermissionsStartOnly=true' "
"/etc/systemd/system/ykxmz-monitor.service 2>/dev/null; "
"sudo touch %s/monitor.log %s/monitor_state.txt && "
"sudo chown admin:admin %s/*.log %s/*.txt %s/*.md 2>/dev/null; "
"ls -la %s/*.log | awk '{print \" \" $3\":\"$4\" \" $9}'"
% (RELAY_DIR, RELAY_DIR, RELAY_DIR, RELAY_DIR, RELAY_DIR,
RELAY_DIR, RELAY_DIR), timeout=180, quiet=True)
for ln in out[1].strip().splitlines():
print(ln)
print()
print(" ── 3. 重置监测状态 + 跑一次 ──")
out = run(
"sudo systemctl daemon-reload; "
"printf 'fails=0\\nlast_rescue=0\\nlast_code=200\\n' > "
"%s/monitor_state.txt; "
"bash %s/monitor.sh; echo '--- 状态 ---'; "
"cat %s/monitor_state.txt; echo '--- 日志尾 ---'; "
"tail -3 %s/monitor.log" % (RELAY_DIR, RELAY_DIR, RELAY_DIR,
RELAY_DIR),
timeout=180, quiet=True)
for ln in out[1].strip().splitlines():
print(" " + ln)
print()
print(" ── 4. 验证探测链路 ──")
out = run(
'printf " %-28s %s\\n" "域名(还没配)" '
'"$(curl -s -o /dev/null -w \'%{http_code}\' --max-time 8 '
'http://%s/v1/health)"; '
'printf " %-28s %s\\n" "IP:80 (兜底)" '
'"$(curl -s -o /dev/null -w \'%{http_code}\' --max-time 8 '
'http://13.54.30.196:80/v1/health)"; '
'printf " %-28s %s\\n" "IP:13245 (已关闭)" '
'"$(curl -s -o /dev/null -w \'%{http_code}\' --max-time 8 '
'http://13.54.30.196:13245/v1/health)"' % DOMAIN,
timeout=120, quiet=True)
for ln in out[1].strip().splitlines():
print(ln)
return 0
if __name__ == "__main__":
sys.exit(main())