Monitoreo y alertas de Kubernetes - Estado de implementación y Pods - Alertas de Telegram
Intermedio
Este es unautomatización que contiene 14 nodos.Utiliza principalmente nodos como If, Code, Telegram, ExecuteCommand, ScheduleTrigger. Despliegue en Kubernetes y monitoreo de Pods con alertas por Telegram
Requisitos previos
- •Bot Token de Telegram
Nodos utilizados (14)
Categoría
-
Vista previa del flujo de trabajo
Visualización de las conexiones entre nodos, con soporte para zoom y panorámica
Exportar flujo de trabajo
Copie la siguiente configuración JSON en n8n para importar y usar este flujo de trabajo
{
"id": "cM2tIagg9TD8Jc0l",
"meta": {
"instanceId": "5c7ce220523e8664f49208a8be668a8dc6fab5f747ce4de865fa1309727919f1"
},
"name": "Kubernetes Monitoring & Alert - Deplyoment & Pods Status - Telegram Alert",
"tags": [],
"nodes": [
{
"id": "f0b0f7f0-b5e1-4bfd-85a0-6c0f90336a6e",
"name": "Disparador Programado",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-480,
208
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes"
}
]
}
},
"typeVersion": 1
},
{
"id": "976ee100-553e-4b40-8b9f-09a3afd980a9",
"name": "Configuración de Kubeconfig",
"type": "n8n-nodes-base.code",
"position": [
-224,
208
],
"parameters": {
"jsCode": "// PASTE YOUR KUBECONFIG CONTENT HERE\nconst kubeconfigContent = `\napiVersion: v1\nkind: Config\nclusters:\n- cluster:\n certificate-authority-data: YOUR_CA_DATA\n server: https://your-k8s-api-server:6443\n name: your-cluster\ncontexts:\n- context:\n cluster: your-cluster\n user: your-user\n name: your-context\ncurrent-context: your-context\nusers:\n- name: your-user\n user:\n token: YOUR_TOKEN\n`;\n\n// Configuration\nconst namespace = 'production';\n\nreturn [{\n json: {\n kubeconfig: kubeconfigContent,\n namespace: namespace\n }\n}];"
},
"typeVersion": 2
},
{
"id": "b95b994d-00b2-4bb7-9d4b-9f417f8bd1e1",
"name": "Obtener Pods",
"type": "n8n-nodes-base.executeCommand",
"position": [
0,
0
],
"parameters": {
"command": "=apk add curl\nKUBECONFIG_PATH=\"/tmp/kubeconfig-$RANDOM.yaml\"\necho '{{$json.kubeconfig}}' > $KUBECONFIG_PATH\ncurl -LO https://dl.k8s.io/release/v1.34.0/bin/linux/amd64/kubectl\nchmod +x ./kubectl\n./kubectl --kubeconfig=$KUBECONFIG_PATH get pods -n {{$json.namespace}} -o json\nrm -f $KUBECONFIG_PATH"
},
"typeVersion": 1
},
{
"id": "0b5b3388-2de0-479e-a9d3-a1b136b0cf56",
"name": "Obtener Deployments",
"type": "n8n-nodes-base.executeCommand",
"position": [
-16,
432
],
"parameters": {
"command": "=apk add curl\nKUBECONFIG_PATH=\"/tmp/kubeconfig-$RANDOM.yaml\"\necho '{{$json.kubeconfig}}' > $KUBECONFIG_PATH\ncurl -LO https://dl.k8s.io/release/v1.34.0/bin/linux/amd64/kubectl\nchmod +x ./kubectl\n./kubectl --kubeconfig=$KUBECONFIG_PATH get deployments -n {{$json.namespace}} -o json\nrm -f $KUBECONFIG_PATH"
},
"typeVersion": 1
},
{
"id": "f0a55a90-5bcf-42dc-96b0-3a21bd83c12e",
"name": "Procesar y Generar Reporte",
"type": "n8n-nodes-base.code",
"position": [
208,
224
],
"parameters": {
"jsCode": "// Debug: Check inputs\nconst inputs = $input.all();\n\n// Try to identify which input is which\nlet podsData, deploymentsData;\n\nfor (let i = 0; i < inputs.length; i++) {\n try {\n const input = inputs[i];\n \n if (!input.json?.stdout) continue;\n \n const data = JSON.parse(input.json.stdout);\n \n if (data.items && data.items.length > 0) {\n const firstKind = data.items[0].kind;\n \n if (firstKind === 'Pod') {\n podsData = data;\n } else if (firstKind === 'Deployment') {\n deploymentsData = data;\n }\n }\n } catch (e) {\n console.log(`Error parsing input ${i}:`, e.message);\n }\n}\n\nconst pods = podsData?.items || [];\nconst deployments = deploymentsData?.items || [];\nconst namespace = podsData?.items?.[0]?.metadata?.namespace || deploymentsData?.items?.[0]?.metadata?.namespace || 'N/A';\n\nconst deploymentStatus = {};\nconst otherWorkloads = {};\nconst standalonePods = [];\nconst alerts = [];\n\n// Process deployments from deployment objects\ndeployments.forEach(deployment => {\n const name = deployment.metadata.name;\n const ns = deployment.metadata.namespace;\n const replicas = deployment.spec.replicas || 0;\n const readyReplicas = deployment.status.readyReplicas || 0;\n const availableReplicas = deployment.status.availableReplicas || 0;\n \n deploymentStatus[name] = {\n name: name,\n namespace: ns,\n replicas: replicas,\n readyReplicas: readyReplicas,\n availableReplicas: availableReplicas,\n pods: []\n };\n \n if (readyReplicas < 1) {\n alerts.push({\n workload: name,\n kind: 'Deployment',\n issue: `No ready pods available! (Ready: ${readyReplicas}/${replicas})`\n });\n }\n});\n\n// Process pods - group by owner\npods.forEach(pod => {\n const podName = pod.metadata.name;\n const phase = pod.status.phase;\n const nodeName = pod.spec.nodeName || 'N/A';\n const conditions = pod.status.conditions || [];\n const readyCondition = conditions.find(c => c.type === 'Ready');\n const isReady = readyCondition?.status === 'True';\n const restartCount = pod.status.containerStatuses?.reduce((sum, c) => sum + c.restartCount, 0) || 0;\n \n const podInfo = {\n name: podName,\n phase: phase,\n ready: isReady,\n node: nodeName,\n restarts: restartCount\n };\n \n // Find owner from ownerReferences\n const ownerRefs = pod.metadata.ownerReferences || [];\n let ownerName = null;\n let ownerKind = null;\n \n for (const owner of ownerRefs) {\n if (owner.kind === 'ReplicaSet') {\n // Extract deployment name from ReplicaSet name\n const parts = owner.name.split('-');\n if (parts.length > 1) {\n parts.pop();\n ownerName = parts.join('-');\n ownerKind = 'Deployment';\n }\n break;\n } else if (['DaemonSet', 'StatefulSet', 'Node'].includes(owner.kind)) {\n ownerName = owner.name;\n ownerKind = owner.kind;\n break;\n }\n }\n \n // Add pod to appropriate owner\n if (ownerKind === 'Deployment') {\n // If deployment exists in deploymentStatus, add there\n if (deploymentStatus[ownerName]) {\n deploymentStatus[ownerName].pods.push(podInfo);\n } else {\n // Otherwise create a deployment entry (discovered from pods)\n if (!deploymentStatus[ownerName]) {\n const readyCount = 0; // Will be calculated later\n deploymentStatus[ownerName] = {\n name: ownerName,\n namespace: namespace,\n replicas: 0,\n readyReplicas: 0,\n availableReplicas: 0,\n pods: [],\n discoveredFromPods: true\n };\n }\n deploymentStatus[ownerName].pods.push(podInfo);\n }\n } else if (ownerName) {\n // Group by other owner types\n const key = `${ownerKind}/${ownerName}`;\n if (!otherWorkloads[key]) {\n otherWorkloads[key] = {\n kind: ownerKind,\n name: ownerName,\n pods: []\n };\n }\n otherWorkloads[key].pods.push(podInfo);\n } else {\n standalonePods.push(podInfo);\n }\n});\n\n// Calculate stats for deployments discovered from pods\nObject.values(deploymentStatus).forEach(dep => {\n if (dep.discoveredFromPods) {\n const totalPods = dep.pods.length;\n const readyPods = dep.pods.filter(p => p.ready).length;\n dep.replicas = totalPods;\n dep.readyReplicas = readyPods;\n dep.availableReplicas = readyPods;\n \n if (readyPods < 1) {\n alerts.push({\n workload: dep.name,\n kind: 'Deployment',\n issue: `No ready pods available! (Ready: ${readyPods}/${totalPods})`\n });\n }\n }\n});\n\n// Check for alerts in non-deployment workloads\nObject.values(otherWorkloads).forEach(owner => {\n const readyCount = owner.pods.filter(p => p.ready).length;\n if (readyCount < 1) {\n alerts.push({\n workload: owner.name,\n kind: owner.kind,\n issue: `No ready pods available! (Ready: ${readyCount}/${owner.pods.length})`\n });\n }\n});\n\n// Generate report\nlet markdown = `# Kubernetes Cluster Status Report\\n\\n`;\nmarkdown += `**Namespace:** ${namespace}\\n`;\nmarkdown += `**Timestamp:** ${new Date().toISOString()}\\n\\n`;\n\nconst deploymentCount = Object.keys(deploymentStatus).length;\nconst otherWorkloadCount = Object.keys(otherWorkloads).length;\nmarkdown += `**Total:** ${deploymentCount} deployments, ${otherWorkloadCount} other workloads, ${pods.length} pods\\n\\n`;\n\n// Deployments section\nif (deploymentCount > 0) {\n markdown += `## Deployments\\n\\n`;\n Object.values(deploymentStatus).forEach(dep => {\n const status = dep.readyReplicas >= 1 ? '✅' : '❌';\n markdown += `### ${status} ${dep.name}\\n`;\n markdown += `- **Replicas:** ${dep.readyReplicas}/${dep.replicas}\\n`;\n markdown += `- **Available:** ${dep.availableReplicas}\\n`;\n markdown += `- **Pods:**\\n`;\n \n if (dep.pods.length === 0) {\n markdown += ` - *No pods*\\n`;\n } else {\n dep.pods.forEach(pod => {\n const podStatus = pod.ready ? '✅' : '❌';\n markdown += ` - ${podStatus} ${pod.name} (${pod.phase}) - Node: ${pod.node}, Restarts: ${pod.restarts}\\n`;\n });\n }\n markdown += `\\n`;\n });\n}\n\n// Other workloads section (DaemonSets, StatefulSets, Static Pods)\nif (otherWorkloadCount > 0) {\n markdown += `## Other Workloads (DaemonSets, StatefulSets, Static Pods)\\n\\n`;\n Object.values(otherWorkloads).forEach(owner => {\n const readyCount = owner.pods.filter(p => p.ready).length;\n const totalCount = owner.pods.length;\n const status = readyCount >= 1 ? '✅' : '❌';\n \n markdown += `### ${status} ${owner.name} (${owner.kind})\\n`;\n markdown += `- **Ready:** ${readyCount}/${totalCount}\\n`;\n markdown += `- **Pods:**\\n`;\n \n owner.pods.forEach(pod => {\n const podStatus = pod.ready ? '✅' : '❌';\n markdown += ` - ${podStatus} ${pod.name} (${pod.phase}) - Node: ${pod.node}, Restarts: ${pod.restarts}\\n`;\n });\n markdown += `\\n`;\n });\n}\n\n// Standalone pods\nif (standalonePods.length > 0) {\n markdown += `## Standalone Pods\\n\\n`;\n standalonePods.forEach(pod => {\n const podStatus = pod.ready ? '✅' : '❌';\n markdown += `- ${podStatus} ${pod.name} (${pod.phase}) - Node: ${pod.node}, Restarts: ${pod.restarts}\\n`;\n });\n markdown += `\\n`;\n}\n\n// Alerts section\nif (alerts.length > 0) {\n markdown += `## ⚠️ Alerts\\n\\n`;\n alerts.forEach(alert => {\n markdown += `- **${alert.workload}** (${alert.kind}): ${alert.issue}\\n`;\n });\n}\n\n// Convert markdown to binary data\nconst buffer = Buffer.from(markdown, 'utf-8');\nconst binaryData = {\n data: buffer.toString('base64'),\n mimeType: 'text/markdown',\n fileName: 'report.md'\n};\n\nreturn [{\n json: {\n markdown: markdown,\n hasAlerts: alerts.length > 0,\n alerts: alerts,\n deploymentCount: deploymentCount,\n podCount: pods.length,\n namespace: namespace\n },\n binary: {\n data: binaryData\n }\n}];"
},
"typeVersion": 2
},
{
"id": "22af800d-d130-4d13-9e6b-6a9176b886b9",
"name": "¿Hay Alertas?",
"type": "n8n-nodes-base.if",
"position": [
432,
224
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{$json.hasAlerts}}",
"value2": true
}
]
}
},
"typeVersion": 1
},
{
"id": "deb746c1-42b4-4e12-8530-d0340cf52e90",
"name": "Enviar Alerta Telegram",
"type": "n8n-nodes-base.telegram",
"position": [
704,
16
],
"webhookId": "ad590700-edca-4911-84c0-bbfd3f1971a0",
"parameters": {
"text": "=🚨 *Kubernetes Alert*\\n\\n*Namespace:* {{$json.namespace}}\\n\\n{{$json.alerts.map(a => `⚠️ *${a.deployment}*: ${a.issue}`).join('\\n')}}\\n\\n---\\n\\n{{$json.markdown}}",
"chatId": "YOUR_TELEGRAM_CHAT_ID",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"credentials": {
"telegramApi": {
"id": "FUXl519hpM0FsK8j",
"name": "Telegram account"
}
},
"typeVersion": 1
},
{
"id": "c8cf22c0-7954-4a5c-84fc-3513ece1e376",
"name": "Guardar Reporte",
"type": "n8n-nodes-base.writeBinaryFile",
"position": [
704,
480
],
"parameters": {
"options": {},
"fileName": "=k8s-report-{{$now.format('YYYY-MM-DD-HHmmss')}}.md",
"dataPropertyName": "=data"
},
"typeVersion": 1
},
{
"id": "277ca9f2-b6bf-4c87-8d56-1734c0fecae0",
"name": "Nota Adhesiva",
"type": "n8n-nodes-base.stickyNote",
"position": [
-752,
0
],
"parameters": {
"width": 360,
"height": 180,
"content": "## Kubernetes Monitoring Workflow\n\nAutomatically monitors your K8s cluster and sends Telegram alerts when workloads have zero ready pods.\n\nReports are saved as markdown files for every execution."
},
"typeVersion": 1
},
{
"id": "da5b3125-2dfd-4ee5-9732-27404ef98455",
"name": "Nota Adhesiva1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-416,
352
],
"parameters": {
"width": 300,
"height": 240,
"content": "## CONFIGURATION REQUIRED\n\n1. Paste your kubeconfig content\n2. Set target namespace (default: 'production')\n\nThe workflow will automatically download kubectl during execution."
},
"typeVersion": 1
},
{
"id": "848a775e-d3c7-4c5b-8b1b-6ad4200b9687",
"name": "Nota Adhesiva2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-96,
-208
],
"parameters": {
"width": 280,
"height": 188,
"content": "## Data Collection\n\nBoth nodes run in parallel to fetch:\n- All pods\n- All deployments\n\nfrom the specified namespace"
},
"typeVersion": 1
},
{
"id": "5e94eb86-fb7a-4cde-a2b5-3778f057c316",
"name": "Nota Adhesiva3",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
384
],
"parameters": {
"width": 280,
"content": "## Processing & Alert Detection\n\nGroups pods by owner (Deployment, DaemonSet, StatefulSet, Node)\n\nDetects alerts: workloads with 0 ready pods\n\nGenerates comprehensive markdown report"
},
"typeVersion": 1
},
{
"id": "83b169f1-7bf5-497d-bdb8-9b999baaa980",
"name": "Nota Adhesiva4",
"type": "n8n-nodes-base.stickyNote",
"position": [
704,
-224
],
"parameters": {
"width": 464,
"height": 216,
"content": "## TELEGRAM CONFIGURATION\n\n1. Create bot via @BotFather\n2. Get bot token & add as credential\n3. Replace YOUR_TELEGRAM_CHAT_ID with your actual chat ID\n\nFind chat ID: message your bot, then visit:\nhttps://api.telegram.org/bot<TOKEN>/getUpdates"
},
"typeVersion": 1
},
{
"id": "00a9cb9f-6e0b-4537-adee-b2b6cef02f86",
"name": "Nota Adhesiva5",
"type": "n8n-nodes-base.stickyNote",
"position": [
704,
624
],
"parameters": {
"width": 280,
"height": 220,
"content": "## Report Output\n\nSaves markdown report with timestamp:\nk8s-report-YYYY-MM-DD-HHmmss.md\n\nExecutes on every run regardless of alert status"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "0f3068a8-25a9-42af-b277-7deb8c6844f1",
"connections": {
"b95b994d-00b2-4bb7-9d4b-9f417f8bd1e1": {
"main": [
[
{
"node": "f0a55a90-5bcf-42dc-96b0-3a21bd83c12e",
"type": "main",
"index": 0
}
]
]
},
"22af800d-d130-4d13-9e6b-6a9176b886b9": {
"main": [
[
{
"node": "deb746c1-42b4-4e12-8530-d0340cf52e90",
"type": "main",
"index": 0
}
],
[
{
"node": "c8cf22c0-7954-4a5c-84fc-3513ece1e376",
"type": "main",
"index": 0
}
]
]
},
"0b5b3388-2de0-479e-a9d3-a1b136b0cf56": {
"main": [
[
{
"node": "f0a55a90-5bcf-42dc-96b0-3a21bd83c12e",
"type": "main",
"index": 0
}
]
]
},
"976ee100-553e-4b40-8b9f-09a3afd980a9": {
"main": [
[
{
"node": "b95b994d-00b2-4bb7-9d4b-9f417f8bd1e1",
"type": "main",
"index": 0
},
{
"node": "0b5b3388-2de0-479e-a9d3-a1b136b0cf56",
"type": "main",
"index": 0
}
]
]
},
"f0b0f7f0-b5e1-4bfd-85a0-6c0f90336a6e": {
"main": [
[
{
"node": "976ee100-553e-4b40-8b9f-09a3afd980a9",
"type": "main",
"index": 0
}
]
]
},
"deb746c1-42b4-4e12-8530-d0340cf52e90": {
"main": [
[
{
"node": "c8cf22c0-7954-4a5c-84fc-3513ece1e376",
"type": "main",
"index": 0
}
]
]
},
"f0a55a90-5bcf-42dc-96b0-3a21bd83c12e": {
"main": [
[
{
"node": "22af800d-d130-4d13-9e6b-6a9176b886b9",
"type": "main",
"index": 0
}
]
]
}
}
}Preguntas frecuentes
¿Cómo usar este flujo de trabajo?
Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.
¿En qué escenarios es adecuado este flujo de trabajo?
Intermedio
¿Es de pago?
Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.
Flujos de trabajo relacionados recomendados
Bot de canal de IA de Telegram - Generador TGPT con soporte para respuestas de texto e imagen
Generar respuestas de texto e imágenes en canales de Telegram con GPT-4 y TGPT
If
Set
Code
+
If
Set
Code
22 NodosVigh Sandor
Implementación de pruebas automatizadas de Kubernetes usando Robot Framework, ArgoCD y un ciclo de vida completo KinD
Pruebas automatizadas de Kubernetes usando Robot Framework, ArgoCD y un ciclo de vida completo de KinD
If
Set
Gitlab
+
If
Set
Gitlab
73 NodosVigh Sandor
DevOps
Respaldo automatizado de Rsync con autenticación por contraseña y sistema de alertas
Respaldo automatizado con Rsync con autenticación por contraseña y sistema de alertas
If
Set
Manual Trigger
+
If
Set
Manual Trigger
21 NodosVigh Sandor
Creación y carga de videos impulsada por IA para Instagram, TikTok y YouTube
Creación y subida de videos impulsada por IA desde Google Drive a Instagram, TikTok y YouTube
If
Set
Code
+
If
Set
Code
53 NodosDevCode Journey
Creación de contenido
Resumen deportivo diario
Usar Google Gemini, Kokoro TTS y FFmpeg para convertir fuentes RSS a podcasts
If
Set
Code
+
If
Set
Code
34 NodosJonas
Creación de contenido
Generador de Podcasts con IA con Suscripción RSS y Voz de ElevenLabs
Generador de Podcasts con IA: Integra suscripción RSS y voz de ElevenLabs
If
Code
Gmail
+
If
Code
Gmail
11 NodosDavid Olusola
Inteligencia Artificial
Información del flujo de trabajo
Nivel de dificultad
Intermedio
Número de nodos14
Categoría-
Tipos de nodos7
Descripción de la dificultad
Autor
Vigh Sandor
@vighsandorEnlaces externos
Ver en n8n.io →
Compartir este flujo de trabajo