Você está na página 1de 98

LEONARDO C. S.

IACOVINI
LUIZ G. SANTOS
RAFAEL L. D. R. SANTOS
RAFAEL R. CORREIA

FORMICARIUM: AMBIENTE DE
DESENVOLVIMENTO EM TEMPO REAL PARA
MICROSSERVIÇOS

São Paulo
2018
LEONARDO C. S. IACOVINI
LUIZ G. SANTOS
RAFAEL L. D. R. SANTOS
RAFAEL R. CORREIA

FORMICARIUM: AMBIENTE DE
DESENVOLVIMENTO EM TEMPO REAL PARA
MICROSSERVIÇOS

Trabalho apresentado à Escola Politécnica


da Universidade de São Paulo para obtenção
do Tı́tulo de Engenheiro de Computação.

Área de Concentração:
Engenharia de Software

Orientador:
Prof. Dr. Jorge Luis Risco Becerra

Co-orientador:
Rafael de França Ferreira

São Paulo
2018
Prof. Dr. Jorge Luis Risco Becerra
AGRADECIMENTOS

Gostarı́amos de agradecer ao Nubank, e em especial a todo corpo de engenharia da


empresa, que deu suporte ao projeto e a sua experimentação, possibilitando o seu desen-
volvimento e testes de sua aplicabilidade.
Agradecimentos singelos ao professor Risco, por sempre nos lembrar da visão de bu-
siness perante um projeto de software.
Ray Kroc, por providenciar “comida que os clientes amam, dia após dia após dia. As
pessoas só querem mais.”
John Pemberton, pelo “valioso tônico cerebral” deixado de legado para as gerações.
“The ability to simplify means to elimi-
nate the unnecessary so that the neces-
sary may speak.”

-- Hans Hofmann
RESUMO

Em uma arquitetura distribuı́da de microsserviços, é comum enfrentar problemas


relacionados à produtividade enquanto o software é desenvolvido. Uma das causas deste
problema é a necessidade de se executar diversos microsserviços ao mesmo tempo para
iterar, testar e validar algum fluxo de negócio. Os recursos computacionais demandados
para tais tarefas em geral não estão disponı́veis na máquina do desenvolvedor.
Este contexto permitiu que uma parceria entre a Poli e o Nubank surgisse, buscando
mitigar esses problemas. O projeto foi co-orientado por um dos engenheiros mais prestigi-
ados do Nubank, responsável pela concepção de grande parte da arquitetura de software
adotada na empresa.
Usando conceitos de computação em nuvem, orquestração de contêineres, Distributed
Tracing e técnicas para sincronização de sistemas de arquivos, esta solução visa aumentar
a produtividade dos engenheiros do Nubank no cotidiano de interação com microsserviços.
Para tanto, foi feito um estudo profundo sobre PaaS, microsserviços, e o status quo
de desenvolvimento de software no Nubank.
O resultado foi um ambiente de desenvolvimento em tempo real para desenvolvimento
de microsserviços: Formicarium.
O projeto foi implantado em um squad do Nubank para teste e coleta de feedbacks,
e cumpriu os requisitos. Foi bem recebido pelos engenheiros, e uma maior difusão não é
uma tarefa complicada. Várias oportunidades de continuidade são possı́veis.
Palavras-Chave – Microsserviço, Docker, Kubernetes, Computação em Nuvem, Dis-
tributed Tracing.
ABSTRACT

It is very common to face productivity issues when developing software in a microservice-


based architecture. One of the reasons is the need to run multiple microservices at the
same time to iterate, test and validate some business flow. The computing resources
needed for such tasks are, more often than not, unavailable on the developer machine.
This context made possible for a partnership between Poli and Nubank to flourish,
looking to tackle these problems. The project was co-supervised by one of the most
prestigious Nubank’s engineers, responsible for conceiving a big share of the enterprise’s
software architecture.
Using Cloud Computing, container orchestration, Distributed Tracing and file sync
concepts, this solution aims to increase the productivity of Nubank Engineers in their
day-to-day interactions with microservices.
To come up with it, a deep study of PaaS, microservices and the company’s software
development status quo was done.
The result is a real-time enviroment for development of large scale applications based
on microservices: Formicarium.
It was introduced in a squad of the company for testing and gathering of feedback, and
it managed to fulfill the requirements. It was well-received among engineers, and further
diffusion is not a complicated task. Various opportunities of continuity are possible as
well.
Keywords – Microservice, Docker, Kubernetes, Cloud Computing, Distributed Tra-
cing.
LISTA DE FIGURAS

1 Diferenças entre VMs e Contêiners . . . . . . . . . . . . . . . . . . . . . . 23

2 Microsserviços vs Monolitos . . . . . . . . . . . . . . . . . . . . . . . . . . 27

3 Anatomia de um Distributed Tracing . . . . . . . . . . . . . . . . . . . . . 29

4 Componentes de um Distributed Tracing . . . . . . . . . . . . . . . . . . . 30

5 Exemplo de Flamegraph do Zipkin . . . . . . . . . . . . . . . . . . . . . . 31

6 Exemplo de Mapa de Topologia de Serviços . . . . . . . . . . . . . . . . . 31

7 Anatomia de um Devspace . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

8 Visão Geral do Formicarium . . . . . . . . . . . . . . . . . . . . . . . . . . 32

9 Anatomia do Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

10 Fluxo de trabalho em Git . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

11 Framework do Scrum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

12 Board do Formicarium no Clubhouse . . . . . . . . . . . . . . . . . . . . . 48

13 Visão Geral Técnica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

14 Processo de Entrega Contı́nua - Construção da imagem Docker . . . . . . . 51

15 Processo de Entrega Contı́nua - Implantação automatizada . . . . . . . . . 52

16 Contextos de informação e seu fluxo . . . . . . . . . . . . . . . . . . . . . . 52

17 Modelagem das Informações no Servidor . . . . . . . . . . . . . . . . . . . 53

18 Modelagem das Informações no Hive . . . . . . . . . . . . . . . . . . . . . 54

19 Modelagem de devspaces fornecida pelo frontend . . . . . . . . . . . . . . . 54

20 Interface de Linha de Comando da Heroku . . . . . . . . . . . . . . . . . . 55

21 Representação da Malha de Roteamento . . . . . . . . . . . . . . . . . . . 55

22 Infraestrutura de Add-ons da Heroku . . . . . . . . . . . . . . . . . . . . . 56

23 Roteamento de Requisições no Formicarium . . . . . . . . . . . . . . . . . 56


24 Desacoplamento Empresa e Formicarium . . . . . . . . . . . . . . . . . . . 57

25 Visão geral do Formicarium . . . . . . . . . . . . . . . . . . . . . . . . . . 58

26 Diagrama de Arquitetura Hexagonal . . . . . . . . . . . . . . . . . . . . . 59

27 Fluxo de Criação de Devspaces . . . . . . . . . . . . . . . . . . . . . . . . 62

28 Fluxo do Tanajura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

29 Visão geral da interação do Hive com os serviços . . . . . . . . . . . . . . . 69

30 Protocolo de comunicaçãp utilizado pelo Hive . . . . . . . . . . . . . . . . 72

31 Propagação de Spans em um Trace . . . . . . . . . . . . . . . . . . . . . . 72

32 Tela Lista de Devspaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

33 Tela Informações do Devspace . . . . . . . . . . . . . . . . . . . . . . . . . 79

34 Tela Serviços do Devspace . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

35 Tela Logs de um serviço . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

36 Tela Sincronização de arquivos em um serviço . . . . . . . . . . . . . . . . 82

37 Tela Detalhes de uma requisição . . . . . . . . . . . . . . . . . . . . . . . . 83

38 Tela Grafo de interações em Tracing . . . . . . . . . . . . . . . . . . . . . . 84

39 Exemplo de flamegraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

40 Modelagem de objetos Kubernetes para stream de logs . . . . . . . . . . . 97


LISTA DE TABELAS

1 Papéis de cada entidade ao longo do projeto. . . . . . . . . . . . . . . . . . 45

2 Princı́pios de Projeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

3 Comandos disponı́veis e uma breve descrição do que fazem . . . . . . . . . 76


LISTA DE ABREVIATURAS E SIGLAS

HTTP - HyperText Transfer Protocol


HTTPS - Hyper Text Transfer Protocol Secure
API - Application Programming Interface
IP - Internet Protocol
SO - Sistema Operacional
SPARC - Scalable Processor ARChitecture
CPU - Central Processing Unit
I/O - Input Output
LXN - LinuX Containers
KVM - Kernel-Base Virtual Machine
VM - Virtual Machine
NIST - National Institute of Standards and Technology
SaaS - Software as a Service
PaaS - Platform as a Service
IaaS - Infrastructure as a Service
DNS - Domain Name System
FTP - File Transfer Protocol
SSH - Secure Shell
JVM - Java Virtual Machine
AJAX - Asynchronous Javascript and XML
SMS - Short Message Service
EC2 - Elastic Compute Cloud
S3 - Simple Storage Service
DOM - Document Object Model
GC - Garbage Collector
RM-ODP - Reference Model of Open Distributed Processing
CLI - Command Line Interface
AWS - Amazon Web Services
REST - Representational State Transfer
SSL - Secure Socket Layer
URL - Universal Resource Location
NPM - Node Package Manager
REPL - Read Eval Print Loop
HTML - HyperText Markup Language
CSS - Cascading Style Sheets
CI/CD - Continuous Integration and Continuous Delivery
UI - User Interface
JSON - Javascript Object Notation
XML - Extensible Markup Language
YAML - YAML Ain’t Markup Language
TCP - Transmission Control Protocol
ACK - Acknowledgement
MVP - Minimum Viable Product
SUMÁRIO

1 Introdução 17

1.1 Motivação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

1.2 Objetivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

1.3 Justificativa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

1.4 Organização do Trabalho . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

2 Aspectos Conceituais 20

2.1 Ambiente de desenvolvimento . . . . . . . . . . . . . . . . . . . . . . . . . 20

2.2 Desenvolvimento em tempo real . . . . . . . . . . . . . . . . . . . . . . . . 20

2.3 Contêineres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

2.3.1 Breve história da tecnologia . . . . . . . . . . . . . . . . . . . . . . 21

2.3.2 Comparação com Máquinas virtuais . . . . . . . . . . . . . . . . . . 22

2.4 Cloud Computing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

2.4.1 Caracterı́sticas básicas de Cloud Computing . . . . . . . . . . . . . 24

2.4.2 Modelos para Cloud Computing . . . . . . . . . . . . . . . . . . . . 25

2.4.3 Modelos de serviços . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

2.5 Programação Funcional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

2.6 Microsserviços . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

2.7 Distributed Tracing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

2.8 Devspaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

3 Tecnologias Utilizadas 33

3.1 Protocolos de Comunicação . . . . . . . . . . . . . . . . . . . . . . . . . . 33

3.1.1 HTTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.1.2 Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

3.1.3 GraphQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

3.1.4 ZeroMQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

3.2 Linguagens de Programação . . . . . . . . . . . . . . . . . . . . . . . . . . 37

3.2.1 Clojure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

3.2.2 Javascript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

3.2.2.1 Typescript . . . . . . . . . . . . . . . . . . . . . . . . . . 38

3.2.2.2 Node.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

3.3 React . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

3.4 Docker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

3.4.1 Base tecnológica . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

3.4.2 Arquitetura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

3.4.3 Objetos Docker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

3.4.3.1 Imagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

3.4.3.2 Contêineres . . . . . . . . . . . . . . . . . . . . . . . . . . 40

3.5 Kubernetes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

4 Metodologia do Trabalho 43

4.1 Gerenciamento do projeto . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

4.1.1 Scrum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

4.1.2 Execução iterativa . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

4.2 Fases do projeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

4.2.1 Definição de Stakeholders e contexto do problema . . . . . . . . . . 45

4.2.2 Revisão da literatura e arquitetura do Nubank . . . . . . . . . . . . 45

4.2.3 Concepção do MVP . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

4.2.4 Definição da arquitetura da solução . . . . . . . . . . . . . . . . . . 46

4.2.5 Experimentações com FileSync . . . . . . . . . . . . . . . . . . . . 46


4.2.6 Implementação do módulo Devspaces e CLI . . . . . . . . . . . . . 46

4.2.7 Implementação do módulo FileSync . . . . . . . . . . . . . . . . . . 46

4.2.8 Implantação em um cluster do Nubank . . . . . . . . . . . . . . . . 47

4.2.9 Implementação Distributed Debugger e UI . . . . . . . . . . . . . . 47

4.2.10 Implantação em um squad . . . . . . . . . . . . . . . . . . . . . . . 47

4.2.11 Coleta de resultados . . . . . . . . . . . . . . . . . . . . . . . . . . 47

4.3 Utilitários . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

4.3.1 GitHub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

4.3.2 Clubhouse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

4.3.3 Slack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

5 Especificação de Projeto 49

5.1 Ponto de Vista da Empresa . . . . . . . . . . . . . . . . . . . . . . . . . . 49

5.2 Ponto de Vista da Informação . . . . . . . . . . . . . . . . . . . . . . . . . 50

5.2.1 Contexto da Empresa . . . . . . . . . . . . . . . . . . . . . . . . . . 50

5.2.2 Contexto do Formicarium . . . . . . . . . . . . . . . . . . . . . . . 50

5.2.2.1 Servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

5.2.2.2 Cliente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

5.3 Ponto de Vista da Computação . . . . . . . . . . . . . . . . . . . . . . . . 51

5.3.1 Arquitetura de Referência . . . . . . . . . . . . . . . . . . . . . . . 51

5.3.1.1 Plataforma . . . . . . . . . . . . . . . . . . . . . . . . . . 53

5.3.1.2 Mecanismo de Roteamento de Requisições . . . . . . . . . 53

5.3.1.3 Infraestrutura de Add-ons . . . . . . . . . . . . . . . . . . 54

5.3.2 Diferenças e Arquitetura do Projeto . . . . . . . . . . . . . . . . . . 54

5.3.2.1 Plataforma . . . . . . . . . . . . . . . . . . . . . . . . . . 55

5.3.2.2 Mecanismo de Roteamento de Requisições . . . . . . . . . 56

5.3.2.3 Desacoplamento Empresa e Formicarium . . . . . . . . . . 57


5.3.2.4 Infraestrutura de Depuração . . . . . . . . . . . . . . . . . 57

5.4 Ponto de Vista da Engenharia . . . . . . . . . . . . . . . . . . . . . . . . . 57

5.4.1 Arquitetura Hexagonal . . . . . . . . . . . . . . . . . . . . . . . . . 58

5.4.2 Fluxos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

5.5 Ponto de Vista da Tecnologia . . . . . . . . . . . . . . . . . . . . . . . . . 60

6 Implementação 61

6.1 Gerenciamento de Devspaces . . . . . . . . . . . . . . . . . . . . . . . . . . 61

6.1.1 Soil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

6.1.2 Criação de Devspaces . . . . . . . . . . . . . . . . . . . . . . . . . . 61

6.1.3 Deleção de Devspaces . . . . . . . . . . . . . . . . . . . . . . . . . . 62

6.1.4 Criação de um Serviço em um Devspace . . . . . . . . . . . . . . . 63

6.2 FileSync . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

6.2.1 Servidor web para Git (Tanajura) . . . . . . . . . . . . . . . . . . . 64

6.2.2 Gerenciador de Processos (Stinger) . . . . . . . . . . . . . . . . . . 66

6.3 Distributed Debugger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

6.3.1 Hive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

6.3.2 Eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

6.3.3 Protocolo de Comunicação . . . . . . . . . . . . . . . . . . . . . . . 70

6.3.4 Rastreamento e Correlação de Eventos (Tracing) . . . . . . . . . . 71

6.4 Frontend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

6.4.1 Organização do código . . . . . . . . . . . . . . . . . . . . . . . . . 73

6.4.2 Interface de linha de comando . . . . . . . . . . . . . . . . . . . . . 74

6.4.3 Aplicação Desktop . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

6.4.3.1 Gerenciamento de Devspaces . . . . . . . . . . . . . . . . 77

6.4.3.2 Infraestrutura do Devspace . . . . . . . . . . . . . . . . . 77

6.4.3.3 Serviços implantados . . . . . . . . . . . . . . . . . . . . . 77


6.4.3.4 Implantação de novos serviços . . . . . . . . . . . . . . . . 78

6.4.3.5 Distributed Debugger . . . . . . . . . . . . . . . . . . . . 79

6.4.3.6 Arquivos sincronizados . . . . . . . . . . . . . . . . . . . . 80

7 Testes e Avaliação 85

7.1 Testes Unitários . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

7.2 Testes Integrados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

7.3 Teste entre os integrantes . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

7.4 Teste com um engenheiro do Nubank . . . . . . . . . . . . . . . . . . . . . 86

7.5 Implantação em um squad . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

8 Considerações Finais 88

8.1 Conclusões do Projeto de Formatura . . . . . . . . . . . . . . . . . . . . . 88

8.2 Contribuições . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

8.3 Perspectivas de Continuidade . . . . . . . . . . . . . . . . . . . . . . . . . 89

Referências 93

Apêndice A 96
17

1 INTRODUÇÃO

1.1 Motivação

A motivação desse projeto vem de alguns fatores. O primeiro é o interesse e a familia-


ridade do grupo quanto ao desenvolvimento de microsserviços provenientes da experiência
adquirida nos estágios na Nubank, e o reconhecimento dos benefı́cios e dificuldades essa
arquitetura. Outro fator importante é a constatação de que, com o crescimento das
aplicações em nuvem computacional, a necessidade e urgência de uma arquitetura dis-
tribuı́da aumenta, e a carência de ferramentas para conter suas dificuldades se torna um
problema cada vez mais importante.

Com isso, foi possı́vel a identificação de um problema relativamente complexo de Enge-


nharia de Software que ainda não teve a devida atenção no mercado, e uma oportunidade
para aplicação e aprofundamento do nosso conhecimento de processo de desenvolvimento
de software. Nós querı́amos, então, criar uma proposta de solução para um problema real
ainda em discussão.

1.2 Objetivo

O objetivo deste trabalho é criar um ambiente de teste e desenvolvimento de software


para aumentar a produtividade e confiança do Engenheiro do Nubank em suas tarefas
diárias de desenvolvimento, desde criação de fluxos de negócio novos.

Esse ambiente é composto por um extenso ferramental que permite o engenheiro re-
alizar diversas interações em tempo real com os microsserviços que está desenvolvendo.
Como por exemplo, visualização do grafo de interações, logs dos processos e desenvolvi-
mento iterativo através de sincronização de sistemas de arquivo.

Para atingir esse objetivo, foi feito um profundo estudo de microsserviços e arquitetura
do Nubank, o que levou a uma solução totalmente baseada em nuvem computacional e
18

orquestração de contêineres.

1.3 Justificativa

O crescimento da arquitetura de microsserviços é notório e pode ser percebido de


várias formas. Tanto no mercado quanto na academia, a discussão só tem crescido, com
cada vez mais empresas grandes decidindo adotar essa tecnologia, tais como Uber e Netflix.
É possı́vel notar também um crescente número de livros que tratam sobre o assunto, como
Production-Ready Microservices da Susan J. Fowler[1] e Microservices Patterns de Chris
Richardson[2]. Esse crescimento permitiu a escalabilidade dessas empresas, mas também
veio com um custo: a crescente complexidade.

É nesse contexto que o Nubank se encontra e faz parceria neste trabalho. O Nubank
é uma fintech brasileira que conta com cerca de 200 engenheiros, trabalhando diariamente
em mais de 200 microsserviços para atender os mais de 5 milhões de clientes ativos em seus
diversos produtos. A empresa é referência nacional e mundial em tecnologia, devido a sua
arquitetura de software moderna, baseada em microsserviços. Porém, as implicações ne-
gativas deste modelo já estão se manifestando na empresa há algum tempo, prejudicando
a produtividade das equipes. Assim, tivemos a oportunidade de firmar essa parceria e
trabalharmos em um contexto real, para resolver uma carência crescente na empresa.

No entanto, este trabalho foi feito de tal forma que, com algum esforço de implantação,
as ferramentas desenvolvidas possam ser usufruı́das por outras empresas que tenham ca-
racterı́sticas de arquitetura semelhantes. Um estudo feito na Universidade de Brighton, no
Reino Unido, indica que dentre 33 publicações referentes à arquitetura de microsserviços,
o desafio mais citado é referente a comunicação e integração dos microsserviços.[3]

1.4 Organização do Trabalho

A seguir é descrita a organização do restante do documento

• Capı́tulo 2 - Aspectos Conceituais: Neste capı́tulo, os principais conceitos utili-


zados neste trabalho estão explicados com o nı́vel de abstração necessário para o
entendimento do restante do documento, principalmente os capı́tulos referentes à
implementação. Os conceitos são apresentados de maneira agnóstica às tecnologias
de fato escolhidas para o projeto.
19

• Capı́tulo 3 - Tecnologias Utilizadas: Neste capı́tulo são apresentadas, juntamente


com uma descrição de seus elementos e funcionamento, as tecnologias mais relevantes
escolhidas para o projeto.

• Capı́tulo 4 - Metodologia de trabalho: Neste capı́tulo, apresenta-se a metodologia


para desenvolvimento deste trabalho.

• Capı́tulo 5 - Especificação do projeto: Neste capı́tulo, está a especificação do projeto


feita a partir das visões do RM-ODP.

• Capı́tulo 6 - Projeto e Implementação: Neste capı́tulo, estão apresentados todos


os módulos que compõe o projeto e uma descrição detalhada do que fazem e das
estratégias adotadas para atender os requisitos do projeto.

• Capı́tulo 7 - Testes e Avaliação: Neste capı́tulo, os testes e a avaliação do sistema


são expostos. Experiências reais de uso da ferramenta no ambiente no Nubank são
apresentadas.

• Capı́tulo 8 - Considerações finais: Neste capı́tulo, estão as considerações finais deste


projeto, incluindo a avaliação do cumprimento dos objetivos, sugestões para traba-
lhos futuros e uma conclusão geral.
20

2 ASPECTOS CONCEITUAIS

2.1 Ambiente de desenvolvimento

O ambiente de desenvolvimento de um projeto de desenvolvimento de software é o


termo usado para todos os itens que o projeto precisa para desenvolver e implantar o sis-
tema, como, por exemplo, ferramentas, diretrizes, processos, templates e infra-estrutura.[4]

Geralmente temos alguns ambientes de de software bem comuns como Produção,


Homologação e Desenvolvimento. Cada um desses ambientes possui suas caracterı́sticas
próprias, requisitos de funcionamento e ferramental.

Em especial o ambiente de desenvolvimento inclui as ferramentas utilizadas pelo de-


senvolvedor para escrever, executar e testar sua aplicação, nesse contexto temos editores
de texto, ferramentas de linha de comando, executores de teste, plataformas de contêineres
locais para execução de serviços, entre outros.

2.2 Desenvolvimento em tempo real

Quando fala-se de ”tempo real”é esperado que o tempo entre ação e feedback seja
curto o suficiente para atender os requisitos de uma determinada aplicação. No caso
de desenvolvimento em tempo real, queremos que o tempo de iteração no processo de
escrita de código e validação deste, seja curto o suficiente para não prejudicar o fluxo
de desenvolvimento do desenvolvedor, geralmente estamos falando de algo na casa de
segundos até poucos minutos.

Esse tempo pode ser por exemplo, a quantidade de tempo para execução de testes
unitários ou de integração que são usados pelo desenvolvedor. Aqui não se incluem pro-
cessos que são executados em pipelines automatizadas ou sistemas de CI/CD, somente as
ferramentas utilizadas pelo desenvolvedor no dia a dia.
21

2.3 Contêineres

2.3.1 Breve história da tecnologia

A história dos Contêineres começa em 1979 no Unix L7. Nele foi introduzido a cha-
mada de sistema (system call ) chroot capaz de mudar o diretório raiz de um processo e
de seus processos filhos para um novo local no sistema de arquivos. Este avanço foi o
inı́cio do conceito de isolamento de processos, pois limitava o acesso de cada processo ao
sistema de arquivos.

Em 2000, surgiu o FreeBSD Jail, que permitia a administradores particionar um


sistema FreeBSD em vários sistemas menores isolados e independentes, chamados de
“jails”. O sistema é similar ao chroot, mas incluiu recursos de isolamento adicionais em
diversos aspectos do sistema operacional, como por exemplo a atribuição de um endereço
[5]
de IP diferente para cada jail.

Em 2001, foi lançado o Linux VServer que, assim como o FreeBSD Jail, também é
um mecanismo de jail que pode particionar recursos (sistema de arquivos, endereços de
rede, memória) em um sistema computacional. Isso é realizado por meio de nı́veis de
isolamento do kernel. Cada partição é chamada security context e o sistema virtualizado
[6]
dentro dele é chamado de virtual private server.

Em 2004, foi lançado o Solaris Zones para sistemas x86 e SPARC. Cada zone age
como um servidor virtual completamente isolado dentro de uma única instância. Existem
dois tipos de zones: zonas globais (Global Zones) e não-globais (Non-Global Zones). A
zona global é o ambiente de SO tradicional e é a área onde o SO Solaris está instalado.
Todas as operações do sistema, como instalações, inicializações e desligamentos são feitas
na zona global. As zonas não-globais, comumente chamadas somente de zonas, tem seus
[7]
recurso e limites definidos pela zona global a que pertencem

Em 2005 a empresa Virtuozzo lançou o OpenVZ, que utiliza o kernel do Linux


para fornecer virtualização, isolamento, gerenciamento de recursos e checkpointing. Cada
contêiner OpenVZ possui um sistema de arquivos isolado, usuários e grupos de usuários,
uma árvore de processos, rede, dispositivos e comunicação entre processos. Uma desvan-
tagem da solução é que seu funcionamento depende da aplicação de um patch no kernel
[8]
do Linux.

Em 2006 engenheiros da Google desenvolveram o Process Containers, projetado para


limitar, contabilizar e isolar o uso de recursos computacionais (CPU, memória, I/O do
22

disco, rede) de uma coleção de processos. Um ano depois o projeto foi renomeado para
Control Groups (cgroups) e foi adicionado ao kernel do Linux na versão 2.6.24.

Em 2008 surgiu o LXC (LinuX Containers), que foi a primeira e mais completa imple-
mentação de um gerenciador de contêineres Linux. Foi implementado utilizando cgroups
e Linux namespaces e funciona no kernel do Linux sem a necessidade da aplicação de
patches.

Em 2011 a empresa CloudFoundry iniciou o projeto Warden, que utilizava LXC em


suas primeiras implementações, porém depois foi substituı́do por soluções próprias da
empresa. Warden roda em formato daemon e pode isolar ambientes em qualquer sistema
operacional. Além disso, provê uma API para o gerenciamento de cgroups, namespaces,
ciclo de vida dos processos e dos contêineres em diversos hosts através de um modelo
cliente-servidor.

Finalmente, em 2013, surgiu o Docker. Neste momento contêineres cresceram muito


em popularidade, juntamente com o Docker em si. Assim como o Warden, o Docker
utilizou LXC em seus primeiros estágios mas logo trocou por uma solução própria para
gerenciamento de contêineres: o libcontainer. O Docker se destacou de seus concorrentes
por oferecer um ecossistema completo para o gerenciamento de contêineres, que será
detalhado mais adiante.

2.3.2 Comparação com Máquinas virtuais

A virtualização é a tecnologia que permite a criação de diferentes ambientes compu-


tacionais, chamados de virtuais por simular a interface que é esperada por um sistema
operacional.

Um dos principais componentes da arquitetura é o Hypervisor. Ele gerencia a distri-


buição dos recursos computacionais na máquina fı́sica (armazenamento, processamento e
memória) entre as várias máquinas virtuais, além de fornecer um isolamento entre elas.
Ele é um componente que fica entre o hardware e a máquina virtual e é necessário para
o processo de virtualização. Entre os gerenciadores de máquinas virtuais mais populares
no mercado, pode-se citar VMWare vSphere, VirtualBox, Xen, Hyper-V e KVM.

No baixo nı́vel, um contêiner é apenas um conjunto de processos que são isolados do


resto do sistema. No caso do Docker, os contêineres compartilham o kernel do sistema
operacional hospedeiro e, frequentemente, os binários e bibliotecas também. Todos os
componentes compartilhados são read-only. Este compartilhamento de componentes re-
23

duzem a necessidade de se replicar código do sistema operacional, e isso significa que um


único servidor pode executar múltiplos contêineres com uma única instalação do sistema
operacional.

Diferentemente, uma máquina virtual é constituı́da do espaço de usuário juntamente


com o espaço do kernel de um sistema operacional e o hardware é virtualizado. Cada
máquina virtual tem um sistema operacional e suas aplicações e elas compartilham entre
si o hardware do computador hospedeiro.

Ambas as tecnologias provém ambientes isolados para execução de aplicações e podem


ser utilizadas para empacotar e distribuir software.

Dado o menor número de camadas entre a aplicação e o hardware, os contêineres


tendem a ser mais leves e mais rápidos, tornando-os mais práticos para os ciclos de
desenvolvimento e implantação de serviços. Comparativamente, o tempo de inicialização
de uma máquina virtual é muito maior do que a de um contêiner equivalente, além de o
[9]
espaço ocupado em disco ser uma ordem de magnitude maior.

Figura 1: Diferenças entre VMs e Contêiners

Fonte: https://www.backblaze.com/blog/vm-vs-containers/

Ambos têm vantagens e desvantagens, e a decisão entre um outro varia dependendo


dos casos de uso especı́ficos, mas pode-se utilizar as seguintes regras como um ponto de
partida:

Máquinas virtuais são melhores para executar aplicações que necessitam de todos os
recursos e funcionalidades do sistema operacional ou quando há uma variedade de sistemas
operacionais para se gerenciar.
24

Contêineres são uma escolha melhor quando a maior prioridade é maximizar o número
de aplicações sendo executadas em um número mı́nimo de servidores.

2.4 Cloud Computing

Computação em Nuvem (do Inglês Cloud Computing) é segundo a NIST 1 um modelo


para prover um acesso de rede a um grupo compartilhado (shared pool ) de recursos com-
putacionais (recursos como capacidade de rede, servidores, armazenamento, aplicações e
serviços) de forma ubı́qua, prática e sob demanda.

Tal acesso deve ser rapidamente provisionado e lançado com o mı́nimo de gerencia-
mento e interação com o provedor de serviços por parte da aplicação.

O modelo de computação em nuvem deve possuir algumas caracterı́sticas básicas, que


estão descritas na seção seguinte.

2.4.1 Caracterı́sticas básicas de Cloud Computing

A NIST define 5 caracterı́sticas essenciais do modelo de Cloud Computing:

• Serviço sob demanda: Um consumidor pode provisionar unilateralmente capacida-


des computacionais, como tempo de servidor e armazenamento de rede, conforme
for necessário, sem qualquer interação humana com o provedor de serviço;

• Agrupamento de recursos: Os recursos de computação do provedor são agrupados


para atender a vários consumidores usando um modelo ”multi inquilino”, com dife-
rentes recursos fı́sicos e virtuais dinamicamente atribuı́dos e reatribuı́dos de acordo
com a demanda do consumidor. Existe uma sensação de independência de loca-
lização pois o cliente geralmente não tem controle ou conhecimento sobre a loca-
lização exata dos recursos fornecidos, mas pode ser capaz de especificar a localização
em um nı́vel de abstração (por exemplo, paı́s, estado ou datacenter). Exemplos de
recursos incluem armazenamento, processamento, memória e largura de banda de
rede.

• Amplo acesso à rede: Os recursos estão disponı́veis na rede e são acessados por meio
de mecanismos que promovam o uso por plataformas heterogêneas por clientes em
diversos dispositivos (por exemplo, telefones celulares, tablets, laptops e estações de
1
National Institute of Standards and Technology, https://www.nist.gov/
25

trabalho). Esta caracterı́stica promove o conceito de computação ubı́qua, isto é, em


toda parte, onipresente.

• Elasticidade rápida: Os recursos podem ser provisionados e liberados elasticamente,


em alguns casos automaticamente, proporcionando uma escalabilidade crescente ou
descrescente conforme a demanda. Os recursos disponı́veis normalmente aparentam
ser ilimitados para o consumidor, podendo ser requisitidados em qualquer quanti-
dade e a qualquer momento.

• Serviço mensurável: Os sistemas em nuvem controlam e otimizam automaticamente


o uso de recursos, aproveitando-se de uma capacidade de medição em um nı́vel de
abstração apropriado ao tipo de serviço (por exemplo, armazenamento, processa-
mento, largura de banda e contas de usuário ativas). O uso de recursos pode ser
monitorado, controlado e reportado, gerando transparência tanto para o fornecedor
e consumidor do serviço utilizado.

