Files
ewoooc/n8n-workflows/14-ollama-health-monitor.json
ogt 1b4f3a7bbe
Some checks failed
CD Pipeline / deploy (push) Failing after 59s
feat: EwoooC 初始化 — 完整專案推版至 Gitea
- 建立 Gitea Actions CD pipeline (.gitea/workflows/cd.yaml)
- 部署模式: rsync Python 檔案至 188 → docker restart (volume mount)
- Dockerfile/requirements 變動時自動重建 Docker image
- 部署通知: Telegram (開始/成功/失敗)
- 健康檢查: https://mo.wooo.work/health (最多 5 次重試)
- 同步最新 CLAUDE.md / ADR-008 / memory (2026-04-19)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 01:21:13 +08:00

431 lines
13 KiB
JSON

{
"name": "Ollama / Open WebUI 健康監控 (含自動修復)",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 5
}
]
}
},
"id": "schedule-trigger",
"name": "每 5 分鐘執行",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [240, 300]
},
{
"parameters": {
"url": "http://192.168.0.188:11434/api/tags",
"options": {
"timeout": 10000
}
},
"id": "check-ollama-api",
"name": "檢查 Ollama API",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [460, 200],
"continueOnFail": true
},
{
"parameters": {
"url": "http://192.168.0.188/",
"options": {
"timeout": 10000
}
},
"id": "check-open-webui",
"name": "檢查 Open WebUI",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [460, 400],
"continueOnFail": true
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "ollama-check",
"leftValue": "={{ $('檢查 Ollama API').first().json.models !== undefined }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
},
{
"id": "webui-check",
"leftValue": "={{ $('檢查 Open WebUI').first().binary !== undefined || $('檢查 Open WebUI').first().json !== undefined }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
}
},
"id": "check-health",
"name": "服務健康?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [680, 300]
},
{
"parameters": {
"jsCode": "// 分析失敗原因\nconst ollamaResult = $('檢查 Ollama API').first();\nconst webuiResult = $('檢查 Open WebUI').first();\n\nlet issues = [];\nlet needsDockerRestart = false;\nlet needsContainerRestart = false;\n\n// 檢查 Ollama API\nif (!ollamaResult.json.models) {\n issues.push({\n service: 'Ollama API',\n status: 'down',\n error: ollamaResult.json.error || 'API 無回應'\n });\n}\n\n// 檢查 Open WebUI\nif (webuiResult.json && webuiResult.json.error) {\n const errorMsg = webuiResult.json.error || '';\n \n if (errorMsg.includes('502') || errorMsg.includes('Bad Gateway')) {\n issues.push({\n service: 'Open WebUI',\n status: '502',\n error: '502 Bad Gateway - Docker 網路失效'\n });\n needsDockerRestart = true;\n } else if (errorMsg.includes('ECONNREFUSED') || errorMsg.includes('timeout')) {\n issues.push({\n service: 'Open WebUI',\n status: 'down',\n error: '連線失敗或超時'\n });\n needsContainerRestart = true;\n } else {\n issues.push({\n service: 'Open WebUI',\n status: 'error',\n error: errorMsg\n });\n }\n} else if (!webuiResult.binary && !webuiResult.json) {\n issues.push({\n service: 'Open WebUI',\n status: 'no_response',\n error: '無回應'\n });\n needsContainerRestart = true;\n}\n\nreturn {\n timestamp: new Date().toISOString(),\n issues: issues,\n issueCount: issues.length,\n needsDockerRestart: needsDockerRestart,\n needsContainerRestart: needsContainerRestart,\n summary: issues.map(i => `${i.service}: ${i.error}`).join('\\n')\n};"
},
"id": "analyze-failure",
"name": "分析失敗原因",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [900, 400]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "docker-restart",
"leftValue": "={{ $json.needsDockerRestart }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
}
},
"id": "needs-docker-restart",
"name": "需要重啟 Docker?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [1120, 400]
},
{
"parameters": {
"authentication": "predefinedCredentialType",
"nodeCredentialType": "sshPassword",
"resource": "command",
"command": "sudo systemctl restart docker && sleep 60 && curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/"
},
"id": "restart-docker",
"name": "重啟 Docker Daemon",
"type": "n8n-nodes-base.ssh",
"typeVersion": 1,
"position": [1340, 300],
"credentials": {
"sshPassword": {
"id": "ollama-ssh",
"name": "Ollama SSH"
}
},
"continueOnFail": true
},
{
"parameters": {
"authentication": "predefinedCredentialType",
"nodeCredentialType": "sshPassword",
"resource": "command",
"command": "docker restart open-webui && sleep 30 && curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/"
},
"id": "restart-container",
"name": "重啟 Open WebUI 容器",
"type": "n8n-nodes-base.ssh",
"typeVersion": 1,
"position": [1340, 500],
"credentials": {
"sshPassword": {
"id": "ollama-ssh",
"name": "Ollama SSH"
}
},
"continueOnFail": true
},
{
"parameters": {
"chatId": "5619078117",
"text": "=🔴 <b>Ollama / Open WebUI 服務異常</b>\n\n{{ $('分析失敗原因').item.json.summary }}\n\n<b>自動修復中...</b>\n時間: {{ new Date().toLocaleString('zh-TW', {timeZone: 'Asia/Taipei'}) }}",
"additionalFields": {
"parse_mode": "HTML"
}
},
"id": "alert-failure",
"name": "發送異常告警",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [1120, 600],
"credentials": {
"telegramApi": {
"id": "telegram-bot",
"name": "Telegram Bot"
}
}
},
{
"parameters": {
"jsCode": "const dockerResult = $('重啟 Docker Daemon').first();\nconst containerResult = $('重啟 Open WebUI 容器').first();\n\nlet repairStatus = 'unknown';\nlet repairAction = '';\nlet httpCode = '000';\n\nif (dockerResult && dockerResult.json) {\n repairAction = '重啟 Docker Daemon';\n httpCode = dockerResult.json.stdout || '000';\n} else if (containerResult && containerResult.json) {\n repairAction = '重啟 Open WebUI 容器';\n httpCode = containerResult.json.stdout || '000';\n}\n\nif (httpCode === '200') {\n repairStatus = 'success';\n} else {\n repairStatus = 'failed';\n}\n\nreturn {\n repairStatus: repairStatus,\n repairAction: repairAction,\n httpCode: httpCode\n};"
},
"id": "check-repair-result",
"name": "檢查修復結果",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1560, 400]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "repair-success",
"leftValue": "={{ $json.repairStatus }}",
"rightValue": "success",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
}
},
"id": "repair-success-check",
"name": "修復成功?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [1780, 400]
},
{
"parameters": {
"chatId": "5619078117",
"text": "=🟢 <b>Ollama / Open WebUI 已恢復</b>\n\n動作: {{ $('檢查修復結果').item.json.repairAction }}\n結果: HTTP {{ $('檢查修復結果').item.json.httpCode }}\n時間: {{ new Date().toLocaleString('zh-TW', {timeZone: 'Asia/Taipei'}) }}",
"additionalFields": {
"parse_mode": "HTML"
}
},
"id": "notify-success",
"name": "通知修復成功",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [2000, 300],
"credentials": {
"telegramApi": {
"id": "telegram-bot",
"name": "Telegram Bot"
}
}
},
{
"parameters": {
"chatId": "5619078117",
"text": "=🔴 <b>Ollama / Open WebUI 修復失敗</b>\n\n嘗試動作: {{ $('檢查修復結果').item.json.repairAction }}\n結果: HTTP {{ $('檢查修復結果').item.json.httpCode }}\n\n<b>需要人工介入處理</b>\nSSH: ssh ollama@192.168.0.188\n\n時間: {{ new Date().toLocaleString('zh-TW', {timeZone: 'Asia/Taipei'}) }}",
"additionalFields": {
"parse_mode": "HTML"
}
},
"id": "notify-failure",
"name": "通知修復失敗",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [2000, 500],
"credentials": {
"telegramApi": {
"id": "telegram-bot",
"name": "Telegram Bot"
}
}
},
{
"parameters": {},
"id": "no-op-healthy",
"name": "服務正常,無需操作",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [900, 200]
}
],
"connections": {
"每 5 分鐘執行": {
"main": [
[
{
"node": "檢查 Ollama API",
"type": "main",
"index": 0
},
{
"node": "檢查 Open WebUI",
"type": "main",
"index": 0
}
]
]
},
"檢查 Ollama API": {
"main": [
[
{
"node": "服務健康?",
"type": "main",
"index": 0
}
]
]
},
"檢查 Open WebUI": {
"main": [
[
{
"node": "服務健康?",
"type": "main",
"index": 0
}
]
]
},
"服務健康?": {
"main": [
[
{
"node": "服務正常,無需操作",
"type": "main",
"index": 0
}
],
[
{
"node": "分析失敗原因",
"type": "main",
"index": 0
}
]
]
},
"分析失敗原因": {
"main": [
[
{
"node": "需要重啟 Docker?",
"type": "main",
"index": 0
},
{
"node": "發送異常告警",
"type": "main",
"index": 0
}
]
]
},
"需要重啟 Docker?": {
"main": [
[
{
"node": "重啟 Docker Daemon",
"type": "main",
"index": 0
}
],
[
{
"node": "重啟 Open WebUI 容器",
"type": "main",
"index": 0
}
]
]
},
"重啟 Docker Daemon": {
"main": [
[
{
"node": "檢查修復結果",
"type": "main",
"index": 0
}
]
]
},
"重啟 Open WebUI 容器": {
"main": [
[
{
"node": "檢查修復結果",
"type": "main",
"index": 0
}
]
]
},
"檢查修復結果": {
"main": [
[
{
"node": "修復成功?",
"type": "main",
"index": 0
}
]
]
},
"修復成功?": {
"main": [
[
{
"node": "通知修復成功",
"type": "main",
"index": 0
}
],
[
{
"node": "通知修復失敗",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1"
},
"tags": [
{
"name": "monitoring"
},
{
"name": "auto-repair"
}
],
"pinData": {},
"meta": {
"instanceId": "momo-pro-system"
}
}