nezha dashboard安装

preface

最近开到了一台甲骨文的amd 1c 1G ram,打算跑 agent 8合一 和作为 nazha dashboard 使用

⚠️ 隐私信息已脱敏处理:

  • 服务器配置:甲骨文AMD 1核1G内存
  • 操作系统:Ubuntu 24.04 Server
  • 具体IP地址、域名、API密钥等敏感信息已替换为示例值

nezha master 安装

官网

1. 按照官网提供的命令安装即可

开始用 ai 给我提供脚本,希望可以复用,但是发现 ai 很坑爹,总是不正确,直接看官网很快就搞定,用的是 claude code haiku4.5,版本和地址都对不上

curl -L https://raw.githubusercontent.com/nezhahq/scripts/refs/heads/main/install.sh -o nezha.sh && chmod +x nezha.sh && sudo ./nezha.sh

2. 配置 tg 通知

目前没啥用

看官网

  1. 先配通知
  2. 再分组
  3. 警报规则

不配置分组,规则选不到通知组

3. 坑点

新版的 nezha dashboard 端口统一了,dashboard 和 grpc 端口统一了,不要搞错了,ai 可能会提供 5555 端口,除非你反向代理了

nezha agent 安装

直接在管理台复制命令即可

我就坑在 dashboard端口号设置错了,然后死活监控不上,看官网的文档直接修改配置文件解决

后续套CDN 和 TLS 加密

lets encrypt 证书获取

需要准备 cf 的 对应域名 call api token

sh 复制代码
# 更新系统包
sudo apt update
# 安装 certbot 和 cloudflare 插件
sudo apt install -y certbot python3-certbot-dns-cloudflare

# 创建配置目录
mkdir -p ~/.secrets/certbot/

# 创建并编辑凭证文件
vim ~/.secrets/certbot/cloudflare.ini
# ~/.secrets/certbot/cloudflare.ini
dns_cloudflare_api_token = apitoken
# 添加权限
chmod 600 ~/.secrets/certbot/cloudflare.ini
# 申请泛域名证书
sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \
  --dns-cloudflare-propagation-seconds 60 \
  -d "example.com" \
  -d "*.example.com" \
  --preferred-challenges dns-01
  

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem

nezha nginx 反代

看官网

让ai给我优化了配置,真坑爹,grpc 可以访问通,但是dashboard 拿不到数据,最后通过看nginx日志 看到访问通,然后再看官网配置出来的

tail -f /var/log/nginx/access.log 可以实时监控agent 是否访问进来了

我这里使用的是 dashboard 套 cf的cdn,grpc 走非cdn

conf 复制代码
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    # listen [::]:443 ssl http2;
    http2 on; # Nginx > 1.25.1,请注释上面两行,启用此行

    server_name dashboard.example.com; # 替换为你的域名
    # 统一使用刚刚申请出来的 Let's Encrypt 通配符证书路径
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    #    ssl_stapling on;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m; # 如果与其他配置冲突,请注释此项
    ssl_protocols TLSv1.2 TLSv1.3;

    underscores_in_headers on;
    include /etc/nginx/conf.d/cloudflare/ips.conf; # 替换为你的 CDN 回源 IP 地址段
    real_ip_header CF-Connecting-IP; # 替换为你的 CDN 提供的私有 header,此处为 CloudFlare 默认
    # 如果你使用nginx作为最外层,把上面两行注释掉

    # websocket 相关
    location ~* ^/api/v1/ws/(server|terminal|file)(.*)$ {
        proxy_set_header Host $host;
        proxy_set_header nz-realip $http_cf_connecting_ip; # 替换为你的 CDN 提供的私有 header,此处为 CloudFlare 默认
        # proxy_set_header nz-realip $remote_addr; # 如果你使用nginx作为最外层,就把上面一行注释掉,启用此行
        proxy_set_header Origin https://$host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_pass http://127.0.0.1:8008;
    }
    # web
    location / {
        proxy_set_header Host $host;
        proxy_set_header nz-realip $http_cf_connecting_ip; # 替换为你的 CDN 提供的私有 header,此处为 CloudFlare 默认
        # proxy_set_header nz-realip $remote_addr; # 如果你使用nginx作为最外层,就把上面一行注释掉,启用此行
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffer_size 128k;
        proxy_buffers 4 256k;
        proxy_busy_buffers_size 256k;
        proxy_max_temp_file_size 0;
        # proxy_set_header X-Forwarded-Proto $scheme; # 如果你使用nginx作为最外层,就启用此行避免无法正确读取访问的协议
        proxy_pass http://127.0.0.1:8008;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    # listen [::]:443 ssl http2;
    http2 on; # Nginx > 1.25.1,请注释上面两行,启用此行

    server_name data.eample.com; # 替换为你的域名
    # 统一使用刚刚申请出来的 Let's Encrypt 通配符证书路径
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    #ssl_stapling on;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m; # 如果与其他配置冲突,请注释此项
    ssl_protocols TLSv1.2 TLSv1.3;

    underscores_in_headers on;

    # grpc 相关
    location ^~ /proto.NezhaService/ {
        grpc_set_header Host $host;
        grpc_set_header nz-realip $http_CF_Connecting_IP; # 替换为你的 CDN 提供的私有 header,此处为 CloudFlare 默认
        # grpc_set_header nz-realip $remote_addr; # 如果你使用nginx作为最外层,就把上面一行注释掉,启用此行
        grpc_read_timeout 600s;
        grpc_send_timeout 600s;
        grpc_socket_keepalive on;
        client_max_body_size 10m;
        grpc_buffer_size 4m;
        grpc_pass grpc://dashboard;
    }
}

