Files
ewoooc/n8n-workflows/23-slo-monitoring.json
OoO d6d8777e41
All checks were successful
CD Pipeline / deploy (push) Successful in 1m12s
V10.601 收斂 Gemini 與密鑰治理
2026-06-06 14:52:46 +08:00

171 lines
7.8 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"name": "SLO 服務等級監控",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 * * * *"
}
]
}
},
"id": "schedule",
"name": "每小時執行",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [240, 300]
},
{
"parameters": {
"url": "https://mo.wooo.work/health",
"options": { "timeout": 10000 }
},
"id": "uat-check",
"name": "UAT 可用性",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [460, 200],
"continueOnFail": true
},
{
"parameters": {
"url": "https://momo.wooo.work/health",
"options": { "timeout": 10000 }
},
"id": "gcp-check",
"name": "GCP 可用性",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [460, 400],
"continueOnFail": true
},
{
"parameters": {
"mode": "combine",
"combineBy": "combineAll",
"options": {}
},
"id": "merge",
"name": "合併結果",
"type": "n8n-nodes-base.merge",
"typeVersion": 3,
"position": [680, 300]
},
{
"parameters": {
"jsCode": "const results = $input.all();\nconst now = new Date();\nconst hour = now.getHours();\nconst dateKey = now.toISOString().slice(0, 10);\n\n// 從 static data 讀取 SLO 數據\nconst sloData = $getWorkflowStaticData('global');\nif (!sloData.metrics) {\n sloData.metrics = {\n uat: { checks: 0, success: 0, latency: [] },\n gcp: { checks: 0, success: 0, latency: [] },\n daily: {}\n };\n}\n\n// UAT 結果\nconst uatResult = results[0]?.json;\nconst uatSuccess = uatResult && uatResult.status === 'healthy';\nconst uatLatency = results[0]?.json?.$responseTime || 0;\n\nsloData.metrics.uat.checks++;\nif (uatSuccess) sloData.metrics.uat.success++;\nif (uatLatency > 0) {\n sloData.metrics.uat.latency.push(uatLatency);\n if (sloData.metrics.uat.latency.length > 168) { // 保留 7 天\n sloData.metrics.uat.latency.shift();\n }\n}\n\n// GCP 結果\nconst gcpResult = results[1]?.json;\nconst gcpSuccess = gcpResult && gcpResult.status === 'healthy';\nconst gcpLatency = results[1]?.json?.$responseTime || 0;\n\nsloData.metrics.gcp.checks++;\nif (gcpSuccess) sloData.metrics.gcp.success++;\nif (gcpLatency > 0) {\n sloData.metrics.gcp.latency.push(gcpLatency);\n if (sloData.metrics.gcp.latency.length > 168) {\n sloData.metrics.gcp.latency.shift();\n }\n}\n\n// 計算 SLO 指標\nconst uatAvailability = (sloData.metrics.uat.success / sloData.metrics.uat.checks * 100).toFixed(2);\nconst gcpAvailability = (sloData.metrics.gcp.success / sloData.metrics.gcp.checks * 100).toFixed(2);\n\nconst avgUatLatency = sloData.metrics.uat.latency.length > 0 \n ? (sloData.metrics.uat.latency.reduce((a, b) => a + b, 0) / sloData.metrics.uat.latency.length).toFixed(0)\n : 0;\nconst avgGcpLatency = sloData.metrics.gcp.latency.length > 0\n ? (sloData.metrics.gcp.latency.reduce((a, b) => a + b, 0) / sloData.metrics.gcp.latency.length).toFixed(0)\n : 0;\n\n// SLO 目標\nconst SLO_AVAILABILITY = 99.5; // 可用性目標 99.5%\nconst SLO_LATENCY = 3000; // 延遲目標 3 秒內\n\nconst uatMeetsSLO = parseFloat(uatAvailability) >= SLO_AVAILABILITY;\nconst gcpMeetsSLO = parseFloat(gcpAvailability) >= SLO_AVAILABILITY;\n\n// 每日報告(每天 09:00\nconst shouldReport = hour === 9;\n\nreturn [{\n json: {\n uat: {\n currentStatus: uatSuccess ? 'healthy' : 'unhealthy',\n availability: uatAvailability + '%',\n avgLatency: avgUatLatency + 'ms',\n meetsSLO: uatMeetsSLO,\n totalChecks: sloData.metrics.uat.checks\n },\n gcp: {\n currentStatus: gcpSuccess ? 'healthy' : 'unhealthy',\n availability: gcpAvailability + '%',\n avgLatency: avgGcpLatency + 'ms',\n meetsSLO: gcpMeetsSLO,\n totalChecks: sloData.metrics.gcp.checks\n },\n sloTarget: {\n availability: SLO_AVAILABILITY + '%',\n latency: SLO_LATENCY + 'ms'\n },\n shouldReport,\n timestamp: now.toISOString()\n }\n}];"
},
"id": "calculate-slo",
"name": "計算 SLO 指標",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [900, 300]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "should-report",
"leftValue": "={{ $json.shouldReport }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-report",
"name": "每日報告?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [1120, 300]
},
{
"parameters": {
"jsCode": "const data = $input.first().json;\nconst now = new Date().toLocaleString('zh-TW', { timeZone: 'Asia/Taipei' });\n\nlet message = `📊 <b>每日 SLO 報告</b>\\n\\n`;\nmessage += `<b>服務等級目標</b>\\n`;\nmessage += `├ 可用性目標: ${data.sloTarget.availability}\\n`;\nmessage += `└ 延遲目標: < ${data.sloTarget.latency}\\n\\n`;\n\nmessage += `<b>🟦 UAT 環境</b>\\n`;\nmessage += `├ 可用性: ${data.uat.availability} ${data.uat.meetsSLO ? '✅' : '❌'}\\n`;\nmessage += `├ 平均延遲: ${data.uat.avgLatency}\\n`;\nmessage += `└ 檢查次數: ${data.uat.totalChecks}\\n\\n`;\n\nmessage += `<b>🟥 GCP 環境</b>\\n`;\nmessage += `├ 可用性: ${data.gcp.availability} ${data.gcp.meetsSLO ? '✅' : '❌'}\\n`;\nmessage += `├ 平均延遲: ${data.gcp.avgLatency}\\n`;\nmessage += `└ 檢查次數: ${data.gcp.totalChecks}\\n\\n`;\n\nmessage += `⏰ 報告時間: ${now}`;\n\nreturn [{ json: { message } }];"
},
"id": "format-report",
"name": "格式化報告",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1340, 200]
},
{
"parameters": {
"url": "https://api.telegram.org/bot<TELEGRAM_BOT_TOKEN>/sendMessage",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"chat_id\": \"5619078117\",\n \"parse_mode\": \"HTML\",\n \"text\": \"{{ $json.message }}\"\n}",
"options": {}
},
"id": "telegram",
"name": "發送報告",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1560, 200]
}
],
"connections": {
"每小時執行": {
"main": [
[
{ "node": "UAT 可用性", "type": "main", "index": 0 },
{ "node": "GCP 可用性", "type": "main", "index": 0 }
]
]
},
"UAT 可用性": {
"main": [
[{ "node": "合併結果", "type": "main", "index": 0 }]
]
},
"GCP 可用性": {
"main": [
[{ "node": "合併結果", "type": "main", "index": 1 }]
]
},
"合併結果": {
"main": [
[{ "node": "計算 SLO 指標", "type": "main", "index": 0 }]
]
},
"計算 SLO 指標": {
"main": [
[{ "node": "每日報告?", "type": "main", "index": 0 }]
]
},
"每日報告?": {
"main": [
[{ "node": "格式化報告", "type": "main", "index": 0 }],
[]
]
},
"格式化報告": {
"main": [
[{ "node": "發送報告", "type": "main", "index": 0 }]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [],
"triggerCount": 0,
"updatedAt": "2026-02-07T17:00:00.000Z",
"versionId": "1"
}