夜莺(Nightingale)架构设计与部署模式精简指南


title: 夜莺(Nightingale)架构设计与部署模式精简指南
date: 2026-08-07T00:00:00+08:00
lastmod: 2026-08-07T00:00:00+08:00


夜莺(Nightingale)架构设计与部署模式精简指南

夜莺(Nightingale)是一款开源监控告警平台,核心进程为 n9e,依赖 MySQL 和 Redis 存储管理数据,可接入 Prometheus、VictoriaMetrics、ElasticSearch 等多种数据源。

它的架构灵活,从单机测试到大规模生产环境都能覆盖;它的设计偏重告警引擎,商业版 Flashcat 则在此基础上提供了一站式智能观测能力。


一、两种数据流模式

夜莺在数据采集层面支持两种不同的使用方式。

模式 1:数据不经夜莺

这是最常见的使用方式:

  • 用户自行解决数据采集,比如通过 Categraf
  • 只把时序库(如 Prometheus、VictoriaMetrics)配置到夜莺中
  • 夜莺用来看图、配置告警规则

这种模式下,夜莺完全不参与数据采集和存储,只做”告警大脑”。

模式 2:数据经夜莺转发

Categraf 通过 remote write 协议把数据推给夜莺,夜莺不直接存储,而是转发到时序库:

  • 转发目标由 config.toml 中的 Pushgw.Writers 决定
  • 新用户推荐使用 VictoriaMetrics,性能更好、支持集群、与 Prometheus 接口兼容

这种模式适合希望统一收集入口、简化 Categraf 配置的场景。


二、四种部署模式

1. 单节点测试模式

最快的上手方式:

