Conteúdo


Executando a transição para uma arquitetura centrada em nuvem

Uma analise aprofundada de como uma equipe de desenvolvimento de produto da IBM passou rapidamente para os microsserviços gravados em Node.js

Comments

Ao introduzir uma abordagem totalmente nova da resolução de problemas, as arquiteturas centradas em nuvem são grandes inovações no setor de tecnologia. Agora, as empresas podem inserir soluções no mercado com mais rapidez e na escala da nuvem, com um investimento menor. As startups e as empresas criadas na nuvem foram as primeiras a adotar o desenvolvimento nativo na nuvem, mas a transição para arquiteturas centradas em nuvem também está ganhando popularidade nas grandes corporações.

A transição para uma arquitetura centrada em nuvem geralmente exige decisões em cinco áreas principais: transporte de dados, formato de dados, segurança, APIs e elasticidade. Este artigo descreve nossa jornada, nossas decisões de tecnologia e as lições aprendidas tomadas em nossa transição para uma arquitetura centrada em nuvem. Você verá como nós transformamos um aplicativo Java™ Platform, Enterprise Edition tradicional em um conjunto leve de microsserviços gravados em Node.js.

Nosso ponto de partida e nossa meta de negócio

Nossa jornada começou com um produto IBM chamado Presence Zones. Ele já estava no mercado há alguns anos e queríamos simplificar sua entrega e aumentar sua capacidade para que mais clientes passem a usá-lo. Mudando sua arquitetura para entregá-la como um serviço baseado em nuvem na plataforma de nuvem IBM Cloud,® conseguimos fornecer valor ao cliente e oferecer novos recursos de forma incremental. A oferta foi renomeada para Presence Insights.

Nossa meta de negócio para o serviço do Presence Insights era fornecer o equivalente à analytics da web para o mundo físico. Por exemplo, imagine que sua cafeteria preferida deseje melhorar a sua experiência. Quando você (e seu dispositivo mobile) entra na cafeteria, que sabe quem você é (você já optou por esse serviço), pergunta se você quer o mesmo que já consumiu anteriormente e oferece um código wifi gratuito porque recentemente você passou três horas lá dentro. Esse reconhecimento requer uma arquitetura de Internet das Coisas na qual o sistema processa eventos emitidos por sensores, como wifi e indicadores, para rastrear movimentos de dispositivo dentro de um local físico.

Inicialmente, nosso público-alvo para o Presence Insights eram os varejistas, pois eles foram os consumidores inovadores, tentando entender e envolver os clientes em seus espaços físicos, mas agora, muitos outros setores e soluções estão lidando com os quatro pilares relacionados a detectar, analisar, agir e envolver os clientes, conforme eles passam por um espaço físico.

Estamos executando a transição do protótipo para o incubador e, enfim, para o serviço disponível ao público no IBM Cloud em menos de seis meses.

Escolhas de tecnologia para nossa arquitetura de microsserviços

Nossa abordagem geral era começar desenvolvendo um conjunto de microsserviços Node.js simples. Alavancamos o ecossistema dos pacotes Node.js existentes que interagiam usando um protocolo de sistema de mensagens leve chamado AMQP (Advanced Message Queuing Protocol) usando MQ Light. Isso forneceu um baixo acoplamento entre os serviços, além de uma maneira fácil de iterar em cada serviço.

Continuando o desenvolvimento da plataforma, adotamos tecnologias mais novas como bancos de dados NoSQL (Cloudant) e mecanismo de procura do Lucene (Elastic Search) para analytics, além de uma nova abordagem do armazenamento em cache na memória (Redis) usando armazenamentos de chave-valor para agirem como um amortecedor de choque para o Cloudant.

Tabela 1. Comparando o Presence Zones e o Presence Insights
Presence Zones (produto)Presence Insights (serviço)
Aplicativo Java Enterprise Edition tradicionalMais de 29 microsserviços
Mais de 317 instâncias do Node.js
Três regiões do IBM Cloud
Modelo de entrega no local Modelo de entrega de SaaS no IBM Cloud
WebSphere Application Server (camada da web)Node.js (camada da web)
DB2 (persistência)Cloudant (persistência)
Cognos (relatório)MQ Light (sistema de mensagens)
Redis (armazenamento em cache/notificação de evento)
Elastic Search (analytics)
D3 (criação de gráfico)
Centro de conhecimento (Docs)Redução de preço (Docs)
Proprietário únicoVários proprietários

