Minerva System

Logomarca do Sistema Minerva

Este documento trata da documentação em geral do Minerva System. O sistema é majoritariamente programado utilizando o método eXtreme Programming, portanto esta documentação não é exaustiva no sentido de uma especificação completa, posto que os requisitos do sistema estarão em constante mudança.

A modificação deste documento é enormemente encorajada, mas é mais importante a prototipação constante do sistema que a escrita da especificação propriamente dita.

Para mitigar os problemas que podem ocorrer mediante foco diminuído neste documento, recomenda-se um grande uso de testes unitários e também de documentação ad-hoc no código dos módulos do projeto, de preferência envolvendo as ferramentas padrão das linguagens aqui utilizadas.

O Minerva System NÃO POSSUI NENHUM INTUITO COMERCIAL, SENDO VEDADA SUA COMERCIALIZAÇÃO. Além disso, não há nenhuma garantia com relação à utilização desse sistema. Em outras palavras, não é garantido que ele funcione como esperado em produção.

TODO E QUALQUER RECURSO NESSE SISTEMA DEVE SER VISTO COMO MATERIAL DIDÁTICO, PRODUZIDO COM O MERO INTUITO DE APRENDIZAGEM A RESPEITO DE DEVOPS, MICROSSERVIÇOS, ENGENHARIA E ARQUITETURA DE SOFTWARE.

O código do sistema é aberto e distribuído sob uma licença de Software Livre, mais especificamente a GNU General Public License, versão 3 (GPLv3). Para detalhes sobre a licença, consulte o arquivo LICENSE no repositório

Changelog

Todas as mudanças notáveis neste projeto serão documentadas nesse arquivo.

O formato é baseado em Keep a Changelog, e este projeto adere ao Versionamento Semântico.

Não-lançado

Relação de versões de microsserviços:

  • REST - v0.3.2
  • RUNONCE - v0.3.0
  • USER - v0.3.1
  • SESSION - v0.1.3
  • DISPATCH- v0.1.1
  • Front-End - v0.1.1 (pré-alfa)

Adicionado

  • Geração de imagens: Reabilitado target de compilação para ARM64.
  • Geração de imagens: Adicionado script que gera uma working tree limpa ao gerar recipe do Cargo Chef.
  • RUNONCE: Estruturas para preparação de message broker (virtual hosts e filas fixas).
  • DISPATCH: Criação do microsserviço.
  • Devcontainers do VSCode: Adicionada configuração inicial (ainda instável).
  • Kubernetes: Exposição dos serviços e ferramentas REST, Grafana e PgAdmin4 nas rotas /api, /grafana e /pgadmin, respectivamente, através do uso de Ingresses.
  • Kubernetes: Adicionados serviços Prometheus e Grafana, com alguns dashboards padronizados.
  • Minerva9: Adicionada documentação para o MVC e repositório do projeto.
  • Kubernetes: Adição de configurações para deploy em ambientes IoT.
  • DATA: Adicionado DTO fixo para retorno de dados de sessão durante login.
  • REST: API agora possui documentação e ferramentas para teste através de Swagger e RapiDoc (respectivamente através das rotas /swagger e /rapidoc).
  • Sonar: Adicionados arquivos de configuração do SonarQube para deploy em Kubernetes.
  • Testes: Adicionada configuração (projeto e pipeline de testes) para deploy dos dados de testes e coberturas para o Sonar, via Tailscale.
  • Testes: Adicionada documentação individual sobre testes, principalmente testes de carga.
  • Sonar: Adicionada documentação sobre Quality Gates do projeto.

Modificado

  • Rust: Define versão 1.65.0 para todo o projeto.
  • RUNONCE: Spinlocks de aguardo de disponibilidade de serviços agora realizam sleep assíncrono de dois segundos após cada falha.
  • RUNONCE: Spinlocks de disponibilidade agora também operam de forma assíncrona.
  • Kubernetes: Ajustes nos Ingresses existentes para que funcionem adequadamente, através de Traefik.
  • Kubernetes: Ajustes nas configurações de deploy para que haja menos arquivos.
  • Kubernetes: Ajustes nos limites de réplicas e de recursos requisitados para alguns serviços.
  • MongoDB: Downgrade para versão 4.
  • Tonic atualizado para v0.8.2.
  • Prost atualizado para v0.11.
  • CI/CD: Adicionada dependência do compilador de Protocol Buffers (protoc), no build via Docker e no ambiente de testes do GitHub Actions.
  • Compose/Swarm: Exposição das portas do PostgreSQL e do MongoDB para acesso remoto.
  • Compose/Swarm: Removidas as ferramentas Mongo Express, Redis Commander e pgAdmin4, já que essas configurações são pensadas primariamente como debug. Para monitorar e inspecionar o MongoDB, o Redis e o PostgreSQL, veja ferramentas ad-hoc como MongoDB Compass, RESP.app ou DBeaver, respectivamente.
  • PostgreSQL: Atualizado para versão 15.
  • Documentação: Adicionados diagramas separados para cada deployment no Kubernetes, bem como um diagrama geral da arquitetura do mesmo.
  • REST: Requisições agora exigem token através de Bearer Token.
  • REST: Requisições agora demandam tenant no início das rotas.
  • REST: Todas as rotas agora possuem tipos de resposta bem-definidos, possibilitando extração de schema para OpenAPI.
  • REST: Alteradas as variáveis de ambiente designando profile e nível de log do Rocket nos vários tipos de deploy (local, Compose, Swarm, K8s)

Consertado

  • USER: Ao remover um usuário, envia mensagem requisitando remoção das sessões do mesmo (cache e coleção de sessões).
  • USER: Caso um usuário falhe em ser criado (ao final da inserção), será considerado como se já existisse.
  • REST (K8s): ConfigMap próprio estava sendo ignorado e agora é utilizado.
  • REST: A API agora é capaz de lidar com a exposição de suas próprias rotas sob um endpoint específico (como /api, por exemplo; isso também funciona para a especificação OpenAPI e para Swagger e Rapidoc).

Removido

  • Removidos projetos Rust de módulos ainda não-iniciados, que causavam lentidão desnecessária na compilação.
  • REST: Removidos exemplos de requisições na documentação das rotas (prefira a documentação via Postman ou use Swagger ou RapiDoc).
  • REST: Removidas cores no texto do console durante deploy em k8s.

Segurança

  • Chaves dos dados de sessão armazenados no Redis agora são codificados usando Base64 para reduzir legibilidade.

Problemas conhecidos

  • A ferramenta Redis Commander conhecidamente funciona apenas em arquitetura AMD64, o que inviabiliza seu deploy no Kubernetes em ambientes ARM. Isso significa que clusters com o Minerva que sejam totalmente configurados em arquitetura ARM perderão alguma observabilidade quanto ao Redis. Adicionalmente, essa limitação também foi refletida na configuração da ferramenta via k8s.
  • As configurações atuais para os Ingresses estão muito relacionadas ao que é necessário para realizar deploy em K3s, com Traefik sendo utilizado como backend para Ingresses. Isso inviabiliza um pouco o uso de Minikube e Microk8s.
  • Builds de imagens Docker com Rust quebram o Qemu com uso excessivo de memória, caso a variável de ambiente CARGO_NET_GIT_FETCH_WITH_CLI não esteja definida como true.

v2 - 2022-07-05

Relação de versões de microsserviços:

  • USER - v0.2.2
  • SESSION - v0.1.2
  • RUNONCE - v0.2.1
  • REST - v0.2.2
  • Front-End - v0.1.1 (pré-alfa)

Adicionado

  • SESSION: Alteração do serviço para abrigar uso de cache via Redis;
  • REST: Catchers para tipos de retorno comuns e retorno genérico padrão;
  • Documentação: Adição de diagramas iniciais de caso de uso e sequência;
  • Projeto: Adição de CHANGELOG e regras de versionamento semântico.

Modificado

  • USER: Alteração do nome do serviço de USERS para USER, evitando maiores enganos;
  • Geração de Imagens: Dockerfile para gerar imagens agora foi unificado, incluindo compilação do frontend também no script, e agora utiliza BuildKit por padrão;
  • Geração de Imagens: Imagens Docker agora são geradas usando Alpine Linux como base, reduzindo tamanho e footprint em deploys no Compose/Swarm/K8s.

Consertado

  • REST: Erros na conexão com um microsserviço agora retornam um erro 503 (Recurso Indisponível);
  • Documentação: Problema na exportação de diagramas usando PlantUML no Github Pages.

Removido

  • Documentação: Removido o Dockerfile específico para PgAdmin4. A partir de agora, será usada a imagem oficial do PgAdmin4, e o arquivo de configuração será montado como necessário (via arquivos de configuração do Docker Compose e do Docker Stack, ou via ConfigMap no K8s).

Segurança

  • Geral: Removida a dependência da crate rustc-serialize na configuração da crate chrono (em confirmidade com alerta Dependabot), para todos os módulos.

Problemas conhecidos

  • Geração de Imagens: O target para ARM64 na criação das imagens Docker foi desabilitado até que seja corrigido um bug no BuildKit que faz com que o Qemu consuma RAM arbitrariamente ao realizar compilação via emulação de hardware.

v1 - 2022-06-17

Relação de versões de microsserviços:

  • USERS - v0.2.1
  • SESSION - v0.1.1
  • RUNONCE - v0.2.0
  • REST - v0.2.0
  • Front-End - v0.0.1 (pré-alfa)

Adicionado

  • Criação de schemas do banco de dados relacional (PostgreSQL 14);
  • Criação das coleções do banco de dados não-relacional (MongoDB 5);
  • Adição de protocol buffers;
  • Adição do microsserviço gRPC USERS;
  • Adição do microsserviço gRPC SESSION;
  • Adição da base para alguns outros microsserviços;
  • Adição da documentação básica;
  • Adição das bibliotecas DATA e RPC;
  • Adição do microsserviço gRPC REST (Rocket v0.5.0-rc.2);
  • Adição de rotas de autenticação e CRUD de usuários;
  • Adição de pooling de conexões com o banco de dados não-relacional;
  • Adição de logs para operações de CRUD de usuários e de sessão;
  • Adição de configuração de teste para Docker Compose;
  • Adição de configuração de deploy para Docker Swarm;
  • Adição de configuração de deploy para Kubernetes;
  • Adição de conceito básico de Front-End (com Flutter 3.0);
  • Adição de automatização de testes;
  • Adição de geração de documentação (mdBook, cargo doc, flutter doc) via GitHub Pages.

Estrutura geral do projeto

O Minerva System é uma aplicação pensada com finalidade de estudo, mas que não deixa de ser um sistema real e com um público alvo. Sendo assim, trata-se de um sistema gerencial, sem finalidade comercial.

O projeto envolve um licenciamento de Software Livre, e busca implementar uma estrutura de microsserviços. Em outras palavras, o sistema opera através da modificação de uma única base de dados, mas possui módulos separados para modificação de partes específicas.

Minerva também utiliza uma dicotomia front-end/back-end, de forma que a aplicação constitui-se de uma interface gráfica web, acessível via navegador, e uma intraestrutura constituída de um ponto de entrada que se comunica com os serviços específicos que a interface gráfica requisitar.

Segmentos

Como supracitado, Minerva constitui-se de microsserviços, especialmente em seu back-end. Para tanto, deve-se pensar em três grandes segmentos:

  • Front-End (aplicação web envolvendo interface gráfica);
  • Back-End (aplicação com um gateway REST, constituída de microsserviços gRPC);
  • Banco de Dados (relacional para gerenciamento de dados, não-relacional para gerenciamento de entidades temporárias, e sistemas de caching e mensageria).

Adicionalmente, existe uma maleabilidade que permite a confecção de outros tipos de Front-End que se comuniquem diretamente com o Back-End da aplicação, como por exemplo, através de programas nativos para Desktop e Mobile.

A imagem a seguir é uma supersimplificação da visão externa do sistema, sem sua complexidade intrínseca.

cluster_webserviceServiço Webcluster_internalInternofrontendFront-End (Web)backendBack-Endfrontend--backenddbBDbackend--dbdesktopFront-End (Desktop)desktop--backendmobileFront-End (Mobile)mobile--backend

Front-End

O Minerva System possui vários front-ends para algumas plataformas, para Web, console e até mesmo para o sistema operacional Plan 9.

Os capítulos a seguir falarão de tais interfaces.

Interface para Plan 9 from Bell Labs

Glenda, o mascote do Plan 9, em seu traje espacial

O Minerva System possui uma interface gráfica projetada especificamente para o sistema operacional Plan 9 from Bell Labs ou, mais especificamente, para um fork moderno e robusto desse sistema operacional, conhecido como 9front.

Minerva9

Apresentação

O front-end conhecido como Minerva9 é uma aplicação totalmente construída na linguagem C, mais especificamente no dialeto de C suportado no Plan 9 (parcialmente C99-compliant), que tenta levar em consideração as características desse sistema peculiar, especialmente no que tange à forma como o sistema lida com o protocolo HTTP.

Motivação

A existência dessa aplicação está relacionada principalmente ao fato de, sendo bem sincero, eu (Lucas) ser um entusiasta, utilizador e estudioso assíduo desse sistema e de suas tecnologias, que continuam atuais mesmo nos dias de hoje.

Este front-end possui algumas características especiais que evidenciam algumas funcionalidades do Plan 9, o que inclui:

  • Uso extensivo da biblioteca microui, uma biblioteca para construir aplicações gráficas no Plan 9, através da linguagem C;
  • Uso extensivo de webfs(4), como forma de realizar requisições REST à API do Minerva, com autenticação baseada em bearer token. Isso está em conformidade com a ideia de utilizar arquivos e sistemas de arquivos como recursos, em vez de importar novas bibliotecas, quando estas não são realmente necessárias (ou existentes);
  • Desenvolvimento rápido de uma interface simples, similar à interface para terminal minerva_tui (também discutida nessa documentação), que não pode ser compilada no Plan 9, ainda que o sistema suporte a linguagem Go;
  • Talvez uma prova-de-conceito para estudar a estrutura do sistema Plan 0, que possui algumas ideias interessantes, conceitos e utilitários que ainda podem ser utilizados em outros sistemas.

Documentação

Não.

Agradecimentos

Obrigado a sigrid por desenvolver a biblioteca microui de forma brilhante, e pelas demais pessoas que mantém o 9front e provam que Plan 9 ainda é muito legal.

Licenciamento

O Minerva9 é distribuído sob uma licença GPLv3, que pode ser consultada em seu repositório.

Interface para Terminal

O Minerva System possui também uma interface feita para rodar em terminal. Essa interface foi planejada principalmente para testes com a API, não sendo necessariamente planejada para a usabilidade de usuários leigos no uso do console.

Exemplo

O vídeo abaixo mostra um exemplo do uso da interface CLI para gerenciar o cadastro de usuários.

Download

O código da interface CLI encontra-se no GitHub. É possível baixar binários pré-compilados para algumas arquiteturas e sistemas operacionais, mais especificamente Windows (x86_64) e Linux (x86_64 e ARM64).

Os binários podem ser baixados na página de Releases.

Back-End

cluster_dbBANCO DE DADOS(multi-tenant)cluster_backendfrontendFRONT-ENDrestREST(API)frontend--restreportREPORTrest--reporttenancyTENANCYrest--tenancyuserUSERrest--usersessionSESSIONrest--sessionproductPRODUCTrest--productstockSTOCKrest--stockclientCLIENTrest--clientauditAUDITrest--auditcommCOMMrest--commdb2inq3db1inq2db3inq1runonceRUNONCErunonce--db1tenancy--db1user--db1session--db1product--db1stock--db1client--db1audit--db1
cluster_legendaLegendakeygRPC  BD  HTTP/REST  key2key:e--key2:wkey:e--key2:wkey:e--key2:w

O back-end Minerva compõe-se de microsserviços, com uma interface comum de comunicação externa que seja simples de usar para os padrões atuais.

O back-end compõe-se dos seguintes componentes:

  1. Componente de API: um serviço composto de rotas HTTP, sendo portanto uma API REST. Este serviço requisita dados sob demanda a cada serviço, dependendo do recurso que foi requisitado por via externa. É efetivamente o intermediário entre Minerva e o mundo externo. As requisições entre este serviço e os outros deverão ser feito através da abertura de uma requisição gRPC em que este módulo seja o cliente requisitante; as respostas recebidas via gRPC são então retornadas como resposta às requisições recebidas via REST, após tratamento para serialização como JSON.
  2. Componente de usuários: Servidor gRPC responsável por realizar o CRUD de usuários e por verificar as regras de negócio destas operações.
  3. Componente de sessão: Servidor gRPC responsável por realizar login, logoff, verificação de senha e gerenciamento de sessão de usuários.
  4. Componente de produtos: Servidor gRPC responsável por realizar o CRUD de produtos e por verificar as regras de negócio destas operações.
  5. Componente de estoque: Servidor gRPC responsável por realizar regras de negócios relacionadas a estoque de produtos (início, baixa, lançamento, etc).
  6. Componente de inquilinos: Servidor gRPC responsável por coordenar a criação ou remoção de novos inquilinos no sistema. Cada inquilino possuirá seu próprio conjunto de dados, e isso afetará diretamente na infraestrutura reservada para o mesmo (criação ou remoção de bancos de dados ou segmentos específicos em certos serviços).
  7. Componente de relatórios: Servidor gRPC responsável pela geração de relatórios humanamente legíveis, envolvendo portanto agregação de dados de acordo com o que for externamente requisitado.
  8. Componente de clientes: Servidor gRPC responsável por realizar o CRUD e a coordenação de dados de clientes do inquilino em questão.
  9. Componente de auditoria: Servidor gRPC responsável por gerenciar a consulta ao logs de auditoria do sistema.
  10. Componente de comunicação instantânea: Servidor gRPC para CRM através de comunicação via canais de mensagens instantâneas.

Os serviços gRPC supracitados tratam-se de servidores gRPC que podem receber conexões vindas do ponto de entrada REST ou mesmo entre si. Além disso, os serviços gRPC devem ser capazes de se comunicar com bancos de dados, que são recursos essenciais para os mesmos (exemplo: PostgreSQL, MongoDB, Redis). Estes serviços devem gravar log de suas operações, mais especificamente nas operações de inserção, atualização e exclusão.

A API REST sempre se comunica diretamente com os serviços gRPC, e os mesmos são encorajados a se comunicarem entre si quando for necessário estabelecer comunicação bloqueante entre os mesmos. Todavia, quando for necessário estabelecer comunicação não-bloqueante entre os microsserviços (leia-se, quando o retorno para os usuários for desnecessário), será feito o uso de mensageria com despacho automático, sem comunicação gRPC direta.

Bibliotecas

As bibliotecas planejadas para o sistema são:

  • minerva-rpc: Implementação de protocolos gRPC e de mensagens destes protocolo. Deve ser importado em todos os módulos, sendo essencial para a criação de clientes e servidores gRPC. Os modelos de comunicação implementados para si devem ser também convertidos para e a partir dos DTOs do módulo de dados.
  • minerva-data: Implementação de utilitários de comunicação com banco de dados (PostgreSQL) e objetos de transferência de dados (DTOs). Deve ser importado em todos os módulos, exceto na comunicação REST. Os DTOs também devem implementar traits e utilitários para conversão das mensagens implementadas em minerva-rpc para os DTOs desta biblioteca.
  • minerva-cache: Implementação de utilitários de comunicação com cache e armazenamento temporário in-memory (Redis). Deve ser importado principalmente no módulo de sessão.
  • minerva-broker: Implementação de utilitários de comunicação com message brokers (RabbitMQ). Deve ser importado em qualquer módulo que envolva processamento assíncrono, mas principalmente no módulo de despacho de mensagens.

Módulos

Os módulos planejados para o sistema são:

  • minerva-tenancy: Servidor gRPC para CRUD de inquilinos. Deve ser capaz de gerenciar inquilinos, mas um inquilino não pode ser deletado através desse serviço, apenas desativado. Apenas administradores do sistema podem ter acesso.
  • minerva-user: Servidor gRPC para CRUD de usuários. Deve ser capaz de manipular as regras de negócios relacionadas a clientes.
  • minerva-session: Servidor gRPC para gerência de sessão de usuário.
  • minerva-product: Servidor gRPC para CRUD de produtos. Deve ser capaz de manipular as regras de negócios relacionadas a produtos, mas que não envolvam controle de estoque.
  • minerva-stock: Servidor gRPC para CRUD de estoque de produtos. Deve ser capaz de manipular as regras de negócios relacionadas a estoque, mas que não envolvam manipulação de produtos.
  • minerva-rest: Servidor REST para comunicação com os demais módulos executáveis. Possui rotas que apontam para serviços específicos, e é por definição um cliente gRPC de todos os servidores gRPC.
  • minerva-runonce: Serviço avulso para configuração do ambiente, de forma assíncrona. Responsável pela execução de migrações do banco de dados e outras operações de configuração inicial.
  • minerva-report: Servidor gRPC para geração de relatórios. Deve receber dados com formatação esperada de um relatório, e então deverá gerar um arquivo PDF e retorná-lo inteiramente como resposta.
  • minerva-client: Servidor gRPC para CRUD de clientes. Deve ser capaz de manipular as regras de negócios relacionadas a clientes.
  • minerva-audit: Servidor gRPC para gerenciamento de logs de auditoria. Possibilita a consulta aos logs de auditoria do sistema.
  • minerva-comm: Servidor gRPC para comunicação externa com clientes via mensagens instantâneas.
  • minerva-dispatch: Serviço de escuta de message broker e despacho de mensagens para demais serviços.

Portas

Os serviços, independente de serem gRPC ou REST, devem ser executados em certas portas padrão para evitarem conflitos durante o tempo de depuração. Cada porta deve também ser configurável através de variáveis de ambiente.

A tabela a seguir discrimina as variáveis de ambiente e as portas padrão de acordo com o serviço em questão.

ServiçoVariávelValor
RESTROCKET_PORT9000
USERUSER_SERVICE_PORT9010
SESSIONSESSION_SERVICE_PORT9011

No caso do serviço REST, verifique o arquivo Rocket.toml para avaliar a configuração em desenvolvimento e em produção do mesmo.

Gateways

Os serviços também podem operar em máquinas diferentes, dependendo de sua rota.

Normalmente, quando todos os serviços são executados manualmente na mesma máquina, operamos com uma rota localhost. Nesse caso, a variável de ambiente de cada serviço é definida como esse valor.

Todavia, num ambiente de orquestração de contêineres (como Docker Compose ou Kubernetes), cada serviço estará operando de forma separada, e poderá comunicar-se com os outros serviços por intermédio de uma rede interna ao qual apenas os serviços têm acesso de forma explícita. Assim, as variáveis de ambiente que determinam o nome do servidor devem ser definidas manualmente, de acordo com a forma como o deploy de cada serviço foi realizado.

A seguir, temos uma tabela relacionando os sistemas com sas variáveis de ambiente. Os valores das variáveis serão definidos de acordo com o orquestrador de contêineres sendo utilizado.

No caso do serviço REST, verifique o arquivo Rocket.toml para avaliar a configuração em desenvolvimento e em produção do mesmo.

ServiçoVariável de ambiente
Banco de dados SQLDATABASE_SERVICE_SERVER
Banco de dados NoSQLMONGO_SERVICE_SERVER
Cache RedisREDIS_SERVICE_SERVER
Message Broker RabbitMQRABBITMQ_SERVICE_SERVER
----------------------------------------------------
RESTREST_SERVICE_SERVER
USERUSER_SERVICE_SERVER
SESSIONSESSION_SERVICE_SERVER

Documentação do Software

Este capítulo lista links para a documentação das partes pertinentes ao código do Minerva System.

Por padrão, a documentação é escrita em Inglês, e pode ser muito pertinente durante a implementação de novas partes do sistema.

Não se esqueça de consultar estes documentos com frequência.

API

Serviços externos

  • FRONTEND
    Front-End do Minerva System.
  • REST
    Gateway REST para acesso aos demais serviços.

Microsserviços

  • RUNONCE
    Utilitário de configuração inicial do sistema durante um deploy.
  • SESSION
    Serviço de gerenciamento de sessão de usuário.
  • USER
    Serviço de gerenciamento de usuários.
  • DISPATCH
    Serviço de consumo de filas do RabbitMQ e despacho de operações.
  • PRODUCT (não implementado)
    Serviço de gerenciamento de produtos.
  • REPORT (não implementado)
    Serviço de gerenciamento e emissão de relatórios.
  • STOCK (não implementado)
    Serviço de gerenciamento de estoques de produtos.
  • CLIENT (não implementado)
    Serviço de gerenciamento de clientes.
  • AUDIT (não implementado)
    Serviço de gerenciamento de logs de auditoria.
  • TENANCY (não implementado)
    Serviço de gerenciamento de inquilinos.
  • COMM (não implementado)
    Serviço de gerenciamento de comunicações via mensagem instantânea.

Bibliotecas

  • DATA
    Biblioteca de manipulação de DTOs e conversões de dados.
  • RPC
    Biblioteca de implementação de Protocol Buffers, mensagens gRPC e afins.
  • CACHE
    Biblioteca para uso e acesso ao cache via serviço Redis.
  • BROKER
    Biblioteca para uso, acesso e configuração do serviço RabbitMQ e mensageria.

Especificação do Projeto

Os capítulos a seguir tratarão de documentação ligada à especificação funcional e arquitetural do projeto.

Multi-Tenancy

O Minerva System é um sistema multi-tenant. Isso significa que é capaz de gerenciar bancos de dados diferentes dependendo do tenant (cliente do serviço) atual. No Sistema Minerva, isso é gerenciado de acordo com a forma como as requisições são recebidas.

Atualmente, o multi-tenancy é gerenciado de forma estática, através de um arquivo de configuração, mas em breve será gerenciado através do microsserviço TENANCY.

Configuração

Os tenants devem ser gerenciados através do arquivo tenancy.toml.

A seguir, um exemplo do conteúdo em potencial deste arquivo.

[[tenants]]
name = "Minerva System"
database = "minerva"
connections = 5

[[tenants]]
name = "Test Database"
database = "teste"
connections = 5

[[tenants]]
name = "Comercial Fulano S/A"
database = "comercial-fulano"
connections = 5

Criação dos bancos de dados

O serviço RUNONCE deverá executar a criação dos bancos de dados, caso não seja possível conectar-se aos mesmos. Isso deve ser feito sobretudo através da leitura do arquivo tenancy.toml, encontrado na pasta de execução do projeto.

Caso um novo tenant seja adicionado ao sistema, o serviço RUNONCE deverá ser forçadamente executado para que o sistema fique apto a utilizar o banco de dados para aquele tenant.

O sistema RUNONCE deverá, para cada tenant listado em tenancy.toml:

  1. Tentar conectar-se aos bancos em questão. Se isso não for possível, deverá criá-los;
  2. Executar as migrations (no BD relacional) para aquele tenant;
  3. Criar as coleções e índices (no BD não-relacional) para aquele tenant;
  4. Criar o usuário admin (no BD relacional) para aquele tenant;
  5. Criar as devidas filas de despacho de mensagens (no message broker) para aquele tenant.

Banco de Dados Relacional

PostgreSQL

As próximas seções dizem respeito a partes relacionadas à estrutura do banco de dados relacional (PostgreSQL) e a como modificá-la.

Executando migrations

As migrations são uma parte vital do Sistema Minerva, não apenas porque definem as tabelas do banco de um tenant, mas porque também definem os schemas para a programação dos módulos.

Pré-requisitos

  • Rust (compilador rustc e gerenciador de pacotes cargo, versão 1.60.0 ou superior);
  • Diesel (versão 1.4.1 ou superior, com suporte a PostgreSQL);
  • diesel_cli com suporte a PostgreSQL;
  • Docker versão 20.10 ou superior.

Para instalar o diesel_cli apenas com suporte a PostgreSQL, use o seguinte comando:

cargo install diesel_cli --no-default-feature --features postgres

Considerações importantes

Toda e qualquer migration deve ser criada no diretório do módulo minerva-runonce, especificamente porque este diretório possui também as configurações de acesso e de geração de schema em minerva-data/src/schema.rs.

Além disso, sempre execute todos os comandos abaixo no diretório do módulo minerva-runonce.

Configuração inicial

Para começar, crie um contêiner Docker para cada um dos bancos de dados:

./make_postgres_db.sh
./make_mongo_db.sh
./make_redis_db.sh

Isso criará contêineres executando PostgreSQL 15, MongoDB 4 e Redis 7.

Após a criação dos contêineres, o processo de preparação dos bancos de dados pode ser um pouco demorado. Acompanhe este processo observando os logs:

# Banco relacional
docker logs -f minerva-postgres

# Banco não-relacional
docker logs -f minerva-mongo

# Cache
docker logs -f minerva-redis

Em seguida, execute a operação inicial de criação de um banco de dados. Para tanto, vamos criar um banco chamado minerva e executar todas as migrations nele, logo de cara:

diesel setup --database-url="postgres://postgres:postgres@localhost/minerva"

Criando uma migration

Para criar uma migration, use um comando similar ao seguinte:

diesel migration generate <nome_da_migration>

Substitua <nome_da_migration> por um nome que faça sentido. Isso gerará uma nova migration no diretório migrations, que possuirá os arquivos up.sql e down.sql. Edite-os de acordo com o necessário.

Executando migrations

Para executar todas as migrations pendentes, execute o comando:

diesel migration run --database-url="postgres://postgres:postgres@localhost/minerva"

Isso também poderá reconstruir o arquivo minerva-data/src/schema.rs, a depender de mudanças no schema.

Para testar a última migration executada:

diesel migration redo --database-url="postgres://postgres:postgres@localhost/minerva"

Removendo banco de dados de teste

Para remover os bancos de dados de teste criados no Docker, use os comandos a seguir.

Estes comandos servem para, respectivamente, parar a execução dos contêineres e então excluí-los.

docker stop minerva-postgres minerva-mongo minerva-redis
docker rm minerva-postgres minerva-mongo minerva-redis

Banco de Dados Não-Relacional

MongoDB

As próximas seções dizem respeito a partes relacionadas à estrutura do banco de dados não-relacional (MongoDB) e a seu uso.

Coleções

Assim como no caso do banco de dados relacional, o banco de dados não-relacional (criado através do MongoDB) também trabalha com um sistema multi-tenant, sendo portanto representável como um banco de dados para cada cliente.

Ainda assim, para cada cliente, algumas coleções são essenciais de serem criadas e configuradas até mesmo antes do primeiro acesso.

Coleção session

A coleção session armazena documentos contendo dados de sessão de um usuário. A responsabilidade de armazenar dados dos usuários é do banco de dados relacional, assim como a responsabilidade de autenticá-los é do serviço SESSION. Esta coleção, todavia, armazena os dados de autenticação após a realização de um login válido.

Cada documento nesta coleção possui um tempo de expiração de uma semana, o que alinha-se com o tempo máximo de uma sessão do usuário ser, igualmente, uma semana. A gerência desse tempo de expiração se dá através de um campo creationDate no documento, que armazena um timestamp indicando a data de início daquela sessão. Caso o documento não possua esse campo, o MongoDB, por padrão, acaba não expirando-o.

A responsabilidade da definição e criação adequada do creationDate é do serviço SESSION.

Cache

Redis

A ferramenta Redis é extensamente usada no Minerva System para armazenamento de informações em cache. Este capítulo descreve algumas das situações e entidades para as quais o cache é feito, e as regras de negócio envolvidas.

Lembre-se de que o Redis é utilizado em ambientes onde o acesso é feito por aplicações seguras, o que significa que, por design, o mesmo deverá ser capaz de armazenar informações sensíveis sem maiores problemas. Sendo um banco de dados in-memory, deve-se esperar que sua reinicialização apague seus dados.

Cache de sessão

Uma sessão do usuário é armazenada no banco de dados não-relacional (MongoDB) na forma de um documento, na coleção session, com um prazo de expiração de uma semana.

Esse processo é feito durante o processo de login. Todavia, para evitar maior estresse no serviço SESSION e no banco de dados não-relacional, o documento do MongoDB é serializado para JSON, e armazenado como valor no Redis, com um tempo de vida de 24 horas. A chave dessa informação é gerada a partir das informações do tenant e do ID do objeto no MongoDB.

Quando uma sessão for removida, além de realizar a remoção no MongoDB, o sistema também fica a cargo de remover a sessão no banco de dados também.

Mensageria

RabbitMQ

O Minerva System utiliza o RabbitMQ para operações de mensageria. Essas operações fazem-se necessárias sobretudo quando há necessidade de operações assíncronas, especialmente as que não dependem diretamente de interação do usuário no sistema, ou que são intensivas em termos de recursos.

Situações para uso de mensageria

O uso de mensageria deverá ser realizado em operações que devem ser gerenciadas a partir de eventos, sobretudo quando a operação é intensiva e com eventual chance de timeout. Esses casos incluem, mas não se limitam a:

  • Processamento de dados em batch;
  • Operações mais demoradas (ex. geração de relatório);
  • Notificações do usuário (ex. conclusão de processos);
  • Operações mais simples, mas que não deverão bloquear o uso do resto do sistema (ex. uma operação decorrente de outra).

Atuais usos no sistema

A seguir, serão listados os atuais usos de mensageria no Minerva System.

Remoção de sessões ao remover usuário

Ao remover o usuário, para garantir que o mesmo não possa realizar mais nenhuma operação no sistema, é interessante realizar a remoção de suas sessões. Isso é feito de forma desvinculada ao processo de remoção do usuário em si, seguindo o seguinte fluxo:

  1. O administrador requisita a remoção de um usuário para o tenant atual.
  2. O usuário é removido pelo serviço USER, para o tenant atual.
  3. O serviço USER enfileira, na fila específica do tenant para isso, uma requisição de remoção de todas as sessões do usuário recentemente removido.
  4. O serviço DISPATCH escuta as mensagens na fila do tenant em questão, recebendo a mensagem de remoção de sessões.
  5. O serviço DISPATCH requisita todas as sessões do usuário removido que estiverem na coleção de sessões do tenant atual, no MongoDB.
  6. O serviço DISPATCH requisita ao serviço SESSION que cada uma dessas sessões seja removida.
  7. Para cada sessão, SESSION remove a sessão do MongoDB para o tenant atual, e remove a sessão do cache no serviço Redis, caso exista.
  8. O serviço DISPATCH dá a mensagem como consumida e retorna à operação de escuta para a próxima mensagem.

Coleta de Logs

Fluentd Elastic

O Minerva System possui um sistema de coleta de logs especificamente no contexto de um deploy para Kubernetes.

A coleta é feita para cada nó do Kubernetes, através de um DaemonSet, que criará um pod em cada nó do cluster. Cada pod terá acesso de leitura aos logs de todos os pods em execução no nó; periodicamente, cada pod do Fluentd enviará um log stash para o serviço do Elasticsearch. O usuário poderá então consultar logs, realizar queries e montar dashboards através do Kibana.

O diagrama a seguir é uma representação simplificada de como a coleta de logs opera.

Diagrama de coleta de logs

Também é possível utilizar o Grafana para realizar a criação de dashboards com dados do Elasticsearch, porém será necessário adicioná-lo como fonte de dados nas configurações do Grafana, pois isso não é feito por padrão.

Para maiores informações, veja como utilizar o ElasticSearch para verificar logs.

Diagramas de Arquitetura

A seguir, serão listados diagramas da arquitetura do sistema, ligados à disciplina de Engenharia de Software. Por questões de utilidade e por se tratarem de verdadeiros guias para implementação de novas funcionalidades, foi escolhido que essa documentação possuísse apenas diagramas de caso de uso e diagramas de sequência relacionados às regras de negócio de cada domínio da aplicação, o que traduz-se mais ou menos como sendo as funcionalidades individuais de cada microsserviço.

Diagramas de Casos de Uso

Os diagramas a seguir representam casos de uso para o sistema. Esses diagramas não têm a pretensão de serem completos, mas sim de ilustrar funcionalidades esperadas para o sistema, de forma visual.

Os casos de uso foram subdivididos em domínios, que poderão ilustrar os microsserviços envolvidos.

Sessão

Usuários

Inquilinos

Auditoria

Relatórios

Produtos

Estoque

Clientes

Comunicação Instantânea

Diagramas de sequência

As seções a seguir enumeram diagramas de sequência. Esses diagramas representam visualmente informações a respeito das operações realizadas para cada ação que a eles der título, e são de grande ajuda no momento do projeto do sistema.

Os diagramas a seguir foram gerados automaticamente através de PlantUML.

Diagramas de Sequência: Sessão

Os diagramas a seguir dizem respeito ao fluxo de gerenciamento da sessão de um usuário do sistema.

A gerência da sessão assume que os dados do usuário possam ou não serem encontrados no banco de dados. Dito isso, não é incumbência da sessão realizar operações CRUD como usuários, e sim com a entidade da sessão em si.

Login do usuário

Logoff do usuário

Diagramas de Sequência: Usuários

Os diagramas a seguir dizem respeito ao fluxo de gerenciamento de usuários no sistema.

A gerência de usuários não diz respeito à sessão em si.

Cadastro de usuários

Listagem de usuários

Consultar usuário

Alteração do cadastro de usuários

Remoção de usuários

Compilação

Este capítulo fala a respeito das formas de compilação do sistema.

Para compilar e executar a aplicação, vamos utilizar recursos da máquina (em caso de situações envolvendo desenvolvimento) ou teste de aplicação usando Docker.

A geração de imagens Docker é particularmente importante para a realização de testes e deploy posteriores, o que será abordado no próximo capítulo.

Compilando e executando com recursos da máquina

Você pode compilar os módulos do sistema individualmente e executá-los usando o próprio ambiente Rust.

Introdução

Este artigo trata da situação mais comum durante o desenvolvimento das partes do sistema, que envolve utilizá-las individualmente em um ambiente de desenvolvimento.

O deploy usando Docker Compose e Kubernetes, enquanto possível em ambiente de homologação e de testes manuais, utiliza muitos recursos da máquina, e não é o ideal de ser utilizado enquanto o programador estiver debugando a aplicação. Além disso, pela própria forma como o sistema foi planejado, é possível executar porções individuais do sistema em que haja interdependência entre elas.

Objetivo

Compilar todos os módulos ou módulos individuais é muito importante do ponto de vista do desenvolvimento. Neste artigo, tratamos de como isso pode ser feito na máquina local de um desenvolvedor.

Dependências

Você precisará de:

  • Rust (compilador rustc e gerenciador de pacotes cargo, versão 1.60.0 ou superior);
  • Diesel (versão 1.4.1 ou superior, com suporte a PostgreSQL);
  • Flutter (versão 3.0.0 ou superior, canal stable. Apenas necessário o target de compilação para web);
  • Dart (versão 2.17.0 ou superior, canal stable);
  • Docker (versão 2.10 ou superior).

O compilador Rust e o Docker são essenciais para compilar os módulos individuais do back-end do projeto, enquanto o Flutter é importante para a confecção do front-end da aplicação. Sendo assim, as dependências podem ser instaladas de acordo com o bom-senso do desenvolvedor.

O Diesel pode ser instalado através do gerenciador de pacotes cargo, e sua instalação pode ser consultada em seu site, linkado acima. Além disso, a linguagem Dart será instalada através do Flutter, de acordo com as instruções que podem ser encontradas no site do mesmo.

Estrutura do projeto

O repositório do projeto é um monorepo, isto é, engloba todas as partes do sistema inteiro. Por isso, as partes relacionadas a back-end estão dispostas em um Workspace, configurável através das próprias ferramentas do cargo e da linguagem Rust, enquanto o front-end encontra-se unicamente no diretório minerva_frontend, e não faz parte do Workspace em si.

Preparação do ambiente

A primeira parte a ser executada deverá ser a preparação do ambiente. Isso inclui a preparação de quaisquer serviços ou bancos de dados externos que possam ser importantes para a execução da aplicação.

No Sistema Minerva, o serviço RUNONCE é responsável por executar essas operações, sendo também o serviço que executa migrations no banco de dados, por exemplo.

Para tanto, precisaremos compilar este módulo específico antes de qualquer outro. Isso será melhor delineado na seção sobre compilação do back-end, mas realizaremos uma configuração rápida nesta seção.

Criando recursos locais com Docker

Para executar localmente, o sistema precisa que alguns serviços sejam instanciados antes de sua execução.

É importante lembrar que os métodos a seguir não são considerados seguros para persistência de dados. Por isso, use-os apenas com a finalidade de testes.

Usando recursos por meio externo (Kubernetes)

Caso você não queira executar os serviços essenciais mais pesados em termos de recursos (PostgreSQL, MongoDB, Redis e RabbitMQ), poderá reaproveitá-los caso tenha realizado deploy dos mesmos em Kubernetes. Para tanto, você poderá usar um script preparado que realiza esse processo. Veja que esse script assume que você possua a ferramenta kubectl com acesso padrão configurado para o cluster que seja seu target.

O script encontra-se excepcionalmente em helpers/port-forwards.sh, na raiz do projeto.

Banco de dados relacional

Como primeira dependência, recomenda-se criar o banco de dados relacional via Docker. Também seria possível instalar o PostgreSQL 15 na máquina local, mas o Docker provê a comodidade necessária para o BD.

Para subir o banco de dados relacional, execute o script make_postgres_db.sh no diretório minerva-runonce, ou execute:

docker run --name minerva-postgres \
       -e POSTGRES_USER=postgres \
       -e POSTGRES_PASSWORD=postgres \
       -p 5432:5432 \
       -d postgres:15-alpine

O contêiner poderá então ser gerenciado através do Docker, como um contêiner qualquer.

Banco de dados não-relacional

Recomenda-se também criar o banco de dados não-relacional via Docker. Igualmente, é possível instalar o MongoDB 4 por métodos convencionais.

Veja que o MongoDB é usado aqui em sua versão 4 especialmente por ser mais compatível com ambientes IoT, em especial K3s executando em um Raspberry Pi 4 Model B, que foi utilizado para testar o deploy em um cluster Kubernetes.

Para subir o banco de dados não-relacional, execute o script make_mongo_db.sh no diretório minerva-runonce, ou execute:

docker run --name minerva-mongo \
       -e MONGO_INITDB_ROOT_USERNAME=root \
       -e MONGO_INITDB_ROOT_PASSWORD=mongo \
       -p 27017:27017 \
       -d mongo:4

Como esperado, o contêiner pode ser gerenciado normalmente através do Docker.

Banco de dados in-memory (cache)

Para o uso do serviço de cache, recomenda-se usar diretamente o Redis através do Docker. Para tanto, execute o script make_redis_db.sh no diretório minerva-runonce, ou execute:

docker run --name minerva-redis \
       -p 6379:6379 \
       -d redis:7-alpine

Assim como antes, gerencie o contêiner criado usando o Docker.

Broker de mensagens (com gerenciador)

O message broker RabbitMQ deverá também ser executado através do Docker. Para a execução do mesmo, todavia, atente-se para o uso de memória elevado que essa ferramenta possui.

Você poderá executar o script make_rabbitmq.sh no diretório minerva_runonce, ou simplesmente executar:

docker run --name minerva-rabbitmq \
       -e RABBITMQ_DEFAULT_USER=rabbitmq \
       -e RABBITMQ_DEFAULT_PASS=minerva \
       -p 15672:15672 \
       -p 5672:5672 \
       -d rabbitmq:3-management-alpine

Executando configuração inicial (módulo RUNONCE)

A seguir, execute o módulo RUNONCE para preparar todos os bancos de dados de tenants, executar as migrations, criar o usuário admin em cada banco e outras operações em outros serviços também.

Você poderá executar o módulo diretamente a partir da raiz do projeto:

cargo run --bin minerva-runonce

Caso haja algum problema com o comando anterior (por exemplo, se o módulo não encontrar o diretório migrations), vá para o diretório do módulo e execute-o:

cd minerva-runonce
cargo run

Após a compilação do módulo RUNONCE, o mesmo aguardará o banco de dados estar pronto para receber as conexões e aplicará as migrations.

Compilação (Back-end)

Você poderá compilar todos os módulos do projeto de uma só vez, ou compilar apenas os módulos necessários.

Compilando todos os módulos

Para compilar todos os módulos, vá para a raiz do projeto e execute um comando de compilação para todo o workspace:

cargo build

De forma similar, você poderá compilar o projeto para produção através da flag --release:

cargo build --release

Compilando um módulo específico

Existem duas formas de compilar um módulo específico: a partir do workspace (diretório raiz do repositório) ou a partir do diretório do módulo específico.

Qualquer módulo pode ser compilado a partir do diretório raiz com um comando como o mostrado a seguir (substitua <módulo> pelo nome do diretório do módulo em questão):

cargo build --bin <módulo>

Isto compilará qualquer módulo que faça parte do workspace, exceto bibliotecas auxiliares (como minerva-rpc, minerva-data e minerva-cache) e o front-end (contido em minerva_frontend).

Da mesma forma, você também poderá ir ao diretório do módulo específico e compilá-lo diretamente; neste caso, a compilação também funcionará para bibliotecas auxiliares.

cd <módulo>
cargo build

De forma similar à compilação geral, ambos os comandos também admitem a flag --release para compilar os módulos para produção.

Execução

É possível executar diretamente um módulo qualquer através do cargo, o que também implica na sua compilação.

Para executar a partir do diretório do workspace (apenas para módulos que geram executáveis):

cargo run --bin <módulo>

Para executar a partir do diretório do módulo em questão:

cd <módulo>
cargo run

Da mesma forma, é possível compilar e executar os módulos no modo de produção através da flag --release.

Testes

Para executar testes unitários e integração, basta seguir um processo similar à execução dos módulos. Testes com binários compilados para produção podem ser igualmente controlados pela flag --release.

Para executar quaisquer testes, é necessário garantir que o banco de dados esteja acessível e adequadamente configurado.

# Para testar todos os módulos do workspace
cargo test

# Para testar apenas um módulo do workspace
cargo test --bin <módulo>

# Para testar apenas um módulo em seu diretório
cd <módulo>
cargo test

Compilação (Front-end)

O front-end é um módulo separado do restante dos módulos, sendo o sistema que envolve a interface gráfica do Sistema Minerva.

Executando o projeto via console

Para executar o projeto via console, basta usar a ferramenta de linha de comando do Flutter.

Preparando o Flutter

Antes de mais nada, garanta que o Flutter esteja configurado para compilar projetos Web:

flutter config --enable-web

Além disso, o Google Chrome deverá estar disponível para ser utilizado no debug. O estado do ambiente Flutter pode ser verificado com o comando flutter doctor.

Caso haja alguma inconsistência no seu ambiente, veja a seção de preparação do Flutter para Web na documentação oficial.

Executando o projeto

Para executar o projeto, vá até o diretório do módulo de front-end, baixe as dependências necessárias, e então execute o projeto no Google Chrome:

cd minerva_frontend
flutter pub get
flutter run -d chrome

Compilando para produção

Para compilar o projeto para produção, vá até a pasta do módulo e execute os comandos a seguir. Eles baixarão as dependências faltantes (caso já não tenham sido baixadas) e gerarão os arquivos estáticos da aplicação.

cd minerva_frontend
flutter pub get
flutter build web

Você poderá encontrar a versão compilada da aplicação front-end no diretório minerva_frontend/build/web.

Gerando imagens via Docker

Script para geração de imagens

Já existe um script separado para a geração das imagens (com tags apropriadas). Para gerá-las, vá até a raiz do repositório e execute o comando:

./generate_images.sh

Esse script foi especialmente feito para um console Bash, e pensado para execução no Linux. No entanto, caso você esteja no Windows, poderá executá-lo via Git Bash, MSYS2 ou similar, desde que seja possível utilizar o Docker através da linha de comando.