2.4.2 Modelos para Cloud Computing

Este capı́tulo introduz brevemente os modelos de serviço de cloud computing que


podem ser adotados por um provedor e os modelos de deployment, nas secções seguintes.

2.4.3 Modelos de serviços

A teoria por trás dos serviços de computação em nuvem abrange três elementos prin-
cipais: software, plataforma e infraestrutura. Temos os seguintes modelos de serviço:

• SaaS, Software as a Service (”Software como um serviço”)

Neste modelo é oferecido ao consumidor o uso de aplicações de um provedor que


rodam sobre uma infraestrutura em nuvem. Estas aplicações são acessı́veis a partir
de vários dispositivos clientes por meio de uma interface simples, como um navegador
da web, ou uma interface de por meio de um programa (mobile ou desktop).

O consumidor não gerencia ou controla a infraestrutura de nuvem por trás da


aplicação, incluindo rede, servidores, sistemas operacionais, armazenamento ou ca-
pacidade da aplicação individual. São disponibilizadas apenas configurações do
aplicativo especı́ficas para aquele usuário individual.
26

Alguns exemplos de SaaS são serviços de webmail, streaming de vı́deos, conversão de


arquivos e trabalho colaborativo com arquivos. Este modelo não será mais detalhado
neste trabalho.

• PaaS, Platform as a Service (”Plataforma como um serviço”)

A capacidade fornecida ao consumidor é de implantar na nuvem aplicações criadas


por meio de linguagens de programação, bibliotecas, serviços e ferramentas supor-
tadas pelo provedor. Tais aplicações podem ser criadas pelo próprio consumidor, ou
consumidas por este.

Assim como no modelo de SaaS, o consumidor geralmente não gerencia ou têm


controle sobre a infraestrutura de nuvem por trás, incluindo rede, servidores, siste-
mas operacionais, ou armazenamento. Porém o consumidor as tem controle sobre
os aplicativos implantados e possivelmente sobre definições de configuração para o
ambiente de hospedagem do aplicativo.

Como exemplos de provedores no mercado temos IBM Bluemix, Heroku, e Windows


Azure Cloud.

• IaaS, Infrastructure as a Service (”Infraestrutura como um serviço”)

A capacidade oferecida ao consumidor é provisionar processamento, armazenamento,


redes e outros recursos fundamentais de computação onde o consumidor é capaz
de implantar e executar software arbitrário, incluindo-se sistemas e aplicações. O
consumidor não gerencia nem controla a infraestrutura de nuvem por trás, mas
tem controle sobre sistemas operacionais, armazenamento e aplicativos implanta-
dos. Possivelmente possui também um controle limitado de componentes de rede
(por exemplo, firewalls de host). Geralmente acompanha serviços de máquinas vir-
tualizadas.

Alguns exemplos de provedores no mercado são Amazon Web Services, Microsoft


Azure, Google Cloud e VMware Cloud on AWS.

2.5 Programação Funcional

Como o próprio nome diz, a programação funcional surge de funções matemáticas,


mais especificamente do cálculo lambda. Programação funcional é um paradigma de pro-
gramação assim como Orientado a Objeto ou Imperativo. As caracterı́sticas mais mar-
cantes desse paradigma são a presença de imutabilidade de dados e estados, funções serem
27

tratadas como valores como quaisquer outros tipos (inteiros, strings, etc...), transparência
referencial e separação de efeitos colaterais. As linguagens de programação podem ser
puramente funcionais, ou parcialmente, isso é, contém elementos desse paradigma porém
não se limitam a ele, ou fazem pequenos desvios em prol da usabilidade.

2.6 Microsserviços

Microsserviços é um termo usado para designar uma arquitetura de software dis-


tribuı́da que possui um certo conjunto de caracterı́sticas, como por exemplo o deploy
independente. É uma abordagem de desenvolvimento que que permite a criação de uma
aplicação única através de um conjunto de pequenos serviços, de modo que cada um é
responsável apenas por uma pequena parte do processo e durante sua execução eles se co-
municam por meio de mecanismos leves, geralmente uma API e/ou mensageria (mas não
se limitando a esses). Cada um dos serviços é focado em uma parte atômica da lógica de
negócio e funcionam através de mecanismos de deploy independente e automatizado. Em
contraste a esse tipo de arquitetura temos uma aplicação monolı́tica, que se caracteriza
[10]
pela centralização da lógica e código em apenas um serviço de grande porte.

Figura 2: Microsserviços vs Monolitos

Fonte: https://martinfowler.com/articles/microservices/images/sketch.png

A arquitetura de microsserviços tem diversas vantagens em relação a monolı́tica, como


por exemplo a divisão da lógica de negócio em pequenos serviços torna esses mais fáceis
28

de compreender em relação a uma grande aplicação única. Esse modelo também torna
mais simples a adoção de novas tecnologias sem a necessidade de intervir em serviços
já existentes, isso acaba também por facilitar o desenvolvimento em equipe, dando mais
independência para times entenderem e realizarem modificações em seus serviços, o que
por fim contribui para o crescimento da empresa e sua escalabilidade.[11]

Microsserviços também são uma opção altamente escalável e permitem que cada
serviço seja dimensionando dinamicamente e individualmente em relação aos outros, o
que significa que em arquiteturas de maior porte permite uma economia de recursos e
melhor dimensionamento de capacidade dos serviços.

Por outro lado, microsserviços também tem seu lado negativo, possuindo algumas
desvantagens em relação ao modelo monolı́tico. Entre elas o sistema altamente distribuı́do
exige que se tenha um alto nı́vel de automação da infra-estrutura para que eles possam
ser utilizados de forma fácil, como por exemplo o uso de algum PaaS. Gerenciamento
de dados distribuı́dos, problemas de consistência eventual e lógica distribuı́da também se
apresentam como desafios desse modelo de arquitetura.

2.7 Distributed Tracing

Distributed Tracing, ou Rastreamento Distribuı́do, é uma técnica utilizada em sis-


temas distribuı́dos para conseguir acompanhar algum fluxo que acaba por interagir com
diversos serviços. Muito utilizado para ajudar desenvolvedor em sessões de depuração
afim de entender qual caminho de código está sendo executado e com quais parâmetros
em um sistema complexo e distribuı́do. Geralmente esse sistema se compõe de um bro-
ker central para onde as métricas e dados de tracing são enviados pelos serviços, e um
conjunto de código que roda em todos os serviços, responsável por realizar a amostra-
gem e enviar esses dados para o broker principal. Enquanto os brokers esperam receber
mensagens representando eventos de entrada ou saı́da dos serviços através de um pro-
cesso coletor, as bibliotecas se encarregam de enviar essa menagem e também propagar
informações de rastreabilidade para as próximas interações com outros serviços. Podemos
ver um digrama exemplificando a arquitetura de um Distributed Tracing nas figuras 3 e
4
[12]
Um dos primeiros Distributed Tracers que foi desenvolvido, foi o Dapper , pela
Google em meados de 2010. Com a abertura do código do Dapper outras empresas
acabaram se interessando pelo projeto e funcionalidades e criaram suas próprias versões
29

Figura 3: Anatomia de um Distributed Tracing

Fonte:
hhttps://cdn-images-1.medium.com/max/838/1*NuwuCvqZHLBJcJycRr1HTw.pngi

de tracers, entre eles alguns dos mais famosos: Zipkin[13], pelo Twitter, e Jaeger[14], pela
Uber. Com o surgimento de novos tracers no mercado, a industria entrou em busca de
um padrão para esse tipo de software, originando-se o OpenTracing[15]. OpenTracing é
um padrão desenvolvido afim de definir normas e regras e padrões para implementação
de Distributed Tracers. Nela são descritos estratégias para se realizar a identificação de
informações gera mente importantes e o formato do tráfego de dados entre os serviços e
os brokers.

Grande parte do valor dos Distributed Tracing vem de suas ferramentas analı́ticas, que
facilitam a vida do desenvolvedor e permitem com que ele faça análises de forma rápida
e eficiente nos sistemas. Geralmente essas ferramentes vem junto com uma aplicação
de Frontend que consegue exibir fluxos e montar gráficos. Entre eles o Flamegraph é
muito utilizado para visualizarmos como a comunicação se aprofunda e se propaga na
rede, uum exemplo de Flamegraph do Zipkin pode ser visto na figura 5. Outro tipo de
visualização muito útil é a de mapa de Topologia de servços, que nada mais é que um
grafo representando as interações entre diversos serviços, temos um exemplo na figura 6
30

Figura 4: Componentes de um Distributed Tracing

Fonte:
hhttp://blog.newrelic.com/wp-content/uploads/Distributed-tracing-components.pngi

2.8 Devspaces

Um Devspace, como foi batizado pelo grupo, é um ambiente de desenvolvimento iso-


lado que roda em cima da plataforma Formicarium. Ele é o ambiente que o desenvolvedor
terá disponı́vel para uso e que vai interagir durante o seu tempo testando e desenvolvendo
os serviços. As Figuras 7 e 8 dão uma visão geral de Devspaces e seu uso, respectivamente.
31

Figura 5: Exemplo de Flamegraph do Zipkin

Fonte: hhttp:
//callistaenterprise.se/assets/blogg/build-microservices-part-7/Zipkin-sample.pngi

Figura 6: Exemplo de Mapa de Topologia de Serviços

Fonte: hhttps://blog-assets.risingstack.com/2016/May/Distributed transaction tracing


with service topology map trace by risingstack-1462456507669.pngi
32

Figura 7: Anatomia de um Devspace

Fonte: os autores

Figura 8: Visão Geral do Formicarium

Fonte: os autores
33

3 TECNOLOGIAS UTILIZADAS

3.1 Protocolos de Comunicação

3.1.1 HTTP

Acrônimo de HyperText Transfer Protocol. Protocolo usado pela Web, define for-
mato de mensagens trafegadas e formatação, dando também orientações semânticas via
códigos[16] de como os browsers ou servidores devem reagir em resposta a diversos coman-
dos.

3.1.2 Git

Git é um sistema de controle de versões distribuı́do amplamente utilizado em desen-


volvimento de software. o Git se organiza através de repositórios que podem ser clonados
e editados localmente pelo usuário e então sincronizados com um servidor remoto, publi-
cando as alterações. Todo o histórico de alterações é mantido nesse processo sem perda
de informações.

Alguns conceitos importantes de serem destacados no funcionamento do Git para o


melhor entendimento desse projeto são os seguintes:

Repositório
Corresponde a um diretório de arquivos gerenciado pelo Git.

Commit
É uma consolidação de um conjunto de alterações realizado no repositório Git. Representa
um ponteiro para um estado especı́fico (uma versão).

Branch
Branch é um ponteiro movél que aponta para um determinado commit. Branches criam
a ideia de uma árvore de caminhos em um repositório apontam sempre para o commit
mais recente de uma bifurcação de mudanças.
34

HEAD
HEAD é um ponteiro que aponta para para o branch ou commit atual que está em uso
no repositório local.

FETCH HEAD
Um ponteiro efêmero que aponta para a última referência buscada por um fetch.

Staging
Área do Git onde as alterações que vão entrar no próximo commit ficam

Figura 9: Anatomia do Git

Fonte: hhttps:
//raw.githubusercontent.com/UnbDroid/AprendendoGithub/master/images/git.pngi

Para realizar a sincronização entre repositórios locais e remotos o Git possibilita o uso
de diversos protocolos de rede, temos entre eles principalmente: HTTP(S), FTP, rsync
ou SSH.

Durante o processo de uso do Git algumas ações são frequentemente utilizadas, e


35

valem ser destacadas aqui, essas são:

• git init: Inicia um repositório git vazio no diretório.

• git clone: Cria um cópia de um repositório remoto localmente, acaba por fazer um
download da estrutura remota para a maquina do usuário.

• git fetch: Sincroniza o estado do repositório remoto com o local sem realizar al-
terações na working tree do desenvolvedor. Baixa os dados existentes no repositório
remote que não existem no computador do usuário.

• git checkout: Alinha o repositório local de acordo com a revisão ou branch espe-
cificado no comando.

• git commit: O commit é o ato de criar um ponto no histórico do repositório


contendo as mudanças que foram adicionas na área de staging.

• git add: O comando add é responsável por adicionar as mudanças especificadas na


área de staging do git para serem comitadas em seguida.

• git push: O push executa a ação de enviar os commits locais para o repositório
remoto especificado.

• git merge: Atualiza os arquivos na working tree para corresponderem ao especifi-


cado no comando. O merge também pode acabar atualizando o HEAD do branch
atual.

• git pull: Esse comando realiza a atualização do repositório local a partir das mu-
danças realizadas no repositório remoto. De forma geral ele acaba realizando um
download dos commits mais recentes que estão presentes no remote. É uma com-
binação do git fetch com o git merge FETCH HEAD

3.1.3 GraphQL

GraphQL é uma linguagem de consulta criada pelo Facebook em 2002 e lançada


publicamente em 2015. É utilizada principalmente em conjunto com o protocolo HTTP
no contexto de um servidor web para que um cliente possa obter os dados que necessita
de um servidor. Ele tem algumas caracterı́sticas que torna seu uso interessante, como
por exemplo: - Ela permite que o cliente especifique exatamente quais dados ele quer
36

Figura 10: Fluxo de trabalho em Git

Fonte: hhttps://kevintshoemaker.github.io/StatsChats/GIT2.pngi

que sejam retornados em determinada requisição - Torna fácil o processo de agregação de


dados vindos de diferentes fontes. - Utiliza um sistema de tipos para descrever dados.

Além disso, a comunidade oferece diversos Frameworks que atuam no lado do cliente
e que são responsáveis pela obtenção, cacheamento e normalização dos dados. Os mais
relevantes são Apollo e Relay (também desenvolvido e mantido pelo Facebook). Do lado
do servidor, existe uma infinidade de implementações de GraphQL nas mais diferentes
linguagens de programação, sendo a mais popular a implementação de referência em
Javascript, mantida pela própria empresa criadora da linguagem.

GraphQL é uma alternativa ao modelo REST e vem ganhando espaço no mercado


nos últimos anos, sendo utilizado por grandes empresas como Facebook e Github em seus
produtos mais importantes.
37

3.1.4 ZeroMQ

ZeroMQ é um biblioteca escrita originalmente em C que provê a ferramentas para


a construção de mecanismos de rede embarcada em aplicações diversas. Ela fornece di-
versos tipos de sockets que carregam mensagens atômicas através de diversos protocolos
de transporte, como por exemplo TCP. O ZeroMQ possibilita a criação de topologias e
padrões de conexão de sockets extremamente variáveis e flexı́veis, entre eles fan-out, pub-
sub, request-reply, entre outros. A implementação do ZeroMQ se utiliza de um modelo
de I/O assı́ncrono que permite fácil escalabilidade e uma grande quantidade de carga.
ZeroMQ tem bibliotecas em implementações na grande maioria das linguagens de pro-
gramação, tornando ele um boa opção para arquiteturas mistas.[17]

3.2 Linguagens de Programação

3.2.1 Clojure

Clojure é uma linguagem de programação dinâmica e funcional. Sendo um dialeto


de LISP que roda na JVM, é uma linguagem compilada, mas que mantém todas carac-
terı́sticas dinâmicas a ser utilizadas em Runtime. Possui compatibilidade e interopera-
bilidade com todo ecossistema Java e de outras linguagens que rodem na JVM. Clojure
enfatiza o uso de estruturas de dados imutáveis e de a filosofia de Code is Data, com
um sistema poderoso de macros e de estruturas de dados mutáveis Thread Safe quando
[18]
necessário.

Clojure é a principal linguagem de programação utilizada no Nubank, e também


é muito familiar para o grupo, além de apresentar caracterı́sticas interessantes para o
desenvolvimento do projeto. como a grande habilidade para lidar com problemas de
concorrência de forma segura e eficiente.

Dentro do desenvolvimento do projeto Clojure foi utilizada como uma das principais
linguagens de programação para serviços de Backend. Um dos recursos que torna Clojure
especialmente interessante é a possı́vel interação em tempo real com o código através de
um REPL (Read Eval Print Loop), algo que foi utilizado em um dos módulos do projeto
como será descrito mais a frente. Além disso a familiaridade dos desenvolvedores do
Nubank com a linguagem foi um fator decisivo para que o projeto pudesse continuar a ser
mantido por mais pessoas no futuro.
38

3.2.2 Javascript