# 从 GitHub Releases 下载发布包后执行
./n9e
  • 默认端口 17000,默认账号 root,密码 root.2020
  • 依赖 etcintegrations 目录
  • 配置数据存储在本地 SQLite(n9e.db
  • 仅供测试,不建议上生产

2. 单节点生产模式

依赖 MySQL 和 Redis,在 etc/config.toml 中配置:

[DB]
DSN = "root:密码@tcp(localhost:3306)/n9e_v6?charset=utf8mb4&parseTime=True&loc=Local"

[Redis]
Address = "127.0.0.1:6379"
RedisType = "standalone"

这是中小规模场景的常规选择。

3. 夜莺集群模式

多台机器部署 n9e 进程,共享同一套 MySQL 和 Redis:

  • 配置文件完全一致
  • 多个 n9e 进程会自动分派告警规则,每条规则只在一个实例上运行
  • 若某实例故障,其他实例会自动接管告警规则
  • 实现告警判定的高可用

4. 边缘模式(n9e-edge)

适用于多机房、网络质量差的场景。

架构特点

  • 中心机房部署 n9e + MySQL + Redis
  • 边缘机房部署 n9e-edge
  • n9e-edge 从中心 n9e 同步告警规则,缓存到本地内存
  • 告警判定在边缘本地完成,内网连接边缘时序库,可靠性更高
  • 网络断开时,n9e-edge 仍可基于缓存规则继续判定
  • 告警事件写回中心 MySQL,并调用钉钉、飞书等发送通知

数据源配置

在夜莺 WebUI 中配置数据源时,需要注意两点:

  • 时序库内网地址:供 n9e-edge 访问边缘时序库
  • 关联告警引擎集群:指定由哪个 n9e-edge 处理该数据源的告警

n9e-edge 需要独立的 Redis(与中心机房 Redis 不同),边缘机房的 Categraf 应连接本地 n9e-edge


三、关键配置样例

中心机房 n9e 配置

文件:etc/config.toml

[HTTP.APIForService]
Enable = true
# BasicAuth 用户,供 n9e-edge 连接
[HTTP.APIForService.BasicAuth]
user001 = "修改为安全的密码"
user002 = "修改为安全的密码"

如果 n9e 暴露在公网,务必修改默认密码。

边缘机房 n9e-edge 配置

文件:etc/edge/edge.toml

[CenterApi]
# 中心 n9e 地址
Address = "http://N9E-CENTER:17000"
# BasicAuth 凭证
Username = "user001"
Password = "修改为安全的密码"

[Ibex]
Enable = true
RPCListen = "0.0.0.0:20090"

[Redis]
Address = "127.0.0.1:6379"
RedisType = "standalone"

默认监听端口 19000,可根据需要修改。

边缘 Categraf 配置

[writers]
url = "http://N9E-EDGE:19000/prometheus/v1/write"

[heartbeat]
url = "http://N9E-EDGE:19000/v1/n9e/heartbeat"

Ibex 故障自愈配置

Categraf 的 ibex 配置:

[ibex]
servers = "N9E-EDGE:20090"

注意这里不带 http:// 前缀。


四、其他实用场景

网络分区安全场景也适用 n9e-edge

某个网络区域只有一台中转机可以连通中心 n9e,其他机器都无法连通。

解决方案:在中转机上部署 n9e-edge,其他 Categraf 连接这个 n9e-edge,即可实现告警管理,而无需每台机器都能直接访问中心。


五、总结

夜莺的架构设计兼顾了简单易用与生产级可靠性:

模式 适用场景 关键特点
单节点测试 本地试用 SQLite、不依赖外部服务
单节点生产 中小规模 MySQL + Redis
夜莺集群 高可用生产 多实例分派规则、故障自动接管
边缘模式 多机房/网络分区 n9e-edge 本地判定、缓存继续告警

通过灵活的配置和组件化设计,夜莺能够适应不同网络条件和安全需求,帮助团队解决告警收敛、降噪、排班、认领、升级等闭环管理问题。

Rufus 快速创建USB启动盘

一、什么是 Rufus

Rufus 是一款免费、开源的 Windows 工具,用于格式化并创建可启动 U 盘(USB 启动盘)。它体积小、速度快,是目前创建 Windows / Linux 系统安装盘最常用的工具之一,支持从 ISO 镜像直接写入 U 盘,也支持制作 BIOS 和 UEFI 双启动盘。

二、准备工作

准备项 说明
Rufus 工具 从官方站点 rufus.ie 下载最新版(免安装,绿色版直接运行)
系统 ISO 镜像 Windows ISO(微软官网下载)或 Linux ISO(各发行版官网),或 PE 镜像
U 盘 建议 8GB 以上,制作前注意备份数据——制作过程会清空 U 盘

三、操作步骤

1. 运行 Rufus

双击运行 Rufus,无需安装。若提示检查更新可跳过。

2. 选择设备与镜像

字段 操作
设备 选择你的 U 盘(注意核对容量,避免选错)
引导类型选择 点击「选择」按钮,找到你的 ISO 镜像文件
分区类型 GPT(UEFI 新电脑)或 MBR(传统 BIOS 老电脑),不确定可保持默认
文件系统 NTFS(Windows 安装盘)或 FAT32(兼容性好)

3. 开始制作

  1. 确认所有设置无误后,点击「开始」
  2. 若提示写入镜像模式,选择「以 ISO 镜像模式写入」(推荐)
  3. 弹窗警告会清空 U 盘,确认「确定」继续
  4. 等待进度条走完,状态显示「就绪」即完成

四、常见问题

问题 解决办法
提示「设备繁忙 / 被占用」 关闭杀毒软件或资源管理器,或重插 U 盘
无法 U 盘启动 进入 BIOS 开启 USB 启动,或关闭 Secure Boot(安全启动)
新电脑引导不了 改用 GPT + UEFI 模式重新制作
制作速度慢 插在 USB 3.0 接口,或换质量好的 U 盘

五、注意事项

  • 务必提前备份 U 盘数据,制作过程会完全清空
  • 请从官方渠道下载 Rufus 和系统镜像,避免携带后门
  • Rufus 完全免费,警惕收费的”破解版”

NGINX 曝 9.2 分高危漏洞:潜伏 18 年,威胁全球约 1/3 服务器

IT之家 5 月 14 日消息,科技媒体 cyberkendra 昨日(5 月 13 日)发布博文,报道称 NGINX 被曝一组高危漏洞,已潜伏约 18 年,威胁全球约三分之一的网络服务器。

本次曝光的漏洞:

  • CVE-2026-42945 — 9.2 Critical
  • CVE-2026-42946 — 8.3 High
  • CVE-2026-40701 — 6.3 Medium
  • CVE-2026-42934 — 6.3 Medium

核心风险: 攻击者无需登录认证,只需发送一条特制 HTTP 请求,就能让 NGINX 工作进程崩溃;在合适条件下,还可能拿到服务器远程代码执行权限(RCE)。

漏洞原理: 最严重的 CVE-2026-42945 可追溯到 2008 年,长期存在于几乎所有标准 NGINX 构建版本中。问题出在 ngx_http_rewrite_module 的处理逻辑——某个内部标志位被设为参数转义状态后没有清掉,后续长度计算按原始字节数估算,但真正写入时却再次转义。攻击者 URI 中的 +%& 等字符会从 1 字节膨胀到 3 字节,导致缓冲区溢出。

depthfirst 已做出概念验证,显示在关闭 ASLR 的条件下可实现未认证 RCE。报告还提到理论方法称攻击者可通过重复请求逐步覆盖指针字节,绕过 ASLR。更麻烦的是 NGINX 的多进程架构反而给了攻击者反复试错机会:某工作进程崩溃后主进程会拉起新进程,且堆布局可能保持一致。

修复建议:

  • NGINX Open Source:升级到 1.31.0 或 1.30.1
  • NGINX Plus:升级到 R36 P4 或 R32 P6
  • 重启服务加载修复后的二进制文件
  • 临时缓解:将受影响 rewrite 规则中的未命名正则捕获改成命名捕获(不会走到有问题的转义路径)

这个漏洞影响面很大——NGINX 承载全球约 1/3 的网站,而且已存在 18 年之久。建议如果服务器有使用 NGINX rewrite 规则的尽快升级版本或采用临时缓解措施。

source/sh/bash

假设 14env.sh 脚本内容只有一行:export MY_VAR=”HelloWorld”

执行 source 14env.sh 后,在终端里直接输入 echo $MY_VAR,会输出 HelloWorld。这个变量会一直存在直到你关闭终端。
执行 sh 14env.sh 或 bash 14env.sh 后,再输入 echo $MY_VAR,会输出空行。因为变量在子进程中被设置,子进程结束变量就消失了。

运维实践建议:

当你需要配置当前工作环境(例如设置 JAVA_HOME, PATH 等)时,使用 source。
当你只是要运行一个完成某项任务的脚本时,使用 bash 或 sh。

centos紧急修复内核依赖库文件libselinux.so.1

[root@k8s-node02 ~]# ls -l /lib64/libselinux.so.1 
-rwxr-xr-x. 1 root root 155744 Apr  1  2020 /lib64/libselinux.so.1
[root@k8s-node02 ~]# rm -fr /lib64/libselinux.so.1
[root@k8s-node02 ~]# init 6
init: error while loading shared libraries: libselinux.so.1: cannot open shared object file: No such file or directory

问题一:xshell窗口无法重启;

问题二:xshell退出激活窗口后,无法通过ssh登录主机;

问题三:控制台登录主机,始终处于login状态,无法跳转至密码对话框;

问题四:重启服务器,无法正常进入页面

恢复误删除的内核文件操作步骤

  1. 光盘引导centttos系统镜像ISO
  2. 进入Troubleshooting->Rescue a CentOS system
  3. 选择1) Continue
  4. cp /lib64/libselinux.so.1 /mnt/sysimage/lib64/
  5. exit

