What? Publish output of one or more programs to an MQTT broker using a single, persistent connection.
How? Start a PIMPER container and let producers write lines containing topic and message to a named pipe.
Why? Add MQTT support to any program while preserving network resources carefully.
This project is a Gateway between programs that write to stdout/stderr and the MQTT world.
The well-known tool mosquitto_pub is able to do this job, but with one drawback:
it requires one connection per topic (mosquitto #3060).
Programs that output to different topics can be covered by calling mosquitto_pub once per message,
resulting in heavy resource use for busy producers (1 DNS lookup + 1 TCP connection + maybe 1 TLS handshake per message).
PIMPER is able to publish output from one or more programs over a single, persistent MQTT connection.
A producer’s output must reach PIMPER somehow. In a containerized world, applications usually communicate over the network. The main purpose of this project is avoidance of network overhead, so this would be no good fit here.
Thus, producers send messages through a pipe in the file system. PIMPER comes with a default pipe that producers can use. It is also possible to configure one or more pipes manually.
|
Note
|
A pipe is not 1:1 but n:m, so multiple producers can use the same pipe. |
Setup is easy: start a PIMPER container and redirect producers' output to the source pipe.
Run docker compose up with this configuration to
-
Start PIMPER in a container
-
Export its default pipe in a volume
-
Start a demo producer in another container
---
services:
pimper:
image: ghcr.io/git-developer/pimper
command:
- 'mqtt://broker?clientId=demo-producer'
volumes:
- 'sources:/var/run/pimper'
producer:
image: busybox
volumes:
- 'sources:/var/run/pimper'
command:
- sh
- '-c'
- |
while true; do
printf '%s %s\n' pimper/demo/date "$(date)"
sleep 2
done >>/var/run/pimper/source
depends_on:
pimper:
condition:
service_healthy
volumes:
sources:Producers do not need to be declared within the same Compose configuration as PIMPER. To redirect messages from other containers, just mount the volume into them.
---
services:
pimper:
image: ghcr.io/git-developer/pimper
command:
- 'mqtt://broker'
volumes:
- 'sources:/var/run/pimper'
volumes:
sources:---
services:
producer:
image: busybox
volumes:
- 'pimper_sources:/var/run/pimper'
command:
- sh
- '-c'
- |
while true; do
printf '%s %s\n' pimper/demo/date "$(date)"
sleep 2
done >>/var/run/pimper/source
volumes:
pimper_sources:
external: trueIt is also possible use a producer that is not running as Docker container.
# Start a demo subscriber to see messages
$ mosquitto_sub -v -L mqtt://broker/pimper/# &
# Create a pipe
$ mkfifo -m 666 /tmp/pimper-source
# Start PIMPER
$ docker run -d --rm -v /tmp/pimper-source:/var/run/pimper/source ghcr.io/git-developer/pimper mqtt://broker >/dev/null
pimper/status mqttjs_f0212988 is connected
# Publish a few messages
$ for i in 1 2 3; do echo "pimper/demo" "This is demo message ${i}"; sleep 1; done >>/tmp/pimper-source
# This output is coming from mosquitto_sub
pimper/demo This is demo message 1
pimper/demo This is demo message 2
pimper/demo This is demo message 3
pimper/status mqttjs_f0212988 is disconnected (Cause: /var/run/pimper/source was closed)
# Stop demo subscriber
$ pkill mosquitto_sub
[1] + Done mosquitto_sub -v -L mqtt://broker/pimper/#Messages are read line-by-line from the source pipe(s). Each line is expected in this format:
<TOPIC> <SEPARATOR> <PAYLOAD> <NEWLINE>
Default for the separator is a space character (⎵).
The payload may contain space characters.
Examples:
sensors/outside/temp 25°C
monitoring/living-room/status No ProblemsIf a producer is unable to output this format, an adapter (e.g. a shell script) can be introduced to convert the program output before it is written to the PIMPER source pipe.
-
The only required option is an URL pointing to a target MQTT broker.
-
All mqttjs options are supported.
Variable |
Description |
Allowed values |
Default |
|
List of sources |
Source definitions |
- file: /var/run/pimper/source |
|
List of targets |
Target definitions |
none |
|
Separator between topic and message for all sources |
Any string |
|
| Variable | Description | Allowed values | Default | Example |
|---|---|---|---|---|
|
Path of the source in the file system |
Path to a readable file |
|
|
|
Separator between topic and message for this source |
Any string |
|
|
|
Behavior when the source file is closed |
|
|
|
| Variable | Description | Allowed values | Default | Example |
|---|---|---|---|---|
|
MQTT URL |
URL containing at least protocol ( |
None |
|
|
MQTT options |
None |
options:
clientId: demo-client |
|
|
MQTT topic for status messages. To disable, set to an empty string. |
MQTT topic |
|
|
|
CA certificate |
File path |
None |
|
-
Configuration can be done via a YAML file and/or command line arguments.
-
When both are given, command line arguments have priority over file configuration.
-
The default path for the configuration file is
/etc/pimper.yml. It may be overriden via environment variablePIMPER_CONFIG_PATH(which is quite unusual because you can achieve the same using a volume mapping). -
Command line arguments are expected as key/value pairs.
-
Key and value separated by an
=character without spaces. -
Keys are expected in dotted notation. List items are represented as 0-based index number.
-
The key for the URL of the first MQTT broker may be omitted, i.e.
targets.0.url=mqtt://brokeris the same asmqtt://broker.
-
| Description | YAML | Command Line Arguments |
|---|---|---|
Read from |
---
sources:
- file: /pipe
targets:
- url: "mqtt://broker" |
none |
Read from |
none |
|
Override broker from YAML with arg |
---
sources:
- file: /pipe
targets:
- url: "mqtt://broker" |
|
---
targets:
- url: "mqtts://broker"
caFile: /opt/custom-ca.crt
options:
clientId: pimper-tls-demoservices:
pimper:
volumes:
- './pimper.yml:/etc/pimper.yml:ro'
- './custom-ca.crt:/opt/custom-ca.crt:ro'topicSeparator: ':'
sources:
- file: /dev/stdin
topicSeparator: ' '
- file: /var/run/pimper/source
onClose: reopen
- file: /tmp/messages
topicSeparator: ','
onClose: ignore
targets:
- url: "mqtt://broker-1?clientId=demo-client"
- url: "mqtts://broker-2"
caFile: /opt/ca.crt
statusTopic: pimper/management
options:
clientId: tls-clientservices:
pimper:
volumes:
- './pimper.yml:/etc/pimper.yml:ro'
- './custom-ca.crt:/opt/ca.crt:ro'PIMPER uses the debug package.
To enable debug messages, include the pimper module in the environment variable DEBUG. Example:
---
services:
pimper:
environment:
DEBUG: 'pimper:*,mqttjs*'
DEBUG_DEPTH: 5