How to create black box monitoring stack using Synpse and NodeRed

"Black box" monitoring is one of the key concepts, when monitoring any application. We will show how to deploy a free DIY downtime monitoring stack, using NodeRed and Synpse

Published on September 20, 21

Synpse is an end-to-end platform to manage your device fleet that can grow to hundreds of thousands of devices, perform OTA software updates, collect metrics, logs, deploy your containerized applications and facilitate tunnel-based SSH access to any of your device. You can find a Quick Start here .


Technologies used

  1. Synpse for hosting and running applications anywhere
  2. NodeRED for instrumenting our monitoring

NodeRED

We will use NodeRED as instrumentation tool to “write code without writing any code” and monitor our web pages availability. If downtime is detected, we will notify administrators via slack or discord message.

First lets deploy NodeRED into Synpse enable IoT device we own:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
name: NodeRed
description: Low-code programming for event-driven applications
type: container
scheduling:
  type: Selector
  # More on selectors https://docs.synpse.net/synpse-core/applications/scheduling
  selectors:
    nodered: master
spec:
  containers:
  - name: nodered
    image: nodered/node-red:latest-minimal
    user: root
    ports:
      - "1880:1880"
    volumes:
      - /data/nodered:/data
      - /etc/localtime:/etc/localtime
      - /root/.ssh:/root/.ssh

Access the remote device via Synpse proxy CLI command to configure it

1
synpse device proxy <device_name> --port 1880

Open the browser page http://localhost:1880. You should be able to see NodeRed running.

Downtime flow

We use Discord webhook integration to send a messages and alert our developers once page is down. Discord webhook documentation can be found here

Discord webhook
Discord webhook

Flow we created is very simple:

  1. Each 5s call cloud.synpse.net health url
  2. If response is not 200, go to next phase, else drop
  3. Rate limit to send only 1 message each 5 min
  4. Create a Webhook payload
  5. Send POST request to webhook URL

Flow itself looks like this (we have left if 200 branch too, in case you want to re-use it for other purposes). Full json flow can be found bellow for you to reuse.

NodeRed flow
NodeRed flow

Press “deploy” and you are done :)

Message
Message

./wrap_up.sh

This is very simple way to monitor your applications or construct any other automation flows. In addition you cab deploy same application to different agents, across the globe, and you have free Region based downtime monitoring!

If you have any questions or suggestions, feel free to start a new discussion in our forum or drop us a line on Discord

Full flow

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
[
    {
        "id": "90902c5d92f08930",
        "type": "tab",
        "label": "NodeRed on Synpse",
        "disabled": false,
        "info": ""
    },
    {
        "id": "ef34b5db67c67385",
        "type": "http request",
        "z": "90902c5d92f08930",
        "name": "<PAGE_TO_CHECK_NAME>",
        "method": "GET",
        "ret": "txt",
        "paytoqs": "ignore",
        "url": "<PAGE_TO_CHECK_URL>",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 370,
        "y": 260,
        "wires": [
            [
                "2109536ce55f85cf",
                "1aae7673af94fb6c"
            ]
        ]
    },
    {
        "id": "3c39826c5c2c418b",
        "type": "inject",
        "z": "90902c5d92f08930",
        "name": "ticker 5s",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "5",
        "crontab": "",
        "once": true,
        "onceDelay": "1",
        "topic": "",
        "payload": "{}",
        "payloadType": "json",
        "x": 150,
        "y": 260,
        "wires": [
            [
                "ef34b5db67c67385"
            ]
        ]
    },
    {
        "id": "2109536ce55f85cf",
        "type": "switch",
        "z": "90902c5d92f08930",
        "name": "if not 200",
        "property": "statusCode",
        "propertyType": "msg",
        "rules": [
            {
                "t": "neq",
                "v": "200",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 580,
        "y": 200,
        "wires": [
            [
                "758156729b55665f"
            ]
        ]
    },
    {
        "id": "1aae7673af94fb6c",
        "type": "switch",
        "z": "90902c5d92f08930",
        "name": "if  200",
        "property": "statusCode",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "200",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 570,
        "y": 300,
        "wires": [
            []
        ]
    },
    {
        "id": "758156729b55665f",
        "type": "delay",
        "z": "90902c5d92f08930",
        "name": "",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "5",
        "rateUnits": "minute",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": true,
        "allowrate": false,
        "x": 740,
        "y": 260,
        "wires": [
            [
                "a062d51eb49713f9"
            ]
        ]
    },
    {
        "id": "2452ffaa52e6333f",
        "type": "http request",
        "z": "90902c5d92f08930",
        "name": "discord webhook",
        "method": "POST",
        "ret": "txt",
        "paytoqs": "ignore",
        "url": "<DISCORD_WEBHOOK_URL>",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 1110,
        "y": 260,
        "wires": [
            []
        ]
    },
    {
        "id": "a062d51eb49713f9",
        "type": "function",
        "z": "90902c5d92f08930",
        "name": "payload",
        "func": "var payload = {\n  \"username\": \"Downtime BOT\",\n  \"avatar_url\": \"https://i.imgur.com/zEGYUVs.jpeg\",\n  \"content\": \"cloud.synpse.net Downtime detected. Next check in 5 min\",\n}\n\nmsg.payload = payload\nreturn msg",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 920,
        "y": 260,
        "wires": [
            [
                "2452ffaa52e6333f"
            ]
        ]
    }
]