tips
/mnt/sysimage 就是救援环境中挂载的“被救援系统”的根目录,对它的修改就是对原系统的修改;若需要在原系统环境中执行命令,建议 chroot 到 /mnt/sysimage 再进行操作。
​/mnt/sysimage​ 是被抢救系统根文件系统的目录。
chroot /mnt/sysimage​ 是对原系统的修复、检查或配置修改。

硬盘性能测试

mkdir test-data
fio –rw=write –ioengine=sync –fdatasync=1 –directory=test-data –size=2200m –bs=2300 –name=mytest

命令分解

mkdir test-data  # 创建测试目录
fio \
  --rw=write      # 测试模式:纯写入
  --ioengine=sync # I/O引擎:同步阻塞写入(模拟最严苛的持久化场景)
  --fdatasync=1   # 每次写入后调用fdatasync(),确保数据刷到磁盘(类似数据库安全写入)
  --directory=test-data  # 测试目录
  --size=2200m    # 每个线程写入总量:2200MB
  --bs=2300       # 块大小:2300字节(模拟非常规小块写入)
  --name=mytest   # 任务名称

关键参数作用

参数意义
--rw=write测试写入性能(非随机写入)。
--ioengine=sync使用同步I/O,每次写操作必须等待磁盘确认完成(性能低但数据安全)。
--fdatasync=1每次写操作后调用 fdatasync(),强制刷新内核缓冲区到磁盘(类似 fsync 但略轻量)。
--bs=2300