Javascript é uma linguagem de programação interpretada baseada em ECMAScript.


Foi originalmente criada para atuar junto com navegadores web, tornando possı́vel que
scripts fossem executados no lado do cliente sem passar pelo servidor, realizando comu-
nicação assı́ncrona (AJAX) a manipulando o conteúdo do documento exibido em páginas
web.

Ela foi concebida par ser uma linguagem script com orientação a objetos baseada
em protótipos, tipagem fraca e dinâmica e funções de primeira classe. Possui suporte à
programação funcional e apresenta recursos como Closures e higher-order functions.

Hoje é uma das linguagens mais populares do mundo e já há alguns anos vem sendo
bastante utilizada no lado do servidor através de ambientes como Node.js.

3.2.2.1 Typescript

Typescript é uma linguagem de programação desenvolvida e mantida pela Microsoft


que tem como objetivo tornar bases de código em Javascript mais fáceis de se manter e
menos suscetı́veis à erros, através da adição de um sistema de tipos à linguagem, entre
outros recursos.

Para que o código escrito em Typescript possa de fato ser executado em ambientes
como navegadores web e Node.js, é preciso compilá-lo para Javascript e este processo pode
ser feito através do compilador oferecido como parte do ferramental da linguagem. Na
verdade, todo código Javascript é um código Typescript válido, porém o contrário não é
verdadeiro.

3.2.2.2 Node.js

Node.js é um ambiente de execução multiplataforma e de código aberto, com a capa-


cidade de executar Javascript fora do contexto de um browser, tornando possı́vel o uso
da linguagem em um ambiente de servidor.

Utiliza o motor de Javascript V8, desenvolvido pela Google e que está presente também
em seu navegador Google Chrome. Node.js segue uma arquitetura event-driven capaz de
realizar I/O de maneira assı́ncrona e não bloqueante. Estas escolhas de projeto tem como
objetivo otimizar o throughput e escalabilidade em aplicações web com muitas operações
de entrada e saı́da, bem como aplicações web real-time como por exemplo programas de
39

comunicação em tempo real e jogos.

Como parte de seu ecossistema, é oferecido um gerenciador de pacotes chamado npm,


que conta com uma interface de linha de comando e um banco de dados público de pacotes,
que podem ser adicionados em projetos através de um sistema de dependência.

Hoje Node.js é utilizado por empresas como GoDaddy, Groupon, IBM, Linkedin,
Microsoft, Netflix, PayPal, Rakuten, Walmart, Yahoo, entre outros.

3.3 React

React é uma biblioteca JavaScript de código aberto para criar interfaces interativas.
É declarativa, eficiente e flexı́vel e permite que o desenvolvedor componha interfaces com-
plexas a partir de partes menores e isoladas chamadas ”componentes”.

Utiliza a técnica de Virtual DOM, o que melhora drasticamente sua performance pois
diminui o número de operações feitas de fato no DOM (Document Object Model ), que
costumam ser tarefas de processamento mais intensivo.

3.4 Docker

Docker é uma plataforma open source para desenvolvimento de aplicações através


de contêineres, criada e mantida pela empresa Docker Inc. Foi escrito na linguagem de
programação Go, que é fortemente tipada e possui garbage collector para gerenciamento
de memória, além de suporte explı́cito para programação concorrente.

No seu ecossistema existem ferramentas para criação de imagens e execução de contêi-


neres (Docker Engine), repositórios para distribuição de imagens (Registries) e também
uma ferramenta para orquestração de contêineres em um cluster (Docker Swarm)

3.4.1 Base tecnológica

O Docker se utiliza de diversos recursos do kernel do sistema operacional para prover


a tecnologia de contêineres, como cgroups, que permitem que os processos sejam organiza-
dos em grupos hierárquicos e cuja utilização de recursos pode ser limitada e monitorada.
Namespaces, que permitem que um sistema isole uma coleção de processos para que não
possam ver certas partes do sistema geral e o Unionfs, que é um sistema de arquivos
empilhável que permite aos usuários especificar um conjunto de diretórios que são apre-
40

sentados como um único diretório virtual, mesmo que estes possam vir de diferentes
sistemas de arquivos.

3.4.2 Arquitetura

Utiliza-se de uma arquitetura cliente-servidor composta por três componentes: um Da-


emon, que é o responsável por criar e manipular os objetos Docker (imagens, contêineres,
redes, volumes, etc.) e age como servidor.

Uma API REST, que define uma interface que os outros programas podem utilizar
para interagir com o daemon e instruı́-lo sobre o que fazer.

Uma interface de linha de comando, que é a principal interface do usuário com o


Docker. Ela age como cliente e se utiliza da API REST para interagir com o daemon
através de scripts ou diretamente de comandos em um terminal.

3.4.3 Objetos Docker

3.4.3.1 Imagens

Uma imagem é um template read-only com instruções para a criação de um contêiner


Docker. Normalmente é baseada em outra imagem, com customizações adicionais. Por
exemplo, é possı́vel construir uma imagem baseada na imagem do ubuntu, porém que
instala e configura um servidor web Apache e uma determinada aplicação.

Para criar uma imagem, é preciso um Dockerfile, arquivo que se utiliza de uma sintaxe
simples para definir os passos necessários para criar a imagem e executá-la. Cada instrução
em um Dockerfile cria uma camada na imagem, que são reaproveitadas quando a imagem é
reconstruı́da caso a instrução continue a mesma. Esta estratégia é responsável por tornar
as imagens leves e rápidas, quando comparadas com outras tecnologias de virtualização.

3.4.3.2 Contêineres

Um contêiner é uma instância executável de uma imagem. É possı́vel criar, pausar,


interromper, mover e remover um contêiner utilizando a interface de linha de comando ou
a API REST. É possı́vel também conectar um contêiner em uma ou mais redes, acoplar
volumes de dados ou mesmo gerar novas imagens a partir do estado atual de um contêiner.

Por padrão, um contêiner é relativamente bem isolado de outros contêineres e da


41

máquina hospedeira, e este nı́vel de isolamento pode ser controlado através dos mecanis-
mos de redes e volumes oferecidos pelo Docker.

Um contêiner é efêmero do ponto de vista que nenhuma das mudanças em seu estado
que não forem armazenadas de maneira persistentes serão perdidas assim que o contêiner
[19]
é removido.

3.5 Kubernetes

Kubernetes é um projeto de código aberto da Google, que possui mais de 1800 con-
tribuidores e ganha cada vez mais atenção no mundo de operação e desenvolvimento de
software. A Google, dado a escala de sua operação, sofria com problemas com o ge-
renciamento de muitas máquinas virtuais. Assim, precisou-se repensar como lidar com
esse problema, o que, depois de anos, levou ao gerenciador e escalonador de contêineres
chamado Kubernetes.

Para entender melhor a necessidade de um gerenciador tal como o Kubernetes, é


necessário dar um passo atrás e olhar para as vantagens e desvantagens dos contêineres.
Contêiners são feitos para serem leves, rápidos, mas de duração curta e frágeis. Assim,
eles trocaram a resiliência de uma máquina virtual pela velocidade e leveza. Isso requer
que contêineres rodem em um ambiente onde em caso de falha ou mudança de carga, esse
ambiente garanta a substituição desses contêineres e gerencie eventuais mudanças de rede
e recursos de memória e CPU do cluster.

Kubernetes trabalha com a ideia de guardar o estado desejado do cluster. Isso porque,
em sistemas distribuı́dos, é importante a construção de um modelo de estado global.[20]
Muitos problemas são resolvidos com uma boa modelagem de estado global. Para isso, o
Kubernetes provê alguns objetos através da sua API. Dentre todos os objetos, os relevantes
para o nosso projeto são:

• Namespace: provêm um escopo de nomes, são projetados para ambientes nos quais
há diversos usuários do cluster. Recursos dentro de um namespace devem ter um
nome único.[21]

• Pod : respresenta a unidade mais simples capaz de ser distribuı́da pelo Kuberne-
tes. É o bloco básico para construção de outros objetos. Encapsulam um ou mais
contêineres que representam uma aplicação, com todos os requisitos de disco e rede.
Pods são efêmeros, isto é, eles podem ser removidos e recriados sem aviso prévio.
42

[22]

• Deployment: é um dos objetos controladores, provém atualizações declarativas aos


Pods. Capaz de criar e gerenciar réplicas de Pods, rollout de atualizações, reinı́cio
[23]
de Pods em caso de falha.

• Service: é uma abstração que define um conjunto lógico de Pods e uma polı́tica de
acesso a ele.[24] Quando um Service é criado dentro de um Namespace, é criado uma
entrada de DNS, permitindo que os Pods se encontrem na rede apenas pelo nome
do Service, garantindo que com a mesma configuração, serviços acessem recursos de
[21]
seus respectivos Namespaces.

[25]
• Ingress: são configurações de acesso externo ao cluster, tipicamente HTTP.
43

4 METODOLOGIA DO TRABALHO

4.1 Gerenciamento do projeto

4.1.1 Scrum

Scrum é um método de desenvolvimento de produtos e serviços. Esse método teve


concepção em 1986[26], Em uma publicação[27] de Takeuchi e Nonaka, que descrevem bons
resultados obtidos em grandes empresas usando a metodologia.

O inı́cio da execução do Scrum é criar uma lista priorizada de features (funciona-


lidades) e outras capacidades necessárias pra se desenvolver um produto com sucesso,
chamada de backlog. A implementação sempre tem que seguir a ordem de prioridade
atribuı́da a cada feature, sendo sempre executado primeiro o que for mais prioritário.

O trabalho é feito em sprints: iterações de tempo curto e limitado (em geral de 1


ou 2 semanas). A execução é feita por uma equipe multi-funcional e auto-contida, com
desenvolvedores, arquitetos, engenheiros, designers, etc, de modo que ao final da sprint
obtém-se uma parte do produto que traz algum valor de negócio.

No final da sprint, a equipe revisa o que foi feito com os stakeholders e ajusta a
priorização de acordo com o feedback. O ciclo recomeça com uma reunião de planejamento
para a próxima iteração.

4.1.2 Execução iterativa

Usamos sprints de duas semanas cada, correspondente ao perı́odo de implementação


do que foi definido como prioridade, a partir do backlog. Este, por sua vez, fora definido
em cada uma das reuniões de sprint planning, usadas por nós para priorizar tarefas do
backlog, identificar os maiores problemas e travas para prosseguir na implementação e
reorganizar e/ou atualizar os milestones.

Trabalhamos três vezes por semana, fazendo sessões de pairing para codificar a solução
44

Figura 11: Framework do Scrum

Fonte: https://www.scrum.org/resources/what-is-scrum

e discuti-la e fazer live-testing em reuniões de 4 horas a 6 horas de duração, em média, de-


pendendo da dificuldade do problema que estávamos tentando resolver, ou disponibilidade
dos integrantes do grupo.

Como estávamos todos na mesma sala discutindo e implementando, ficou mais simples
de difundir o conhecimento da arquitetura e das decisões de projeto para cada integrante,
e problemas técnicos ou arquiteturais foram resolvidos com bastante eficiência, pois os
outros integrantes estavam ao lado, caso qualquer dúvida ocorresse. Devido ao teor orga-
nizacional dessas reuniões de projeto que fizemos, vimos que as daily meetings propostas
pela metodologia de Scrum seriam supérfluas para nosso método de trabalho, e acabamos
não adotando-as.

Não coincidindo necessariamente com o final da sprint, fizemos reuniões que uniram as
ideias de sprint reviews e sprint retrospective para mostrar demo aos nossos stakeholders,
pegar feedback e reorganizar o backlog segundo o feedback dos mesmos, além de identificar
o que deu errado, para poder se preparar melhor para o próximo perı́odo de trabalho.

Vale mencionar também que antes de termos a ideia do produto formalizada também
fizemos algumas reuniões com um de nossos stakeholders e co-orientador do projeto: Ra-
fael Ferreira, que nos deu grande ajuda para entendermos do que se tratava o produto,
45

além de proporcionar feedbacks quanto ao andamento do projeto, soluções técnicas e


milestones.

Escolhemos não ter Scrum Master pois o papel do mesmo de filtrar forças distrativas
no nosso trabalho não se mostrou necessário, e também não tivemos Product Owner pois
o nosso backlog foi feito de forma conjunta, pelos 4 desenvolvedores e a terceirização da
comunicação com os stakeholders não foi necessária, pois tı́nhamos liberdade e disponibi-
lidade para falar com os mesmos diretamente, quando precisássemos.

4.2 Fases do projeto

4.2.1 Definição de Stakeholders e contexto do problema

Um stakeholder é uma pessoa ou organização com direitos ou interesse com respeito


ao sistema ou às propriedades dele. Os stakeholders dão forma ao software, gerando
oportunidades e limitações ao desenvolvimento, sendo também a fonte de requisitos do
sistema. Os desenvolvedores podem propor requisitos, mas cabe aos stakeholders acatar
as sugestões ou não.

A tabela 1 mostra os stakeholders de nosso projeto, e em seguida temos uma des-


crição das dores e influência de cada um na nossa solução. O método de alinhamento de
expectativas está descrito na próxima seção.

Tabela 1: Papéis de cada entidade ao longo do projeto.


Entidade Papel
Leonardo Iacovini Desenvolvedor, stakeholder
Luiz Gustavo Desenvolvedor, stakeholder
Rafael Leal Desenvolvedor, stakeholder
Rafael Correia Desenvolvedor, stakeholder
Rafael Ferreira Stakeholder
Integrantes da equipe de platform Stakeholder
Integrantes da equipe de lending Stakeholder

4.2.2 Revisão da literatura e arquitetura do Nubank

Um profundo estudo sobre microsserviços e computação em nuvem foi realizado de


modo a preparar a equipe para as fases seguintes. Além disto, um aprofundamento das
particularidades de arquitetura do Nubank também foi feito e, somado ao conhecimento
adquirido durante os perı́odos de estágio na empresa, foi capaz de fornecer uma base
46

conceitual para criação das soluções que seriam discutidas nas próximas fases.

4.2.3 Concepção do MVP

Tendo em mãos o contexto do problema a ser resolvido e uma base conceitual forte,
nesta fase foi desenhado o que seria o MVP do projeto para guiar o desenvolvimento das
próximas fases, bem como um roadmap com entregáveis parciais.

4.2.4 Definição da arquitetura da solução

Nesta fase foi estruturado como seria a arquitetura da solução. Dado o problema não
trivial a ser resolvido, um tempo considerável foi investido para projetar a comunicação
e divisão dos diversos módulos envolvidos na solução.

4.2.5 Experimentações com FileSync

Como um dos requisitos mais complexos a ser atendidos era o módulo de sincronização
de arquivos, foi decidido (com orientação do co-orientador do projeto) dedicar totalmente
uma fase do projeto à experimentações de tecnologias e soluções para atender este requi-
sito. Nesta fase diversas alternativas foram testadas como por exemplo Syncthing, Git,
RSync e FTP, sendo o Git a solução escolhida para ser levada adiante.

4.2.6 Implementação do módulo Devspaces e CLI

Nesta fase foram desenvolvidos os serviços que compõe o módulo de Devspaces. Para
testar de maneira mais prática o que estava sendo construı́do, alguns comandos da CLI
foram desenvolvidos em paralelo e um cluster na AWS EC2 foi disponibilizado na conta
pessoal de um dos integrantes da equipe para simular o que seria o ambiente real do
Nubank.

4.2.7 Implementação do módulo FileSync

Após as diversas experimentações de tecnologias e mecanismos de sincronização de


arquivos e tendo em mãos o módulo de Devspaces (que era uma dependência para este
módulo), o módulo FileSync foi o escolhido para ser desenvolvido nesta fase do projeto.
47

4.2.8 Implantação em um cluster do Nubank

Tendo já em mãos as funcionalidades completas de Devspaces e FileSync, era hora de


mover infraestrutura do projeto para um cluster real fornecido pelo Nubank. A migração
não seria trivial, pois existem diferenças entre a topologia de rede e requisitos de segurança
no cluster do Nubank e no cluster provisório que estava sendo utilizado até então. Por
este motivo, uma fase inteira foi dedicada para este processo.

4.2.9 Implementação Distributed Debugger e UI

Uma vez garantido que os módulos de Devspaces e FileSync estavam operando cor-
retamente no cluster do Nubank, o foco da equipe se voltou para o desenvolvimento do
módulo de Distributed Debugger juntamente com o módulo de UI (que serve como a ca-
mada de visualização desta módulo). Desta maneira, o máximo de valor pôde ser entregue
ao final desta fase.

