Logotipo do Mosca

Broker MQTT escalonável com Mosca + Redis

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 criação do 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

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');

let redis = url.parse(process.env.REDIS_URL);

let 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...');

let 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

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

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.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *