
Em uma postagem anterior, eu mostrei como configurar um broker MQTT no Kubernetes com o Mosquitto. Mas o suporte do Mosquitto para a criação de clusters usando bridges é muito limitado. Felizmente, existe uma solução muito mais simples, que utiliza o Redis para criar o cluster e o Mosca para suporte ao protocolo MQTT.
Essa ideia surgiu quando eu encontrei um artigo no Medium descrevendo basicamente a mesma arquitetura que eu irei mostrar aqui. O artigo original (em inglês) pode ser acessado em How to Build an High Availability MQTT Cluster for the Internet of Things.
Criação do Broker Link to heading
De forma resumida, pode-se dizer que o Mosca é um framework para criação de um broker MQTT. O Mosca em si não implementa nenhum suporte a pub/sub (que é necessário para o funcionamento do MQTT), mas, em vez disso, permite utilizar outras implementações como backend (incluindo Mosquitto, Redis, MongoDB e outros). O Mosca se preocupa apenas com o protocolo MQTT.
Bom, com o funcionamento do Mosca bem entendido, o próximo passo é criar o broker.
No caso do Mosca, isso significa criar uma aplicação NodeJS e instalar as
dependências pelo NPM. E as única dependências necessárias são o mosca
e um
backend, nesse caso o redis
.
$ npm i -s mosca redis
Esse comando deve instalar as duas dependências e adicioná-las ao package.json
.
Em seguida, é criado o arquivo principal do projeto, que vai iniciar o Mosca e
configurar o backend utilizado. Eu chamei esse arquivo de app.js
.
const url = require('url');
const mosca = require('mosca');
const authorizer = require('./authorizer');
const redis = url.parse(process.env.REDIS_URL);
const settings = {
port: parseInt(process.env.MQTT_PORT) || 1883,
backend: {
type: 'redis',
redis: require('redis'),
host: redis.hostname,
port: redis.port,
db: parseInt(redis.path.slice(1)),
password: redis.password,
},
};
console.log('Broker MQTT iniciando...');
const server = new mosca.Server(settings);
server.on('ready', () => {
// Configura callbacks de autenticação e autorização
server.authenticate = authorizer.authenticate;
server.authorizePublish = authorizer.authorizePublish;
server.authorizeSubscribe = authorizer.authorizeSubscribe;
console.log('Broker MQTT aguardando conexões na porta', settings.port);
});
server.on('published', (packet, client) => {
console.log(
'Message published on topic',
packet.topic,
':',
packet.payload.toString(),
);
});
O módulo authorizer
que foi incluído nesse arquivo possui a lógica de autenticação
e autorização dos clientes. Como autenticação não é o foco desse post, irei deixar
aqui apenas um template de como os métodos podem ser implementados:
module.exports.authenticate = function authenticate(
client,
username,
passwordBuffer,
callback,
) {
if (usuarioESenhaEstaoCorretos) {
// Você pode adicionar informações (ex.: permissões)
// sobre o cliente no objeto client, se quiser:
client.superuser = true;
callback(null, true);
}
else {
callback(null, false);
}
};
module.exports.authorizePublish = function authorizePublish(
client,
topic,
payload,
callback,
) {
// Você pode verificar dados adicionados
// no objeto cliente durante autenticação:
if (client.superuser) {
callback(null, true);
}
else {
callback(null, false);
}
};
module.exports.authorizeSubscribe = function authorizeSubscribe(
client,
topic,
callback,
) {
// Você pode verificar dados adicionados
// no objeto cliente durante autenticação:
if (client.superuser) {
callback(null, true);
}
else {
callback(null, false);
}
};
Agora, para executar o broker, basta rodá-lo como uma aplicação NodeJS comum (sem esquecer de passar a variável de ambiente com a URL do Redis):
$ REDIS_URL=redis://localhost:6379/0 node app.js
Mosca em um container Link to heading
Um Dockerfile bem simples para adicionar essa aplicação do Mosca para um container Docker seria assim:
FROM node:6-alpine
EXPOSE 1883
WORKDIR /app
ADD package.json /app/package.json
RUN apk add --update --no-cache --virtual .build-deps \
build-base zeromq-dev && \
npm install && \
apk del --purge .build-deps && \
rm -rf /var/cache/apk
ADD . /app
CMD node app.js
Mosca no Kubernetes Link to heading
Tendo a aplicação com o Mosca rodando dentro de um container, para publicar em um cluster do Kubernetes não é muito diferente do que publicar o Mosquitto. E, para escalonar o cluster, um Horizontal Pod Autoscaler deve resolver.