This commit is contained in:
jingrow 2025-08-11 01:10:10 +08:00
parent baf702291c
commit 0ea8cdd9bc
3 changed files with 324 additions and 220 deletions

267
README.md
View File

@ -1,29 +1,31 @@
# jsite 自动化部署和管理脚本
# jsite.sh - 前端自动化部署脚本
这是一个集成了完整部署和日常管理功能的自动化脚本,支持多种任务模式。
## 概述
## 功能特性
`jsite.sh` 是一个用于自动化部署和管理前端网站的bash脚本。它支持多种操作模式包括部署、启动、停止、重启等。
- 🚀 **完整部署**一键部署完整的jsite环境
- 🏗️ **网站管理**:创建、删除、构建、启动、停止、重启网站
- 📊 **状态监控**:查看网站运行状态和日志
- 🔧 **环境管理**Docker、Node.js、PM2、Traefik等环境组件
- ⚡ **智能端口分配**:自动分配和管理端口资源
## 主要功能
- 自动创建和管理jingrow用户
- 安装和配置Node.js、npm、PM2
- 自动化部署前端项目
- 集成Traefik反向代理
- 支持多种操作模式
## 使用方法
### 基本语法
```bash
./install_jsite.sh --mode <任务模式> [其他参数]
./jsite.sh --mode <模式> --site-name <网站名称>
```
### 支持的任务模式
### 支持的模式
| 模式 | 功能 | 示例 |
| 模式 | 描述 | 示例 |
|------|------|------|
| `deploy` | 完整部署模式(默认) | `--mode deploy --site-name myproject` |
| `create` | 创建新网站 | `--mode create --site-name myproject --git-repo <仓库地址>` |
| `deploy` | 完整部署网站 | `--mode deploy --site-name myproject` |
| `create` | 创建新网站 | `--mode create --site-name myproject` |
| `delete` | 删除网站 | `--mode delete --site-name myproject` |
| `build` | 构建网站 | `--mode build --site-name myproject` |
| `start` | 启动网站 | `--mode start --site-name myproject` |
@ -33,157 +35,152 @@
| `list` | 列出所有网站 | `--mode list` |
| `logs` | 查看网站日志 | `--mode logs --site-name myproject` |
### 常用参数
| 参数 | 说明 | 默认值 |
|------|------|--------|
| `--mode` | 任务模式 | `deploy` |
| `--site-name` | 网站名称 | `jingrow` |
| `--git-repo` | Git仓库地址 | `http://git.jingrow.com:3000/jsite/jingrow` |
| `--node-version` | Node.js版本 | `22` |
| `--start-port` | 起始端口 | `3001` |
| `--site-url` | 网站域名 | `starrbud.com` |
### 使用示例
#### 1. 完整部署新项目
### 常用命令示例
```bash
./install_jsite.sh \
--mode deploy \
--site-name myproject \
--git-repo http://git.example.com/myproject \
--site-url myproject.com \
--public-ip 8.217.167.199
```
# 部署新网站
./jsite.sh --mode deploy --site-name jingrow
#### 2. 创建新网站
```bash
./install_jsite.sh \
--mode create \
--site-name myproject \
--git-repo http://git.example.com/myproject \
--site-url myproject.com
```
#### 3. 构建和启动网站
```bash
# 构建项目
./install_jsite.sh --mode build --site-name myproject
# 启动项目
./install_jsite.sh --mode start --site-name myproject
```
#### 4. 管理网站状态
```bash
# 查看状态
./install_jsite.sh --mode status --site-name myproject
# 停止网站
./install_jsite.sh --mode stop --site-name myproject
# 启动网站
./jsite.sh --mode start --site-name jingrow
# 重启网站
./install_jsite.sh --mode restart --site-name myproject
./jsite.sh --mode restart --site-name jingrow
# 查看网站状态
./jsite.sh --mode status --site-name jingrow
# 查看所有网站
./jsite.sh --mode list
```
#### 5. 查看所有网站
## 故障排除
### 常见错误及解决方案
#### 1. PM2进程未找到错误
**错误信息:**
```
[PM2][ERROR] Process or Namespace jingrow not found
```
**原因:** 网站尚未部署或PM2进程未正确启动
**解决方案:**
1. 首先检查网站是否存在:
```bash
./jsite.sh --mode status --site-name jingrow
```
2. 如果网站不存在,先部署:
```bash
./jsite.sh --mode deploy --site-name jingrow
```
3. 如果网站存在但未运行,启动网站:
```bash
./jsite.sh --mode start --site-name jingrow
```
#### 2. 权限错误
**解决方案:**
```bash
./install_jsite.sh --mode list
# 确保脚本有执行权限
chmod +x jsite.sh
# 以root用户运行
sudo ./jsite.sh --mode start --site-name jingrow
```
#### 6. 查看日志
#### 3. 端口冲突
**解决方案:**
```bash
./install_jsite.sh --mode logs --site-name myproject
# 检查端口占用
netstat -tlnp | grep :3001
# 停止占用端口的进程
sudo kill -9 <进程ID>
```
## 工作流程
### 手动检查和修复
### 完整部署流程 (deploy)
1. 创建jingrow用户
2. 安装Docker可选跳过
3. 安装Node.js和NVM
4. 克隆项目代码
5. 创建环境配置文件
6. 安装项目依赖
7. 安装PM2并启动项目
8. 安装Traefik并配置反向代理
#### 检查PM2状态
```bash
# 切换到jingrow用户
su - jingrow
### 网站管理流程
- **create**: 克隆代码 → 分配端口 → 创建配置 → 提示下一步
- **build**: 安装依赖 → 构建项目
- **start**: 检查状态 → 分配端口 → 启动PM2进程
- **stop**: 停止PM2进程
- **restart**: 重启PM2进程或启动如果未运行
- **delete**: 停止进程 → 清理配置 → 删除文件
# 加载nvm环境
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
## 目录结构
# 查看PM2进程列表
pm2 list
# 查看PM2日志
pm2 logs
```
#### 手动启动PM2进程
```bash
# 切换到jingrow用户
su - jingrow
# 加载nvm环境
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
# 启动进程
pm2 start jingrow
# 或者重新加载配置
pm2 reload jingrow
```
## 配置说明
### 环境变量
脚本支持以下环境变量配置:
- `SITE_NAME`: 网站名称默认jingrow
- `GIT_REPO`: Git仓库地址
- `NODE_VERSION`: Node.js版本默认22
- `START_PORT`: 起始端口默认3001
- `SITE_URL`: 网站域名
- `BACKEND_SERVER_URL`: 后端服务器地址
### 目录结构
```
/home/jingrow/
├── jsite/ # 网站项目目录
│ ├── myproject1/ # 项目1
│ ├── myproject2/ # 项目2
│ └── site_port.json # 端口分配记录
├── traefik-docker/ # Traefik配置目录
│ └── conf.d/website/ # 网站配置文件
└── .nvm/ # Node.js版本管理
├── .nvm/ # Node版本管理器
├── traefik-docker/ # Traefik配置
└── ...
```
## 注意事项
1. **权限要求**脚本需要root权限运行
2. **端口管理**:自动分配端口,避免冲突
3. **环境依赖**确保系统支持Docker和Node.js
4. **域名配置**需要正确配置DNS解析
5. **SSL证书**Traefik自动申请Let's Encrypt证书
## 故障排除
### 常见问题
1. **端口被占用**
- 检查端口分配文件:`/home/jingrow/jsite/site_port.json`
- 手动修改端口或停止占用进程
2. **PM2启动失败**
- 检查项目依赖是否正确安装
- 查看PM2日志`pm2 logs <项目名>`
- 确认package.json中有start脚本
3. **Traefik配置问题**
- 检查配置文件:`/home/jingrow/traefik-docker/conf.d/website/`
- 重启Traefik服务
4. **域名访问问题**
- 确认DNS解析正确
- 检查防火墙设置
- 验证SSL证书状态
### 日志查看
```bash
# PM2日志
pm2 logs <项目名>
# Traefik日志
docker logs <traefik容器名>
# 系统日志
journalctl -u docker
```
1. 脚本需要以root权限运行
2. 确保服务器有足够的磁盘空间和内存
3. 确保网络连接正常能够访问Git仓库
4. 建议在生产环境使用前先在测试环境验证
## 更新日志
- **v2.0**: 整合jsite.sh功能支持多种任务模式
- **v1.0**: 基础部署功能
### v1.1.0
- 修复了start_site和restart_site函数的无限递归问题
- 改进了错误处理和日志输出
- 优化了PM2进程管理逻辑
## 许可证
## 技术支持
MIT License
如果遇到问题,请检查:
1. 脚本执行日志
2. PM2进程状态
3. 系统资源使用情况
4. 网络连接状态