Gerando uma imagem em específico

Caso seja necessário, você poderá gerar uma imagem em específico de um projeto.

Para qualquer projeto, poderá executar o seguinte comando a partir da raiz do repositório:

docker buildx build \
	-f build/Dockerfile \
	--target minerva_<projeto> \
	-t seu_username/minerva_<projeto>:latest \
	.

Lembre-se de substituir <projeto> pelo projeto em questão.

Criando uma tag para a imagem

Todas as imagens são geradas automaticamente com tags de acordo com o projeto do qual está sendo gerado (arquivos Cargo.toml e pubspec.yaml).

Se você estiver gerando as imagens manualmente, poderá definir uma tag como no exemplo a seguir:

# Faça algo similar para cada uma das imagens
docker image tag \
	seu_username/minerva_user \
	seu_username/minerva_user:0.2.0

Nomes e tags das imagens geradas

As imagens geradas pelos passos anteriores são geradas com nomes específicos. Esses nomes serão muito úteis do ponto de vista do envio dessas imagens para o DockerHub e do deploy via Docker Compose, Docker Swarm e Kubernetes.

As imagens são sempre geradas com a tag latest, mas também receberão tags de acordo com seus arquivos de projeto (Cargo.toml e pubspec.yaml).

A seguir, temos uma tabela relacionando os serviços com os nomes e tags das imagens geradas. Veja que elas se relacionam, inclusive, com a forma como essas imagens encontram-se no DockerHub (sob o username luksamuk):

ServiçoNome e tag da imagem
frontendluksamuk/minerva_frontend:latest
restluksamuk/minerva_rest:latest
runonceluksamuk/minerva_runonce:latest
userluksamuk/minerva_user:latest
sessionluksamuk/minerva_session:latest
----------------------------------------------------
pgadmindpage/pgadmin4:latest (Não gerado)
postgresqlpostgres:15 (Não gerado)
mongodbmongo:4 (Não gerado)

Subindo imagens para o DockerHub

Para enviar uma imagem para o DockerHub, primeiro é necessário se certificar de que essa imagem possua uma tag adequada.

Em seguida, poderemos enviar todas as tags das imagens para o DockerHub.

docker image push -a luksamuk/minerva_frontend
docker image push -a luksamuk/minerva_rest
docker image push -a luksamuk/minerva_runonce
docker image push -a luksamuk/minerva_user
docker image push -a luksamuk/minerva_session

Geração de imagens cross-platform

O script generate_and_push.sh gera imagens cross-platform e de forma otimizada, sendo o script preerido a ser executado para enviar imagens para o DockerHub.

Use esse script se a intenção for compilar para mais arquiteturas ou gerar versão oficial, de forma otimizada.

SonarQube e Quality Gates

O projeto Minerva System atualmente está hospedado no GitHub e, por isso, utiliza as ferramentas de pipelines e CI/CD do mesmo para execução de testes e geração de builds.

Para garantir a qualidade no processo de entrega de cada pull request, foi adicionado suporte a SonarQube, principalmente através do arquivo sonar-project.properties que se encontra na raiz desse projeto.

O GitHub possui acesso ao SonarQube, que está em execução em infraestrutura própria que, por enquanto, trata-se de um servidor on-premise em um Raspberry Pi 4, mais especificamente executando K3s (uma implementação de Kubernetes). Este servidor também opera atualmente como o ambiente de testes do Minerva System, uma vez que o mesmo não possui um ambiente de QA ou produção.

Por isso, pelo menos por enquanto, o projeto não possui um portal de métricas explícito que possa ser acompanhado por desenvolvedores.

Para que um pull request passe nos quality gates do projeto, ele deverá garantir que o código possua as seguintes estatísticas:

EstatísticaValor para falha
Cobertura de testesMenor que 50%*
Linhas duplicadas (%)Maior que 3%*
Code SmellsMaior que 0
VulnerabilidadesMaior que 0

As estatísticas marcadas com * estão sujeitas a mudança no futuro. Idealmente, o código deverá ter uma cobertura de no mínimo 80% e a quantidade de linhas duplicadas poderá ser diminuída após mais análise.

Deploy

As sessões a seguir tratarão do deploy da aplicação, ou mais especificamente, de formas escaláveis de subir a aplicação num ambiente que simule produção.

Este capítulo trata de três tipos específicos de deploy:

  • Usando Docker Compose;
  • Usando Docker Swarm;
  • Usando Kubernetes, através do Minikube.

O Docker Compose poderá ser utilizado em situações de teste e desenvolvimento, especialmente porque seu arquivo de configuração sempre aponta para a imagem com tag latest de todos os contêineres (o que significa que utilizará as imagens que tiverem sido recém-geradas na máquina).

O Docker Swarm trabalha de forma similar ao Compose em sua configuração, porém utilizaremos a ferramenta docker stack, além de certa configuração manual, para subir um cluster com docker-machine, que pode também ser configurado de forma doméstica. A configuração poderá então ser usada para orquestração de contêineres.

Já o Kubernetes, utilizando uma máquina virtual KVM2 através do Minikube, possibilita uma orquestração de contêineres ainda mais flexível. Esta configuração é utilizada também para um ambiente local, mas será a forma mais próxima de colocar o sistema em produção.

Deploy usando Docker Compose

Docker Compose

Você pode realizar deploy do projeto usando Docker Compose. Todavia, esta não é a forma mais recomendada de realização de deploy.

Introdução

Docker Compose é uma ferramenta simples de orquestração de contêineres. Para o Minerva System, é principalmente uma forma de testar a forma como o serviço se comporta em rede.

Objetivo

O deploy usando Docker Compose é útil principalmente do ponto de vista da geração das imagens das aplicações dos microsserviços do Minerva System, mas também não é a forma mais recomendada de colocar o sistema em produção, porque não prevê fatores de escalabilidade como o deploy usando Kubernetes.

Utilize esta forma principalmente quando quiser avaliar o comportamento do sistema no que tange a interconexões entre os serviços numa rede virtual.

Dependências

Você precisará ter:

  • Docker versão 20.10 ou superior;
  • Docker Compose versão 2.2.3 ou superior;
  • As imagens do projeto (se não estiverem localmente disponíveis, serão baixadas).

Além disso, todos os comandos a seguir devem ser executados no diretório deploy/coompose deste projeto.

Preparando-se para a execução dos serviços

Primeiramente, você deverá se preparar para a geração de arquivos de log em cada serviço. Para tanto, no Linux, execute o script make_log_dir.sh. Esse script criará uma pasta log que será montada para o usuário com UID 1000, correspondente ao usuário appuser na maioria das imagens dos serviços do Minerva System.

Executando dependências

O Minerva System depende essencialmente de quatro serviços de terceiros:

  • PostgreSQL;
  • MongoDB;
  • Redis;
  • RabbitMQ.

É possível realizar a execução desses serviços através de algum provedor que os facilite, mas, para eventuais testes locais, você poderá usar uma configuração de Docker Compose específica ou utilizar diretamente esses serviços caso estejam hospedados em um cluster Kubernetes, através de port-forward.

Executando via Docker Compose

Para executar os serviços na máquina atual, você deverá navegar até a pasta services e iniciar o Docker Compose:

cd services
docker compose up

Os serviços estarão expostos nas seguintes portas:

PortaServiço
8484PgAdmin 4
8686Mongo Express
8787Redis Commander
5672RabbitMQ (Serviço)
15672RabbitMQ (Gerenciador)

O gerencialmento funcionará de forma similar aos serviços em si, portanto, para maiores informações sobre o uso do Compose, veja a seção Executando os serviços.

Usando Port Forward do Kubernetes

Caso você não queira executar os serviços essenciais mais pesados em termos de recursos (PostgreSQL, MongoDB, Redis e RabbitMQ), poderá reaproveitá-los caso tenha realizado deploy dos mesmos em Kubernetes. Para tanto, você poderá usar um script preparado que realiza esse processo. Veja que esse script assume que você possua a ferramenta kubectl com acesso padrão configurado para o cluster que seja seu target.

O script encontra-se excepcionalmente em helpers/port-forwards.sh, na raiz do projeto.

Executando os serviços

Para executar os serviços usando Docker Compose, use o seguinte comando:

docker compose up

Caso você queira desligar o funcionamento dos serviços da sessão atual do console, poderá executá-los em forma de daemon:

docker compose up -d

Neste caso em específico, para localhost, estarão abertas as seguintes portas para acesso aos serviços:

PortaServiço
9000API REST (endpoint /api)
9010USER
9011SESSION

Acompanhando logs

Para acompanhar os logs de um deploy via daemon ou de um outro console, você poderá realizá-lo através do comando:

docker compose logs -f

Caso seja necessário acompanhar os logs de apenas um serviço:

docker compose logs -f <servico>

Lembre-se de que o nome do serviço em questão deverá ser informado como listado em docker-compose.yml.

Reiniciando um único serviço

Você poderá reiniciar um único serviço, caso tenha recompilado a imagem do mesmo, por exemplo.

Nesse caso, basta usar o seguinte comando:

docker compose up -d --no-deps <servico>

Caso você queira incluir o passo de recompilação da imagem:

docker compose up -d --no-deps --build <servico>

Encerrando os serviços

Para encerrar imediatamente o serviço, execute o seguinte comando:

docker compose down

Caso você queira também remover os volumes associados aos serviços (por exemplo, nocaso do PostgreSQL e do pgAdmin), use este comando em vez do anterior:

docker compose -v down

Deploy via Docker Swarm + Vagrant

Docker Swarm + Vagrant

Além do deploy via Docker Compose, também é possível disponibilizar a stack do Sistema Minerva em um cluster do Docker Swarm.

Para tanto, é necessário inicializar o cluster. Isso pode ser feito, por exemplo, com máquinas virtuais para finalidade de teste (neste caso, pode ser utilizado o VirtualBox para prover essa facilidade).

Nesse capítulo, veremos como fazer isso de forma automatizada através de uma configuração do Vagrant. Essa configuração criará máquinas virtuais e também inicializará o sistema no cluster.

Pré-requisitos

  • Vagrant versão 2.2.19 ou superior;
  • VirtualBox versão 6.1 ou superior.

Você pode utilizar outro provider além de VirtualBox (como libvirt), mas precisará alterar o arquivo Vagrantfile.

NOTA: Qualquer comando do Vagrant deve ser executado no diretório deploy/swarm, para que o Vagrant tenha acesso ao Vagrantfile.

Reinicializando o cluster

Caso você já tenha iniciado o cluster com Vagrant, basta ir até o diretório deploy/swarm e executar vagrant up. Isso reiniciará as máquinas virtuais, mas também executará o docker stack deploy novamente para o arquivo de configuração, o que forçará uma atualização em todos os serviços.

Criando o cluster

Para criar o cluster, vá até o diretório deploy/swarm e execute o Vagrant.

cd deploy/swarm
vagrant up

Isso utilizará o arquivo Vagrantfile para criar sete máquinas virtuais (dois managers e dois workers), e também realizará automaticamente o deploy do sistema Minerva usando o arquivo docker-stack.yml.

Alguns arquivos extras serão criados na pasta. Eles dizem respeito respectivamente ao IP do primeiro gerente e aos tokens de ingresso no cluster para gerentes e trabalhadores.

Em geral, a relação das máquinas virtuais do cluster será:

  • manager01: Manager, líder, inicializador original dos serviços;
  • manager02 e manager03: Managers adicionais;
  • worker01 a worker04: Workers.

Para verificar o formato do cluster e as informações acima, use o comando:

vagrant status

Fazendo deploy do Sistema Minerva

Caso você realize modificações no arquivo docker-stack.yml, poderá querer fazer deploy novamente dos serviços de forma manual.

Para tanto, entre em qualquer um dos managers via SSH. Por exemplo, para o primeiro manager:

vagrant ssh manager01

O diretório deploy/swarm fica montado dentro de todas as máquinas virtuais em /vagrant (que é mutável apenas durante a criação do cluster). Todavia, você ainda poderá modificar os arquivos no host e terá acesso a eles.