–size=2200m
设置非常规块大小(通常用4K/8K),可能是为了模拟特定业务场景的小数据块写入。
总写入量足够大,避免缓存影响测试结果。

典型应用场景

  1. 数据库持久化测试
  • 模拟事务日志(如WAL)的写入性能,因为 sync+fdatasync 是数据库保证ACID的常用配置。
  1. 磁盘可靠性验证
  • 测试磁盘在强制刷盘模式下的实际吞吐和延迟。
  1. 性能调优基准
  • 对比不同文件系统/磁盘设备在小块同步写入时的表现。

输出结果关注点

运行后会显示以下关键指标:

  • IOPS:每秒写入次数(2300字节/次)。
  • 带宽(BW):实际写入吞吐(如 MB/s)。
  • 延迟(lat):每次写入的平均耗时(尤其关注 syncfdatasync 的开销)。
    示例输出片段:
write: IOPS=500, BW=1.12MiB/s (1.17MB/s)
  sync (usec): min=100, max=10000, avg=2000
  fdatasync: 1 calls, total=10ms

注意事项

  1. 磁盘压力:此命令会高负载写入,避免在生产环境直接运行。
  2. 块大小优化bs=2300 可能导致未对齐写入,正常测试建议用 4K 的整数倍。
  3. 对比测试:移除 --fdatasync=1--ioengine=sync 可测试缓存写入性能(速度会快很多)。

su 与 su – 切换root变量

[ceshizhangzhao@ceshizhangzhao1 ~]$ whoami #确认当前用户为ceshizhangzhao   root
ceshizhangzhao
[ceshizhangzhao@ceshizhangzhao1 ~]$ su #不加用户就表示切换到root,当然也可以su root root
Password:
[root@ceshizhangzhao1 ceshizhangzhao]# env
HOSTNAME=ceshizhangzhao1
SHELL=/bin/bash
HISTSIZE=500
USER=ceshizhangzhao <--
MAIL=/var/spool/mail/ceshizhangzhao
PWD=/home/ceshizhangzhao <--
HOME=/root
LOGNAME=ceshizhangzhao
以下省略无关内容…
提示:使用su而不加上“-”这个参数,切换前的用户的相关信息还会存在。切换用户时,“su - 用户名”。
[root@ceshizhangzhao1 ceshizhangzhao]# exit #退出当前用户,这个命令也可以用ctrl+d
[ceshizhangzhao@ceshizhangzhao1 ~]$ su - root
Password:
[root@ceshizhangzhao1 ~]# env | egrep "USER|MALL|PWD|LOGNAME"
USER=root <--
PWD=/root <--
LOGNAME=root

总结:请su –

定期清理日志

!/bin/bash
设置变量
LOG_DIR="/a/b/c" # 替换为实际的日志目录
ARCHIVE_DIR="/a/b/c/history_archive" # 替换为存储归档的目录
DAYS_TO_KEEP=30 # 保留日志的天数
LOG_FILE="/a/b/c/history_archive/op_record.log" # 替换为实际的日志文件路径
创建归档目录(如果不存在)
mkdir -p "$ARCHIVE_DIR"
获取当前日期
CURRENT_DATE=$(date +%Y-%m-%d)
记录开始时间
echo "[$CURRENT_DATE] 开始压缩和归档日志…" >> "$LOG_FILE"
压缩并归档日志
for dir in "$LOG_DIR"/20*; do
if [ -d "$dir" ]; then
# 获取目录名
dir_name=$(basename "$dir")
# 压缩目录 tar -zcf "$ARCHIVE_DIR/${dir_name}.tar.gz" -C "$LOG_DIR" "$dir_name" # 检查压缩是否成功 if [ $? -eq 0 ]; then echo "[$CURRENT_DATE] 已压缩并归档: $dir_name" >> "$LOG_FILE" # 删除原始日志目录 rm -rf "$dir" echo "[$CURRENT_DATE] 已删除原始日志目录: $dir_name" >> "$LOG_FILE" else echo "[$CURRENT_DATE] 压缩失败: $dir_name" >> "$LOG_FILE" fi fi
done
清理超过指定天数的归档文件
find "$ARCHIVE_DIR" -type f -name "*.tar.gz" -mtime +$DAYS_TO_KEEP -exec rm -f {} \;
echo "[$CURRENT_DATE] 已删除超过 $DAYS_TO_KEEP 天的归档文件" >> "$LOG_FILE"
记录结束时间
echo "[$CURRENT_DATE] 完成压缩和归档日志." >> "$LOG_FILE"