4.2.10 Implantação em um squad

Com todos os módulos do MVP funcionando corretamente e em um ambiente já


acessı́vel para os engenheiros do Nubank, esta fase foi dedicada para apresentação e im-
plantação da solução em um squad real. Nesta fase, diversas apresentações foram feitas,
além da criação de um canal no Slack para que toda a empresa pudesse se envolver e
discutir o projeto.

4.2.11 Coleta de resultados

Como última fase do projeto, a equipe se dedicou a coletar os feedbacks dos engenheiros
que estavam utilizando a ferramenta para avaliar a solução e, principalmente, entender o
que poderia ser feito em melhorias futuras.

4.3 Utilitários

Aqui apresentaremos algumas ferramentas auxiliares que utilizamos para organizar o


andamento do projeto.
48

4.3.1 GitHub

GitHub é um servidor de git.[28]

4.3.2 Clubhouse

Clubhouse é uma ferramenta online de gerenciamento de projetos de software, pro-


vendo funcionalidades interessantes para criar tasks, definir milestones, verificar o an-
damento do projeto com boards e alguns gráficos. Também provê integração com o
GitHub.[29]

Figura 12: Board do Formicarium no Clubhouse

Fonte: os autores - 2018-11-10

4.3.3 Slack

Slack software para equipes que proporciona mensageria instantânea, canais de con-
versa, workspaces e muitas integrações (por exemplo, utilizamos a do GitHub para acom-
[30]
panhar os nossos repositórios).
49

5 ESPECIFICAÇÃO DE PROJETO

Escolhemos descrever o sistema do nosso projeto seguindo o modelo de referência


ODP, pois ele apresenta uma forma de simplificar a descrição de sistemas complexos
por meio dos seus Pontos de Vista.[31] Sabı́amos também que gostarı́amos se seguir alguns
princı́pios de projeto, descritos na Tabela 2. Esses princı́pios foram resultado de conversas
com engenheiros de software e interessados no projeto, e nos ajudaram a guiar durante a
definição dos requisitos de sistema.

5.1 Ponto de Vista da Empresa

As empresas que podem se beneficiar do nosso projeto devem ter alguma semelhança
com o processo de negócio de desenvolvimento e entrega de novas funcionalidades, descri-
tos respectivamente nas Figuras 14 e 15. O primeiro processo descreve como uma alteração
de código por um desenvolvedor gera novas imagens Docker, que são guardadas em um
catálogo chamado Docker Registry. Exemplos de Docker Registry são o DockerHub e o
Quay.io. Já o segundo processo, descreve como uma imagem desses catálogo é implantada
de forma automatizada pelo Servidor de Entrega Contı́nua.

Tabela 2: Princı́pios de Projeto


Princı́pio de Projeto Descrição
Ambientes de curta duração O desenvolvedor deve ser capaz de criar e deletar ambi-
entes com rapidez e facilidade
Visualização de fluxos complexos O desenvolvedor deve ser capaz de visualizar o resultado
de suas requisições HTTP e mensagens Kafka
Ambiente parecido com o local O desenvolvedor deve ter uma experiência de desenvol-
vimento similar com o ambiente de sua máquina
Esconde complexidade de configuração O desenvolvedor não deve precisar conhecer a confi-
guração dos serviços que ele precisa disponı́veis no am-
biente dele.

Fonte: os autores
50

Figura 13: Visão Geral Técnica

Fonte: os autores

5.2 Ponto de Vista da Informação

O objetivo dessa seção é apresentar a organização das informações por todos os compo-
nentes do sistema. A Figura 16 representa os três principais contextos onde as informações
são modeladas, e como é o fluxo de informações.

5.2.1 Contexto da Empresa

O contexto da empresa refere-se a uma base de dados pré-existente capaz de fornecer


as configurações de uma aplicação dado o ambiente: teste, homologação e produção,
tradicionalmente. Assim, a modelagem dessa base de dados pode ser arbitrária em relação
ao sistema do Formicarium.

5.2.2 Contexto do Formicarium

5.2.2.1 Servidor

A Figura 17 representa a modelagem da configuração necessária para rodarmos uma


aplicação. Note que uma aplicação pode possuir múltiplas interfaces e pode constituir
por múltiplos contêineres. Isso permite maior flexibilidade na implantação e modelagem
51

Figura 14: Processo de Entrega Contı́nua - Construção da imagem Docker

Fonte: os autores

de aplicações.

A Figura 18 representa a modelagem de coleta e manipulação de eventos do Hive.

5.2.2.2 Cliente

A Figura 19 representa a visão dos modelos do Formicarium utilizadas no cliente,


para montagem dinâmica de telas e representação na linha de comando.

Além disso, a Figura 40, disposta no Apêndice, representa a tipagem dos contratos
importantes do Kubernetes que tornam possı́vel o stream de logs na tela.

5.3 Ponto de Vista da Computação

O Ponto de Vista da computação está diretamente ligado a distribuição. Não diz


respeito aos mecanismos de interação, mas decompõe o sistema em objetos capazes de
[31]
realizar funções e interagir por interfaces bem definidas .

5.3.1 Arquitetura de Referência

A arquitetura deste projeto compartilha conceitos e mecanismos com arquiteturas de


Plataformas como Serviço (Platform as a Service), em particular a arquitetura da Heroku.

A Heroku se descreve da seguinte forma: ”Heroku é uma plataforma de nuvem base-


52

Figura 15: Processo de Entrega Contı́nua - Implantação automatizada

Fonte: os autores

ada em um sistema de contêineres gerenciado, com integração a serviços de dados e um


poderoso ecossistema, para implantação e execução de aplicações modernas. O Heroku
Developer Experience é uma abordagem centrada na aplicação para entrega de software,
integrada com as mais populares ferramentas e fluxos de trabalhos atuais.”(Heroku[32],
2018).

A arquitetura da Heroku têm diversos componentes, destacados a seguir aqueles rele-


vantes ao projeto.

Figura 16: Contextos de informação e seu fluxo

Fonte: os autores
53

Figura 17: Modelagem das Informações no Servidor

Fonte: os autores

5.3.1.1 Plataforma

A plataforma consiste no sistema operacional, runtime da linguagem e bibliotecas


associadas. Esses subcomponentes já mudaram com o tempo. Hoje, a plataforma da
Heroku adota o Ubuntu como sistema operacional, o que permite suporte a múltiplas
linguagens de programação como Java, Clojure, Python, Ruby e Node.js.[33]

Para o desenvolvedor usuário da plataforma, é necessário prover um arquivo Procfile


que representa um processo UNIX, e provê o ambiente de execução da aplicação, quanti-
dade de réplicas e modularidade. Além disso, a plataforma fornece uma interface de linha
de comando com métodos análogos ao UNIX, como representado na Figure 20. Através
dessa interface, também é possı́vel gerenciar atualizações da aplicação.[33]

5.3.1.2 Mecanismo de Roteamento de Requisições

Além de gerenciar os processos da aplicação, a plataforma também fornece um sub-


domı́nio sobre herokuapp.com. Quando uma requisição HTTP chega à infraestrutura da
Heroku, a malha de roteamento direciona para o servidor onde o contêiner, que a Heroku
chama de dyno, está sendo executado.[33]
54

Figura 18: Modelagem das Informações no Hive

Fonte: os autores

Figura 19: Modelagem de devspaces fornecida pelo frontend

Fonte: os autores

5.3.1.3 Infraestrutura de Add-ons

Add-ons são uma maneira de terceiros fornecerem extensões das funcionalidades da


plataforma. Exemplos possı́veis são bancos de dados, serviços de mensageria, email e
SMS. A Figura 22 mostra a necessidade de uma interface entre a plataforma e o provedor
[33]
de serviços terceiros.

5.3.2 Diferenças e Arquitetura do Projeto

O principal ponto de diferença, é claro, são os objetivos das plataformas. Enquanto a


Heroku se propõe a ser uma plataforma que executa aplicações em ambiente de produção,
o Formicarium é focado em ambientes de desenvolvimento dentro das empresas.
55

Figura 20: Interface de Linha de Comando da Heroku

Fonte: Heroku Cloud Application Development

Figura 21: Representação da Malha de Roteamento

Fonte: Heroku Cloud Application Development

5.3.2.1 Plataforma

O Formicarium utiliza o Kubernetes como plataforma, e assim, abstrai os requisitos


de sistema operacional. No entanto, é altamente recomendado utilizar Nodes com sis-
tema operacional Linux. Trabalhando com contêineres Docker também permite maior
flexibilidade no suporte a linguagens de programação, uma vez que, caso não exista ainda
um contêiner com o ambiente apropriado para ele, é possı́vel construir este sem grandes
dificuldades.

O Kubernetes também fornece um modelo de informação inicial para trabalharmos:


os objetos da API Kubernetes como pods, deployments e ingresses. No entanto, esses obje-
tos foram modelados no contexto de operação e implantação de software, e não ambientes
de desenvolvimento. Trabalhar com eles seria, então, desnecessariamente trabalhoso. As-
sim, desenvolvemos uma modelagem mais apropriada, já descrita no Ponto de Vista da
Informação.
56

Figura 22: Infraestrutura de Add-ons da Heroku

Fonte: Heroku Cloud Application Development

5.3.2.2 Mecanismo de Roteamento de Requisições

Para o roteamento de requisições, o Formicarium têm requisitos bem menos exigentes


que a Heroku, uma vez que o todo o fluxo é dado pelos desenvolvedores. Assim, é possı́vel
substituir a malha de roteamento, que exige uma infraestrutura de rede mais complexa,
por um reverse proxy.

O funcionamento do reverse proxy se dá pelo direcionamento da requisição com base


no hostname. Isso permite que um domı́nio DNS represente diversas aplicações.

Figura 23: Roteamento de Requisições no Formicarium

Fonte: os autores
57

5.3.2.3 Desacoplamento Empresa e Formicarium

O Formicarium não possui uma infraestrutura de add-ons. No entanto, é necessário


uma forma de criar um desacoplamento entre os modelos de configuração das aplicações
pela empresa e da configuração necessária para criação dessas aplicações na plataforma.

Assim, a empresa é responsável por desenvolver um serviço de configuração das


aplicações e configurar o Formicarium para acessá-lo. A partir de então, o desenvolvedor
é capaz de criar serviços de forma transparente.

Figura 24: Desacoplamento Empresa e Formicarium

Fonte: os autores

5.3.2.4 Infraestrutura de Depuração

Para a depuração de problemas nas aplicações, o Formicarium possui comandos e


ferramentas assim como a plataforma da Heroku. No entanto, dado que esse é o foco da
plataforma, é necessário mais do que simplesmente inspeção de logs.

Para isso, há um componente especializado em agregar informações relevantes de


depuração. Esse componente é criado junto com os Devspaces, e possuem um armazena-
mento efêmero.

5.4 Ponto de Vista da Engenharia

O Ponto de Vista da Engenharia enfoca os mecanismos de distribuição e a provisão


de vários tipos de transparência necessários para dar suporte a distribuição. De certa
58

forma, muitos dos mecanismos de distribuição são fornecidos pela plataforma escolhida:
Kubernetes. No entanto, é necessário entender esses mecanismos e fazer uso adequado.

A Figura 25 mostra uma visão geral do projeto. Temos ambientes isolados e indepen-
dentes, nos quais desenvolvedores podem criar e depurar serviços. Vemos também que
para o funcionamento do sistema, é necessário alguns serviços como Hive, Tanajura, Soil e
Config Server. Esses serviços seguem uma arquitetura comum: a Arquitetura Hexagonal,
descrita a seguir.

Figura 25: Visão geral do Formicarium

Fonte: os autores

5.4.1 Arquitetura Hexagonal

Também conhecida com Arquitetura de Ports and Adapters é um modelo de arquite-


tura de software em camadas que presa pela separação entre domı́nio e lógica da aplicação
com comunicação e interação com o mundo exterior. Esse modelo possui as seguintes ca-
madas:

• Port

• Adapter

• Controller/Application

• Domain/Logic
59

Sendo as responsabilidades de cada camada as seguintes:

Ports: São a camada responsável por realizar o interfaceamento com algum tipo de
IO, seja HTTP, Mensageria, Banco de Dados, ou qualquer outro.

Adapters: Camada responsável por realizar a adaptação e transformação entre dados


de um modelo do domı́nio interno para uma representação a ser usada externamente para
alguma porta.

Controller/Application: Tem o papel de controlar a interação e chamadas entre


as portas junto as funcionalidades do domı́nio da aplicação.

Domain/Logic: Lógica e modelos internos e especı́fico da aplicação, contém a lógica


principal do serviço.

Figura 26: Diagrama de Arquitetura Hexagonal

Fonte:
https://lmonkiewicz.com/wp-content/uploads/2017/04/hexagonal-e1491766585240.png

A grande possibilidade desse modelo é que ele permite que se tenham diversas portas
e adaptadores diferentes para um mesmo domı́nio/lógica da aplicação, como o diagrama
abaixo mostra.

Essa arquitetura é muito interessante para microsserviços pois permite criar um de-
sacoplamento de forma natural e da grandes possibilidades de meios de comunicação e
60

interação dos serviços entre si.

5.4.2 Fluxos

As apresentações dos componentes, suas interfaces e participações nos fluxos principais


do Formicarium são apresentadas no capı́tulo Implementação.

5.5 Ponto de Vista da Tecnologia

As escolhas de tecnologias e suas aplicações são apresentadas no capı́tulo Imple-


mentação.
61

6 IMPLEMENTAÇÃO

6.1 Gerenciamento de Devspaces

6.1.1 Soil

O serviço Soil é um componente central no projeto. Sendo assim, ele possui duas
importantes responsabilidades, listadas a seguir.

• Interfacear a API do Kubernetes para os projetos de frontend do Formicarium.

• Forncecer pontos de acesso aos serviços

A primeira responsabilidade listada diz respeito a necessidade de modelos de in-


formações apropriado para ambientes de desenvolvimento, como já discutido na seção
anterior. Já a segunda responsabilidade é o que faz o desenvolvedor conseguir acessar os
serviços, coletar logs, reiniciar o serviço, conectar em uma interface REPL, etc.

6.1.2 Criação de Devspaces

Os Devspaces são provisionados e configurados de forma automática pelo serviço Soil,


a medida que um desenvolvedor requisita sua criação através da CLI ou pela aplicação
Desktop. Para obter dados de configuração e configuração inicial do Devspace, o Soil
consulta um servidor de configurações via Webhooks, esse encarregado de fornecer todo
configuração especı́fica da empresa para fazer a inicialização de um novo Devspace.

Objetivamente, os Devspaces são Namespaces do Kubernetes que são gerenciados


pelo Soil, além dos recursos padrões do Kubernetes, esse Namespaces acabam recebendo
serviços adicionais do Formicarium, sendo esses o Hive e o Tanajura. A interação com
os Devspaces é feito sempre através da CLI do Formicarium, não exigindo que o usuário
tenha que ter o ferramental do Kubernetes instalado e configurado em sua máquina.
62

Dentro de um Devspace os serviços que rodam nele conseguem se localizar entre si


por meio do servidor de DNS do Kubernetes que garante isolamento entre Namespaces,
dessa forma conseguimos alcançar um isolamento entre serviços entre Devspaces, mesmo
que 2 desenvolvedores estejam executando o mesmo serviço. Além disso, cada Devspace
recebe do Config Server um conjunto de aplicações de infra-estrutura ou de uso comum,
que também garante isolamento dos demais Devspaces, como por exemplo, a existência
de um broker de mensageria por Devspace. não havendo confusão na produção e consumo
de mensagens.

Figura 27: Fluxo de Criação de Devspaces

Fonte: os autores

6.1.3 Deleção de Devspaces

O fluxo da deleção de Devspace começa com a requisição feita pelo desenvolvedor


através da CLI ou da aplicação Desktop para o Soil. Então, o Soil irá efetuar a deleção do
Namespace na API do Kubernetes, que automaticamente deleta Pods, Services, Ingresses
e Deployments daquele Namespace. Assim, a deleção de um Devspace deleta todo o estado
daquele ambiente: possı́veis estados internos de serviços, mensageria e, é claro, bancos de
dados.
63

6.1.4 Criação de um Serviço em um Devspace

A criação de um serviço pode acontecer de duas formas:

• local
Nesse modo, o desenvolvedor diz qual diretório está o código localmente na máquina
dele e, então, o Soil irá se comunicar com o Config Server, e criar um contêiner
Chamber remoto com as configurações necessárias para iniciar o serviço em modo
de desenvolvimento. E, então, o desenvolvedor pode fazer mudanças e enviá-las para
o contêiner. Esse mecanismo é explicado com mais detalhes na seção FileSync.

• image
Nesse modo, o desenvolvedor diz que quer criar um serviço pré-construı́do, com uma
imagem disponı́vel no Docker Registry da empresa. Assim como o modo local, o
Soil se comunica com o Config Server para obter as configurações de implantação
desse serviço e, a partir da API do Kubernetes, cria o Deployment, Service e Ingress
relevantes para a aplicação.

6.2 FileSync

Um dos grandes desafios do projeto foi a construção de um sistema para sincronização


de arquivos em uma arquitetura distribuı́da de microsserviços.

O caso de uso principal para esta funcionalidade é permitir que o engenheiro da Nu-
bank pudesse iterar de maneira mais rápida no desenvolvimento de um novo microsserviço
ou em modificações de algum existente.

Do ponto de vista da empresa, esta era um dos requisitos mais desejados e que mais
agregaria valor ao produto final, dado que é uma ferramenta extremamente poderosa para
o engenheiro, garantindo-lhe mais produtividade e liberdade enquanto estivesse desenvol-
vendo.

Este sistema de sincronização de arquivos precisava atender algumas premissas:

1. Deve se integrar facilmente ao workflow do engenheiro, para haver um incentivo à


sua adoção.

2. Segurança: Os arquivos trafegados não devem ser, de maneira alguma, expostos para
a internet ou suscetı́veis a ataques como Man in the middle, dado que o conteúdo
trafegado é extremamente sensı́vel (códigos fonte dos produtos da empresa)
64

3. Velocidade: Quanto mais rápido for o processo de sincronização, mais dinâmico será
o processo, permitindo iterações mais rápidas e, consequentemente, maior produti-
vidade.

Para entender o problema que o sistema de Filesync se propõe a resolver, é preciso


entender o cenário:

Os serviços em que o engenheiro está trabalhando não estão sendo compilados e exe-
cutados na mesma máquina em que está o código fonte. Na verdade eles estão sendo
executados em um contêiner Docker, gerenciado por um cluster Kubernetes, operando
em uma infraestrutura de hardware fornecida pela AWS EC2. Para que mudanças no
código fonte fossem de fato percebidas pelo desenvolvedor, este precisaria compilar uma
nova imagem Docker com o código fonte atualizado, publica-lá em um repositório de
contêineres e realizar a implantação desta nova imagem no cluster remoto. Este pro-
cesso, em um contexto de desenvolvimento, é extremamente ineficiente, o que tornaria a
ferramenta pouco dinâmica e não atrativa para o engenheiro.

Para evitar a geração de uma nova imagem Docker a cada iteração no código fonte,
foi desenvolvida uma imagem genérica capaz de compilar e executar qualquer serviço da
Nubank em modo de desenvolvimento. Esta imagem foi apelidada de Chamber e uma de
suas caracterı́sticas é a sua extensibilidade, permitindo que em melhorias futuras sejam
desenvolvidas versões para outras linguagens e plataformas (Como por exemplo Node.js,
Ruby, Scala, etc.). Para implantação no Nubank, foi desenvolvida a imagem Chamber-
lein, capaz de executar programas escritos em Clojure, principal linguagem utilizada nos
microsserviços do Nubank.

Além disto, esta imagem tem um componente de software capaz de atualizar o código
do serviço que está sendo executado dentro do contêiner através do Git.

6.2.1 Servidor web para Git (Tanajura)

Um dos componentes do sistema de sincronização de arquivos é um servidor web


apelidado de Tanajura. O serviço tem como principal responsabilidade ser um hub de
repositórios Git.

Nestes repositórios está o código fonte dos serviços em que o desenvolvedor está traba-
lhando no momento e o Git é utilizado como mecanismo de sincronização entre o sistema
de arquivos local da máquina do desenvolvedor e o sistema de arquivos do contêiner em
que está sendo executado a aplicação, em um cluster Kubernetes remoto.
65

O serviço conta com endpoints para gerenciamento de repositórios (operações de


criação, remoção e consulta) através de uma interface REST, bem como um endpoint
que implementa os protocolos do Git para transferência e dados através de HTTP. Assim
é possı́vel para qualquer cliente Git a realização das operações básicas como push, pull e
fetch.

Os repositórios Git neste serviço têm como caracterı́stica a efemeridade, ou seja,


eles não precisam ser de mecanismos robustos de persistência e só existem para fins de
sincronização entre dois sistemas de arquivos, podendo ser sobrescritos e apagados a
qualquer momento sem perda de dados importantes.

A escolha do Git para este sistema de sincronização pode ser justificado por algumas
de suas caracterı́sticas que atendiam os requisitos definidos:

- O binário do Git já está presente na máquina do desenvolvedor do Nubank, pois


todos trabalham com Git em seu dia-a-dia.

- É um método eficiente para troca de dados, pois os commits são criados a partir das
modificações feitas em arquivos, ou seja, apenas as diferenças são transmitidas pela rede.

- A troca de dados pode ser criptografada com SSL.

Ao receber um push em algum repositório Git, o Tanajura identifica qual o serviço


associado ao repositório e faz uma requisição HTTP para o Soil a fim de obter a URL do
Gerenciador de Processo (Stinger ) deste serviço.

Uma vez obtida esta URL, o serviço Tanajura faz uma requisição para o Stinger
avisando-o de que há uma nova versão do código disponı́vel para ser sincronizada.

Este, por sua vez, realiza um pull no repositório remoto para obtenção do código atua-
lizado e, após o recebimento dos arquivos, executa os scripts necessários para recompilação
e execução do código.

Esta parte do processo pode ser representada pelo seguinte diagrama de sequência:

Em termos práticos, o processo ocorre da seguinte maneira:

Quando o desenvolvedor deseja implantar em seu devspace um serviço com capacida-


des de sincronização, ele digita o seguinte o comando:

fmc service:deploy:local my-service -f ./config.json /path/to/service

Este comando irá realizar a implantação de um serviço chamado ”my-service”, utili-


66

Figura 28: Fluxo do Tanajura

Fonte: os autores

zando o arquivo de configuração ”./config.json” a partir da pasta local ”/path/to/service”

6.2.2 Gerenciador de Processos (Stinger)

Para que fosse possı́vel o método de sincronização de arquivos utilizando Git foi ne-
cessário o desenvolvimento de uma aplicação que, através de uma interface HTTP exposta
para o cluster, pudesse fazer pull em um repositório Git remoto para obtenção do código
fonte atualizado e, após o recebimento dos arquivos, fosse possı́vel executar um comando
arbitrário para que o processo do serviço pudesse ser recarregado.

Esta aplicação foi apelidada de Stinger e é o binário executado primariamente pela


imagem Docker onde os serviços são executados (esta imagem será descrita em detalhes
mais adiante). Os endpoints HTTP disponı́veis neste serviço são:

POST /reset
Ao receber uma requisição neste endpoint, o Stinger termina o processo do serviço em
execução através de seu identificador de processo (process ID) e executa o script de
cleanup. Após a garantia de que o processo do serviço foi terminado com êxito, é executado
o script de start, que garante sua reinicialização.

POST /pull
67

Ao receber uma requisição neste endpoint, o Stinger executa o comando git pull origin
master para obter a versão mais recente do código no repositório remoto. É possı́vel,
através de parâmetros no corpo da requisição, fazer com que o processo do serviço seja
reinicializado logo após o recebimento do código atualizado.

Quando o Stinger é iniciado, ele verifica a existência do repositório Git local e, caso
não o encontre, executa o comando git clone [url do repositório] [pasta local] para obter
a versão inicial do código. Logo após o recebimento dos arquivos, é feito o processo de
inicialização do serviço.

É possı́vel configurar diversos parâmetros de funcionamento do Stinger através de


variáveis de ambiente. Os mais relevantes são:

APP PATH
É a o caminho para o diretório local onde o Stinger irá clonar o código do repositório
remoto.

exemplo: /app

STINGER SCRIPTS
Caminho para o diretório local onde o Stinger irá executar os scripts para gerenciar os
ciclos de vida do serviço. Dentro desta pasta, devem estar presentes os scripts de start e
cleanup.

exemplo: /scripts

GIT URL
É a URL de onde o Stinger fará o git clone inicial dos arquivos.

exemplo: hhttps:// tanajura.joe.formicarium.nubank.com.br/ service-namei

START AFTER PULL


Caso esta variável esteja setada, o Stinger irá reinicializar o processo sempre após o
recebimento de novo código através do pull. Esta configuração existe porque em certas
linguagens de programação (como a utilizada pela Nubank) não é necessário recompilar e
nem executar nenhum comando para que o novo código obtido seja aplicado no processo
em execução.

De maneira geral, o Stinger é um gerenciador de processos integrado com o Git e que


expõe suas funcionalidades através de um servidor HTTP.
68

6.3 Distributed Debugger

Parte importante do Formicarium é a possibilidade de se realizar a depuração das


aplicações que estão em execução no Devspace do desenvolvedor, para isso foi elaborado
um sistema semelhante a um Distributed Tracing, porém com mais caracterı́sticas volta-
das a um ambiente de desenvolvimento e depuração, por isso, se aproxima mais de um
depurador distribuı́do.

Esse sistema é construı́do a partir de dois principais componentes, o Hive, que é o


servidor central de agregação de eventos de debug, e uma biblioteca de instrumentação que
deve ser integrada aos serviços em execução no Devspace. A comunicação entre os serviços
instrumentados por essas bibliotecas e o Hive se dá através de sockets ZeroMQ, isso
possibilita que qualquer linguagem de programação consiga implementar uma biblioteca
de instrumentação capaz de se comunicar com o Hive.

6.3.1 Hive

Hive é um servidor de agregação de eventos central, para cada Devspace criado, existe
um Hive em execução para coletar e interagir com os serviços daquele Devspace. O Hive é
um serviço backend escrito em Clojure que possui 3 principais componentes: uma interface
ZeroMQ para coleta de eventos dos demais serviços, uma interface HTTP/GraphQL para
servir os dados coletados para a aplicação Desktop e uma interface de conexão nREPL
para permitir interação programática com seu banco de eventos ou mesmo stream destes.

6.3.2 Eventos

Eventos são as mensagens que são enviadas pelos serviços para o Hive com o intuito de
reportar alguma interação (geralmente de IO) que ocorreu consigo. Esse eventos serializa-
dos em JSON e enviados através de sockets assı́ncronos ZeroMQ e possui uma estrutura
bem definida. Os eventos são enviados de forma atômica, porem são compostos de 3
partes lógicas: Identity, Meta e Payload.

O campo de Identity é responsável por informar a identidade do serviço que está


reportando o evento, ele é o identificador único e é utilizado para roteamento da resposta
enviada pelo Hive.

Meta é um campo com a mesma estrutura independente do tipo do evento, nele encon-
tramos informações relativas a metadados do evento em si, como por exemplo Timestamp
69

Figura 29: Visão geral da interação do Hive com os serviços

Fonte: Elaborado pelos autores

de envia e tipo do evento.

O Payload é uma parte do evento que pode variar seu conteúdo de acordo com o tipo
do evento, ele contém mais informações e detalhes a respeito do que está sendo reportado
em si.

Existem 4 tipos de eventos que podem ser enviados por um serviço para o Hive, estes
são: new-event, heartbeat, register, close. Cada um deles possui uma estrutura de Payload
distinta.

O evento de new-event é o principal deles, é o evento que o serviço envia para reportar
uma nova interação que ocorreu em uma de suas portas instrumentadas. Geralmente ele
vem em seu Payload com informações de tracing para correlação entre outros eventos,
70

e também dados a respeito da interação, como por exemplo: Tipo (HTTP ou Kafka),
Direção (Request, Response), Headers, etc...

heartbeat é um evento simples que é enviado continuamente pelo serviço a cada 5


segundos e serve para 2 principais propósitos, manter a conexão TCP aberta e funcionando
entre o serviço e o Hive, e também para reportar o estado de saúde do serviço para o Hive,
que consegue então identificar se o serviço está sendo executado de forma correta, ou se
houve algum problema que impediu o seu funcionamento, dessa forma o serviço é marcado
como Unresponsive e mais tardar como Dead

O evento de register é o primeiro evento enviado pelo serviço para o Hive, ele serve
para firmar a conexão entre ambas partes, esse evento faz o Hive criar as estruturas de
dados referentes a esse novo serviço em seu sistema de armazenamento e também registrar
sua Identity para enviar respostas.

Por fim, o close é o evento enviado quando o serviço deseja encerrar a conexão de forma
graciosa com o Hive, isso possibilita a limpeza de recursos imediatamente e a remoção
do serviço da lista de serviços ativos. Esse evento não é estritamente necessário, pois
esse mesmo processo é realizado caso não seja recebida nenhuma mensagem de heartbeat
durante um intervalo de tempo após o serviço ser marcado como Dead.

6.3.3 Protocolo de Comunicação

Toda comunicação entre os serviços e o Hive é Push Based, portanto é iniciada sempre
pelo serviço em direção ao Hive. Com já dito ZeroMQ é a biblioteca utilizada por debaixo
dos panos na implementação do protocolo de comunicação, o ZeroMQ oferece diversos
tipos de sockets que podem ser utilizados para construir diversas topologias e padrões de
interação. No caso do Hive, foram utilizados os sockets de tipo Dealer para os clientes
(serviços) e Router para o Hive em si. Esses são tipos de sockets mais flexı́veis e se
caracterizam por adotarem um padrão de Request-Response assı́ncrono e multiplexado,
isso é, um socket do tipo Router é capaz de receber diversas conexões e rotear respostas
de forma assı́ncrona para eventos enviados através destas.

Todo evento enviado para o Hive espera receber uma resposta de ACK de volta para
ter certeza que o pacote foi entregue com sucesso. Esse ACK não garante que o evento
foi processado e armazenado corretamente pelo Hive, esse não é o objetivo, ele apenas
serve para saber que não houve nenhum problema de rede que possa ter prejudicado o
recebimento da mensagem. Desse modo, diferente de um padrão convencional de Re-
71

quisição e Reposta, o ACK é apenas uma confirmação da entrega, garantido integridade


e conectividade da rede, e não a corretude lógica da aplicação.

Dois dois lados da comunicação apenas 1 socket de cada tipo é usado, e enquanto a
interface de comunicação é totalmente assı́ncrona e não-bloqueante, os eventos são enfi-
leirados de forma justa para serem enviados um por vez, evitando qualquer problema de
concorrência que possa existir. Esse enfileiramento é feito de forma concorrente em um
buffer especı́fico para isso, não prejudicando o serviço de enviar vários eventos simultane-
amente do pronto de vista da aplicação. Do lado do Hive os eventos logo que recebidos
são despachados para uma fila de processamento assı́ncrona que garante o processamento
paralelo dos eventos e não bloqueia o recebimento de novos eventos.

Como já descrito, existem os eventos do tipo heartbeat que servem para garantir
a integridade da conexão entre serviços e Hive. Esses eventos são enviados a cada N
segundos, e também esperam um ACK de resposta vindo do Hive, isso garante que os
dois lado estão funcionando corretamente. Caso o ACK não seja recebido depois de um
determinado tempo definido, é considerado timeout naquela mensagem e ela é enviada
novamente. Após algumas tentativas de re-envio, se o ACK não foi recebido, o cliente
inicia um processo de reconexão com o Hive, fechando todos os recursos e sockets abertos,
e tentando re-estabelecer contato com o Hive. Caso as re-conexões falhem, então uma
exceção é lançada na Thread de execução da biblioteca cliente e os eventos param de ser
enviados até que algum desenvolvedor depure o problema.

6.3.4 Rastreamento e Correlação de Eventos (Tracing )

Uma parte importante desse mecanismo de depuração distribuı́do é que seja possı́vel
de se correlacionar eventos e rastrear a origem deles de forma subsequente. Esse é um
mecanismo muito utilizado por Distributed Tracers e por isso mesmo foi baseado na es-
pecificação de Open Tracing que criamos uma sistema de rastreamento de eventos para o
Hive. Utilizamos também parte do que já se era utilizado internamento no Nubank para
criar uma solução adequada para sua implantação na empresa.

