

昨天我的 pve 系统整个挂掉了,之前搭建的告警服务自然也死掉了,这就导致了我不能及时发现网站崩掉了,重启机器。
于是,我就把目光锁定到了家里的软路由上面,它是 x86 架构的,也安装了 docker ,我只需要用 python 写个脚本,做个 docker 服务即可。
有了想法后,接下来需要先确定下要实现什么功能。
docker-compose up -d 就搞定,不需要在宿主机安装什么复杂依赖接下来就跟大家分享下我的具体实现过程。
smtp + socket,做一个循环脚本:ping 、 ca-certificates ,配置时区,使得:docker-compose up -d 就能全自动运行。import os import smtplib import time import socket from email.mime.text import MIMEText from email.header import Header from email.utils import formataddr # 监控配置 TARGET_HOST = os.getenv("TARGET_HOST", "127.0.0.1") TARGET_PORT = int(os.getenv("TARGET_PORT", "80")) INTERVAL_SEC = int(os.getenv("INTERVAL_SEC", "60")) FAIL_THRESHOLD = int(os.getenv("FAIL_THRESHOLD", "3")) # 邮件配置( QQ 邮箱) SMTP_HOST = os.getenv("SMTP_HOST", "smtp.qq.com") SMTP_PORT = int(os.getenv("SMTP_PORT", "587")) SMTP_USER = os.getenv("SMTP_USER", "") SMTP_PASS = os.getenv("SMTP_PASS", "") MAIL_FROM = os.getenv("MAIL_FROM", SMTP_USER) MAIL_TO = os.getenv("MAIL_TO", "") def check_port(host: str, port: int, timeout=2) -> bool: """ 返回 True 表示端口可连接,False 表示失败 """ try: with socket.create_connection((host, port), timeout=timeout): return True except Exception: return False def send_mail(subject: str, content: str): if not (SMTP_HOST and SMTP_USER and SMTP_PASS and MAIL_TO): print("SMTP 配置不完整,无法发送邮件") return from_addr = MAIL_FROM or SMTP_USER msg = MIMEText(content, "plain", "utf-8") msg["From"] = formataddr(("Ping 告警系统", from_addr)) msg["To"] = formataddr(("告警接收人", MAIL_TO)) msg["Subject"] = Header(subject, "utf-8") print(f" [邮件] 准备连接 SMTP: host={SMTP_HOST}, port={SMTP_PORT}, user={SMTP_USER}") server = None try: if SMTP_PORT == 465: print(" [邮件] 使用 SMTP_SSL 连接( 465 端口)") server = smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT, timeout=10) else: print(" [邮件] 使用 SMTP + STARTTLS 连接") server = smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=10) server.ehlo() server.starttls() server.ehlo() server.login(SMTP_USER, SMTP_PASS) # sendmail 如果不抛异常,就认为成功 failed = server.sendmail(from_addr, [MAIL_TO], msg.as_string()) if failed: print(" [邮件] 部分收件人发送失败:", failed) else: print(" [邮件] 告警邮件已发送( sendmail 返回正常)") except smtplib.SMTPResponseException as e: if e.smtp_code == -1 and e.smtp_error == b'\x00\x00\x00': print(" [邮件] QQ 在 QUIT 阶段返回 (-1, b'\\x00\\x00\\x00'),可忽略,邮件已经入队。") else: print(f" [邮件] SMTPResponseException:code={e.smtp_code}, error={e.smtp_error}") except Exception as e: print(f" [邮件] 发送失败:{repr(e)},类型:{type(e)}") finally: if server is not None: try: server.quit() except Exception as e: # 这里的异常直接吞掉即可 print(f" [邮件] 关闭连接时异常(可忽略):{repr(e)}") def main(): fail_count = 0 print( f"开始监控 {TARGET_HOST}:{TARGET_PORT},每 {INTERVAL_SEC}s 检测一次," f"连续失败 {FAIL_THRESHOLD} 次触发一次告警" ) while True: now = time.strftime("%F %T") ok = check_port(TARGET_HOST, TARGET_PORT) if ok: print(f"{now} [OK] {TARGET_HOST}:{TARGET_PORT} 端口可访问") if fail_count > 0: print(f"{now} 恢复正常,之前连续失败 {fail_count} 次,计数清零") fail_count = 0 else: fail_count += 1 print(f"{now} [FAIL] {TARGET_HOST}:{TARGET_PORT} 无法连接,连续失败次数:{fail_count}") if fail_count == FAIL_THRESHOLD: subject = f"[告警] {TARGET_HOST}:{TARGET_PORT} 无法访问" cOntent= ( f"目标 {TARGET_HOST}:{TARGET_PORT} 已连续 {FAIL_THRESHOLD} 次连接失败。\n" f"时间:{now}" ) send_mail(subject, content) time.sleep(INTERVAL_SEC) if __name__ == "__main__": main() 编写 DockerFile 镜像文件
FROM python:3.11-slim ENV TZ=Asia/Shanghai WORKDIR /app RUN apt-get update && \ apt-get install -y iputils-ping ca-certificates tzdata && \ ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ echo $TZ > /etc/timezone && \ update-ca-certificates && \ rm -rf /var/lib/apt/lists/* COPY ping_alert.py . CMD ["python", "-u", "ping_alert.py"] 编写构建脚本
#!/usr/bin/env sh set -e # === 配置区:按需修改 === IMAGE_NAME="magiccoders/ping-alert" # magiccoders 需要改成你的 docker-hub 的用户名 TAG="latest" BUILD_COnTEXT="./app" # ======================= echo "==> 构建镜像: ${IMAGE_NAME}:${TAG}" docker build -t "${IMAGE_NAME}:${TAG}" "${BUILD_CONTEXT}" echo "==> 推送镜像到仓库: ${IMAGE_NAME}:${TAG}" docker push "${IMAGE_NAME}:${TAG}" echo "==> 完成:${IMAGE_NAME}:${TAG} 已发布" 执行此脚本前,需要先在终端执行 docker login 命令登录到你的 docker-hub 账户。
构建好镜像后,需要创建docker-compose.yml文件来编排这个镜像运行所需的环境变量。
version: '3.8' services: ping-alert: image: magiccoders/ping-alert:latest # 此处就是存储在 docker-hub 上的镜像 container_name: ping-alert restart: always environment: # ===== 监控目标配置 ===== TARGET_HOST: "192.168.9.131" #监控目标机器 ip TARGET_PORT: "80" # 目标机器端口号 INTERVAL_SEC: "30" # 每 30 秒检查一次 FAIL_THRESHOLD: "3" # 连续 3 次失败发一封告警邮件 # ===== QQ 邮箱 SMTP 配置 ===== SMTP_HOST: "smtp.qq.com" SMTP_PORT: "465" SMTP_USER: "" # 你的 QQ 邮箱 SMTP_PASS: "" # 开通 SMTP 服务时得到的授权码 MAIL_FROM: "" # 和 SMTP_USER 保持一致 MAIL_TO: "" # 接受告警的邮箱 # 直接复用宿主机网络,方便访问内网 IP network_mode: "host" 我的软路由使用DPanel来管理 docker ,此处我就以它为例来讲解如何使用这个镜像。
如图所示,切换到 compose 选项卡,点击创建任务。

在打开的面板中,填写标识、名称,以及刚才的 docker-compose 配置代码,按需更改里面的变量即可

做完这些操作后,启动容器,查看日志,如果你的服务正常运行你就能看到如下所示的输出:

我把端口关闭,再来验证下失败的情况。


邮箱也收到了邮件。

最后,我启动服务,再来验证下他是否会清零计数。


至此,文章就分享完毕了。
我是神奇的程序员,一位前端开发工程师。
如果你对我感兴趣,请移步我的个人网站,进一步了解。
1 KagurazakaNyaa 3 小时 32 分钟前 直接用 uptime-kuma 不就好了,https://github.com/louislam/uptime-kuma |
2 tf2 3 小时 19 分钟前 www.kaisir.cn sent an invalid response. ERR_SSL_PROTOCOL_ERROR |
3 MagicCoder OP @tf2 网络波动吧,现在应该好了 |
4 hukei 3 小时 0 分钟前 @KagurazakaNyaa #1 一直在用 感觉良好 |
5 MagicCoder OP @KagurazakaNyaa 这项目不错 |
6 lisxour 2 小时 35 分钟前 直接上青龙面板,几行脚本的事 |
7 052678 2 小时 26 分钟前 直接上青龙面板,几行脚本的事 |
8 MagicCoder OP @lisxour 哈哈 我突发奇想的,想着就一个简单的东西,顺手撸出来,然后发出来,看看能不能帮到有需要的人 |
9 suni PRO |
10 sparkssssssss 1 小时 54 分钟前 分享下我的做法 主路由器上,bash 探测需要监控的 ip ,单纯的 ping ,不通就发到微信上 我的主路有是拨号的,所以,使用了一个 saas 的国外探测平台,会监控我的外网,如果外网挂了,则通知我。 所以,监控机挂了怎么办? |
11 MagicCoder OP @sparkssssssss 可以,你这个方案更简单。我现在的内网环境,全部 all in 软路由,拨号也在它,挂的概率很低 |
12 gotoschool 1 小时 39 分钟前 别说别的,你这个图用 ai 生成全是错误的字 |
13 euph 1 小时 19 分钟前 via Android 我是用 ping 包检测,不需要指定端口 |
14 MagicCoder OP @gotoschoolgpt 太拉垮了,有没有推荐的 ai 生成图的服务的 |
15 SSang 55 分钟前 不如直接 grafana |
16 stinkytofux 54 分钟前 又是重复造轮子 |
17 FrankAdler 33 分钟前 via Android gatus 单文件部署,支持的协议很多还只是很多种推送,我用的 ntfy |