Todas as nossas escolhas de tecnologia foram arquiteturas comprovadas, com escalabilidade na nuvem, usadas com sucesso por empresas criadas na nuvem. O resultado final dessas escolhas também comprovou o sucesso dessas escolhas para nossa equipe. Estamos executando a transição do protótipo para o incubador e, enfim, para o serviço disponível ao público no IBM Cloud em menos de seis meses.

A Figura 1 mostra o fluxo de dados de eventos de sensor entrando no sistema e interagindo com o restante do sistema. O Presence Insights pode ser acessado a partir de uma perspectiva de tempo de execução por meio de sensores e também de uma perspectiva de painel, que os administradores podem configurar para definir um site, configurar regras ou visualizar métricas analíticas relacionadas ao tráfego de eventos do sensor em períodos de tempo específicos.

Figura 1. Arquitetura de nuvem técnica do Presence Insights
Presence Insights technical cloud architecture
Presence Insights technical cloud architecture

Para eventos de sensor, o principal ponto de entrada fica na camada do conector. Cada conector suportado tem um formato separado descrito pelo fornecedor do sensor (wifi ou indicador). Os conectores devem "disparar e esquecer" e têm o propósito principal de receber as cargas úteis dos sensores, validar as cargas de trabalho e publicar os eventos rapidamente em um tópico no barramento de mensagem (MQ Light). Existem vários "assinantes" para cada tópico, e cada assinante tem um propósito bem definido (de acordo com o modelo baseado em agente, como o kit de ferramentas de software livre Akka, no qual cada um executa um único processo e o executa muito bem). Os propósitos incluem serializar os eventos que entram no sistema, armazenar os eventos no repositório de analytics hospedado dentro do Elastic Search on Compose e disparar eventos de webhook para sistemas que adotaram ações específicas (por exemplo, um usuário está permanecendo em um local específico por um determinado período de tempo).

Misturado a esses fluxos está o cache na memória baseado no Redis e a camada de persistência que aproveita o Cloudant. Nesse modelo, definimos o sistema de uma forma que se alguma parte do sistema não estiver funcionando, o sistema continua a operar: existem dependências mínimas ou pontos únicos de falha, conforme os dados fluem no sistema.

Da mesma forma, para o painel de gerenciamento, um conjunto de microsserviços foi incumbido no painel da UI (interface com o usuário). Conforme aumentaram os recursos de gerenciamento, começamos a dividir os serviços com base em função, como um microsserviço para analytics, um para configuração de site e um para processamento de regras. Com isso, foi possível iterar rapidamente em cada elemento do sistema e entregar de forma independente as atualizações de cada serviço, conforme o necessário.

A arquitetura de microsserviços permitiu uma implementação flexível de cada serviço. Em nossa jornada, implementamos os tempos de execução de diversas maneiras, começando com buildpacks do Node.js, imagens de VM e imagens do Docker implementados nativamente no IBM Cloud e também nos datacenters da SoftLayer. Para cada implementação, replicamos as topologias em regiões localizadas em Dallas, Londres e Sydney. Esse é realmente um dos principais benefícios de desenvolver uma arquitetura centrada em nuvem: copiar e substituir componentes da arquitetura enquanto o restante do sistema permanece perfeitamente intacto.

De que forma as tendências do mercado direcionam a evolução para a computação em nuvem

Surgiu um padrão relacionado a grandes inovações como a computação em nuvem. Cada grande inovação é uma evolução natural da primeira grande inovação.

Figura 2. A evolução das grandes inovações no setor de TI
Evolution of disruption
Evolution of disruption


Quando começou a mudança de cliente/servidor para a web, o setor testemunhou uma transição significativa rumo à entrega de aplicativos baseados na web que são executados no navegador, e o conteúdo entregue ficou muito mais rico. Como resultado, foi necessário definir normas relacionadas à maneira de acessar dados, principalmente, com relação aos sistemas de registros e aos sistemas de mainframe que, historicamente, têm mantido esses dados na empresa.

