Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 23c88b5f48 | |||
| c9c4da01b6 | |||
| 7a104bbe42 | |||
| e0d97c4486 | |||
| bb73e0f384 |
48
INSTALL.md
48
INSTALL.md
@@ -37,13 +37,17 @@ GITHUB_BRANCH=develop curl -fsSL https://raw.githubusercontent.com/yourbask/link
|
|||||||
|
|
||||||
1. **检测系统** - 自动识别 Linux 发行版和 CPU 架构
|
1. **检测系统** - 自动识别 Linux 发行版和 CPU 架构
|
||||||
2. **安装依赖** - 自动安装 Git、Go、ping、traceroute、dnsutils 等工具
|
2. **安装依赖** - 自动安装 Git、Go、ping、traceroute、dnsutils 等工具
|
||||||
3. **克隆源码** - 从 GitHub 克隆 node 项目源码到 `/opt/linkmaster-node`
|
3. **下载发布包** - 从 Releases 下载预编译发布包(包含二进制文件和所有脚本)
|
||||||
4. **编译安装** - 自动编译源码并安装二进制文件
|
4. **提取文件** - 从发布包提取所有文件到 `/opt/linkmaster-node`(无需克隆 Git)
|
||||||
5. **创建服务** - 自动创建 systemd 服务文件(使用 run.sh 启动)
|
5. **编译安装** - 如果下载失败,自动从源码编译安装
|
||||||
6. **启动服务** - 自动启动并设置开机自启
|
6. **创建服务** - 自动创建 systemd 服务文件(使用 run.sh 启动)
|
||||||
7. **验证安装** - 检查服务状态和健康检查
|
7. **启动服务** - 自动启动并设置开机自启
|
||||||
|
8. **验证安装** - 检查服务状态和健康检查
|
||||||
|
|
||||||
**注意:** 每次服务启动时会自动拉取最新代码并重新编译,确保使用最新版本。
|
**重要说明:**
|
||||||
|
- ✅ **新格式发布包**:包含二进制文件、安装脚本、运行脚本等所有必要文件,安装时直接从压缩包提取,无需克隆 Git 仓库
|
||||||
|
- ✅ **向后兼容**:如果发布包是旧格式(仅包含二进制文件),安装脚本会自动从 Git 克隆获取脚本文件
|
||||||
|
- ⚠️ **源码编译模式**:如果下载失败,会从 Git 克隆源码并编译(需要网络连接)
|
||||||
|
|
||||||
## 安装后管理
|
## 安装后管理
|
||||||
|
|
||||||
@@ -109,7 +113,37 @@ curl http://localhost:2200/api/health
|
|||||||
|
|
||||||
如果无法使用一键安装脚本,可以手动安装:
|
如果无法使用一键安装脚本,可以手动安装:
|
||||||
|
|
||||||
### 1. 克隆源码并编译
|
### 方式一:从发布包安装(推荐)
|
||||||
|
|
||||||
|
**优点**:无需 Git 和 Go 环境,直接使用预编译文件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 下载发布包(替换为实际版本和平台)
|
||||||
|
wget https://gitee.nas.cpolar.cn/yoyo/linkmaster-node/releases/download/v1.1.4/agent-linux-amd64-v1.1.4.tar.gz
|
||||||
|
|
||||||
|
# 2. 解压
|
||||||
|
tar -xzf agent-linux-amd64-v1.1.4.tar.gz
|
||||||
|
cd agent-linux-amd64-v1.1.4
|
||||||
|
|
||||||
|
# 3. 复制文件到安装目录
|
||||||
|
sudo mkdir -p /opt/linkmaster-node
|
||||||
|
sudo cp -r * /opt/linkmaster-node/
|
||||||
|
sudo chmod +x /opt/linkmaster-node/agent
|
||||||
|
sudo chmod +x /opt/linkmaster-node/*.sh
|
||||||
|
|
||||||
|
# 4. 复制二进制文件到系统目录
|
||||||
|
sudo cp /opt/linkmaster-node/agent /usr/local/bin/linkmaster-node
|
||||||
|
sudo chmod +x /usr/local/bin/linkmaster-node
|
||||||
|
|
||||||
|
# 5. 创建配置文件(从示例复制)
|
||||||
|
sudo cp /opt/linkmaster-node/config.yaml.example /opt/linkmaster-node/config.yaml
|
||||||
|
# 编辑配置文件,设置后端地址
|
||||||
|
sudo nano /opt/linkmaster-node/config.yaml
|
||||||
|
|
||||||
|
# 6. 创建 systemd 服务(参考下面的服务配置)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式二:克隆源码并编译
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 克隆仓库
|
# 克隆仓库
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ GITHUB_BRANCH=develop curl -fsSL https://gitee.nas.cpolar.cn/yoyo/linkmaster-nod
|
|||||||
- 自动安装系统依赖(curl, wget, git, ping, traceroute 等)
|
- 自动安装系统依赖(curl, wget, git, ping, traceroute 等)
|
||||||
- 自动安装 Go 环境(优先使用系统包管理器,失败则从官网下载)
|
- 自动安装 Go 环境(优先使用系统包管理器,失败则从官网下载)
|
||||||
- 优先从 Releases 下载预编译二进制文件,失败则从源码编译
|
- 优先从 Releases 下载预编译二进制文件,失败则从源码编译
|
||||||
|
- **发布包包含所有必要文件**:二进制文件、安装脚本、运行脚本等,无需从 Git 拉取
|
||||||
- 自动创建 systemd 服务并配置自启动
|
- 自动创建 systemd 服务并配置自启动
|
||||||
- 自动配置防火墙规则(开放 2200 端口)
|
- 自动配置防火墙规则(开放 2200 端口)
|
||||||
- 自动登记节点到后端服务器
|
- 自动登记节点到后端服务器
|
||||||
@@ -411,6 +412,7 @@ BACKEND_URL=http://192.168.1.100:8080 ./run.sh start
|
|||||||
**功能特性:**
|
**功能特性:**
|
||||||
- ✅ **自动从 `version.json` 读取版本号和标签**(无需手动指定)
|
- ✅ **自动从 `version.json` 读取版本号和标签**(无需手动指定)
|
||||||
- ✅ **Token 已硬编码**(无需手动指定)
|
- ✅ **Token 已硬编码**(无需手动指定)
|
||||||
|
- ✅ **自动打包所有必要文件**:二进制文件、安装脚本、运行脚本、配置文件等
|
||||||
- ✅ 自动打包二进制文件(tar.gz 或 zip)
|
- ✅ 自动打包二进制文件(tar.gz 或 zip)
|
||||||
- ✅ 自动创建发布说明
|
- ✅ 自动创建发布说明
|
||||||
- ✅ 支持指定平台上传
|
- ✅ 支持指定平台上传
|
||||||
|
|||||||
@@ -279,16 +279,52 @@ pack_files() {
|
|||||||
local pack_name="${PROJECT_NAME}-${os}-${arch}-${VERSION}"
|
local pack_name="${PROJECT_NAME}-${os}-${arch}-${VERSION}"
|
||||||
local pack_file
|
local pack_file
|
||||||
|
|
||||||
|
# 创建临时打包目录
|
||||||
|
local pack_dir="${TEMP_DIR}/${pack_name}"
|
||||||
|
mkdir -p "$pack_dir"
|
||||||
|
|
||||||
|
# 复制二进制文件并重命名为 agent
|
||||||
|
if [ "$os" = "windows" ]; then
|
||||||
|
cp "$binary" "$pack_dir/agent.exe"
|
||||||
|
else
|
||||||
|
cp "$binary" "$pack_dir/agent"
|
||||||
|
chmod +x "$pack_dir/agent"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 复制必要的脚本文件
|
||||||
|
local scripts=("install.sh" "run.sh" "start-systemd.sh" "uninstall.sh")
|
||||||
|
for script in "${scripts[@]}"; do
|
||||||
|
if [ -f "$script" ]; then
|
||||||
|
cp "$script" "$pack_dir/"
|
||||||
|
chmod +x "$pack_dir/$script"
|
||||||
|
echo -e "${BLUE} [包含]${NC} $script"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW} [警告]${NC} $script 不存在,跳过"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 复制示例配置文件
|
||||||
|
if [ -f "config.yaml.example" ]; then
|
||||||
|
cp "config.yaml.example" "$pack_dir/config.yaml.example"
|
||||||
|
echo -e "${BLUE} [包含]${NC} config.yaml.example"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW} [警告]${NC} config.yaml.example 不存在,跳过"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 打包
|
||||||
if [ "$os" = "windows" ]; then
|
if [ "$os" = "windows" ]; then
|
||||||
pack_file="${TEMP_DIR}/${pack_name}.zip"
|
pack_file="${TEMP_DIR}/${pack_name}.zip"
|
||||||
echo -e "${BLUE}[打包]${NC} ${platform} -> ${pack_name}.zip"
|
echo -e "${BLUE}[打包]${NC} ${platform} -> ${pack_name}.zip"
|
||||||
(cd "$BUILD_DIR" && zip -q "${pack_file}" "$(basename $binary)")
|
(cd "$TEMP_DIR" && zip -q -r "${pack_file}" "$(basename $pack_dir)")
|
||||||
else
|
else
|
||||||
pack_file="${TEMP_DIR}/${pack_name}.tar.gz"
|
pack_file="${TEMP_DIR}/${pack_name}.tar.gz"
|
||||||
echo -e "${BLUE}[打包]${NC} ${platform} -> ${pack_name}.tar.gz"
|
echo -e "${BLUE}[打包]${NC} ${platform} -> ${pack_name}.tar.gz"
|
||||||
tar -czf "$pack_file" -C "$BUILD_DIR" "$(basename $binary)"
|
tar -czf "$pack_file" -C "$TEMP_DIR" "$(basename $pack_dir)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 清理临时目录
|
||||||
|
rm -rf "$pack_dir"
|
||||||
|
|
||||||
echo "$pack_file"
|
echo "$pack_file"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
18
config.yaml.example
Normal file
18
config.yaml.example
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
server:
|
||||||
|
port: 2200
|
||||||
|
backend:
|
||||||
|
url: http://your-backend-server:8080
|
||||||
|
heartbeat:
|
||||||
|
interval: 60
|
||||||
|
log:
|
||||||
|
file: node.log
|
||||||
|
level: info
|
||||||
|
debug: false
|
||||||
|
node:
|
||||||
|
id: 0
|
||||||
|
ip: ""
|
||||||
|
country: ""
|
||||||
|
province: ""
|
||||||
|
city: ""
|
||||||
|
isp: ""
|
||||||
|
|
||||||
354
install.sh
354
install.sh
@@ -410,6 +410,119 @@ install_dependencies() {
|
|||||||
echo -e "${GREEN}✓ 系统依赖安装完成${NC}"
|
echo -e "${GREEN}✓ 系统依赖安装完成${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 配置时间同步
|
||||||
|
sync_time() {
|
||||||
|
echo -e "${BLUE}配置时间同步...${NC}"
|
||||||
|
|
||||||
|
# 1. 设置时区为 Asia/Shanghai
|
||||||
|
echo -e "${BLUE}[1/6] 设置时区为 Asia/Shanghai${NC}"
|
||||||
|
if command -v timedatectl > /dev/null 2>&1; then
|
||||||
|
sudo timedatectl set-timezone Asia/Shanghai 2>/dev/null || {
|
||||||
|
echo -e "${YELLOW}⚠ timedatectl 设置时区失败,尝试其他方法${NC}"
|
||||||
|
# 尝试创建时区链接(适用于较老的系统)
|
||||||
|
if [ -f /usr/share/zoneinfo/Asia/Shanghai ]; then
|
||||||
|
sudo ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
elif [ -f /usr/share/zoneinfo/Asia/Shanghai ]; then
|
||||||
|
sudo ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. 安装 chrony
|
||||||
|
echo -e "${BLUE}[2/6] 安装 chrony${NC}"
|
||||||
|
if [ "$OS" = "ubuntu" ] || [ "$OS" = "debian" ]; then
|
||||||
|
if ! dpkg -l 2>/dev/null | grep -q "^ii.*chrony"; then
|
||||||
|
sudo apt-get install -y chrony
|
||||||
|
else
|
||||||
|
echo -e "${BLUE}chrony 已安装,跳过${NC}"
|
||||||
|
fi
|
||||||
|
elif [ "$OS" = "centos" ] || [ "$OS" = "rhel" ] || [ "$OS" = "rocky" ] || [ "$OS" = "almalinux" ]; then
|
||||||
|
if ! rpm -q chrony &>/dev/null; then
|
||||||
|
sudo yum install -y chrony
|
||||||
|
else
|
||||||
|
echo -e "${BLUE}chrony 已安装,跳过${NC}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}⚠ 未知系统类型,跳过 chrony 安装${NC}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. 配置 NTP 服务器
|
||||||
|
echo -e "${BLUE}[3/6] 配置 NTP 服务器${NC}"
|
||||||
|
CONF="/etc/chrony.conf"
|
||||||
|
|
||||||
|
if [ -f "$CONF" ]; then
|
||||||
|
# 备份配置文件
|
||||||
|
if [ ! -f "${CONF}.backup.$(date +%Y%m%d)" ]; then
|
||||||
|
sudo cp "$CONF" "${CONF}.backup.$(date +%Y%m%d)" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 注释掉原有的 server 行
|
||||||
|
sudo sed -i 's/^server /#server /g' "$CONF" 2>/dev/null || true
|
||||||
|
|
||||||
|
# 添加中国 NTP 服务器(如果还没有)
|
||||||
|
if ! grep -q "ntp.aliyun.com" "$CONF"; then
|
||||||
|
sudo tee -a "$CONF" > /dev/null <<EOF
|
||||||
|
|
||||||
|
# China NTP servers (added by LinkMaster Node installer)
|
||||||
|
server ntp.aliyun.com iburst
|
||||||
|
server ntp.tencent.com iburst
|
||||||
|
server ntp1.aliyun.com iburst
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}⚠ chrony.conf 不存在,跳过配置${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. 启动并启用 chronyd
|
||||||
|
echo -e "${BLUE}[4/6] 启动 chronyd${NC}"
|
||||||
|
if command -v systemctl > /dev/null 2>&1; then
|
||||||
|
sudo systemctl enable chronyd --now 2>/dev/null || {
|
||||||
|
# 如果 systemctl 失败,尝试使用 service 命令
|
||||||
|
if command -v service > /dev/null 2>&1; then
|
||||||
|
sudo service chronyd start 2>/dev/null || true
|
||||||
|
sudo chkconfig chronyd on 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
elif command -v service > /dev/null 2>&1; then
|
||||||
|
sudo service chronyd start 2>/dev/null || true
|
||||||
|
sudo chkconfig chronyd on 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 等待服务启动
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# 5. 立即强制同步
|
||||||
|
echo -e "${BLUE}[5/6] 强制同步系统时间${NC}"
|
||||||
|
if command -v chronyc > /dev/null 2>&1; then
|
||||||
|
sudo chronyc -a makestep 2>/dev/null || {
|
||||||
|
# 如果 makestep 失败,尝试使用 sources 和 sourcestats
|
||||||
|
sudo chronyc sources 2>/dev/null || true
|
||||||
|
sudo chronyc sourcestats 2>/dev/null || true
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 6. 写入硬件时间
|
||||||
|
echo -e "${BLUE}[6/6] 写入硬件时钟${NC}"
|
||||||
|
if command -v hwclock > /dev/null 2>&1; then
|
||||||
|
sudo hwclock --systohc 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 显示时间状态
|
||||||
|
echo -e "${BLUE}当前时间状态:${NC}"
|
||||||
|
if command -v timedatectl > /dev/null 2>&1; then
|
||||||
|
sudo timedatectl status 2>/dev/null || true
|
||||||
|
else
|
||||||
|
date
|
||||||
|
if command -v hwclock > /dev/null 2>&1; then
|
||||||
|
echo -e "${BLUE}硬件时钟:${NC}"
|
||||||
|
sudo hwclock 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ 时间同步配置完成${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
# 从官网下载安装 Go
|
# 从官网下载安装 Go
|
||||||
install_go_from_official() {
|
install_go_from_official() {
|
||||||
echo -e "${BLUE}从 Go 官网下载安装...${NC}"
|
echo -e "${BLUE}从 Go 官网下载安装...${NC}"
|
||||||
@@ -833,21 +946,20 @@ download_binary_from_releases() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# 解压文件
|
# 解压文件
|
||||||
echo -e "${BLUE}解压二进制文件...${NC}"
|
echo -e "${BLUE}解压发布包...${NC}"
|
||||||
cd "$temp_dir"
|
cd "$temp_dir"
|
||||||
|
|
||||||
|
local extracted_dir=""
|
||||||
if [ "$file_ext" = "tar.gz" ]; then
|
if [ "$file_ext" = "tar.gz" ]; then
|
||||||
if ! tar -xzf "$download_file" 2>/dev/null; then
|
if ! tar -xzf "$download_file" 2>/dev/null; then
|
||||||
echo -e "${YELLOW}⚠ 解压失败,将使用源码编译${NC}"
|
echo -e "${YELLOW}⚠ 解压失败,将使用源码编译${NC}"
|
||||||
rm -rf "$temp_dir"
|
rm -rf "$temp_dir"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
# 查找解压后的二进制文件(优先查找 agent,然后是 agent-*)
|
# 查找解压后的目录(可能是直接解压到当前目录,也可能是在子目录中)
|
||||||
local binary_file=""
|
extracted_dir=$(find . -maxdepth 1 -type d ! -name "." ! -name ".." | head -1)
|
||||||
if [ -f "./agent" ] && [ -x "./agent" ]; then
|
if [ -z "$extracted_dir" ]; then
|
||||||
binary_file="./agent"
|
extracted_dir="."
|
||||||
else
|
|
||||||
binary_file=$(find . -maxdepth 1 -type f \( -name "agent" -o -name "agent-*" -o -name "Agent" -o -name "Agent-*" \) ! -name "*.tar.gz" ! -name "*.zip" 2>/dev/null | head -1)
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# Windows zip 文件(虽然脚本主要在 Linux 上运行,但保留兼容性)
|
# Windows zip 文件(虽然脚本主要在 Linux 上运行,但保留兼容性)
|
||||||
@@ -856,26 +968,82 @@ download_binary_from_releases() {
|
|||||||
rm -rf "$temp_dir"
|
rm -rf "$temp_dir"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
local binary_file=$(find . -maxdepth 1 -type f \( -name "agent*.exe" -o -name "Agent*.exe" \) 2>/dev/null | head -1)
|
extracted_dir=$(find . -maxdepth 1 -type d ! -name "." ! -name ".." | head -1)
|
||||||
|
if [ -z "$extracted_dir" ]; then
|
||||||
|
extracted_dir="."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$extracted_dir" || {
|
||||||
|
echo -e "${YELLOW}⚠ 无法进入解压目录,将使用源码编译${NC}"
|
||||||
|
rm -rf "$temp_dir"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 显示解压后的目录内容(用于调试)
|
||||||
|
echo -e "${BLUE}解压目录内容:${NC}"
|
||||||
|
ls -la . 2>/dev/null || true
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 查找二进制文件(先检查当前目录,再递归查找)
|
||||||
|
local binary_file=""
|
||||||
|
if [ "$OS_TYPE" = "windows" ]; then
|
||||||
|
if [ -f "./agent.exe" ]; then
|
||||||
|
binary_file="./agent.exe"
|
||||||
|
elif [ -f "agent.exe" ]; then
|
||||||
|
binary_file="agent.exe"
|
||||||
|
else
|
||||||
|
# 递归查找所有 .exe 文件
|
||||||
|
binary_file=$(find . -type f -name "*.exe" ! -name "*.tar.gz" ! -name "*.zip" 2>/dev/null | grep -i agent | head -1)
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Linux/macOS: 先检查常见位置
|
||||||
|
if [ -f "./agent" ]; then
|
||||||
|
binary_file="./agent"
|
||||||
|
elif [ -f "agent" ]; then
|
||||||
|
binary_file="agent"
|
||||||
|
else
|
||||||
|
# 递归查找所有文件,排除压缩包和目录
|
||||||
|
# 查找名为 agent 的文件(不是目录)
|
||||||
|
binary_file=$(find . -type f \( -name "agent" -o -name "agent-*" \) ! -name "*.tar.gz" ! -name "*.zip" 2>/dev/null | head -1)
|
||||||
|
|
||||||
|
# 如果还是找不到,尝试查找所有可执行文件
|
||||||
|
if [ -z "$binary_file" ]; then
|
||||||
|
binary_file=$(find . -type f -perm +111 ! -name "*.tar.gz" ! -name "*.zip" ! -name "*.sh" 2>/dev/null | head -1)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$binary_file" ] || [ ! -f "$binary_file" ]; then
|
if [ -z "$binary_file" ] || [ ! -f "$binary_file" ]; then
|
||||||
echo -e "${YELLOW}⚠ 未找到解压后的二进制文件,将使用源码编译${NC}"
|
echo -e "${YELLOW}⚠ 未找到解压后的二进制文件,将使用源码编译${NC}"
|
||||||
echo -e "${YELLOW} 解压目录内容:${NC}"
|
echo -e "${YELLOW} 当前目录: $(pwd)${NC}"
|
||||||
ls -la "$temp_dir" 2>/dev/null || true
|
echo -e "${YELLOW} 查找的文件: agent 或 agent-*${NC}"
|
||||||
|
echo -e "${YELLOW} 所有文件列表:${NC}"
|
||||||
|
find . -type f 2>/dev/null | head -20 || true
|
||||||
rm -rf "$temp_dir"
|
rm -rf "$temp_dir"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 确保使用绝对路径
|
||||||
|
local binary_path=""
|
||||||
|
if [[ "$binary_file" == /* ]]; then
|
||||||
|
binary_path="$binary_file"
|
||||||
|
else
|
||||||
|
# 转换为绝对路径
|
||||||
|
binary_path="$(cd "$(dirname "$binary_file")" && pwd)/$(basename "$binary_file")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ 找到二进制文件: ${binary_path}${NC}"
|
||||||
|
|
||||||
# 验证二进制文件是否可执行
|
# 验证二进制文件是否可执行
|
||||||
if [ ! -x "$binary_file" ]; then
|
if [ ! -x "$binary_path" ]; then
|
||||||
chmod +x "$binary_file" 2>/dev/null || true
|
chmod +x "$binary_path" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 验证二进制文件类型(Linux 应该是 ELF 文件)
|
# 验证二进制文件类型(Linux 应该是 ELF 文件)
|
||||||
if [ "$OS_TYPE" = "linux" ]; then
|
if [ "$OS_TYPE" = "linux" ]; then
|
||||||
if command -v file > /dev/null 2>&1; then
|
if command -v file > /dev/null 2>&1; then
|
||||||
local file_type=$(file "$binary_file" 2>/dev/null || echo "")
|
local file_type=$(file "$binary_path" 2>/dev/null || echo "")
|
||||||
if [ -n "$file_type" ] && ! echo "$file_type" | grep -qi "ELF"; then
|
if [ -n "$file_type" ] && ! echo "$file_type" | grep -qi "ELF"; then
|
||||||
echo -e "${YELLOW}⚠ 二进制文件类型异常: ${file_type}${NC}"
|
echo -e "${YELLOW}⚠ 二进制文件类型异常: ${file_type}${NC}"
|
||||||
echo -e "${YELLOW} 将使用源码编译${NC}"
|
echo -e "${YELLOW} 将使用源码编译${NC}"
|
||||||
@@ -885,93 +1053,98 @@ download_binary_from_releases() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 保存下载的二进制文件到临时位置
|
# 保存当前目录(extracted_dir)
|
||||||
local downloaded_binary="${temp_dir}/downloaded_agent"
|
local extracted_path="$(pwd)"
|
||||||
sudo cp "$binary_file" "$downloaded_binary"
|
|
||||||
sudo chmod +x "$downloaded_binary"
|
|
||||||
|
|
||||||
# 验证复制后的文件
|
# 检查是否是新格式的发布包(包含脚本文件)
|
||||||
if [ ! -f "$downloaded_binary" ] || [ ! -x "$downloaded_binary" ]; then
|
local has_scripts=false
|
||||||
echo -e "${YELLOW}⚠ 二进制文件验证失败,将使用源码编译${NC}"
|
if [ -f "$extracted_path/install.sh" ] || [ -f "$extracted_path/run.sh" ] || [ -f "$extracted_path/start-systemd.sh" ]; then
|
||||||
rm -rf "$temp_dir"
|
has_scripts=true
|
||||||
return 1
|
echo -e "${GREEN}✓ 检测到新格式发布包(包含脚本文件)${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 清理临时下载文件
|
# 创建源码目录
|
||||||
rm -f "$download_file"
|
|
||||||
|
|
||||||
# 克隆仓库(用于获取 run.sh 和 start-systemd.sh 等脚本)
|
|
||||||
echo -e "${BLUE}克隆仓库以获取启动脚本...${NC}"
|
|
||||||
if [ -d "$SOURCE_DIR" ]; then
|
if [ -d "$SOURCE_DIR" ]; then
|
||||||
sudo rm -rf "$SOURCE_DIR"
|
sudo rm -rf "$SOURCE_DIR"
|
||||||
fi
|
fi
|
||||||
|
sudo mkdir -p "$SOURCE_DIR"
|
||||||
|
|
||||||
if ! sudo git clone --branch "${GITHUB_BRANCH}" "https://gitee.nas.cpolar.cn/${GITHUB_REPO}.git" "$SOURCE_DIR" 2>&1; then
|
if [ "$has_scripts" = true ]; then
|
||||||
echo -e "${YELLOW}⚠ 克隆仓库失败,将使用源码编译${NC}"
|
# 新格式:从压缩包提取所有文件
|
||||||
rm -rf "$temp_dir"
|
echo -e "${BLUE}从发布包提取所有文件...${NC}"
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 对比 Git commit hash
|
|
||||||
if [ -n "$release_commit" ]; then
|
|
||||||
echo -e "${BLUE}验证 Git commit 版本...${NC}"
|
|
||||||
|
|
||||||
# 切换到源码目录(如果存在)
|
# 复制二进制文件
|
||||||
if [ -d "$SOURCE_DIR" ] && [ -d "$SOURCE_DIR/.git" ]; then
|
sudo cp "$binary_path" "$SOURCE_DIR/agent"
|
||||||
cd "$SOURCE_DIR" || {
|
sudo chmod +x "$SOURCE_DIR/agent"
|
||||||
echo -e "${YELLOW}⚠ 无法切换到源码目录,跳过验证${NC}"
|
echo -e "${GREEN}✓ 已提取二进制文件${NC}"
|
||||||
cd /tmp || true
|
|
||||||
}
|
# 复制脚本文件
|
||||||
|
local scripts=("install.sh" "run.sh" "start-systemd.sh" "uninstall.sh")
|
||||||
|
for script in "${scripts[@]}"; do
|
||||||
|
if [ -f "$extracted_path/$script" ]; then
|
||||||
|
sudo cp "$extracted_path/$script" "$SOURCE_DIR/"
|
||||||
|
sudo chmod +x "$SOURCE_DIR/$script"
|
||||||
|
echo -e "${GREEN}✓ 已提取 $script${NC}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 复制示例配置文件
|
||||||
|
if [ -f "$extracted_path/config.yaml.example" ]; then
|
||||||
|
sudo cp "$extracted_path/config.yaml.example" "$SOURCE_DIR/config.yaml.example"
|
||||||
|
echo -e "${GREEN}✓ 已提取 config.yaml.example${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ 所有文件已从发布包提取,无需克隆 Git 仓库${NC}"
|
||||||
|
else
|
||||||
|
# 旧格式:只有二进制文件,需要克隆 Git 仓库获取脚本
|
||||||
|
echo -e "${BLUE}检测到旧格式发布包(仅包含二进制文件)${NC}"
|
||||||
|
echo -e "${BLUE}克隆仓库以获取启动脚本...${NC}"
|
||||||
|
|
||||||
|
if ! sudo git clone --branch "${GITHUB_BRANCH}" "https://gitee.nas.cpolar.cn/${GITHUB_REPO}.git" "$SOURCE_DIR" 2>&1; then
|
||||||
|
echo -e "${YELLOW}⚠ 克隆仓库失败,将使用源码编译${NC}"
|
||||||
|
rm -rf "$temp_dir"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 对比 Git commit hash(仅旧格式需要)
|
||||||
|
if [ -n "$release_commit" ]; then
|
||||||
|
echo -e "${BLUE}验证 Git commit 版本...${NC}"
|
||||||
|
|
||||||
# 获取当前分支的最新 commit hash
|
if [ -d "$SOURCE_DIR/.git" ]; then
|
||||||
local current_commit=$(git rev-parse HEAD 2>/dev/null || echo "")
|
cd "$SOURCE_DIR" || {
|
||||||
|
echo -e "${YELLOW}⚠ 无法切换到源码目录,跳过验证${NC}"
|
||||||
if [ -n "$current_commit" ]; then
|
cd /tmp || true
|
||||||
# 截取短 commit hash 用于显示(前7位)
|
}
|
||||||
local release_commit_short=""
|
|
||||||
local current_commit_short=$(echo "$current_commit" | cut -c1-7)
|
|
||||||
|
|
||||||
if [ "${#release_commit}" -eq 40 ]; then
|
local current_commit=$(git rev-parse HEAD 2>/dev/null || echo "")
|
||||||
release_commit_short=$(echo "$release_commit" | cut -c1-7)
|
|
||||||
else
|
|
||||||
release_commit_short="$release_commit"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "${BLUE} Release commit: ${release_commit_short}${NC}"
|
if [ -n "$current_commit" ] && [ "${#release_commit}" -eq 40 ] && [ "${#current_commit}" -eq 40 ]; then
|
||||||
echo -e "${BLUE} 当前代码 commit: ${current_commit_short}${NC}"
|
local release_commit_short=$(echo "$release_commit" | cut -c1-7)
|
||||||
|
local current_commit_short=$(echo "$current_commit" | cut -c1-7)
|
||||||
# 对比 commit hash(只有当 release_commit 是完整的 commit hash 时才对比)
|
|
||||||
if [ "${#release_commit}" -eq 40 ] && [ "${#current_commit}" -eq 40 ]; then
|
echo -e "${BLUE} Release commit: ${release_commit_short}${NC}"
|
||||||
|
echo -e "${BLUE} 当前代码 commit: ${current_commit_short}${NC}"
|
||||||
|
|
||||||
if [ "$release_commit" != "$current_commit" ]; then
|
if [ "$release_commit" != "$current_commit" ]; then
|
||||||
echo -e "${YELLOW}⚠ Commit hash 不匹配,二进制文件可能不是最新代码编译的${NC}"
|
echo -e "${YELLOW}⚠ Commit hash 不匹配,二进制文件可能不是最新代码编译的${NC}"
|
||||||
echo -e "${YELLOW} Release 基于较旧的代码,将使用源码编译最新版本${NC}"
|
echo -e "${YELLOW} Release 基于较旧的代码,将使用源码编译最新版本${NC}"
|
||||||
# 保留已克隆的仓库目录,供 build_from_source 复用
|
|
||||||
cd /tmp || true
|
cd /tmp || true
|
||||||
rm -rf "$temp_dir"
|
rm -rf "$temp_dir"
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
echo -e "${GREEN}✓ Commit hash 匹配,二进制文件是最新代码编译的${NC}"
|
echo -e "${GREEN}✓ Commit hash 匹配${NC}"
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
echo -e "${YELLOW}⚠ 无法获取有效的 commit hash 进行对比,跳过验证${NC}"
|
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
echo -e "${YELLOW}⚠ 无法获取当前代码的 commit hash,跳过验证${NC}"
|
cd /tmp || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 返回临时目录
|
|
||||||
cd /tmp || true
|
|
||||||
else
|
|
||||||
echo -e "${YELLOW}⚠ 源码目录不存在,跳过验证${NC}"
|
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
echo -e "${YELLOW}⚠ 无法获取 release 的 commit hash,跳过验证${NC}"
|
# 用下载的二进制文件覆盖克隆目录中的文件
|
||||||
|
sudo cp "$binary_path" "$SOURCE_DIR/agent"
|
||||||
|
sudo chmod +x "$SOURCE_DIR/agent"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 用下载的二进制文件覆盖克隆目录中的文件
|
|
||||||
sudo cp "$downloaded_binary" "$SOURCE_DIR/agent"
|
|
||||||
sudo chmod +x "$SOURCE_DIR/agent"
|
|
||||||
|
|
||||||
# 复制到安装目录
|
# 复制到安装目录
|
||||||
sudo mkdir -p "$INSTALL_DIR"
|
sudo mkdir -p "$INSTALL_DIR"
|
||||||
sudo cp "$SOURCE_DIR/agent" "$INSTALL_DIR/$BINARY_NAME"
|
sudo cp "$SOURCE_DIR/agent" "$INSTALL_DIR/$BINARY_NAME"
|
||||||
@@ -982,9 +1155,9 @@ download_binary_from_releases() {
|
|||||||
|
|
||||||
# 显示文件信息
|
# 显示文件信息
|
||||||
local binary_size=$(du -h "$SOURCE_DIR/agent" | cut -f1)
|
local binary_size=$(du -h "$SOURCE_DIR/agent" | cut -f1)
|
||||||
echo -e "${GREEN}✓ 二进制文件下载完成 (文件大小: ${binary_size})${NC}"
|
echo -e "${GREEN}✓ 安装文件准备完成 (文件大小: ${binary_size})${NC}"
|
||||||
echo -e "${BLUE}版本: ${latest_tag}${NC}"
|
echo -e "${BLUE}版本: ${latest_tag}${NC}"
|
||||||
echo -e "${BLUE}二进制文件: ${SOURCE_DIR}/agent${NC}"
|
echo -e "${BLUE}安装目录: ${SOURCE_DIR}${NC}"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -1455,26 +1628,29 @@ main() {
|
|||||||
echo -e "${BLUE}后端地址: ${BACKEND_URL}${NC}"
|
echo -e "${BLUE}后端地址: ${BACKEND_URL}${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
echo -e "${BLUE}[1/8] 检测系统类型...${NC}"
|
echo -e "${BLUE}[1/11] 检测系统类型...${NC}"
|
||||||
detect_system
|
detect_system
|
||||||
|
|
||||||
# 检查是否已安装,如果已安装则先卸载
|
# 检查是否已安装,如果已安装则先卸载
|
||||||
if check_installed; then
|
if check_installed; then
|
||||||
echo -e "${BLUE}[2/8] 卸载已存在的服务...${NC}"
|
echo -e "${BLUE}[2/11] 卸载已存在的服务...${NC}"
|
||||||
uninstall_service
|
uninstall_service
|
||||||
else
|
else
|
||||||
echo -e "${BLUE}[2/8] 检查已安装服务...${NC}"
|
echo -e "${BLUE}[2/11] 检查已安装服务...${NC}"
|
||||||
echo -e "${GREEN}✓ 未检测到已安装的服务${NC}"
|
echo -e "${GREEN}✓ 未检测到已安装的服务${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${BLUE}[3/8] 检测并配置镜像源...${NC}"
|
echo -e "${BLUE}[3/11] 检测并配置镜像源...${NC}"
|
||||||
detect_fastest_mirror
|
detect_fastest_mirror
|
||||||
|
|
||||||
echo -e "${BLUE}[4/8] 安装系统依赖...${NC}"
|
echo -e "${BLUE}[4/11] 安装系统依赖...${NC}"
|
||||||
install_dependencies
|
install_dependencies
|
||||||
|
|
||||||
|
echo -e "${BLUE}[5/11] 配置时间同步...${NC}"
|
||||||
|
sync_time
|
||||||
|
|
||||||
# 优先尝试从 Releases 下载二进制文件
|
# 优先尝试从 Releases 下载二进制文件
|
||||||
echo -e "${BLUE}[5/8] 下载或编译二进制文件...${NC}"
|
echo -e "${BLUE}[6/11] 下载或编译二进制文件...${NC}"
|
||||||
if ! download_binary_from_releases; then
|
if ! download_binary_from_releases; then
|
||||||
echo -e "${BLUE}从 Releases 下载失败,开始从源码编译...${NC}"
|
echo -e "${BLUE}从 Releases 下载失败,开始从源码编译...${NC}"
|
||||||
build_from_source
|
build_from_source
|
||||||
@@ -1482,19 +1658,19 @@ main() {
|
|||||||
echo -e "${GREEN}✓ 使用预编译二进制文件,跳过编译步骤${NC}"
|
echo -e "${GREEN}✓ 使用预编译二进制文件,跳过编译步骤${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${BLUE}[6/8] 创建 systemd 服务...${NC}"
|
echo -e "${BLUE}[7/11] 创建 systemd 服务...${NC}"
|
||||||
create_service
|
create_service
|
||||||
|
|
||||||
echo -e "${BLUE}[7/8] 配置防火墙规则...${NC}"
|
echo -e "${BLUE}[8/11] 配置防火墙规则...${NC}"
|
||||||
configure_firewall
|
configure_firewall
|
||||||
|
|
||||||
echo -e "${BLUE}[8/8] 登记节点到后端服务器...${NC}"
|
echo -e "${BLUE}[9/11] 登记节点到后端服务器...${NC}"
|
||||||
register_node
|
register_node
|
||||||
|
|
||||||
echo -e "${BLUE}[9/9] 启动服务...${NC}"
|
echo -e "${BLUE}[10/11] 启动服务...${NC}"
|
||||||
start_service
|
start_service
|
||||||
|
|
||||||
echo -e "${BLUE}[10/10] 验证安装...${NC}"
|
echo -e "${BLUE}[11/11] 验证安装...${NC}"
|
||||||
verify_installation
|
verify_installation
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -114,7 +114,30 @@ func handleGet(c *gin.Context, urlStr string, params map[string]interface{}) {
|
|||||||
seq = seqVal
|
seq = seqVal
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析URL
|
// 清理URL:去除多余的空格和重复的协议前缀
|
||||||
|
urlStr = strings.TrimSpace(urlStr)
|
||||||
|
// 如果URL中包含多个 http:// 或 https://,只保留第一个
|
||||||
|
if strings.Contains(urlStr, "http://") {
|
||||||
|
// 找到第一个 http:// 的位置
|
||||||
|
firstHttp := strings.Index(urlStr, "http://")
|
||||||
|
if firstHttp > 0 {
|
||||||
|
// 如果 http:// 不在开头,说明前面有内容,需要清理
|
||||||
|
urlStr = urlStr[firstHttp:]
|
||||||
|
}
|
||||||
|
// 移除后续重复的 http://
|
||||||
|
urlStr = strings.Replace(urlStr, "http://http://", "http://", -1)
|
||||||
|
urlStr = strings.Replace(urlStr, "http://https://", "https://", -1)
|
||||||
|
}
|
||||||
|
if strings.Contains(urlStr, "https://") {
|
||||||
|
firstHttps := strings.Index(urlStr, "https://")
|
||||||
|
if firstHttps > 0 {
|
||||||
|
urlStr = urlStr[firstHttps:]
|
||||||
|
}
|
||||||
|
urlStr = strings.Replace(urlStr, "https://https://", "https://", -1)
|
||||||
|
urlStr = strings.Replace(urlStr, "https://http://", "http://", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果URL没有协议前缀,添加 http://
|
||||||
if !strings.HasPrefix(urlStr, "http://") && !strings.HasPrefix(urlStr, "https://") {
|
if !strings.HasPrefix(urlStr, "http://") && !strings.HasPrefix(urlStr, "https://") {
|
||||||
urlStr = "http://" + urlStr
|
urlStr = "http://" + urlStr
|
||||||
}
|
}
|
||||||
@@ -194,6 +217,7 @@ func handleGet(c *gin.Context, urlStr string, params map[string]interface{}) {
|
|||||||
result["ip"] = "访问失败"
|
result["ip"] = "访问失败"
|
||||||
}
|
}
|
||||||
result["error"] = errMsg
|
result["error"] = errMsg
|
||||||
|
result["statuscode"] = 0
|
||||||
result["totaltime"] = "*"
|
result["totaltime"] = "*"
|
||||||
result["downtime"] = "*"
|
result["downtime"] = "*"
|
||||||
result["downsize"] = "*"
|
result["downsize"] = "*"
|
||||||
@@ -212,6 +236,7 @@ func handleGet(c *gin.Context, urlStr string, params map[string]interface{}) {
|
|||||||
// 没有响应也没有错误,不应该发生
|
// 没有响应也没有错误,不应该发生
|
||||||
result["error"] = "未知错误"
|
result["error"] = "未知错误"
|
||||||
result["ip"] = "访问失败"
|
result["ip"] = "访问失败"
|
||||||
|
result["statuscode"] = 0
|
||||||
result["totaltime"] = "*"
|
result["totaltime"] = "*"
|
||||||
result["downtime"] = "*"
|
result["downtime"] = "*"
|
||||||
result["downsize"] = "*"
|
result["downsize"] = "*"
|
||||||
@@ -313,7 +338,30 @@ func handlePost(c *gin.Context, urlStr string, params map[string]interface{}) {
|
|||||||
seq = seqVal
|
seq = seqVal
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析URL
|
// 清理URL:去除多余的空格和重复的协议前缀
|
||||||
|
urlStr = strings.TrimSpace(urlStr)
|
||||||
|
// 如果URL中包含多个 http:// 或 https://,只保留第一个
|
||||||
|
if strings.Contains(urlStr, "http://") {
|
||||||
|
// 找到第一个 http:// 的位置
|
||||||
|
firstHttp := strings.Index(urlStr, "http://")
|
||||||
|
if firstHttp > 0 {
|
||||||
|
// 如果 http:// 不在开头,说明前面有内容,需要清理
|
||||||
|
urlStr = urlStr[firstHttp:]
|
||||||
|
}
|
||||||
|
// 移除后续重复的 http://
|
||||||
|
urlStr = strings.Replace(urlStr, "http://http://", "http://", -1)
|
||||||
|
urlStr = strings.Replace(urlStr, "http://https://", "https://", -1)
|
||||||
|
}
|
||||||
|
if strings.Contains(urlStr, "https://") {
|
||||||
|
firstHttps := strings.Index(urlStr, "https://")
|
||||||
|
if firstHttps > 0 {
|
||||||
|
urlStr = urlStr[firstHttps:]
|
||||||
|
}
|
||||||
|
urlStr = strings.Replace(urlStr, "https://https://", "https://", -1)
|
||||||
|
urlStr = strings.Replace(urlStr, "https://http://", "http://", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果URL没有协议前缀,添加 http://
|
||||||
if !strings.HasPrefix(urlStr, "http://") && !strings.HasPrefix(urlStr, "https://") {
|
if !strings.HasPrefix(urlStr, "http://") && !strings.HasPrefix(urlStr, "https://") {
|
||||||
urlStr = "http://" + urlStr
|
urlStr = "http://" + urlStr
|
||||||
}
|
}
|
||||||
@@ -394,6 +442,7 @@ func handlePost(c *gin.Context, urlStr string, params map[string]interface{}) {
|
|||||||
result["ip"] = "访问失败"
|
result["ip"] = "访问失败"
|
||||||
}
|
}
|
||||||
result["error"] = errMsg
|
result["error"] = errMsg
|
||||||
|
result["statuscode"] = 0
|
||||||
result["totaltime"] = "*"
|
result["totaltime"] = "*"
|
||||||
result["downtime"] = "*"
|
result["downtime"] = "*"
|
||||||
result["downsize"] = "*"
|
result["downsize"] = "*"
|
||||||
@@ -412,6 +461,7 @@ func handlePost(c *gin.Context, urlStr string, params map[string]interface{}) {
|
|||||||
// 没有响应也没有错误,不应该发生
|
// 没有响应也没有错误,不应该发生
|
||||||
result["error"] = "未知错误"
|
result["error"] = "未知错误"
|
||||||
result["ip"] = "访问失败"
|
result["ip"] = "访问失败"
|
||||||
|
result["statuscode"] = 0
|
||||||
result["totaltime"] = "*"
|
result["totaltime"] = "*"
|
||||||
result["downtime"] = "*"
|
result["downtime"] = "*"
|
||||||
result["downsize"] = "*"
|
result["downsize"] = "*"
|
||||||
|
|||||||
@@ -90,21 +90,31 @@ func NewReporter(cfg *config.Config) *Reporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reporter) Start(ctx context.Context) {
|
func (r *Reporter) Start(ctx context.Context) {
|
||||||
ticker := time.NewTicker(time.Duration(r.cfg.Heartbeat.Interval) * time.Second)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
// 立即发送一次心跳
|
// 立即发送一次心跳
|
||||||
r.sendHeartbeat()
|
r.sendHeartbeat()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
// 计算到下一分钟第1秒的时间
|
||||||
|
now := time.Now()
|
||||||
|
nextMinute := now.Truncate(time.Minute).Add(time.Minute)
|
||||||
|
nextHeartbeatTime := nextMinute.Add(1 * time.Second)
|
||||||
|
durationUntilNext := nextHeartbeatTime.Sub(now)
|
||||||
|
|
||||||
|
// 等待到下一分钟的第1秒
|
||||||
|
timer := time.NewTimer(durationUntilNext)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
timer.Stop()
|
||||||
return
|
return
|
||||||
case <-r.stopCh:
|
case <-r.stopCh:
|
||||||
|
timer.Stop()
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-timer.C:
|
||||||
|
// 在每分钟的第1秒发送心跳
|
||||||
r.sendHeartbeat()
|
r.sendHeartbeat()
|
||||||
}
|
}
|
||||||
|
timer.Stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
53
time.sh
Normal file
53
time.sh
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=== CentOS 7 时间同步脚本开始 ==="
|
||||||
|
|
||||||
|
# 1. 检查是否 root
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
echo "请使用 root 用户执行"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. 设置时区
|
||||||
|
echo "[1/6] 设置时区为 Asia/Shanghai"
|
||||||
|
timedatectl set-timezone Asia/Shanghai
|
||||||
|
|
||||||
|
# 3. 安装 chrony
|
||||||
|
echo "[2/6] 安装 chrony"
|
||||||
|
if ! rpm -q chrony &>/dev/null; then
|
||||||
|
yum install -y chrony
|
||||||
|
else
|
||||||
|
echo "chrony 已安装,跳过"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. 配置 NTP 服务器
|
||||||
|
echo "[3/6] 配置 NTP 服务器"
|
||||||
|
CONF="/etc/chrony.conf"
|
||||||
|
|
||||||
|
sed -i 's/^server /#server /g' "$CONF"
|
||||||
|
|
||||||
|
grep -q "ntp.aliyun.com" "$CONF" || cat >> "$CONF" <<EOF
|
||||||
|
|
||||||
|
# China NTP servers
|
||||||
|
server ntp.aliyun.com iburst
|
||||||
|
server ntp.tencent.com iburst
|
||||||
|
server ntp1.aliyun.com iburst
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 5. 启动并启用 chronyd
|
||||||
|
echo "[4/6] 启动 chronyd"
|
||||||
|
systemctl enable chronyd --now
|
||||||
|
|
||||||
|
# 6. 立即强制同步
|
||||||
|
echo "[5/6] 强制同步系统时间"
|
||||||
|
chronyc -a makestep
|
||||||
|
|
||||||
|
# 7. 写入硬件时间
|
||||||
|
echo "[6/6] 写入硬件时钟"
|
||||||
|
hwclock --systohc
|
||||||
|
|
||||||
|
echo "=== 时间同步完成 ==="
|
||||||
|
echo
|
||||||
|
timedatectl status
|
||||||
Reference in New Issue
Block a user