upstream dashboard {
    server 127.0.0.1:8008;
    keepalive 512;
}

安全处理:

  1. ip 访问端口拦截
  2. 80 强直443
conf 复制代码
# 拦截直接 IP 访问 80/443
 server {
     listen 80 default_server;
     listen [::]:80 default_server;
     server_name _;
     return 444;
 }

 server {
     listen 443 ssl default_server;
     listen [::]:443 ssl default_server;
     server_name _;

    # 必须给个证书,随便用哪个域名的都行
     ssl_certificate /etc/letsencrypt/live/eample.com/fullchain.pem;
     ssl_certificate_key /etc/letsencrypt/live/eample.com/privkey.pem;

     return 444;
 }

 server {
     listen 80;
     server_name mywarden.exmaple.com data.example.com;  # 换成你的域名
     return 301 https://$host$request_uri;
 }

cnd 节点ip 段定期更新

创建 自动化脚本

sudo vim /usr/local/bin/update_cloudflare_ips.sh

添加如下内容

bash 复制代码
#!/bin/bash

#############################################################
# Cloudflare IP 自动更新脚本
# 功能:每 6 个月更新一次 Cloudflare IP 段
# 配置位置:/etc/nginx/conf.d/cloudflare/
#############################################################

# ========== 配置部分 ==========
IP_FILE="/etc/nginx/conf.d/cloudflare/ips.conf"
IP_BACKUP_DIR="/etc/nginx/conf.d/cloudflare/backups"
LOG_FILE="/var/log/cloudflare_ips_update.log"
TEMP_FILE="/tmp/cloudflare_ips_temp_$$.conf"
LOCK_FILE="/tmp/cloudflare_ips_update.lock"

# ========== 函数定义 ==========

# 日志函数
log_info() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $1" | tee -a $LOG_FILE
}

log_error() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1" | tee -a $LOG_FILE
}

log_success() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [SUCCESS] $1" | tee -a $LOG_FILE
}

# 防止重复执行
check_lock() {
    if [ -f $LOCK_FILE ]; then
        log_error "脚本已在运行,跳过本次执行"
        exit 1
    fi
    touch $LOCK_FILE
    trap "rm -f $LOCK_FILE" EXIT
}

# 创建备份目录
create_backup_dir() {
    if [ ! -d $IP_BACKUP_DIR ]; then
        mkdir -p $IP_BACKUP_DIR
        log_info "创建备份目录:$IP_BACKUP_DIR"
    fi
}

# 获取 IP 并生成配置文件
fetch_cloudflare_ips_simple() {
    log_info "开始获取 Cloudflare IP 段..."
    
    > $TEMP_FILE
    
    # 头部注释
    {
        echo "# Cloudflare IP Configuration"
        echo "# Updated: $(date '+%Y-%m-%d %H:%M:%S')"
        echo "# Source: https://www.cloudflare.com/ips/"
        echo ""
    } >> $TEMP_FILE
    
    # 获取 IPv4
    log_info "获取 IPv4..."
    if curl -s https://www.cloudflare.com/ips-v4 2>/dev/null | while read ip; do
        if [ ! -z "$ip" ]; then
            echo "set_real_ip_from $ip;" >> $TEMP_FILE
        fi
    done; then
        log_info "✅ IPv4 成功"
    else
        log_error "❌ IPv4 获取失败"
        return 1
    fi
    
    # 获取 IPv6
    echo "" >> $TEMP_FILE
    echo "# IPv6 Addresses" >> $TEMP_FILE
    log_info "获取 IPv6..."
    if curl -s https://www.cloudflare.com/ips-v6 2>/dev/null | while read ip; do
        if [ ! -z "$ip" ]; then
            echo "set_real_ip_from $ip;" >> $TEMP_FILE
        fi
    done; then
        log_info "✅ IPv6 成功"
    else
        log_error "❌ IPv6 获取失败"
        return 1
    fi
    
    # 添加 header 配置
    {
        echo ""
        echo "# Real IP Header Configuration"
    } >> $TEMP_FILE
    
    return 0
}

# 检查文件有效性
validate_config() {
    log_info "验证配置文件..."
    
    # 检查文件大小
    file_size=$(stat -c%s "$TEMP_FILE" 2>/dev/null)
    if [ $file_size -lt 500 ]; then
        log_error "❌ 文件过小,可能获取失败。文件大小:$file_size 字节"
        return 1
    fi
    
    # 检查是否包含必要的配置
    if grep -q "set_real_ip_from" $TEMP_FILE && grep -q "real_ip_header" $TEMP_FILE; then
        log_info "✅ 配置文件验证成功"
        log_info "IP 段数量:$(grep -c 'set_real_ip_from' $TEMP_FILE)"
        return 0
    else
        log_error "❌ 配置文件格式错误"
        return 1
    fi
}

# 备份原文件
backup_old_config() {
    if [ -f $IP_FILE ]; then
        backup_file="$IP_BACKUP_DIR/ips.conf.$(date +%Y%m%d_%H%M%S)"
        cp $IP_FILE $backup_file
        log_info "原配置已备份:$backup_file"
        
        # 保留最近 10 个备份
        backup_count=$(ls -1 $IP_BACKUP_DIR/ips.conf.* 2>/dev/null | wc -l)
        if [ $backup_count -gt 10 ]; then
            log_info "清理旧备份(保留最近 10 个)..."
            ls -1t $IP_BACKUP_DIR/ips.conf.* | tail -n +11 | xargs rm -f
        fi
    fi
}

# 更新配置文件
update_config() {
    log_info "更新配置文件..."
    backup_old_config
    mv $TEMP_FILE $IP_FILE
    chmod 644 $IP_FILE
    log_success "配置文件更新成功:$IP_FILE"
}

# ✅ 重新加载 Nginx(必须!)
reload_nginx() {
    log_info "正在重新加载 Nginx..."
    
    # 测试配置
    if nginx -t > /dev/null 2>&1; then
        log_info "✅ Nginx 配置语法正确"
        
        # 重新加载 Nginx
        if nginx -s reload > /dev/null 2>&1; then
            sleep 1
            log_success "✅ Nginx 重新加载成功(进程号:$(pgrep -f nginx | head -1))"
            return 0
        else
            log_error "❌ Nginx 重新加载失败"
            return 1
        fi
    else
        log_error "❌ Nginx 配置语法错误"
        nginx -t
        return 1
    fi
}

# 回滚操作(出错时)
rollback() {
    log_error "============ 开始回滚 ============"
    latest_backup=$(ls -1t $IP_BACKUP_DIR/ips.conf.* 2>/dev/null | head -1)
    if [ ! -z "$latest_backup" ]; then
        cp $latest_backup $IP_FILE
        log_info "已恢复备份:$latest_backup"
        
        if nginx -s reload > /dev/null 2>&1; then
            log_info "Nginx 已重新加载"
        fi
    else
        log_error "没有可用的备份文件"
    fi
}

# ========== 主程序 ==========

main() {
    log_info "========================================="
    log_info "Cloudflare IP 自动更新脚本启动"
    log_info "目录:/etc/nginx/conf.d/cloudflare/"
    log_info "========================================="
    
    # 检查锁文件
    check_lock
    
    # 创建备份目录
    create_backup_dir
    
    # 获取 IP
    if fetch_cloudflare_ips_simple; then
        # 验证配置
        if validate_config; then
            # 更新配置
            update_config
            
            # ✅ 重新加载 Nginx(必须!)
            if reload_nginx; then
                log_success "========================================="
                log_success "IP 段更新完成,已应用到生产环境"
                log_success "========================================="
                exit 0
            else
                log_error "Nginx 加载失败,开始回滚..."
                rollback
                log_error "========================================="
                log_error "更新失败,已回滚到上一个版本"
                log_error "========================================="
                exit 1
            fi
        else
            log_error "配置验证失败"
            exit 1
        fi
    else
        log_error "获取 IP 段失败,未进行任何更改"
        exit 1
    fi
}

# 执行主程序
main

继续

复制代码
# 2. 赋予权限
sudo chmod +x /usr/local/bin/update_cloudflare_ips.sh

# 3. 创建备份目录
sudo mkdir -p /etc/nginx/conf.d/cloudflare/backups

# 4. 测试脚本
sudo /usr/local/bin/update_cloudflare_ips.sh

# 查看日志
tail -f /var/log/cloudflare_ips_update.log