Em seguida, a SOA (arquitetura orientada a serviços) tornou-se a maneira indicada para descrever APIs e fornecer terminais bem definidos, além de definições de dados (em Web Service Definition Language ou WSDL) para APIs de serviços da web. Conforme a Web 2.0 ganhou notoriedade, os navegadores passaram a entender JSON com muito mais naturalidade do que XML, e a definição de serviços usando substantivos e verbos produziu um modelo ainda mais simples do que os serviços da web.

Foi aí que surgiu o REST. Na mesma época, surgiu a computação mobile e começou o retorno à abordagem cliente/servidor, mas de uma forma muito diferente. O dispositivo mobile estava começando a ganhar importância como a plataforma de aplicativos mais escolhida. Usando o REST, conseguimos nos beneficiar dos investimentos nas inovações iniciais.

Com a passagem para a nuvem, vemos o mesmo acontecendo. Agora, a Internet das Coisas está acelerando ainda mais o aumento do número de dispositivos em ação. Precisamos de um modelo que permita ajustar a escala rapidamente, com base na demanda de serviços. O uso de ambientes virtuais na nuvem tornou-se um modelo muito mais plausível para executar sistemas em escala.

A lógica por trás das decisões

Agora, vamos analisar o que significa usar uma arquitetura centrada na nuvem e como nós alcançamos seus cinco aspectos principais: transporte de dados, formato de dados, segurança, APIs e elasticidade.

A decisão de quais protocolos de rede terão suporte geralmente está aberta ao debate. Em nossa investigação, a escolha foi direcionada pelos requisitos do aplicativo, como os tipos de clientes que interagem com nosso serviço em relação a dados de entrada. Para oferecer suporte a uma variedade mais ampla de fornecedores de sensores, escolhemos o menor denominador comum: HTTP(s). Embora os protocolos de tecnologia MQTT, Websockets e outros estivessem disponíveis, a maioria das integrações existentes com as quais queríamos começar era baseadas em HTTP e poderiam fornecer um alcance maior.

Para o formato de dados, escolhemos JSON como padrão. O JSON progrediu do JavaScript Object Notation inicial para um formato multiuso muito eficiente usado para uma variedade de interações, desde o navegador até o dispositivo mobile e a comunicação de máquina a máquina. Sentimos que o JSON era o mais fácil de identificar, tinha o suporte mais amplo dos fornecedores de sensores e tinha o conjunto mais forte de bibliotecas e ferramentas para oferecer suporte à codificação e decodificação de cargas úteis, de uma maneira altamente escalável e com alto desempenho. O JSON também é o formato padrão nativo do Node.js (a tecnologia que escolhemos para implementar serviços).

A segurança e as APIs caminham juntas. Para que nossas APIs pudessem ser consumidas com facilidade, precisávamos reduzir os obstáculos de entrada, portanto, agrupamos nossas APIs em duas categorias, adotando os princípios definidos pelo REST. A primeira categoria era relacionada às APIs de máquina a máquina, na qual não há interação humana (sensores enviam dados ao Presence Insights). Nesse modelo, adotamos a autenticação básica para essas APIs. Para as APIs com maior probabilidade de envolver interação humana (APIs de gerenciamento), escolhemos o OAUTH como modelo de segurança e o usamos para gerar o modelo de permissão para as APIs. Decidimos usar os modelos de segurança de proprietário e realmente focamos em abordagens baseadas em padrões abertos para as APIs, visando garantir que elas pudessem ser consumidas, mantidas e protegidas.

O elemento final é a elasticidade. Como focamos na construção de um conjunto altamente escalável de APIs, com forte foco em ocupação variada, queríamos ter a capacidade de ajustar a escala com rapidez e sob demanda, para atender às necessidades dos clientes. Para conseguir isso, garantimos que as APIs fossem medidas com um conjunto de métricas de API relacionadas ao número de solicitações por instância e aos tempos de resposta. Usando essas métricas para aumentar a capacidade conforme o necessário e seguindo os princípios do REST quanto à eliminação do gerenciamento de estado nas APIs, foi possível ajustar a escala rapidamente sob demanda.

Padrões e melhores práticas