Para aplicar manualmente o arquivo docker-stack.yml:

# Em manager01
docker stack deploy --compose-file /vagrant/docker-stack.yml minerva

Gerenciando a stack

Podemos gerenciar a stack facilmente dentro de uma VM manager.

Para listar as stacks ativas:

# Em manager01
docker stack ls

Se quisermos observar os serviços de uma stack em específico:

# Em manager01
docker stack services minerva

Ou, em último caso, se quisermos remover uma stack:

# Em manager01
docker stack rm minerva

Acessando os serviços

Os serviços que podem ser acessados de forma externa estarão disponíveis normalmente assim como no Docker Compose, porém sob um IP diferente.

Por padrão, todos os managers possuem um IP começado com 172.20.20.1X, algo definido através do Vagrantfile. Os workers terão um IP iniciado com 172.20.20.10X. A variável X será sempre um número contado a partir de 1.

Seguindo essas regras, as VMs possuirão os seguintes IPs:

IPHostname
172.20.20.11manager01
172.20.20.12manager02
172.20.20.13manager03
172.20.20.101worker01
172.20.20.102worker02
172.20.20.103worker03

Para acessar os serviços, use qualquer IP do cluster. A descoberta do serviço será realizada através do routing mesh do Docker Swarm.

Abaixo, temos uma relação das portas utilizadas para cada um dos serviços disponíveis no cluster.

PortaServiço
80Front-End
9000API REST
8484PgAdmin 4
8585Visualizador do cluster
8686Mongo Express
8787Redis Commander
5672RabbitMQ (Serviço)
15672RabbitMQ (Gerenciador)

Encerrando o serviço

Para encerrar todas as máquinas virtuais sem perder o estado das mesmas, use:

vagrant suspend

Ou, se você desejar destruir o cluster completamente:

vagrant destroy -f

Deploy via Docker Swarm + Docker Machine

Docker Swarm

Outra forma de realizar deploy usando o Docker Swarm é através do Docker Machine, um utilitário capaz de criar máquinas virtuais com Docker já instalado a partir de uma imagem Linux especial.

O Docker Machine é uma ferramenta defasada, mas pode ser uma alternativa quando for do interesse do programador fazer um trabalho mais manual. Por ser o método utilizado antes do uso do Vagrant, esta página existe como referência ao uso da ferramenta.

Pré-requisitos

  • Docker Machine versão 0.16.2;
  • VirtualBox versão 6.1 ou superior.

Instalando o Docker Machine no Linux

Como o Docker Machine é uma ferramenta defasada, é necessário executar passos como os a seguir para instalar:

base=https://github.com/docker/machine/releases/download/v0.16.2
curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/tmp/docker-machine
sudo install /tmp/docker-machine /usr/local/bin/docker-machine

Para instalar os drivers de KVM2, caso você prefira utilizá-los:

base=https://github.com/praveenkumar/machine-kvm2-driver/releases/download/v0.11.0
curl -L $base/docker-machine-driver-kvm2 >/tmp/docker-machine-driver-kvm2
sudo install /tmp/docker-machine-driver-kvm2 /usr/local/bin/docker-machine-driver-kvm2

Reinicializando o cluster

Caso você já tenha construído um cluster e feito deploy via Docker Machine anteriormente, é bem provável que você não precise fazer a maioria do trabalho. Você poderá simplesmente reiniciar as máquinas virtuais:

docker-machine start `docker-machine ls "minerva-*" -q`

Existe a possibilidade de as máquinas virtuais não conseguirem se reconhecer por uma mudança de IP. Se isso ocorrer, reconfigure o cluster manualmente, gerando os tokens para cada máquina virtual e inserindo-as no cluster.

Criando o cluster

Se você ainda não tiver um cluster criado, poderá criar o cluster através da ferramenta Docker Machine. Comece criando uma máquina virtual chamada minerva-vm1, que será nosso inicializador do cluster.

docker-machine create -d virtualbox --swarm-master minerva-vm1

Iniciando o cluster

Vamos começar iniciando o Docker Swarm na primeira máquina virtual.

Para acessar o console de uma máquina virtual via SSH, use também o Docker Machine para isso.

docker-machine ssh minerva-vm1

Para iniciar o cluster, precisamos descobrir também o IP dessa máquina virtual. Você poderá ver o IP de uma máquina virtual em específico via Docker Machine também, em outro console:

docker-machine ip minerva-vm1

Voltando ao console da VM, vamos iniciar o Docker Swarm.

# Em minerva-vm1
docker swarm init --advertise-addr <IP>

Criando mais managers

Uma arquitetura básica de managers e workers do Swarm, para que o algoritmo de consenso RAFT opere como esperado, poderia envolver três managers e dois workers -- portanto, cinco máquinas virtuais.

Vamos criar mais duas máquinas virtuais que vão servir de managers (minerva-vm2 e minerva-vm3):

docker-machine create -d virtualbox --swarm-master minerva-vm2
docker-machine create -d virtualbox --swarm-master minerva-vm3

Para adicionar essas VMs no cluster, vamos obter o token de entrada no cluster para managers, que será um mero comando do console. Copiamos esse comando e colamos no console das duas máquinas virtuais recém-criadas.

# Em minerva-vm1
docker swarm join-token manager

# Em minerva-vm2 e minerva-vm3: Cole o comando
docker swarm join --token...

Criando workers

Criaremos mais duas máquinas virtuais com o Swarm preparado, mas dessa vez, vamos prepará-las para serem meros workers:

docker-machine create -d virtualbox --swarm minerva-vm4
docker-machine create -d virtualbox --swarm minerva-vm5

O princípio para adicionar workers no cluster é o mesmo, porém usaremos um comando diferente para gerar o token. Geramos esse comando, copiamos e colamos no console das VMs minerva-vm4 e minerva-vm5.

# Em minerva-vm1
docker swarm join-token worker

# Em minerva-vm4 e minerva-vm5: Cole o comando
docker swarm join --token...

Verificando a topologia do cluster

Vamos verificar a topologia do cluster. Podemos observar a atividade das máquinas virtuais diretamente através do Docker Machine:

docker-machine ls
NAME          ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER      ERRORS
minerva-vm1   -        virtualbox   Running   tcp://192.168.99.108:2376           v19.03.12
minerva-vm2   -        virtualbox   Running   tcp://192.168.99.109:2376           v19.03.12
minerva-vm3   -        virtualbox   Running   tcp://192.168.99.110:2376           v19.03.12
minerva-vm4   -        virtualbox   Running   tcp://192.168.99.111:2376           v19.03.12
minerva-vm5   -        virtualbox   Running   tcp://192.168.99.112:2376           v19.03.12

Para avaliarmos o cluster em si e a forma como os nós se conectam, poderemos ver a topologia dos nós diretamente dentro da primeira VM:

# Em minerva-vm1
docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
exgmsiju6pnrl01tt33n5guui *   minerva-vm1         Ready               Active              Leader              19.03.12
cpxtnhalvu9tat9ek4n1n0117     minerva-vm2         Ready               Active              Reachable           19.03.12
p2v7v8ac93wuhwhcdsjl00p8y     minerva-vm3         Ready               Active              Reachable           19.03.12
jihrf6wgm145xzr0pdb6tnrck     minerva-vm4         Ready               Active                                  19.03.12
b1wfgme22m14pmjceo8ktn1hj     minerva-vm5         Ready               Active                                  19.03.12

Outra opção interessante é acompanhar também os serviços e os contêineres criados:

# Em minerva-vm1
docker service ls
docker container ls

Fazendo backup do cluster

Caso você queira fazer backup da topologia do cluster, lembre-se de copiar o diretório /var/lib/docker/swarm em minerva-vm1.

# Em minerva-vm1
sudo cp -r /var/lib/docker/swarm ./swarm
sudo chown -R $USER ./swarm

# No host
docker-machine scp -r minerva-vm1:/home/docker/swarm localhost:~/swarm-backup

Fazendo deploy do Sistema Minerva

Para fazer deploy do sistema, dado que o cluster esteja configurado, basta reutilizar o arquivo preparado para isso no repositório do Sistema Minerva.

Copiamos o arquivo para dentro da VM principal e então realizamos deploy:

docker-machine scp localhost:./deploy/swarm/docker-stack.yml minerva-vm1:/home/docker/docker-stack.yml

# Em minerva-vm1
docker stack deploy --compose-file docker-stack.yml minerva

Gerenciando a stack

Podemos gerenciar a stack facilmente dentro de uma VM manager.

Para listar as stacks ativas:

# Em minerva-vm1
docker stack ls

Se quisermos observar os serviços de uma stack em específico:

# Em minerva-vm1
docker stack services minerva

Ou, em último caso, se quisermos remover uma stack:

# Em minerva-vm1
docker stack rm minerva

Acessando os serviços

Para visualizar os serviços, primeiro visualize o IP das Docker Machines:

docker-machine ls

É possível usar o IP de qualquer Docker Machine, neste ponto. Basta utilizar as portas certas:

PortaServiço
80Front-end
9000API REST
8484PgAdmin 4
8585Visualizador do cluster
8686Mongo Express
8787Redis Commander
5672RabbitMQ (Serviço)
15672RabbitMQ (Gerenciador)

Encerrando o serviço

Caso você queira parar todas as máquinas virtuais, use o comando a seguir:

docker-machine stop `docker-machine ls "minerva-*" -q`

Ou, se você quiser remover realmente as máquinas virtuais:

docker-machine rm `docker-machine ls "minerva-*" -q`

Deploy usando Kubernetes

Kubernetes

Você pode realizar deploy do projeto usando Kubernetes. Nos passos a seguir, será mostrado como realizar deploy em um ambiente Kubernetes.

É importante salientar também que o deploy do Minerva System geralmente é feito em um ambiente K3s, o que pode impactar em algumas formas de configuração.

Introdução

Kubernetes é uma ferramenta sofisticada de orquestração de contêineres. O Minerva System é planejado para que seu deploy seja feito utilizando o Kubernetes.

Objetivo

O deploy usando Kubernetes é planejado desde o início do projeto, sendo uma das formas de estado da arte de deploy de aplicações web. Para simular este cenário, utilizamos uma instalação em um cluster local com K3s, em computadores cuja arquitetura seja x86_64 ou ARM64.

Dependências

As configurações de deploy são preparadas de forma a utilizar o próprio registry do DockerHub como fonte para as imagens previamente geradas. Assim, é necessário apenas ter acesso a um cluster com Kubernetes instalado, algo que pode ser simulado através da ferramenta K3s.

Para monitoramento e configuração, use as ferramentas a seguir:

  • Kubectl v 1.23.3 ou superior, de acordo com o compatível com seu cluster;
  • k9s versão 0.25.18 ou superior, para monitoramento (opcional);
  • kubernetes-el, pacote do editor de texto Emacs que permite monitorar um cluster Kubernetes.

A instalação local do K3s é opcional, e poderia ser substituída pelo Minikube, porém essa substituição pode ser impactante na configuração de alguns recursos, especialmente em Ingresses.

Realizando deploy de serviços

Nos passos a seguir, será mostrado como realizar deploy de cada um dos serviços e objetos k8s que fazem parte do Minerva System. Recomenda-se seguir os tópicos em ordem.

Os passos também assumem que a ferramenta kubectl esteja configurada localmente e que tenha acesso ao cluster.

Geralmente, o kubectl lê a configuração em ~/.kube/config por padrão; caso sua máquina não possua esse arquivo, verifique se consegue obter a configuração do Kubernetes para substituí-lo. No caso do K3s, esse arquivo está em /etc/rancher/k3s/k3s.yaml, e pode ser copiado, de forma paliativa, para uma outra máquina, desde que o host do cluster seja adequado na chave server.

Dependências iniciais

Para realizar o deploy do Minerva System, primeiramente precisaremos provisionar as dependências iniciais, software externos que são utilizados pelo resto do sistema.

Namespace do sistema

O namespace deve ser aplicado para que todos os objetos do sistema existam dentro do mesmo. Assim, temos uma forma de encapsular o sistema inteiro com a maior parte de suas dependências.