# 检查生成的文件
ls -lh /etc/nginx/conf.d/cloudflare/

# 查看 IP 段内容
cat /etc/nginx/conf.d/cloudflare/ips.conf | head -20

corn定时任务

bash 复制代码
# 编辑 crontab
sudo crontab -e
# 添加这两行(每年 1 月 1 日和 7 月 1 日凌晨 2 点执行)
0 2 1 1 * /usr/local/bin/update_cloudflare_ips.sh >> /var/log/cloudflare_ips_cron.log 2>&1
0 2 1 7 * /usr/local/bin/update_cloudflare_ips.sh >> /var/log/cloudflare_ips_cron.log 2>&1

# 查看定时任务
sudo crontab -l
# 查看备份
ls -lh /etc/nginx/conf.d/cloudflare/backups/

# 查看配置文件
cat /etc/nginx/conf.d/cloudflare/ips.conf | wc -l

agent 安装网络问题,那就手动下载安装

bash 复制代码
# 登录vps创建文件夹
sudo mkdir -p /opt/nezha/agent
# 主机下载到本地,然后 scp 过去
 scp ./nezha-agent_linux_amd64.zip ubuntu128:/opt/nezha/agent

# 登录vps 解压
sudo unzip nezha-agent_linux_amd64.zip
Archive:  nezha-agent_linux_amd64.zip
  inflating: nezha-agent
kyle@ubuntu128:/opt/nezha/agent$ ls
nezha-agent  nezha-agent_linux_amd64.zip

# 赋予执行权限
sudo chmod +x ./nezha-agent


# /opt/nezha/agent 目录创建配置文件
 vim ./config.yml

# 添加如下
client_secret: <your secret>
debug: false
disable_auto_update: false
disable_command_execute: false
disable_force_update: false
disable_nat: false
disable_send_query: false
gpu: false
insecure_tls: false
ip_report_period: 1800
report_delay: 3
self_update_period: 0
server: <your server ip/domain and port>
skip_connection_count: false
skip_procs_count: false
temperature: false
tls: <your 是否开启tls传输>
use_atomgit_to_upgrade: false
use_gitee_to_upgrade: false
use_ipv6_country_code: false
uuid: <your uuId>

# 然后设置开机启动服务
sudo vim /etc/systemd/system/nezha-agent.service

[Unit]
Description=哪吒监控 Agent
ConditionFileIsExecutable=/opt/nezha/agent/nezha-agent
[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/opt/nezha/agent/nezha-agent "-c" "/opt/nezha/agent/config.yml"
WorkingDirectory=/opt/nezha/agent
Restart=always
RestartSec=30
EnvironmentFile=-/etc/sysconfig/nezha-agent
[Install]
WantedBy=multi-user.target

# 启动下试试
sudo systemctl start nezha-agent
# 运行成功
sudo systemctl status nezha-agent
● nezha-agent.service - 哪吒监控 Agent
     Loaded: loaded (/etc/systemd/system/nezha-agent.servi>
     Active: active (running) since Thu 2026-05-28 17:56:5>
   Main PID: 117460 (nezha-agent)
      Tasks: 13 (limit: 18955)
     Memory: 9.5M (peak: 10.5M)
        CPU: 252ms
     CGroup: /system.slice/nezha-agent.service
             └─117460 /opt/nezha/agent/nezha-agent -c /opt>

May 28 17:56:54 ubuntu128 systemd[1]: Started nezha-agent.>

# 设置开机启动
sudo systemctl enable nezha-agent
Created symlink /etc/systemd/system/multi-user.target.wants/nezha-agent.service → /etc/systemd/system/nezha-agent.service.

nezha 安全防火

因为nezha 具有肉鸡管控能力,防止dashboard 被攻破后,其他机器也会被控制

☑ disable_command_execute(禁用命令执行):

核心安全防御:勾选它后,将彻底切断网页端的 WebSSH 终端控制权,以及后台批量下发、触发任意 Shell 脚本的能力。黑客就算拿到你哪吒后台,也无法将其作为肉鸡下发木马。

☑ disable_nat(禁用内网穿透):

切断隧道后门:哪吒的 NAT 功能允许将 VPS 内部未公开的端口穿透出来。禁用它能防止攻击者利用哪吒的通道反向偷渡进你甲骨文机器的内部网络。

☑ disable_auto_update(禁用自动更新)

如果对安全性要求极高,建议把这个也勾上。它的作用是防止哪吒面板官方的 GitHub 仓库万一哪天被黑客供应链攻击、植入了带有后门的 Agent 新版本。勾选后,这台甲骨文小鸡就只会死守当前运行的、安全的 Agent 版本,不再自作多情地去自动下载更新。