Depois de definir como queríamos desenvolver nosso conjunto de serviços, definimos um conjunto de padrões que foram fundamentais para o sucesso do Presence Insights. Primeiro consideramos como oferecer suporte a eventos de sensores em escala. Queríamos uma solução sem bloqueios para receber eventos, o que resultou na escolha da programação baseada em eventos usando Node.js e sistema de mensagens. Focamos no sistema de criação de eventos "disparar e esquecer" no qual aproveitamos nossa experiência em sistema de mensagens com o MQ Light para gerar eventos para uma série de tópicos. Eliminando a natureza de bloqueio que existe em linguagens como Java, foi possível usufruir dos tempos de execução do Node.js, sem precisar lidar com conceitos como encadeamento e espera de conclusão de encadeamento. A natureza de retorno de chamada do Node.js foi perfeita para nossa arquitetura.

Também escolhemos o MQ Light para a comunicação entre cada um dos serviços em um modelo de publicar/assinar em vez de HTTP. Escolhemos esse modelo como uma maneira mais simples e rápida de aumentar a capacidade do processamento de serviços de modo fracamente acoplado.

Quando surgiu a questão da persistência, sabíamos que queríamos oferecer suporte a uma variedade de formatos de dados e ter a capacidade de introduzir novos formatos de dados sempre que desejado. Ao passar para uma abordagem NoSQL, foi possível consumir rapidamente grandes quantias de dados não estruturados em escala, e ainda manter um modelo de consistência oferecendo suporte para processos como consultar analytics. Embora realmente houvesse uma necessidade de dados estruturados, pudemos definir esquemas para esses casos de uso e aproveitar os scripts de validação do Node.js para assegurar a consistência nos formatos de dados e para atualizar documentos, conforme o necessário, para migrar para novos modelos de dados.

A peça final do quebra-cabeça para entregar um padrão era como desenvolver um serviço em nível de produção em escala. Isso envolvia lidar com falhas, dependências e operações. Para qualquer sistema distribuído, temos que assumir que alguma parte parte do processo falhará, por falha humana ou de máquina. Contar com a falha nos forçou a desenvolver um serviço muito mais resiliente.

Também limitamos as dependências. Introduzir uma enorme quantidade de código comum com uma enorme quantidade de dependências é uma receita para o desastre. Ao adotar serviços pequenos que podem ser compostos e têm dependências limitadas (mas ainda usam utilitários de núcleo comuns), conseguimos obter insights mais profundos quanto ao código em execução e monitorar indicadores como o uso de memória e de CPU com muito mais precisão.

O consumo de memória e de CPU pode limitar a escala do serviço. É necessário assegurar que os serviços sejam stateless e que, portanto, possam ter sua escala ajustada de forma elástica. Esse é provavelmente o elemento mais importante para desenvolver uma arquitetura centrada em nuvem e a falha mais comum observada no mercado, pois os desenvolvedores de aplicativos têm se baseado em statefulness desde quando surgiu a programação na web.

Adoção de software livre

Quando formamos a equipe de desenvolvimento, queríamos criar um ambiente no qual os desenvolvedores curtissem trabalhar. Isso significou adotar tecnologias de software livre, da perspectiva do tempo de execução e também da perspectiva de desenvolvimento, construção e implementação. Nossa solução de ponta a ponta foi direcionada por uma combinação de tecnologias já estabelecidas como JavaScript e ferramentas mais novas favorecidas pela ampla comunidade da IBM, como GIT, Travis e Jenkins.

Para maximizar os recursos da equipe, adotamos o modelo de "plataforma como serviço". Aproveitamos o máximo possível da plataforma IBM Cloud, o que permitiu manter um foco maior na arquitetura e na implementação e menor no gerenciamento de infraestrutura. Como resultado, desenvolvemos uma plataforma maior, usando os principais serviços de base como Node.js (como buildpack), MQ Light (executando a transição para o Kafka), Cloudant (como um armazenamento NoSQL), Elastic Search e Redis (por meio da aquisição do Compose.io). Esse foco em fornecer valor aos usuários finais nos trouxe um melhor entendimento sobre o que esses sistemas subjacentes precisavam nos oferecer (que ainda não ofereciam), e isso nos ajudou decidir a reduzir os requisitos ou executar nosso próprio hosting.

Sempre nos perguntam por que escolhemos o Node.js. (Nossa escolha foi antes da aquisição da StrongLoop pela IBM em 2015.) A resposta é bem simples. Queríamos gravar o código em uma linguagem que agradasse nossos desenvolvedores e que tivesse uma rica seleção de bibliotecas existentes, permitindo integrar tecnologias de software livre com facilidade. Na verdade, o uso que fizemos do Node.js em todo o processo de desenvolvimento é visto como o principal motivador da capacidade que tivemos de entregar a solução ao mercado em menos de seis meses.

Três principais lições aprendidas

Acreditamos que três decisões são vitais para desenvolver arquiteturas escaláveis, principalmente com Node.js e determinar a estratégia de dados apropriada.

Quando iniciamos o desenvolvimento em Node.js, queríamos usufruir de um conjunto de módulos de nós comuns entre todos os microsserviços, para fornecer consistência às nossas equipes de desenvolvimento. Esses módulos variavam de autenticação de segurança e autorização até módulos de criação de log e tudo que estava entre eles, como bancos de dados e wrappers de armazenamento em cache, para eliminar algumas interações do banco de dados e melhorar a depuração. Embora esses pacotes fossem valiosos para nossa equipe de desenvolvimento, eles não eram candidatos para um NPM público, portanto, era necessária uma solução focada em repositórios privados. Continuando a acumular um grande número de módulos, passou a ser necessário construir, implementar e gerenciar várias versões dessas bibliotecas.

Descobrimos que uma solução de software livre chamada Sinopia (veja a barra lateral) atendia perfeitamente às nossas necessidades. Trata-se de um Gerenciador de Pacote de Nós privado que pode ser implementado na nuvem. Ao hospedar nosso próprio servidor do Sinopia, reduzimos significativamente os tempos de implementação no IBM Cloud, melhoramos o suporte de versionamento dos módulos de nó e implementamos nosso serviço na nuvem de maneira altamente segura e escalável. Saiba mais sobre como implementar seu próprio Gerenciador de Pacote de Nó privado no IBM Cloud.

Definição de escopo de serviços

Um dos aspectos mais difíceis do desenvolvimento da arquitetura de microsserviços é determinar o escopo apropriado para um determinado serviço. Na nossa definição de microsserviço, cada serviço executa um único processo e o executa muito bem. Mas quando começamos a desenvolver nossas próprias APIs de gerenciamento, descobrimos que a presença de todas essas APIs de gerenciamento em um único microsserviço não era sustentável.

Então, foi preciso decidir como dividir o serviço. Como nossas APIs foram desenvolvidas com um design para vários proprietários, a mesma API pode ser acessada por um proprietário com uma configuração grande e também por um proprietário com uma configuração pequena. Para testar como ajustar a escala para esses dois tipos de proprietários, foi necessário avaliar o que era o ideal para o nosso tempo de execução. É viável implementar um grande número de aplicativos Node.js com uma pequena quantidade de memória configurada para cada aplicativo ou é mais viável implementar um número menor de aplicativos Node.js com uma quantidade maior de memória RAM? Já havia experimentos semelhantes relacionados ao processamento de eventos de sensor de entrada, nos quais é possível configurar sistemas para enviar diversas cargas úteis pequenas a uma taxa rápida ou criar lotes de carga útil e enviar eventos em um intervalo maior.

Descobrimos que nenhuma solução única atende a todos os casos de uso. A melhor maneira de validar uma abordagem é capturar métricas no sistema em execução e usar reproduções desses eventos com várias políticas de ajuste de escala pra ver como os dados são processados no sistema, com a menor latência possível.

Escolhendo e testando soluções de dados

Como mencionamos, nossa meta de negócios era fornecer insights acionáveis com base em dados de evento de sensor e envolver os usuários quase em tempo real, por meio de integrações de terceiros, como notificações push.

Descobrimos que nem todas as soluções de dados são iguais. É fundamental utilizar a ferramenta certa para a tarefa. Por exemplo, algumas soluções são otimizadas para consultas, enquanto outras são otimizadas para um grande número de gravações. Vamos analisar algumas das soluções de dados que empregamos.

Cloudant

  • Controla usuários (entrada/saída/serialização) por meio do listener de feed de mudança
  • Tem problemas de escalabilidade com um número muito grande de instâncias do Node.js em conexões longas com o Cloudant

MQ Light

  • Controla usuários (entrada/saída/serialização) por meio de tópicos do MQ
  • Não tem a capacidade de ser notificado quando uma chave de tópico expira

Redis

  • Controla usuários (entrada/saída/serialização) por meio de publicar/assinar
  • Warlock (node-redis-warlock) é necessário para bloqueio distribuído (não nativo no Redis)

Começamos com o Cloudant, pois já estávamos persistindo nossos eventos de sensor no Cloudant e queríamos explorar nosso armazenamento de dados escalável para rastrear os movimentos de dispositivos que os sensores estavam detectando. Descobrimos que o uso do listener de feed de mudança para agir como um acionador para ser notificado quando um usuário se movimenta pode não apresentar um bom ajuste de escala em uma arquitetura de microsserviços, com um grande número de instâncias conectando-se ao Cloudant.

Em seguida, passamos para o MQ Light e usamos tópicos para rastrear os movimentos de dispositivo. O principal elemento ausente aqui foi a capacidade de expirar uma chave no caso de ausência de dispositivos por um certo período de tempo.

Em seguida, incluímos o Redis na infraestrutura para atender a essa necessidade específica e, mais tarde, ampliamos o uso do Redis para armazenamento em cache e também para criação de evento em tempo real.

Aprendemos que esses tipos de experimentos são apenas inevitáveis, mas também extremamente saudáveis, além de representarem um sinal de crescimento de uma arquitetura centrada em nuvem. Na verdade, o fato de termos dados concretos, em vez de apenas opiniões e suposições, nos ajudou a fazer mudanças na arquitetura com mais confiança.

Experimentos semelhantes relacionados a analytics nos levaram a uma combinação entre Cloudant, Elastic Search e Spark para fornecer insights acionáveis ao produto. Como nosso principal foco de negócios era fornecer insights acionáveis, precisávamos ter a capacidade de analisar e visualizar os dados a partir de vários pontos de vista diferentes. Esses conjuntos de dados precisavam ser altamente disponíveis e distribuídos, além de ter TTLs (tempos de vida) bem definidos e modelos bem definidos para resumir os dados, conforme eles se transformam em dados históricos.

Para reiterar, não existe uma única solução de dados que atenda a todos os casos de uso na nuvem. Para obter resultados melhores, considere uma combinação de tecnologias como Cloudant, Redis, MongoDB, Cassandra, Spark e Elastic Search.

Lições da nossa jornada para microsserviços

Esperamos que as lições que compartilhamos e o raciocínio por trás das nossas decisões de tecnologia tenham agregado valor ao seu conhecimento. Se você seguir esses princípios, provavelmente estará no caminho certo.

  • Desenvolva um ponto de vista sobre as arquiteturas centradas em nuvem. Elas estão em constante mudança e há muitas formas de alcançar o sucesso, portanto, assegure-se de escolher a ferramenta certa para o trabalho!
  • Documente as etapas da sua jornada para que outras pessoas possam se beneficiar das lições que você aprendeu e das escolhas que fez.
  • Adote as melhores tecnologias da categoria. As soluções de software livre são ótimos aceleradores.
  • Crie uma equipe técnica vibrante. Atrair e reter os melhores talentos técnicos é a prioridade número um. Seja a equipe da qual outros desenvolvedores querem participar!
  • Compartilhe as lições aprendidas com a comunidade mais ampla por meio de artigos (como este!), apresentações, conferências e mídia social.

Conclusão

Os desenvolvedores continuarão a melhorar (e revolucionar) as tecnologias existentes. E esses esforços continuarão a produzir novas ferramentas, técnicas e soluções de software livre que poderão ajudá-lo a construir e desenvolver sua própria arquitetura centrada em nuvem.

Se a sua jornada envolve desenvolver ou executar a transição para tecnologias em nuvem, você já experimentou desenvolver e executar aplicativos no IBM Cloud, a plataforma de nuvem da IBM? Crie seu próximo app com IBM Cloud Lite. É de graça. Não requer cartão de crédito. Comece gratuitamente.


Recursos para download


Temas relacionados


Comentários

Acesse ou registre-se para adicionar e acompanhar os comentários.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Cloud computing
ArticleID=1036192
ArticleTitle=Executando a transição para uma arquitetura centrada em nuvem
publish-date=08162016