模糊的命令

1、命令$(basename $n)

n="/data/path-a/path-b/2024-log-dir"
COMPRESS_DIR=$(basename "$n")
echo $COMPRESS_DIR
2024-log-dir

2、tar -C目录路径

/data/abcp/access-mo-dispatch/2025-01-01
LOG_DIR="/data/abcp/access-mo-dispatch"
COMPRESS_DIR="2025-01-01"
tar -zcf "/data/abcp/access-mo-dispatch/2025_history_archive/2024-03-14_2024-log-dir.tar.gz" -C "/data/abcp/access-mo-dispatch" "2025-01-01"
归档压缩的结果
[root@k8s-node02 access-mo-dispatch]# tree
.
├── 2025-01-01
├── 2025-01-02
├── 2025-01-03
└── 2025_history_archive
└── 2024-03-14_2024-log-dir.tar.gz
4 directories, 1 file

SecureCRT改过期Linux密码

$language = "VBScript"
$interface = "1.0"
crt.Screen.Synchronous = True
Sub Main
Dim ipAddresses
ipAddresses = Array( _
"192.168.1.54", _
"192.168.1.52", _
"192.168.1.53",
)
Dim i
For i = LBound(ipAddresses) To UBound(ipAddresses)
crt.Screen.Send "ssh wh@" & ipAddresses(i) & chr(13) 'ssh登录linux系统账号
crt.Screen.WaitForStrings Array("assword: ", "密码") '输入密码
crt.Screen.Send "L+CHsKtBvdhbc8ga99" & chr(13) '在自动填入密码
crt.Screen.WaitForString "$" '期待响应的字符
crt.Screen.Send "su - root" & chr(13) '切换root
crt.Screen.WaitForStrings Array("assword: ", "密码", "新的 密码:")
crt.Screen.Send "3xhikeDffYFdujeA99" & chr(13)
crt.Screen.WaitForStrings Array("assword: ", "密码:", "重新输入新的 密码:")
crt.Screen.Send "3xhikeDffYFdujeA99" & chr(13)
crt.Screen.WaitForStrings Array("assword: ", "密码:", "重新输入新的 密码:")
crt.Screen.Send "L+CHsKtBvdhbc8ga99" & chr(13)
crt.Screen.WaitForStrings Array("assword: ", "密码:", "重新输入新的 密码:")
crt.Screen.Send "L+CHsKtBvdhbc8ga99" & chr(13)
crt.Screen.WaitForString "#"
crt.Screen.Send "echo " & chr(34) & "3xhikeDffYFdujeA99" & chr(34) & " " & chr(124) & " passwd --stdin root" & chr(13)
crt.Screen.WaitForStrings Array("~]#", "#")
crt.Screen.Send " su - cl-weih" & chr(13)
crt.Screen.WaitForString "]$"
crt.Screen.Send "su - root" & chr(13)
crt.Screen.WaitForStrings Array("assword: ", "密码")
crt.Screen.Send "3xhikeDffYFdujeA99" & chr(13)
crt.Screen.WaitForString "~]#"
crt.Screen.Send "exit" & chr(13)
crt.Screen.WaitForString "~]$"
crt.Screen.Send "exit" & chr(13)
crt.Screen.WaitForString "~]#"
crt.Screen.Send "exit" & chr(13)
crt.Screen.WaitForString "~]$"
crt.Screen.Send "exit" & chr(13)
Next
End Sub