mirror of
https://github.com/yyhuni/xingrin.git
synced 2026-01-31 19:53:11 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f63e40fbba | ||
|
|
54573e210a | ||
|
|
6179dd2ed3 | ||
|
|
34ac706fbc |
@@ -10,6 +10,8 @@ class WorkerNode(models.Model):
|
||||
('deploying', '部署中'),
|
||||
('online', '在线'),
|
||||
('offline', '离线'),
|
||||
('updating', '更新中'),
|
||||
('outdated', '版本过低'),
|
||||
]
|
||||
|
||||
name = models.CharField(max_length=100, help_text='节点名称')
|
||||
|
||||
@@ -134,6 +134,17 @@ class WorkerNodeViewSet(viewsets.ModelViewSet):
|
||||
"need_update": true/false,
|
||||
"server_version": "v1.0.19"
|
||||
}
|
||||
|
||||
状态流转:
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 场景 │ 状态变化 │
|
||||
├─────────────────────────────┼───────────────────────────────────────┤
|
||||
│ 首次心跳 │ pending/deploying → online │
|
||||
│ 远程 Worker 版本不匹配 │ online → updating → (更新成功) online │
|
||||
│ 远程 Worker 更新失败 │ updating → outdated │
|
||||
│ 本地 Worker 版本不匹配 │ online → outdated (需手动 update.sh) │
|
||||
│ 版本匹配 │ updating/outdated → online │
|
||||
└─────────────────────────────┴───────────────────────────────────────┘
|
||||
"""
|
||||
from apps.engine.services.worker_load_service import worker_load_service
|
||||
from django.conf import settings
|
||||
@@ -165,9 +176,19 @@ class WorkerNodeViewSet(viewsets.ModelViewSet):
|
||||
)
|
||||
|
||||
# 远程 Worker:服务端主动通过 SSH 触发更新
|
||||
# 旧版 agent 不会解析 need_update,所以需要服务端主动推送
|
||||
if not worker.is_local and worker.ip_address:
|
||||
self._trigger_remote_agent_update(worker, server_version)
|
||||
else:
|
||||
# 本地 Worker 版本不匹配:标记为 outdated
|
||||
# 需要用户手动执行 update.sh 更新
|
||||
if worker.status != 'outdated':
|
||||
worker.status = 'outdated'
|
||||
worker.save(update_fields=['status'])
|
||||
else:
|
||||
# 版本匹配,确保状态为 online
|
||||
if worker.status in ('updating', 'outdated'):
|
||||
worker.status = 'online'
|
||||
worker.save(update_fields=['status'])
|
||||
|
||||
return Response({
|
||||
'status': 'ok',
|
||||
@@ -184,7 +205,8 @@ class WorkerNodeViewSet(viewsets.ModelViewSet):
|
||||
import redis
|
||||
from django.conf import settings as django_settings
|
||||
|
||||
redis_client = redis.from_url(django_settings.REDIS_URL)
|
||||
redis_url = f"redis://{django_settings.REDIS_HOST}:{django_settings.REDIS_PORT}/{django_settings.REDIS_DB}"
|
||||
redis_client = redis.from_url(redis_url)
|
||||
lock_key = f"agent_update_lock:{worker.id}"
|
||||
|
||||
# 尝试获取锁(60秒过期,防止重复触发)
|
||||
@@ -192,6 +214,9 @@ class WorkerNodeViewSet(viewsets.ModelViewSet):
|
||||
logger.debug(f"Worker {worker.name} 更新已在进行中,跳过")
|
||||
return
|
||||
|
||||
# 获取锁成功,设置状态为 updating
|
||||
self._set_worker_status(worker.id, 'updating')
|
||||
|
||||
# 提取数据避免后台线程访问 ORM
|
||||
worker_id = worker.id
|
||||
worker_name = worker.name
|
||||
@@ -230,11 +255,15 @@ class WorkerNodeViewSet(viewsets.ModelViewSet):
|
||||
|
||||
if success:
|
||||
logger.info(f"Worker {worker_name} 远程更新成功")
|
||||
# 更新成功后,新 agent 心跳会自动把状态改回 online
|
||||
else:
|
||||
logger.warning(f"Worker {worker_name} 远程更新失败: {message}")
|
||||
# 更新失败,标记为 outdated
|
||||
self._set_worker_status(worker_id, 'outdated')
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Worker {worker_name} 远程更新异常: {e}")
|
||||
self._set_worker_status(worker_id, 'outdated')
|
||||
finally:
|
||||
# 释放锁
|
||||
redis_client.delete(lock_key)
|
||||
@@ -242,6 +271,14 @@ class WorkerNodeViewSet(viewsets.ModelViewSet):
|
||||
# 后台执行,不阻塞心跳响应
|
||||
threading.Thread(target=_async_update, daemon=True).start()
|
||||
|
||||
def _set_worker_status(self, worker_id: int, status: str):
|
||||
"""更新 Worker 状态(用于后台线程)"""
|
||||
try:
|
||||
from apps.engine.models import WorkerNode
|
||||
WorkerNode.objects.filter(id=worker_id).update(status=status)
|
||||
except Exception as e:
|
||||
logger.error(f"更新 Worker {worker_id} 状态失败: {e}")
|
||||
|
||||
@action(detail=False, methods=['post'])
|
||||
def register(self, request):
|
||||
"""
|
||||
|
||||
@@ -297,6 +297,8 @@ export function DeployTerminalDialog({
|
||||
{isConnected && currentStatus === 'deploying' && '正在部署中,点击查看进度'}
|
||||
{isConnected && currentStatus === 'online' && '节点运行正常'}
|
||||
{isConnected && currentStatus === 'offline' && '节点离线,可尝试重新部署'}
|
||||
{isConnected && currentStatus === 'updating' && '正在自动更新 Agent...'}
|
||||
{isConnected && currentStatus === 'outdated' && '版本过低,需要更新'}
|
||||
</div>
|
||||
|
||||
{/* 右侧:操作按钮 */}
|
||||
@@ -334,6 +336,28 @@ export function DeployTerminalDialog({
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* 更新中 -> 显示"查看进度" */}
|
||||
{currentStatus === 'updating' && (
|
||||
<button
|
||||
onClick={handleAttach}
|
||||
className="inline-flex items-center px-3 py-1.5 text-sm rounded-md bg-[#e0af68] text-[#1a1b26] hover:bg-[#e0af68]/80 transition-colors"
|
||||
>
|
||||
<IconEye className="mr-1.5 h-4 w-4" />
|
||||
查看进度
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* 版本过低 -> 显示"重新部署" */}
|
||||
{currentStatus === 'outdated' && (
|
||||
<button
|
||||
onClick={handleDeploy}
|
||||
className="inline-flex items-center px-3 py-1.5 text-sm rounded-md bg-[#f7768e] text-[#1a1b26] hover:bg-[#f7768e]/80 transition-colors"
|
||||
>
|
||||
<IconRocket className="mr-1.5 h-4 w-4" />
|
||||
重新部署
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* 已部署(online/offline) -> 显示"重新部署"和"卸载" */}
|
||||
{(currentStatus === 'online' || currentStatus === 'offline') && (
|
||||
<>
|
||||
|
||||
@@ -51,6 +51,8 @@ const STATUS_MAP: Record<WorkerStatus, 'online' | 'offline' | 'maintenance' | 'd
|
||||
offline: 'offline',
|
||||
pending: 'maintenance',
|
||||
deploying: 'degraded',
|
||||
updating: 'degraded',
|
||||
outdated: 'offline',
|
||||
}
|
||||
|
||||
// 状态中文标签
|
||||
@@ -59,6 +61,8 @@ const STATUS_LABEL: Record<WorkerStatus, string> = {
|
||||
offline: '离线',
|
||||
pending: '等待部署',
|
||||
deploying: '部署中',
|
||||
updating: '更新中',
|
||||
outdated: '版本过低',
|
||||
}
|
||||
|
||||
// 统计卡片组件
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
// Worker 状态枚举(前后端统一)
|
||||
export type WorkerStatus = 'pending' | 'deploying' | 'online' | 'offline'
|
||||
export type WorkerStatus = 'pending' | 'deploying' | 'online' | 'offline' | 'updating' | 'outdated'
|
||||
|
||||
// Worker 节点
|
||||
export interface WorkerNode {
|
||||
|
||||
Reference in New Issue
Block a user