kubectl apply -f minerva-namespace.yml

PostgreSQL

O próximo passo é realizar o deploy do banco de dados relacional. O Minerva System usa PostgreSQL para tanto. A configuração envolve um Secret, um PersistentVolumeClaim, um Deployment, e um Service de tipo ClusterIP para garantir que o mesmo só possa ser acessado dentro do cluster.

kubectl apply -f postgresql.yml

MongoDB

Para banco de dados não-relacional, utilizaremos o MongoDB. O Minerva System usa o MongoDB principalmente para armazenamento de dados de seção do usuário. A configuração envolve um Secret, um PersistentVolumeClaim, um Deployment, e um Service de tipo ClusterIP, para que o MongoDB só possa ser acessado dentro do cluster.

kubectl apply -f mongodb.yml

Redis

Como serviço de cache, usamos um cluster Redis configurado manualmente. Esse cluster levanta um mínimo de duas instâncias do Redis, de forma que uma instância seja mestre e as demais sejam instâncias que operam como meras réplicas.

O Redis possui um ConfigMap que define um arquivo de configuração para o cluster. Além disso, temos um PersistentVolumeClaim, e o cluster definido através de um StatefulSet Temos também um Service de tipo ClusterIP para que ele possa ser acessado, e um HorizontalPodAutoscaler que adiciona ou remove réplicas sob demanda.

kubectl apply -f redis.yml

RabbitMQ

O Minerva System usa o RabbitMQ para serviços de mensageria.

O RabbitMQ é utilizado majoritariamente para abrigar mensagens de operações que possam ser despachadas de forma assíncrona, sem uma interferência direta do usuário, ou que sejam efeito colateral de outras operações no sistema.

Para provisionar o RabbitMQ, precisamos provisionar um operador de cluster para RabbitMQ, e então criar uma instância de cluster do RabbitMQ, que criará réplicas do serviço de forma eficiente.

Operador de cluster

Para realizarmos o deploy de um operador de cluster, utilizaremos o projeto RabbitMQ Cluster Operator. O arquivo para deploy do cluster está reproduzido no repositório; igualmente, sua licença pode ser encontrada em rabbitmq-cluster-operator.LICENSE.

kubectl apply -f rabbitmq-cluster-operator.yml
Instância do cluster

Agora, para criarmos uma instância do cluster RabbitMQ com três réplicas, podemos aplicar a configuração criada para o Minerva System. Isso criará um StatefulSet e um Service de tipo ClusterIP para o nosso cluster RabbitMQ.

kubectl apply -f broker-rabbitmqcluster

Configuração das dependências

Uma vez que as dependências iniciais estejam preparadas, poderemos configurar os bancos de dados, criar filas de mensagens e preparar tenants.

Configuração geral de Servers e Portas

Inicialmente, precisaremos criar dois ConfigMaps, que registram os hosts e portas dos serviços que serão criados. Essas configurações são compartilhadas pela maioria dos serviços.

kubectl apply -f servers-configmap.yml
kubectl apply -f ports-configmap.yml

Serviço de preparação

Isso pode ser feito através do módulo RUNONCE, composto de um ConfigMap, um Secret e um Job. Ao aplicar sua configuração, o Job será executado e preparará as dependências.

kubectl apply -f minerva-runonce.yml

Módulos do Minerva System

A partir desse momento, podemos começar o deploy dos módulos do Minerva System. Cada um dos módulos é basicamente uma aplicação stateless, sendo geralmente composto de um Deployment, um Service de tipo ClusterIP, e um HorizontalPodAutoscaler que criará ou destruirá réplicas dos Pods sob demanda. Alguns módulos também possuem o próprio ConfigMap para definir algumas variáveis de ambiente necessárias.

USER

O serviço gRPC de gerenciamento de usuários.

kubectl apply -f minerva-user.yml

SESSION

O serviço gRPC de gerenciamento de sessões de usuários.

kubectl apply -f minerva-session.yml

DISPATCH

O serviço de despacho de mensagens vindas do RabbitMQ.

Esse serviço não possui um Service, já que não pode ser acessado diretamente; seu único objetivo é despachar mensagens para outros serviços, de acordo com o que for requisitado através de mensagens via filas no RabbitMQ.

kubectl apply -f minerva-dispatch.yml

REST (Gateway)

O serviço REST funciona como um gateway para o back-end do Minerva System. Através desse serviço, a API poderá ser acessada, e as operações serão redirecionadas para outros serviços, de acordo com o domínio da regra de negócio em questão.

REST é o único módulo cujo Service possui um tipo diferente, sendo um tipo LoadBalancer, que fornece acesso direto à API através da porta 30000.

kubectl apply -f minerva-rest.yml

Utilitários de monitoramento

Com o deploy do sistema feito, poderemos também provisionar algumas ferramentas para ajudar no monitoramento do mesmo.

Prometheus

O Prometheus é um serviço que realiza scraping de informações expostas em endpoints de métricas. Através dele, é possível recuperar dados que podem ser posteriormente trabalhados para analisar a saúde de serviços ou até mesmo do cluster Kubernetes.

Configurando o k3s

Primeiramente, configure a sua instância do K3s, como orienta esse link; basta apenas, no seu nó master do cluster, criar o arquivo /etc/rancher/k3s/config.yaml com o conteúdo:

etcd-expose-metrics: true

Em seguida, reinicie o serviço do K3s.

Instalando o Kube-State-Metrics

O serviço kube-state-metrics provê algumas métricas coletadas através dos serviços no cluster Kubernetes. Voce pôderá instalar uma configuração padrão, como colocado neste link, clonando o repositório do projeto e aplicando essa configuração:

git clone https://github.com/kubernetes/kube-state-metrics
kubectl apply -f kube-state-metrics/examples/standard/
Provisionando o Prometheus

Como o Prometheus realiza scraping de dados nos serviços do Kubernetes e até mesmo em outros serviços, ele necessita de configurações de RBAC, o que implica na criação de objetos como ClusterRoles, ClusterRoleBindings e ServiceAccounts. Além disso, o Prometheus possui arquivos de configuração em um ConfigMap, um Deployment e um Service.

kubectl apply -f util/prometheus.yml

Mongo Express

O Mongo Express é uma ferramenta para explorar e editar o conteúdo do banco de dados não-relacional MongoDB. Esse serviço é exposto através de um Service de tipo NodePort, na porta 31085.

O Mongo Express do Minerva já vem configurado com configuração de acesso ao MongoDB. Caso sejam requisitadas credenciais para abrir o aplicativo, utilize o usuário mongo e a senha mongo.

ATENÇÃO: Caso seja necessário usar uma ferramenta mais específica, como MongoDB Compass, você poderá realizar port-forward do Deployment do MongoDB e conectar-se à porta via localhost.

kubectl apply -f util/mongoexpress.yml

PgAdmin4

O PgAdmin4 é uma ferramenta para explorar e editar o conteúdo do banco de dados relacional PostgreSQL. Esse serviço é exposto através de um Service de tipo NodePort, na porta 31084.

O PgAdmin4 do Minerva já vem configurado com dados de acesso ao PostgreSQL. Para efetuar login, use o usuário admin@admin.com e a senha admin. Para acessar o PostgreSQL, você precisará do usuário e senha padrão do mesmo também (postgres para ambos, por padrão).

ATENÇÃO: Caso seja necessário usar uma ferramenta mais específica, como DBeaver, você poderá realizar port-forward do Deployment do PostgreSQL e conectar-se à porta via localhost.

kubectl apply -f util/pgadmin.yml

Grafana

O Grafana é um serviço para criação e mostra de dashboards que operam a partir de métricas coletadas em serviços variados. No caso do Minerva, esses dados vêm principalmente do serviço Prometheus, previamente configurado.

Para o Grafana, provisionaremos alguns ConfigMaps com configuração inicial e alguns dashboards padrão para o cluster RabbitMQ. Como essa configuração inicial produz um arquivo relativamente grande, é necessário forçá-lo a ser processado de forma server-side.

As credenciais padrão do Grafana são o usuário admin e senha admin. Durante o primeiro login, você poderá alterar a senha inicial.

kubectl apply -f --server-side=true util/grafana-data.yml
kubectl apply -f util/grafana.yml

Para mais detalhes da configuração do Grafana, veja a página de Ferramentas para monitoramento externo.

Ingresses (via Traefik)

Os Ingresses são formas mais explícitas de acesso a alguns serviços do cluster, pois abrem rotas propriamente ditas que "camuflam" requisições nas mesmas e realizam forwarding para serviços específicos.

Os Ingresses atualmente preparados são compatíveis com a infraestrutura padrão do K3s, que utiliza Traefik como proxy reverso de Ingresses padrão.

Atualmente, os ingresses exportam as seguintes rotas:

  • /api, para a API do Minerva System;
  • /pgadmin, para o PgAdmin4;
  • /mongoexpress, para o Mongo Express;
  • /grafana, para o Grafana.
kubectl apply -f minerva-ingress.yml

Acessando serviços externamente

Pode ser necessário acessar, de forma externa, algum serviço que esteja no cluster, especialmente se o mesmo possuir um Service do tipo ClusterIP, que não permite acesso externo diretamente.

O Kubernetes provê algumas formas de realizar este acesso.

Acessando com Port Forward

Uma forma muito útil de acesso externo é o port forwarding, que realiza bind de uma porta da máquina local a uma porta específica de um pod específico. Também é possível referenciar um conjunto inteiro de réplicas através de um Deployment, ReplicaSet ou StatefulSet.

Para tanto, basta ter o kubectl instalado na sua máquina e a configuração correta de acesso ao cluster.

O exemplo a seguir realiza port forwarding do MongoDB para a máquina local, de forma que a porta 27017 do pod esteja acessível localmente através da porta 9050. Esse tipo de manobra será útil especialmente para acesso externo, por exemplo, au usar o MongoDB Compass.

kubectl port-forward deployment/mongodb-deployment 9050:27017

Acesso via NodePort

Esse tipo de acesso envolve acessar alguns Services que possuam um tipo NodePort ou LoadBalancer, estando expostos sob uma porta em qualquer IP do cluster. A tabela a seguir mostra uma relação dessas portas.

ServiçoPorta
API30000
PgAdmin 431084
Mongo Express31085
Redis Commander31086
Grafana31087

Monitoramento e testes

A seguir, estão listadas algumas configurações relacionadas a utilitários de monitoramento e testes do Minerva no cluster.

k9s

k9s

Uma das ferramentas possíveis de se utilizar para monitorar o cluster é o k9s.

A ferramenta utiliza uma edição modal, muito parecida com o editor Vim. Os comandos possuem um sistema de autocompletar e são também mostrados na tela. Alguns comandos interessantes de serem utilizados são:

  • :q: Sair da aplicação.
  • :po: Lista de Pods.
  • :svc: Lista de Services.
  • :dp: Lista de Deployments.
  • :ing: Lista de Ingresses.
  • :hpa: Lista de HorizontalPodAutoscalers.
  • :pvc: Lista de PersistentVolumeClaims.
  • :pv: Lista de PersistentVolumes.

Você poderá usar o k9s para visualizar logs e também para modificar algumas propriedades mais avançadas também. É possível até mesmo acessar diretamente o console dos contêineres.

kubernetes-el

kubernetes-el

O kubernetes-el é um pacote para o editor de texto Emacs que permite inspecionar um cluster Kubernetes.

Para mais informações, veja o site oficial do pacote.

Dashboard do Kubernetes

É possível acessar o Dashboard padrão do Kubernetes para realizar gerenciamento e monitoramento, porém o K3s exige configuração extra.

Para ter acesso ao Dashboard, siga as orientações no site oficial da Rancher.

Testes de Stress

Há alguns testes de stress para o Minerva System. Estes testes servem principalmente para testar os limites e o comportamento do sistema quando o mesmo estiver sob uma grande carga de operações.

Testes de spike com K6

Os testes padrão são realizados através da ferramenta K6, porém essa ferramenta demanda a extensão Faker para que funcione adequadamente. Você poderá encontrar as instruções de instalação dessa extensão no repositório da mesma.