196
jsite.sh
View File

@ -301,18 +301,50 @@ get_site_status() {
return
fi
# 检查PM2进程状态
# 检查PM2进程状态 - 改进的检测逻辑
local pm2_output=$(su - "jingrow" -c "
export NVM_DIR=\"\$HOME/.nvm\"
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
pm2 list 2>/dev/null | grep -w '$site_name' || echo 'not_found'
")
# 如果没有找到进程
if [ "$pm2_output" = "not_found" ] || [ -z "$pm2_output" ]; then
echo "stopped"
return
fi
# 检查进程状态
local pm2_status=$(su - "jingrow" -c "
export NVM_DIR=\"\$HOME/.nvm\"
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
pm2 list 2>/dev/null | grep -w '$site_name' | grep -c 'online' || echo '0'
pm2 list 2>/dev/null | grep -w '$site_name' | awk '{print \$10}' || echo 'unknown'
")
if [ "$pm2_status" != "0" ]; then
echo "running"
else
echo "stopped"
fi
# 根据PM2状态返回相应结果
case "$pm2_status" in
"online")
echo "running"
;;
"stopped"|"stopping")
echo "stopped"
;;
"errored"|"error")
echo "error"
;;
"restarting"|"waiting")
echo "starting"
;;
*)
# 如果状态未知,进一步检查进程是否真的在运行
local process_check=$(su - "jingrow" -c "
export NVM_DIR=\"\$HOME/.nvm\"
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
pm2 list 2>/dev/null | grep -w '$site_name' | grep -q 'online' && echo 'running' || echo 'stopped'
")
echo "$process_check"
;;
esac
}
# 1. 创建jingrow用户
@ -436,9 +468,6 @@ get_available_port() {
# 检查项目是否已有分配的端口
if [ -f "$port_file" ]; then
# 确保jq工具已安装
ensure_jq_installed
# 使用jq查询已分配的端口
if command -v jq &> /dev/null; then
local existing_port=$(jq -r ".$site_name // empty" "$port_file" 2>/dev/null || echo "")
@ -465,15 +494,12 @@ get_available_port() {
user_specified_port=true
fi
if [ "$user_specified_port" = true ]; then
# 用户传入了自定义端口,优先使用用户指定的端口
# 检查用户指定的端口是否已被使用
local port_available=true
if [ -f "$port_file" ]; then
# 确保jq工具已安装
ensure_jq_installed
if command -v jq &> /dev/null; then
if [ "$user_specified_port" = true ]; then
# 用户传入了自定义端口,优先使用用户指定的端口
# 检查用户指定的端口是否已被使用
local port_available=true
if [ -f "$port_file" ]; then
if command -v jq &> /dev/null; then
local used_ports=$(jq -r '.[]' "$port_file" 2>/dev/null || echo "")
if echo "$used_ports" | grep -q "^$base_port$"; then
port_available=false
@ -492,9 +518,6 @@ get_available_port() {
else
# 用户指定的端口已被使用,使用最大端口号 + 1
if [ -f "$port_file" ]; then
# 确保jq工具已安装
ensure_jq_installed
if command -v jq &> /dev/null; then
local max_port=$(jq -r 'max(.[])' "$port_file" 2>/dev/null || echo "$base_port")
if [ -n "$max_port" ] && [ "$max_port" != "null" ]; then
@ -511,9 +534,6 @@ get_available_port() {
else
# 用户没有传入自定义端口,使用自动分配逻辑
if [ -f "$port_file" ]; then
# 确保jq工具已安装
ensure_jq_installed
if command -v jq &> /dev/null; then
# 使用jq找到最大端口号
local max_port=$(jq -r 'max(.[])' "$port_file" 2>/dev/null || echo "$base_port")
@ -547,9 +567,6 @@ save_port_assignment() {
chown jingrow:jingrow /home/jingrow/jsite
fi
# 确保jq工具已安装
ensure_jq_installed
# 检查jq是否可用
if command -v jq &> /dev/null; then
# 使用jq保存到JSON文件
@ -601,9 +618,6 @@ get_or_assign_port() {
local already_saved=false
if [ -f "$port_file" ]; then
# 确保jq工具已安装
ensure_jq_installed
if command -v jq &> /dev/null; then
local existing_port=$(jq -r ".$site_name // empty" "$port_file" 2>/dev/null || echo "")
if [ -n "$existing_port" ] && [ "$existing_port" != "null" ] && [ "$existing_port" != "empty" ]; then
@ -640,9 +654,6 @@ show_port_assignments() {
fi
log_info "当前端口分配情况:"
# 确保jq工具已安装
ensure_jq_installed
if command -v jq &> /dev/null; then
# 使用jq格式化输出
jq -r 'to_entries[] | " - \(.key): \(.value)"' "$port_file" 2>/dev/null || log_warning "无法解析端口分配文件"
@ -1142,45 +1153,6 @@ install_docker() {
log_success "将jingrow用户添加到docker组"
}
# 6.1 安装jq工具如果未安装
install_jq() {
log_info "检查jq工具安装状态..."
if command -v jq &> /dev/null; then
log_warning "jq工具已安装"
else
log_info "开始安装jq工具..."
# 设置非交互式环境
export DEBIAN_FRONTEND=noninteractive
export DEBCONF_NONINTERACTIVE_SEEN=true
# 更新包索引
apt-get update
# 安装jq工具
apt-get install -y --force-yes jq
if command -v jq &> /dev/null; then
log_success "jq工具安装完成"
else
log_error "jq工具安装失败"
return 1
fi
fi
}
# 6.2 确保jq工具已安装
ensure_jq_installed() {
if ! command -v jq &> /dev/null; then
log_warning "jq工具未安装正在安装..."
if ! install_jq; then
log_error "jq工具安装失败无法处理JSON文件"
return 1
fi
fi
}
# 7. 启动traefik
start_traefik() {
log_info "启动traefik服务..."
@ -1532,9 +1504,6 @@ delete_site() {
# 删除端口分配记录
log_info "删除端口分配记录..."
local port_file="$JSITE_BASE_DIR/site_port.json"
# 确保jq工具已安装
ensure_jq_installed
if [ -f "$port_file" ] && command -v jq &> /dev/null; then
if jq "del(.$SITE_NAME)" "$port_file" > "${port_file}.tmp" 2>/dev/null; then
mv "${port_file}.tmp" "$port_file"
@ -1604,9 +1573,31 @@ build_site() {
# 启动网站
start_site() {
local status=$(get_site_status "$SITE_NAME")
if [ "$status" = "not_exists" ]; then
log_error "网站 $SITE_NAME 不存在,请先部署网站"
return 1
fi
if [ "$status" = "running" ]; then
log_warning "网站 $SITE_NAME 已在运行中"
return 0
fi
log_info "启动网站: $SITE_NAME"
restart_site
return $?
# 尝试启动PM2进程
if ! su - "jingrow" -c "
export NVM_DIR=\"\$HOME/.nvm\"
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
pm2 start '$SITE_NAME'
"; then
log_error "网站启动失败"
return 1
fi
log_success "网站 $SITE_NAME 启动成功"
}
# 停止网站
@ -1657,8 +1648,15 @@ restart_site() {
fi
else
# 如果未运行,则启动
start_site
return $?
log_info "网站未运行,正在启动..."
if ! su - "jingrow" -c "
export NVM_DIR=\"\$HOME/.nvm\"
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
pm2 start '$SITE_NAME'
"; then
log_error "网站启动失败"
return 1
fi
fi
log_success "网站 $SITE_NAME 重启成功"
@ -1679,16 +1677,41 @@ show_site_status() {
"stopped")
log_warning "网站 $SITE_NAME 已停止"
;;
"error")
log_error "网站 $SITE_NAME 运行出错"
;;
"starting")
log_warning "网站 $SITE_NAME 正在启动中"
;;
*)
log_warning "网站 $SITE_NAME 状态未知: $status"
;;
esac
# 显示PM2详细状态
if [ "$status" != "not_exists" ]; then
log_info "PM2状态:"
log_info "PM2详细状态:"
su - "jingrow" -c "
export NVM_DIR=\"\$HOME/.nvm\"
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
pm2 list | grep -E '(App name|$SITE_NAME)' || echo '未找到PM2进程'
"
# 显示端口监听状态
local port_file="/home/jingrow/jsite/site_port.json"
if [ -f "$port_file" ] && command -v jq &> /dev/null; then
local port=$(jq -r ".$SITE_NAME // empty" "$port_file" 2>/dev/null)
if [ -n "$port" ] && [ "$port" != "null" ]; then
log_info "检查端口 $port 监听状态:"
if netstat -tlnp 2>/dev/null | grep -q ":$port "; then
log_success "端口 $port 正在监听"
elif ss -tlnp 2>/dev/null | grep -q ":$port "; then
log_success "端口 $port 正在监听"
else
log_warning "端口 $port 未被监听"
fi
fi
fi
fi
}
@ -1713,8 +1736,14 @@ list_sites() {
"stopped")
echo " - $site_name (已停止)"
;;
"error")
echo " - $site_name (运行出错)"
;;
"starting")
echo " - $site_name (启动中)"
;;
*)
echo " - $site_name (未知状态)"
echo " - $site_name (状态: $status)"
;;
esac
fi
@ -1789,9 +1818,6 @@ main() {
log_warning "跳过Docker安装"
fi
# 安装jq工具用于处理JSON文件
install_jq
install_nodejs
if ! clone_jsite_project; then

81
test_status.sh Normal file
View File

@ -0,0 +1,81 @@
#!/bin/bash
# 测试修复后的状态检测功能
echo "=== 测试修复后的状态检测功能 ==="
# 测试get_site_status函数
test_get_site_status() {
echo "测试 get_site_status 函数..."
# 模拟PM2 list输出
echo "模拟PM2 list输出:"
echo "┌─────┬────────────────┬──────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐"
echo "│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │"
echo "├─────┼────────────────┼──────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤"
echo "│ 0 │ jingrow │ default │ 1.0.0 │ fork │ 12345 │ 2D │ 0 │ online │ 0% │ 45.0mb │ jingrow │ disabled │"
echo "│ 1 │ testapp │ default │ 1.0.0 │ fork │ 12346 │ 0s │ 0 │ stopped │ 0% │ 0b │ jingrow │ disabled │"
echo "│ 2 │ erroredapp │ default │ 1.0.0 │ fork │ 0 │ 0s │ 5 │ errored │ 0% │ 0b │ jingrow │ disabled │"
echo "└─────┴────────────────┴──────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘"
echo ""
echo "状态解析说明:"
echo "- jingrow: 状态为 'online' -> 应该返回 'running'"
echo "- testapp: 状态为 'stopped' -> 应该返回 'stopped'"
echo "- erroredapp: 状态为 'errored' -> 应该返回 'error'"
}
# 测试端口检测
test_port_detection() {
echo ""
echo "=== 测试端口检测功能 ==="
# 检查常用端口
local ports=(3001 3002 8080 80 443)
for port in "${ports[@]}"; do
echo "检查端口 $port:"
if netstat -tlnp 2>/dev/null | grep -q ":$port "; then
echo " ✓ 端口 $port 正在监听 (netstat)"
elif ss -tlnp 2>/dev/null | grep -q ":$port "; then
echo " ✓ 端口 $port 正在监听 (ss)"
else
echo " ✗ 端口 $port 未被监听"
fi
done
}
# 测试PM2命令可用性
test_pm2_availability() {
echo ""
echo "=== 测试PM2命令可用性 ==="
# 检查jingrow用户的PM2
echo "检查jingrow用户的PM2:"
if su - jingrow -c "command -v pm2" 2>/dev/null; then
echo " ✓ PM2命令可用"
# 检查PM2版本
local pm2_version=$(su - jingrow -c "pm2 -v" 2>/dev/null)
if [ -n "$pm2_version" ]; then
echo " ✓ PM2版本: $pm2_version"
else
echo " ✗ 无法获取PM2版本"
fi
# 检查PM2进程列表
echo " PM2进程列表:"
su - jingrow -c "pm2 list" 2>/dev/null || echo " ✗ 无法获取PM2进程列表"
else
echo " ✗ PM2命令不可用"
fi
}
# 运行测试
test_get_site_status
test_port_detection
test_pm2_availability
echo ""
echo "=== 测试完成 ==="
echo "现在可以运行修复后的脚本:"
echo " /tmp/jsite.sh --mode status --site-name jingrow"