Partimos a partir da especificação de Open Tracing que define que todo evento deve
enviar, e propagar para o próximo serviço, 3 identificadores diferentes, esses são: TraceID,
SpanID, ParentID. Quando esses 3 identificadores são enviados através da rede para outro
serviço, eles são chamados de Span Context.

Para se entender um pouco melhor cada um desses identificadores primeiro é preciso


72

Figura 30: Protocolo de comunicaçãp utilizado pelo Hive

Fonte: Elaborado pelos autores

compreender 2 conceitos importantes do Open Tracing: Trace e Spans. O Trace é o


que representa uma transação ou fluxo em um sistema distribuı́do, ele atravessa diversos
serviços e pode se propagar de diversas maneiras. O Span é uma operação nomeada e
cronometrada que representa uma parte da execução desse fluxo, um Span pode originar
novos Spans a partir dele, quando isso acontece, chamamos o Span que originou o evento
de Parent Span e o Span originado de Child Span, todos pertencentes ao mesmo Trace.[34]

Figura 31: Propagação de Spans em um Trace

Fonte: hhttps://opentracing.io/img/overview-intro/tracing1 0.pngi

Com isso em mente, podemos agora dizer que o TraceID é o identificador que repre-
73

senta um Trace e o SpanID e ParentID representam Spans, sendo o ParentID o span


pai do SpanID atual, caso seja o primeiro Span da um Trace, o ParentID não existe.
Com esses identificadores sendo enviados para o Hive a cada evento e propagados no Span
Context para o próximo serviço, é possı́vel então criarmos uma correlação de eventos
agrupando todos eventos com o mesmo TraceID e montando a relação de causa-efeito a
partir da corrente de ParentID e SpanID.

6.4 Frontend

6.4.1 Organização do código

Para oferecer uma interface amigável aos usuários, foram construı́das duas aplicações:
uma Interface de linha de comando e uma Aplicação Desktop. Ambas têm funcionalidades
semelhantes, porém a segunda oferece elementos de interface gráfica essenciais para a
construção do módulo de Distributed Debugger do sistema.

Como as duas aplicações compartilham diversos módulos, todo o código comum foi
extraı́do para uma biblioteca (chamada common) e seu código fica disponı́vel para ambas
as aplicações através de um sistema de dependências oferecido pelo NPM.

Foi tomado o cuidado de não se criar acoplamentos dos módulos desta biblioteca
com casos de uso especı́ficos de cada aplicação, tornando o código mais reaproveitável e
desacoplado, com responsabilidades bem definidas.

O código dos três módulos vive em mesmo repositório Git, dividido apenas por pastas.
Esta prática é comum para o gerenciamento de múltiplos módulos que compõe um sistema
maior e repositórios com esta caracterı́stica são denominados monorepos.

Este tipo de organização é vantajosa pois preserva os benefı́cios de desacoplamento das


diferentes partes do sistema, porém eliminando, através do uso de ferramentas especı́ficas,
as complexidades de se gerenciar as dependências entre os diversos módulos, tanto no
estágio de desenvolvimento quanto nos estágios de compilação e publicação.

Para gerenciar as dependências do monorepo, foi utilizado a ferramenta yarn workspa-


ces, que otimiza o uso de memória em disco do código fonte e o tempo de instalação inicial
para novos desenvolvedores através do compartilhamento das dependências comuns entre
os vários módulos. Ela também é responsável por criar links simbólicos (symlinks) entre
os módulos quando em estágio de desenvolvimento. Os symlinks permitem iterações mais
rápidas de desenvolvimento, pois uma mudança no código de uma dependência irá refletir
74

automaticamente nos módulos dependentes, sem a necessidade de se incrementar versões


e recompilar partes do programa.

Todos os módulos do projeto utilizam Semantic Versioning como padrão versiona-


mento. O incremento é feito automaticamente através de uma ferramenta que analisa as
mensagens presentes nos commits do Git para determinar, através de tokens pré-definidos,
qual será o incremento de versão: Major, minor, patch.

Além do incremento automático em cada módulo, foi utilizada a ferramenta Lerna


para determinar quais módulos e dependências devem ter suas versões atualizadas, através
também da análise de quais arquivos foram modificados nos commits desde a última
atualização.

Todo este ferramental tornou a experiência de desenvolvimento deste projeto muito


mais eficiente e facilitará também sua manutenção.

6.4.2 Interface de linha de comando

Para a construção deste software, foi utilizado a linguagem Typescript, com a plata-
forma Node.js. A framework para desenvolvimento foi a ocli, mantida pela Salesforce.

Este software é distribuı́do através do NPM. Por ser uma ferramenta já presente nos
computadores dos engenheiros do Nubank, a instalação da linha de comando se torna
simples e rápida, basta executar o comando

npm install -g @formicarium/cli

Com isso, o binário fmc já fica disponı́vel para ser executado através do terminal.

Para atualizar a versão do software após a publicação de alguma modificação, basta


o comando

npm update -g @formicarium/cli

Este mecanismo de fácil distribuição e atualização foi escolhido para facilitar ainda
mais a adesão dos engenheiros ao uso do ecossistema Formicarium.

A interface de linha de comando apresenta suas funcionalidades divididas em sub-


programas, cada um referente a um conjunto de casos de uso agrupados logicamente.
75

Utilizando o comando help, o engenheiro tem acesso à documentação da ferramenta, po-


dendo listar os subprogramas e comandos disponı́veis dentro de cada nı́vel hierárquico de
funcionalidades e entender o que fazem através de descrições e exemplos de uso.

Por exemplo, ao requisitar ajuda no contexto geral da linha de comando:

A CLI to operate on Formicarium


VERSION
@formicarium/cli/1.3.2 darwin-x64 node-v11.1.0
USAGE
$ fmc [COMMAND]
COMMANDS
devspace Creates a Devspace
git Configures local fmcgit and hive
help display help for fmc
repl Connects to remote repl. If no service is provided, connects
service Deletes a service in the current Devspace
setup Configures Formicarium CLI to one cluster

Ao requisitar ajuda para um contexto especı́fico (no caso, o contexto dfevspace):

Creates a Devspace

USAGE
$ fmc devspace:COMMAND

COMMANDS
devspace:create Creates a Devspace
devspace:delete Deletes a Devspace
devspace:info Get information for the current devspace
devspace:list List availables Devspaces
devspace:services Lists the services in your devspace
devspace:use Configures to use one Devspace context

Ao requisitar ajuda para um comando especı́fico (no caso, o comando devspace:create):


76

Creates a Devspace

USAGE
$ fmc devspace:create ID

OPTIONS
-h, --help show CLI help
--test

EXAMPLE
$ fmc devspace:create paps

A facilidade proporcionada ao engenheiro do Nubank para entender o uso da fer-


ramenta através desta documentação interativa fez parte da estratégia do grupo para
disseminação e adesão da ferramenta por toda a empresa de maneira escalável.

Tabela 3: Comandos disponı́veis e uma breve descrição do que fazem


Comando Descrição
devspace:create Cria um novo devspace
devspace:delete Deleta um devspace existente
devspace:info Obtém informações sobre o devspace atual
devspace:list Lista todos os devspaces disponı́veis
devspace:services Lista todos os serviços em execução no devspace atual
devspace:use Muda o contexto do Formicarium para utilizar outro devspace
sync:push Sincroniza o sistema de arquivos local com o de um serviço remoto
service:delete Deleta uma serviço em execução no devspace atual
service:deploy Implanta um novo serviço no devspace atual
service:logs Obtém os logs de um serviço
service:restart Reinicia o processo do serviço
repl Inicia uma sessão de REPL com o serviço

6.4.3 Aplicação Desktop

Como parte do ferramental do ambiente Formicarium, foi desenvolvida uma aplicação


Desktop multiplataforma (Windows, Linux, macOS) com o intuito de melhorar a ex-
periência dos usuários nas funções mais comuns e também para atender requisitos do
sistema que exigiam interfaces gráficas mais interativas e avançadas. A aplicação foi de-
senvolvida utilizando tecnologias web (HTML5, CSS3 e Javascript), o que tornou possı́vel
sua execução em diferentes plataformas utilizadas pelos engenheiros do Nubank. Como
77

era necessário o uso de APIs nativas do sistema (como acesso ao sistema de arquivos e
execução de outros programas a partir da interface gráfica) que não seriam possı́vel no
browser, foi utilizada a framework Electron para empacotar a aplicação em um binário
capaz de ter acesso às system calls necessárias. Electron é a framework por trás de di-
versos projetos populares como Slack, WhatsApp Desktop, Visual Studio Code, Atom,
Spotify entre outros. Além disso, conta com uma enorme comunidade, melhorando ainda
as caracterı́sticas de manutenibilidade de software da solução.

Para a sua distribuição dentro do Nubank, o software compilado foi disponibilizado


em um bucket da AWS S3 acessı́vel apenas para funcionários credenciados download e
execução para começar a utilizá-lo.

A aplicação ainda não conta com sistemas automatizados de atualização, sendo ne-
cessária a obtenção manual de uma nova versão sempre que o engenheiro veja a necessi-
dade de atualização. Este mecanismo de atualização poderá ser aprimorado em melhoria
futuras.

Nesta seção será feita uma breve descrição das diversas telas implementadas na solução
bem como a exposição de capturas de tela em diversos estados da aplicação.

6.4.3.1 Gerenciamento de Devspaces

Esta área tem como objetivo mostrar ao engenheiro os Devspaces disponı́veis para
serem utilizados. Normalmente o Devspace pessoal do engenheiro estará selecionado,
porém é possı́vel por exemplo alternar entre este e o Devspace do squad ou ainda o de
algum colega caso queiram compartilhar alguma configuração para reproduzir ambientes
de testes e desenvolvimento.

6.4.3.2 Infraestrutura do Devspace

Nesta área o engenheiro pode conferir o status dos serviços básicos de infraestrutura
do Devspace, como o Hive (para Distributed Tracing) e o Tanajura (para sincronização
de arquivos). Também é possı́vel extrair os logs destes serviços em tempo real.

6.4.3.3 Serviços implantados

Nesta área o engenheiro pode conferir o status dos serviços implantados no Devspace
atual e também interagir com cada um deles de forma independente. É possı́vel a obtenção
em tempo real dos logs (tudo que for direcionado ao stdout e stderr do processo), a
78

Figura 32: Tela Lista de Devspaces

Fonte: Elaborado pelos autores

reinicialização do processo e também a sua remoção do Devspace através de botões. A


lista também apresenta as interfaces de cada processo expostas para a internet para
permitir outros tipos de interação, como por exemplo a interface de um possı́vel servidor
HTTP do serviço e a interface HTTP do Stinger, que é um gerenciador de processos
oferecido pelo Formicarium e que está presente nos serviços implantados no Devspace.

6.4.3.4 Implantação de novos serviços

Esta área conta com um formulário para implantação de um novo serviço. É possı́vel
inserir o nome, escolher se o serviço é do tipo Syncable (o que fará com que a imagem
Docker Chamber seja usada). Caso esta opção esteja selecionada, o engenheiro deve
apontar o diretório local em que está o código fonte do serviço para que este seja enviado
para o contêiner remoto e possa ser sincronizado em seguida.
79

Figura 33: Tela Informações do Devspace

Fonte: Elaborado pelos autores

6.4.3.5 Distributed Debugger

Nesta interface gráfica, o engenheiro consegue visualizar as interações entre os serviços


em seu devspace (ou seja, as trocas de mensagens HTTP e Kafka) através de um grafo.
Este grafo é construı́do por um algoritmo que, através da lista de mensagens coletadas
em um perı́odo pelo Hive, determina seus nós e arestas.

Os nós serão sempre os serviços envolvidos nas trocas de mensagens, enquanto que
as arestas representam as interações entre estes serviços. Estas interações podem ser
de diversos tipos, porém na implementação atual apenas mensagens HTTP e Kafka são
corretamente interpretadas pelo sistema.

Esta área conta também com uma visualização em forma de árvore, agrupando as
mensagens hierarquicamente utilizando seu SpandID. Este tipo de visualização ajuda a
acompanhar os eventos em função de sua ”profundidade”. A profundidade de um evento é
determinada pelo número de eventos que tiveram de ocorrer para que este fosse finalmente
desencadeado.
80

Figura 34: Tela Serviços do Devspace

Fonte: os autores - 2018-11-10

É possı́vel aplicar filtros na lista de eventos para que apenas os de interesse sejam usa-
dos na geração das visualizações. Por exemplo, é possı́vel filtrar por eventos provenientes
apenas de determinados serviços ou por tipos de eventos especı́ficos.

O grafo é interativo para o usuário, ou seja, é possı́vel movimentar com o cursor os nós
e arestas para rearranjá-lo, de modo a facilitar a visualização de uma determinada região
de interesse. Ao realizar um clique em uma aresta, uma nova tela com detalhes daquela
interação é exibida onde o engenheiro pode obter informações completas da interação,
como timestamps, corpo e cabeçalho da mensagem, remetente e destinatário, códigos de
status de resposta, etc.

6.4.3.6 Arquivos sincronizados

Nesta tela é possı́vel visualizar os serviços que estão em modo de sincronização habili-
tado e acompanhar quais arquivos foram de fato sincronizados e o estado dessa operação
(carregando, sucesso ou erro).
81

Figura 35: Tela Logs de um serviço

Fonte: Elaborado pelos autores


82

Figura 36: Tela Sincronização de arquivos em um serviço

Fonte: Elaborado pelos autores


83

Figura 37: Tela Detalhes de uma requisição

Fonte: Elaborado pelos autores


84

Figura 38: Tela Grafo de interações em Tracing

Fonte: Elaborado pelos autores


85

7 TESTES E AVALIAÇÃO

7.1 Testes Unitários

Testes unitários foram feitos para por à prova pedaços especı́ficos do código fonte de
cada sistema. São muito úteis para evidenciar erros, e no caso do nosso projeto, foram
feitos em escopo de função (quando a mesma apresentasse algum tipo de lógica não-trivial,
como transformação de dados, criação de Devspaces, etc). Os mesmos eram executados
em tempo de desenvolvimento, e também foram feitos passos de teste no nosso ambiente
de CI/CD, permitindo que os artefatos com versões novas dos serviços e bibliotecas só
pudessem ser gerados caso todos os testes unitários estivessem passando.

7.2 Testes Integrados

Testes Integrados foram feitos para por à prova um conjunto de módulos de cada
microsserviço, que no nosso caso corresponde aos módulos de banco de dados, camada
web, camada de lógica de negócio, módulo de mensageria Kafka, dentre outras.

O teste integrado correspondeu a subir os módulos referidos, com algumas confi-


gurações simplificadas (por exemplo, banco de dados local, Kafka mockado, etc) e desen-
volver asserções.

Fizemos testes integrados no Soil para estressar toda a manipulação de Devspaces,


simulando requisições da CLI ou UI, como por exemplo, um disparo no endpoint de criar
ou deletar Devspace.

Fizemos testes integrados para o Config Server do Nubank também, mais focados
nas respostas de variáveis de ambiente e como fonte de documentação para nós mesmos
adaptarmos o código do Soil para lidar corretamente com a criação de Devspace a partir
86

da resposta do mesmo.

No Hive, testes de integração foram feitos para estressar todo o fluxo de eventos, desde
receber um evento até servir o mesmo de resposta para a UI.

7.3 Teste entre os integrantes

Este foi um caso simples de teste de teste de aceitação de usuário, onde os usuários
eram 3 dos 4 integrantes do grupo. Foi uma experiência feita por um dos integrantes, sem
avisar os outros três de que a usabilidade do Formicarium estava sendo testada.

Enquanto refatorávamos as bibliotecas de cliente usadas em um serviço do Nubank,


um dos integrantes fez uma alteração de código em um dos componentes do microsserviço,
introduzindo um bug que causava uma NullPointerException, possı́vel de ser evidenci-
ada apenas em tempo de execução. Mais especificamente, o código responsável por enviar
as mensagens via ZeroMQ transforma os mapas de evento de Clojure para uma string
JSON. A alteração foi feita para causar essa transformação falhar, e tentarmos enviar um
nulo para o Hive, causando a exceção referida. Tal cenário é impossı́vel de ser reproduzido
em testes unitários pois para os mesmos, os sockets de ZeroMQ são mockados.

Com essa modificação feita, o integrante pediu ajuda do resto da equipe para resolver
o problema, e a equipe foi modificando iterativamente cada parte do código para tentar
encontrar o problema (a saber, colocando logs e verificando requisições no contêiner),
usando o file watch do Formicarium, até descobrir e resolver o problema, em cerca de 30
minutos. Ao final da experiência, o integrante responsável por introduzir o bug comentou
do experimento, e a equipe concluiu que a infraestrutura do Formicarium facilitou o
processo.