No diretório deploy/tests/k6, você poderá executar o k6 compilado com a extensão referida. O host do cluster deverá ser informado através da variável de ambiente MINERVA_HOST. Note que o k6 assume que o host informado possua uma rota /api, então você deverá garantir que o Ingress do cluster esteja funcionando.

O exemplo a seguir mostra como executar os spike tests para stress de operações de sessão e de usuários.

# Testes de sessão
MINERVA_HOST=http://$IP_DO_CLUSTER k6 run session_spike.js

# Testes de usuários
MINERVA_HOST=http://$IP_DO_CLUSTER k6 run users_spike.js

A execução dos testes gerará um arquivo html ao final, que mostra um formato interativo para inspeção dos resultados de testes.

Testes de stress legados (via Bash)

Para realizar testes de stress com o formato legado, use o script deploy/tests/legacy/stress_test.sh.

Você poderá testar cada sistema crucial usando um comando como este:

./deploy/tests/legacy/stress_test.sh $IP_DO_CLUSTER/api user

Para maiores informações, execute o script sem argumentos e veja instruções rápidas de utilização.

Ferramentas para monitoramento externo

Esta seção demonstra algumas ferramentas que podem ser utilizadas para monitoramento dos serviços cujo deploy tenha sido realizado por meio de Kubernetes.

A maioria dos serviços a seguir pode ser acessado através de port-forward.

Swagger e RapiDoc

Swagger RapiDoc

A API do Minerva System possui uma especificação compliant com OpenAPI 3, sendo portanto testável a partir de vários serviços. A especificação OAS do Minerva System é sempre exportada através do arquivo openapi.json, pela própria API (acessível na raiz do subpath no qual a mesma é provisionada; no caso do k8s, como a API opera em /api, esse arquivo pode ser encontrado em /api/openapi.json).

Como forma padrão para testar requisições, a API do Minerva System provisiona também duas interfaces: Swagger e RapiDoc. Ambas são construídas pela API em si e não são criadas de forma externa à mesma. Além disso, ambas estarão acessíveis mesmo ao executar o Gateway da aplicação (microsserviço minerva-rest) localmente.

Usando o Swagger

O Swagger é a interface gráfica mais utilizada atualmente para testar uma API pública. Para acessá-la, abra seu navegador de internet e acesse a página pela rota /api/swagger.

Tela inicial do Swagger

Após utilizar a requisição de login, você poderá clicar no botão Authorize e, na tela a seguir, colar o token de autenticação.

Tela de autorização do Swagger

Após clicar no botão Authorize da tela acima, requisições subsequentes serão realizadas com um Bearer Token incluído no cabeçalho das mesmas.

Usando o RapiDoc

O RapiDoc é uma interface alternativa ao Swagger, sendo mais compacta e com algumas funcionalidades diferentes. Para acessá-la, abra seu navegador de internet e acesse a página pela rota /api/rapidoc.

Tela inicial do RapiDoc

De forma similar ao Swagger, após utilizar a requisição de login, você poderá colar o token de autenticação na caixa de texto api-token da seção AUTHENTICATION, e clicar no botão SET. Requisições subsequentes serão realizadas com um Bearer Token incluído no cabeçalho das mesmas.

Grafana

Grafana

A ferramenta Grafana é uma ferramenta de criação de dashboards para monitoramento. O Minerva System provisiona o Grafana pelo Kubernetes, e o alimenta com dados primariamente através do serviço Prometheus.

O Grafana não exige configuração extra para ser acessado, basta acessar o cluster através de um navegador, na rota /grafana, já que existe um Ingress exclusivo para este serviço. Ao realizar login, você poderá entrar na conta do administrador através do usuário padrão admin e senha inicial admin; após o login, você também poderá mudar a senha do admin, se quiser.

Ao acessar o Grafana, você será agraciado com uma tela inicial.

Página Inicial do Grafana

É possível, por fim, acessar alguns dashboards já provisionados através da configuração padrão do Minerva System, ou instalar algum outro dashboard, desde que este seja capaz de trabalhar com as métricas fornecidas por padrão.

Dashboard no Grafana

Por padrão, o Prometheus fornece para o Grafana métricas de:

  • Cluster (Kubernetes);
  • RabbitMQ.

Alguns dashboards úteis

Os dashboards a seguir são úteis para monitoramento do cluster Kubernetes através dos dados obtidos pelo Prometheus através do scraping das várias rotas de métricas do K8s, e podem ser importados diretamente pelo Grafana.

MongoDB Compass (para MongoDB)

MongoDB

O MongoDB Compass é uma ferramenta para desktop que permite inspecionar documentos no MongoDB.

Para utilizá-lo, primeiramente realize port-forward do MongoDB para sua porta padrão na máquina atual:

kubectl port-forward -n minerva deployment/mongodb-deployment 27017:27017

Em seguida, conecte-se à instância do MongoDB através do host localhost:27017, com usuários e senha padrão root e mongo.

MongoDB Compass

RESP.app (para Redis)

Redis

O RESP.app é uma ferramenta para desktop que permite inspecionar dados em cache em um serviço Redis ou Redis Cluster.

Para acessar o Redis através dele, realize primeiro o port-forward para a porta padrão do Redis na sua máquina:

kubectl port-forward -n minerva statefulset/redis 6379:6379

Em seguida, clique em Connect To Redis Server. No campo de URL, digite o mesmo texto do hint apresentado (redis://localhost:6379), e então clique em Import.

Na próxima janela, dê um nome para a conexão e clique em OK. Não é necessário segurança ou autenticação.

RESP.app

DBeaver Community Edition (para PostgreSQL)

PostgreSQL

O DBeaver é um cliente universal para banco de dados que permite inspecionar tabelas, schemas, e executar código SQL.

Para acessar o banco de dados PostgreSQL através dele, primeiramente exponha o banco do cluster localmente através de port-forward na porta padrão esperada:

kubectl port-forward -n minerva deployment/postgresql-deployment 5432:5432

Em seguida, abra o DBeaver e clique na opção para uma nova conexão, no menu Banco de dados (ou Database). Selecione a opção PostgreSQL, clique em Next, e instale quaisquer drivers necessários que venham a ser pedidos.

Configuração do DBeaver

Em seguida, você poderá manter o host e porta padrões (localhost:5432), mas deverá alterar o campo Database de acordo com o tenant que você deseja acessar.

Além disso, por padrão, use usuário postgres e senha postgres.

Configuração do DBeaver

Você poderá agora navegar nas abas à esquerda para localizar quaisquer tabalas que desejar, e poderá também fazer outras operações com o banco.

Configuração do DBeaver

RabbitMQ

RabbitMQ

O serviço de mensageria RabbitMQ já provê um painel de monitoramento e configuração em seu deployment. Esse painel não fica normalmente exposto pelo Minerva System, mas pode ser acessado através de port-forward.

O RabbitMQ expõe três portas:

  • 5672 (porta amqp, para conexões com filas);
  • 15672 (porta management, para gerenciamento da aplicação);
  • 15692 (porta vhost, para gerenciamento de hosts virtuais, em especial usado para multi-tenant).

Para acessar o painel do RabbitMQ, vamos expor localmente, através de port-forward, a porta management (15672). Mas caso seja necessário realizar alguma operação extra, você poderá expor as portas anteriores também.

kubectl port-forward -n minerva deployment/rabbitmq-deployment 15672:15672

Em seguida, acesse o painel do RabbitMQ pelo navegador, em http://localhost:15672. Para realizar login, use o usuário padrão rabbitmq e a senha minerva.

Painel do RabbitMQ

ElasticSearch

Elastic

A ferramenta ElasticSearch é uma ferramenta de busca e analytics através de documentos que podem se interrelacionar, como por exemplo, no caso de logs. O Minerva System provisiona o ElasticSearch pelo Kubernetes para indexação e armazenamento de logs. Tais dados são alimentados primariamente através do serviço Fluentd, que realiza scraping dos logs de cada pod.

Para realizar queries e analisar os dados armazenados no ElasticSearch, o Minerva System também provisiona o Kibana, uma interface gráfica própria que se integra com o ElasticSearch.

O Kibana não exige configuração extra e nem mesmo autenticação para ser acessado (note que isso poderá mudar no futuro). Para tanto, você poderá utilizar o Ingress exclusivo para o Kibana, que poderá ser acessado na rota /kibana do cluster.

Ao acessar o Kibana, você verá a seguinte tela inicial.

Página Inicial do Kibana

Em seguida, no menu lateral, vá até a sessão Analytics e selecione Discover.

Menu lateral do Kibana

Você verá a tela de gerenciamento e logs. Para adicionar os logs do cluster (enviados ao ElasticSearch via Fluentd através de log stashes), clique no botão de data views (canto superior esquerdo) e selecione a opção Create a data view, como mostra a seguir.

Menu de data view na tela Discover

Na janela que abre, dê o nome que quiser ao data view, e no campo Index pattern, escreva logstash-*, para indicar que você deseja consumir dados de todos os log stashes criados pelo Fluentd.

Tela de criação de um Data View

Após clicar em "Save data view to Kibana", você poderá realizar queries diretamente no data view informado. A tela a seguir exemplifica uma query para exibir logs dos microsserviços.

Criação de uma query no Kibana

Você poderá também adicionar novos campos à tabela, selecionando campos possíveis no menu de filtros que fica à esquerda.

Seleção de filtros no Discover
Discover com filtros selecionados

Diagramas de arquitetura do cluster

Esta seção contém diagramas relacionados à arquitetura do Minerva System, quando realizado o seu deploy através do uso de Kubernetes.

Nesta página, você poderá observar a arquitetura do sistema de forma geral. As próximas subseções mostram diagramas específicos demonstrando o deployment de cada um dos componentes necessários no Kubernetes.

Arquitetura do Minerva System

Serviço REST

Serviço SESSION

Serviço USER

Serviço DISPATCH

Serviço PostgreSQL

Serviço MongoDB

Serviço Redis

Serviço RabbitMQ

Serviço Prometheus

Serviço Fluentd

Serviço Elasticsearch

Serviço Grafana

Serviço Kibana

Testes

As seções a seguir descrevem ferramentas e práticas relacionadas a testes na aplicação. Estes testes podem estar relacionados a testes unitários, testes de integração e testes de carga e stress.

Testes unitários

Objetivo

Os testes unitários são criados de acordo com a entrega de cada demanda da aplicação. Eles devem ser feitos para testar unidades do projeto que venham a ser suficientemente pequenas para tal.

Execução

Os testes unitários são executados juntamente com os de integração ao usar o comando cargo test na raiz do projeto.

Testes de integração

Objetivo

Os testes de integração são criados de forma mais rara, por exigirem um pouco mais de esforço de implementação, pois geralmente demandam que um ou mais serviço esteja em execução para que a integração entre as partes do sistema seja testada.

Atualmente, apenas o módulo minerva-rest possui testes de integração. Estes testes geralmente iniciam outros microsserviços com os quais a aplicação se comunica.

Um padrão comum no Minerva System é garantir que os testes de integração sejam executados sequencialmente, e não paralelamente, para evitar concorrência entre os módulos.

Execução

Os testes de integração são executados juntamente com os unitários ao usar o comando cargo test na raiz do projeto.

Testes de Carga

Grafana k6

A aplicação possui também testes de carga para avaliar como o sistema age sob um grande volume de acessos e operações. Para isso, usamos a ferramenta Grafana k6.

Requisitos

Para executar os testes, você precisará do k6 instalado e também do Node v16.19.1.

Preparando os testes

Os testes encontram-se no diretório deploy/tests/k6/load-test. Navegue até o mesmo, e então instale as dependências:

npm install

Em seguida, você poderá, opcionalmente, definir algumas variáveis de ambiente para que o teste funcione, de acordo com a tabela a seguir:

VariávelDescriçãoValor Padrão
MINERVA_HOSTHost da API do Minervahttp://localhost:9000/api
MINERVA_TENANTInquilino a ser usado para testesteste

Executando os testes

Em seguida, execute os testes usado o comando:

npm run test