Esse foi um exemplo de teste sem maiores planejamentos, porém foi uma experiência
que nos permitiu por à prova se o Formicarium de fato teria alguma utilidade prática, caso
conseguisse lidar com um dos casos de erro mais comuns evidenciados no desenvolvimento
de microsserviços.

7.4 Teste com um engenheiro do Nubank

Na segunda semana de Outubro, pedimos para um engenheiro do Nubank experimen-


tar o nosso software, como testador alfa. Fizemos o setup da máquina e nos prontificamos
para lidar com quaisquer dúvidas. Ao longo de um dia de uso, o engenheiro sofreu com
87

alguns problemas da rede local, mas conseguiu executar todas as funcionalidades do For-
micarium.

7.5 Implantação em um squad

Em seguida, tentamos implementar o produto em uma equipe no Nubank, onde apre-


sentamos o produto em uma reunião e pedimos para teste dos integrantes, caso desejassem.

O produto não teve muito uso, fora os dois primeiros dias. Conversando com os
integrantes para entender o motivo, identificamos que a infraestrutura e API’s são úteis,
porém fazer todo o setup de estado inicial é muito custoso, e os engenheiros nem sempre
sabem quais são os serviços envolvidos em um fluxo de negócio que desejavam aprender
utilizando o Formicarium.
88

8 CONSIDERAÇÕES FINAIS

8.1 Conclusões do Projeto de Formatura

Atacar um problema real e não resolvido de produtividade de Engenharia direcionado


à empresa de estágio se mostrou uma experiência importante para aprendizado e ama-
durecimento das competências de engenharia dos integrantes. foi possı́vel atravessar a
fronteira teórica de múltiplos conceitos-chave, alguns vistos no curso, para a fabricação
de um produto útil, voltado para outros engenheiros.

O caráter multidisciplinar do projeto se mostrou primordial para tanto: O simples


conhecimento aprofundado das ferramentas por si só não é o suficiente para chegar à
um produto de sucesso, mesmo que na esfera local da empresa. É preciso saber lidar sob
pressão, correndo contra prazos, ser autocrı́tico para ver rapidamente que uma tentativa de
solução é inviável, e trocar de plano, de modo ágil. Sempre entender onde se deseja chegar,
e como se pretende alcançar um objetivo. Estar disposto a aprender algo completamente
novo e sair da zona de conforto.

Tentar traçar os desejos dos usuários da forma mais detalhada possı́vel para desen-
volver algo que atenda às expectativas é algo que exige anos de experiência na área, mas
pudemos sentir um pouco do gosto de tal atividade na pele. Fora os obstáculos técnicos,
um projeto real de sucesso também exige um gerenciamento cuidadoso das pessoas partici-
pantes e planejamento profundo dos processos adotados para execução do mesmo, pontos
que são dificilmente desenvolvidos fora a participação em um projeto real de Engenharia,
mas indispensáveis para o desenvolvimento de um profissional responsável.

Com esses aprendizados em mente, unidos à boa perspectiva de uso do produto no con-
texto que desejamos abordar, podemos afirmar que o Formicarium se mostra um sucesso
para nós no presente, e uma promessa de sucesso ainda maior para todos os engenheiros
do Nubank no futuro.
89

8.2 Contribuições

Durante o projeto, foi utilizado e integrado diversas soluções abertas. As mais rele-
vantes listadas a seguir:

• Kubernetes: projeto que é a plataforma da nossa solução, permitiu maior facili-


dade de interação com sistemas distribuı́dos.

• node-git-server: um projeto open source que facilita a criação de servidores Git


customizados.

• jeromq: projeto que implementa o protocolo do ZeroMQ para Java Virtual Ma-
chine.

8.3 Perspectivas de Continuidade

Mesmo com a relativa complexidade do projeto desenvolvido nessa monografia, di-


versos pontos se mantém abertos no backlog, provenientes da metodologia descrita em
capı́tulos passados desse documento. Com a completude do MVP, é esperado que es-
ses pontos sejam atacados no ano de 2019, idealmente com os integrantes da concepção
participando do desenvolvimento de forma direta ou indireta (consultiva). Quanto às pers-
pectivas de manutenção, melhorias e desenvolvimento de novas features do Formicarium,
alguns modos de trabalho se mostram possı́veis:

1. Squad (com 3 a 5 engenheiros) totalmente alocados para execução destas atividades.

2. Pack (2 a 5 engenheiros) dentro de um squad horizontal (que dá suporte aos outros
engenheiros, como devops, engineering productivity, etc.) alocados para a execução
dessas atividades.

3. Task Force (pelo menos dois engenheiros), participantes de quaisquer squads, que
desenvolvem features no tempo livre das responsabilidades e priorizações de seus
squads.

4. Suspensão do projeto por tempo determinado ou indeterminado.

Supondo que a alocação de pessoas para trabalhar no projeto se mostre interessante


e esteja alinhada aos interesses da empresa (o último item da lista acima não seja o
90

escolhido), levantamos com os stakeholders os seguintes requisitos, que agregam algum


tipo de valor à experiência do usuário final (por agora não mensurados, porém com ganhos
qualitativos evidentes) ou por ajudarem a manutenção e testabilidade da code base. A
descrição de alto nı́vel dessas tarefas está a seguir:

Cache compartilhado de dependências


As dependência de Maven dos microsserviços Clojure desenvolvidos no Nubank são
salvas na pasta h∼\.m2i do contêiner alocado para execução do processo. Os tes-
tes empı́ricos realizados com manipulação de Devspaces mostraram um pequeno
incômodo: Quando o serviço é executado pela primeira vez no cluster, nenhuma
dependência existe no repositório mencionado. Por conta disso, toda a árvore de
dependências tem que ser obtida do repositório remoto do Maven. Este processo
pode levar até 20 minutos para um serviço usual do Nubank, apesar de sincronizações
futuras (utilizando o mecanismo de FileSync) serem praticamente instantâneas.
Estes 20 minutos de espera da primeira vez podem gerar frustrações, e é desejado
uma experiência mais ágil de interação e desenvolvimento dos microsserviços. Para
resolver este problema, uma possı́vel solução é fazer uma espécie de cache comparti-
lhado dos pacotes Maven. Como todos os Devspaces são executados em um mesmo
ambiente da empresa (a saber, stack de teste), é possı́vel criar um volume (utili-
zando o AWS Elastic Block Storage) compartilhado por todos os Devspaces onde a
pasta h∼\.m2i será montada. A montagem deste volume compartilhado dentro do
contêiner obviamente é mais rápido do que o download de todas as dependências
através da internet, tornando o deploy de um serviço novo no Formicarium muito
mais rápido.

Kafka Facade Service


Um outro projeto de melhoria possı́vel seria a criação de um Facade para lidar com
a API de kafka via linha de comando ou UI. A primeira versão do Formicarium,
descrita nessa monografia suporta apenas chamadas HTTP como forma de iniciar
um fluxo que envolve vários microsserviços. No entanto, uma coisa muito corrente
que se deseja avaliar enquanto se lida com microsserviços é a maneira como o mesmo
”altera o mundo”(eg, mudanças de estado, chamadas de API, mensagens enviadas,
logs reportados, etc) a partir do consumo de uma mensagem Kafka. O Formicarium
provê facilmente para o desenvolvedor apenas a interação via HTTP. Engenheiros
mais versados em infraestrutura de Kafka poderão lidar com o consumo de mensa-
gens para iniciar um fluxo no Devspace usando a API nativa do Kafka, mas este é
um processo cansativo e propenso a erros.
91

Para lidar com isso, pensamos na criação de um serviço para servir como Facade
para a API nativa do Kafka, expondo via HTTP uma simplificação/agregação das
funcionalidades expostas nos binários de implementação de Kafka. Com o suporte
nativo dado pelo Formicarium para HTTP ficaria simples de portar essas chamadas
ao Facade na UI e na CLI. Vale apontar que esse Facade tem valor de negócio para
a empresa independentemente do Formicarium, pois facilita a interação direta dos
desenvolvedores para uma das entradas de dados de seus microsserviços.

Arquivo de descrição de fluxo de negócio


Na implementação corrente o Formicarium não provê uma maneira de fazer deploy
simultâneo de diversos serviços ”de uma vez”. Isso quer dizer que, para testar a
interação de diversos microsserviços, hoje o usuário do Formicarium terá que saber
a priori, todos os serviços envolvidos no fluxo e subir todos, um a um. Um projeto
possı́vel de extensão do Formicarium com bastante valor seria criar uma funcio-
nalidade que permitisse a descrição de Fluxos de Negócio (conjunção de vários
serviços), descritos num arquivo com formatação especı́fica (podendo ser yaml, xml,
json, etc).
Só isso já economizaria bastante tempo do desenvolvedor, que daria um único co-
mando para fazer deploy de diversos serviços e não precisaria conhecer todos, a
priori.
Com isso funcionando, uma feature de exportagem e compartilhamento de flu-
xos, compostos por um conjunto de serviços e versões especı́ficas dos mesmos seria
possı́vel, dando ainda maior poder e autonomia para os engenheiros pensarem em
modificações e impactos das mesmas na lógica de negócio que envolve mais de um
serviço.

Flamegraph
Flamegraph é um tipo de gráfico bastante interessante para representação tempo-
ral de um fluxo de eventos (opcionalmente independentes entre si). Ele demonstra
o tempo gasto processando cada evento, exatamente na ordem em que os eventos
foram processados. A figura 39 mostra um exemplo de flamegraph.
Hoje o Formicarium provê apenas uma visualização de grafo de acontecimento dos
eventos, que é muito bom para ver a estrutura de chamadas e os serviços envol-
vidos, mas deixa um pouco a desejar quando o usuário quer ter um detalhamento
mais granular nos tempos de processamento envolvidos. Nesse contexto, implemen-
tar uma visualização de Flamegraph para os eventos se mostraria vantajoso pois
estenderia as possibilidades de debugging de um conjunto de eventos providas pelo
92

Formicarium.

Figura 39: Exemplo de flamegraph

Fonte: https://rickosborne.org/blog/tag/flame-graph/
93

REFERÊNCIAS

1 FOWLER, S. J. Production-Ready Microservices. [S.l.]: O’Reilly, 2016.

2 RICHARDSON, C. Microservices Patterns. [S.l.]: Manning, 2016.

3 ALSHUQAYRAN, N.; ALI, N.; EVANS, R. A systematic mapping study in


microservice architecture. IEEE, Brighton, UK, 2016.

4 UFPR. Conceitos: Ambiente de Desenvolvimento. 2001. Disponı́vel em: hhttp:


//www.funpar.ufpr.br:8080/rup/process/workflow/environm/co\ deven.htmi. Acesso
em: 01 dez. 2018.

5 OSNAT, R. A Brief History of Containers: From the


1970s to 2017. 2018. Disponı́vel em: hhttps://blog.aquasec.com/
a-brief-history-of-containers-from-1970s-chroot-to-docker-2016i. Acesso em: 25
out. 2018.

6 LINUX-VSERVER. Linux-VServer. 2013. Disponı́vel em: hhttp://linux-vserver.org/


Overviewi. Acesso em: 25 out. 2018.

7 ORACLE. Chapter 16 Introduction to Solaris Zones. 2010. Disponı́vel em:


hhttps://docs.oracle.com/cd/E19455-01/817-1592/zones.intro-1/index.htmli. Acesso em:
25 out. 2018.

8 WIKIPEDIA. OpenVZ. 2018. Disponı́vel em: hhttps://en.wikipedia.org/wiki/


OpenVZi. Acesso em: 25 out. 2018.

9 BAUER, R. What’s the Diff: VMs vs Containers. 2018. Disponı́vel em:


hhttps://www.backblaze.com/blog/vm-vs-containers/i. Acesso em: 27 out. 2018.

10 LEWIS, M. F. . J. Microsserviços em poucas palavras. 2015. Disponı́vel em:


hhttps://www.thoughtworks.com/pt/insights/blog/microservices-nutshelli. Acesso em:
22 nov. 2018.

11 RICHARDSON, C. Microservices: Decomposição de Aplicações para Implantação


e Escalabilidade. 2014. Disponı́vel em: hhttps://www.infoq.com/br/articles/
microservices-introi. Acesso em: 01 dez. 2018.

12 GOOGLE. Dapper, a Large-Scale Distributed Systems Tracing Infrastructure. 2010.


Disponı́vel em: hhttps://ai.google/research/pubs/pub36356i. Acesso em: 08 nov. 2018.

13 TWITTER. Zipkin. 2018. Disponı́vel em: hhttps://zipkin.io/i. Acesso em: 08 nov.


2018.

14 UBER. Evolving Distributed Tracing at Uber Engineering. 2017. Disponı́vel em:


hhttps://eng.uber.com/distributed-tracing/i. Acesso em: 08 nov. 2018.
94

15 OPENTRACING. OpenTracing. 2018. Disponı́vel em: hhttps://opentracing.io/i.


Acesso em: 08 nov. 2018.

16 SOCIETY, I. HTTP status codes. 1999. Disponı́vel em: hhttps://tools.ietf.org/html/


rfc2616i. Acesso em: 22 nov. 2018.

17 HINTJENS, P. ØMQ - The Guide. 2018. Disponı́vel em: hhttp://zguide.zeromq.org/


page:alli. Acesso em: 01 dez. 2018.

18 CLOJURE. Clojure Rationale. 2018. Disponı́vel em: hhttps://clojure.org/about/


rationalei. Acesso em: 08 nov. 2018.

19 INC., D. Docker overview. 2018. Disponı́vel em: hhttps://docs.docker.com/engine/


docker-overview/i. Acesso em: 01 dez. 2018.

20 BABAOğLU Özalp; MARZULLO, K. Consistent global states of distributed systems:


Fundamental concepts and mechanisms. Bologna, Itália, 1993.

21 CONTRIBUTORS, K. Namespaces. 2018. Disponı́vel em: hhttps://kubernetes.io/


docs/concepts/overview/working-with-objects/namespaces/i. Acesso em: 08 nov. 2018.

22 CONTRIBUTORS, K. Pod Overview. 2018. Disponı́vel em: hhttps://kubernetes.io/


docs/concepts/workloads/pods/pod-overview/i. Acesso em: 08 nov. 2018.

23 CONTRIBUTORS, K. Deployments. 2018. Disponı́vel em: hhttps://kubernetes.io/


docs/concepts/workloads/controllers/deployment/i. Acesso em: 08 nov. 2018.

24 CONTRIBUTORS, K. Services. 2018. Disponı́vel em: hhttps://kubernetes.io/docs/


concepts/services-networking/service/i. Acesso em: 08 nov. 2018.

25 CONTRIBUTORS, K. Ingress. 2018. Disponı́vel em: hhttps://kubernetes.io/docs/


concepts/services-networking/ingress/i. Acesso em: 08 nov. 2018.

26 RUBIN, K. S. A pratical guide to the most popular Agile Process. [S.l.]:


Addison-Wesley Professional, 2012.

27 TAKEUCHI, N. The New New Product Development Game. 1986. Disponı́vel em:
hhttps://hbr.org/1986/01/the-new-new-product-development-gamei. Acesso em: 22 nov.
2018.

28 GITHUB. GitHub. 2018. Disponı́vel em: hhttps://github.com/i. Acesso em: 14 nov.


2018.

29 CLUBHOUSE. Project Management for Software Development teams. 2018.


Disponı́vel em: hhttps://clubhouse.io/i. Acesso em: 14 nov. 2018.

30 SLACK. slack. 2018. Disponı́vel em: hhttps://slack.com/i. Acesso em: 14 nov. 2018.

31 ISO; ITU. Odp reference model part 1 overview. Australia, 1995.

32 HEROKU. Heroku Platform. 2018. Disponı́vel em: hhttps://www.heroku.com/


platform/i. Acesso em: 19 nov. 2018.

33 HANJURA, A. Heroku Cloud Application Development. [S.l.]: Packt, 2014.


95

34 OPENTRACING. OpenTracing Overview. 2018. Disponı́vel em: hhttps:


//opentracing.io/docs/overview/i. Acesso em: 01 dez. 2018.
96

APÊNDICE A
97

Figura 40: Modelagem de objetos Kubernetes para stream de logs

Fonte: os autores

Você também pode gostar