DevSecOpsARTIGO

Projetando aplicativos para a nuvem

A computação em nuvem é uma ferramenta importante no kit de
ferramentas de qualquer arquiteto. Ela permite que você faça o protótipo
e lance novos projetos sem ter que se preocupar com a configuração do
servidor, além de poder diminuir os custos e reduzir os riscos de
gerenciar seus servidores. Ela também pode possibilitar uma fácil
escalabilidade, com a capacidade de criar instâncias extras sempre que
precisar delas.

Porém, há várias questões arquitetônicas que devem ser consideradas
ao começar a desenvolver aplicativos para a nuvem. Este artigo destaca
algumas das considerações a serem feitas quando você for projetar seu
primeiro aplicativo baseado em nuvem.

Benefícios da computação em nuvem

Antes de tratarmos das recomendações para projetar para a nuvem,
vale a pena rever os benefícios que uma arquitetura baseada em nuvem
pode oferecer:

  • Tempo de colocação no mercado. Se você tiver um novo aplicativo para ativar, é possível se concentrar no desenvolvimento de recursos em vez de fornecer a infraestrutura. Não há tempo de processamento para a compra, instalação e configuração de servidores. Com experiência, seria possível escrever o código de uma prova de conceito em uma manhã e executá-lo em um servidor em nuvem na mesma tarde.
  • Escala. Frequentemente, você não sabe que tipo de carga seus aplicativos encontrarão. Assim, é necessário encontrar o equilíbrio perfeito entre o superfornecimento para cargas que podem nunca se tornar reais e o subfornecimento, que causa problemas de desempenho até que você compre, configure e implemente hardware adicional. Com infraestruturas baseadas em nuvem, é possível ativar um conjunto mínimo de instâncias e ajustar a escala do número de instâncias se a carga aumentar.
  • Flexibilidade. Com o ajuste automático de escala em uma infraestrutura baseada em nuvem, é possível alterar dinamicamente o número de instâncias de servidor. Assim, se houver uma diferença significativa entre as cargas de pico (horários do dia em que eventos especiais são ativados, anúncios são exibidos e assim por diante) e as cargas médias, não é necessário fornecer e pagar pelas instâncias da carga de pico contínua.
  • Simplicidade. Projetar e implementar aplicativos escaláveis internamente exige que você lide com muita complexidade. É necessário pesquisar, selecionar e configurar balanceadores de carga, assim como desenvolver soluções para fazer o script da reconfiguração de endereços IP em falhas de hardware, projetar serviços de e-mail escaláveis baseados em sistema de mensagens e também descobrir como implementar recursos de backup e recuperação de desastres para dados críticos. A maioria dos provedores de nuvem inclui soluções comprovadas para esses problemas que você pode usar.
  • Testes fáceis. Ao usar uma infraestrutura baseada em nuvem, é fácil criar instâncias adicionais tanto para testes funcionais quanto de carga, para que você não tenha que manter hardware caro disponível de modo contínuo para a execução de testes.
  • Custo inicial. Sem custos de capital iniciais, a computação em nuvem também reduz o custo de lançar um novo projeto no mercado, e os custos de hospedagem só se tornam significativos se o aplicativo se tornar popular. Esse é um excelente problema para se ter.
  • Recuperação. Devido à maneira que se deve projetar e desenvolver aplicativos para a nuvem, é geralmente muito mais fácil lidar com o backup de dados importantes e a recuperação automática de falhas de hardware ou rede.

Projetando para a nuvem

Para muitos arquitetos, um aplicativo baseado em nuvem é sua
introdução à criação efetiva de projetos para escala, já que há bons
motivos para se usar a nuvem até mesmo para aplicativos em menor escala.
Há várias considerações para ajustar a escala de um aplicativo que
podem não ser intuitivas para os arquitetos acostumados a maximizar o
desempenho em um único servidor.

Ajustar a escala interna x externa

Na primeira vez em que você desenvolve um aplicativo que se torna
popular, a resposta inicial geralmente é ajustar a escala interna.
Adicionar mais RAM. Aumentar a velocidade do processador. Atualizar para
discos rígidos mais rápidos. Implementar cache para consultas lentas ou
cálculos comuns.

Todas essas técnicas são razoáveis, mas se seu
aplicativo tiver que ser escalado além de um determinado ponto, será
necessário ir além do ajuste da escala interna e projetar tendo em vista
o ajuste da escala externa, usando vários servidores. Fazer isso
frequentemente requer mudanças consideráveis na arquitetura do
aplicativo. A própria natureza dos aplicativos em nuvem significa que
você deve, desde o início, fazer a arquitetura para poder ajustar a
escala externa.

Além disso, quando você faz a arquitetura para a nuvem, é
importante não se concentrar na micro-otimização do desempenho. Em vez
de incluir muitos caches complexos e ajustes de desempenho, é muito
melhor manter o design simples e se assegurar de que você pode ajustar a
escala externa do aplicativo. Os servidores são geralmente muito mais
baratos do que os desenvolvedores, e as micro-otimizações de desempenho
normalmente não justificam a perda de tempo do desenvolvedor e a
sustentabilidade reduzida do aplicativo.

Principais considerações do ajuste de escala externo

Quando um aplicativo precisa de um ajuste de escala externo, concentre-se em cinco abordagens arquitetônicas:

  • Minimizar o estado mutável.
  • Avaliar armazenamentos de dados NoSQL.
  • Criar serviços assíncronos.
  • Automatizar a implementação.
  • Consideração das falhas no design.

Minimizar o estado mutável

Talvez o mais importante padrão para aumentar a escalabilidade
horizontal na nuvem seja a minimização do estado mutável. É o motivo
pelo qual as linguagens de programação funcionais estão se tornando tão
populares e padrões como o fornecimento de eventos (ao final do artigo, consulte
Recursos para obter um link para mais informações) estão sendo aplicados mais
frequentemente.

O problema com o estado mutável compartilhado (isto é,
variáveis compartilhadas em todo o aplicativo e que podem ser alteradas
ao longo do tempo) é que ele sempre causa problemas na escalabilidade.
Vários servidores e processos que tentam atualizar as mesmas variáveis
ao mesmo tempo resultam em conflitos, expirações de tempo limite e
transações com falha.
Três lugares em que é desejável minimizar ou eliminar o estado mutável
em seu aplicativo são os servidores da Web, o aplicativo e
(possivelmente até) o banco de dados.

Sistemas de arquivos imutáveis

Com aplicativos em nuvem, não há garantias de que uma determinada
instância será durável. Se houver três instâncias de servidor da Web
tratando de solicitações, qualquer uma delas poderá ficar inativa a
qualquer momento. Assim, é bom armazenar no sistema de arquivos local
apenas informações que você possa perder, caso a instância do servidor
falhe.

Se os usuários estiverem fazendo upload de arquivos (sejam eles
figuras de perfil ou relatórios TPS), é desejável garantir que os
uploads sejam feitos em um sistema de arquivos remoto e redundante — não
apenas no disco rígido do servidor que estejam acessando. Se desejar
registrar informações cujo acesso seja realmente necessário mesmo que
uma instância de servidor fique inativa, é uma boa ideia salvá-las em um
armazenamento NoSQL, como Cassandra, em vez de fazer sua persistência
em um disco rígido local em um servidor da Web.

Aplicativos imutáveis

O estado mutável compartilhado é particularmente útil na
programação orientada a objetos, mas é importante minimizar a quantidade
do estado mutável compartilhado e pensar cuidadosamente sobre as
implicações ao ajustar a escala dos seus aplicativos baseados em nuvem.

Eric Evans incluiu alguns ótimos padrões em nível de código em Domain Driven Design
para minimizar o estado mutável, como tornar suas entidades (mutáveis)
as mais finas possíveis e usar objetos de valor (imutáveis) o máximo
possível para o estado do seu aplicativo.

Por exemplo, um usuário é uma
entidade mutável, mas um objeto de valor imutável pode ser usado para
representar o endereço do usuário, mudando apenas o endereço para o qual
você aponta para o usuário se ele se move. Em geral, dê preferência a
objetos de valor e valores imutáveis em relação ao estado mutável,
principalmente para um estado que possa precisar ser atualizado
regularmente por muitas partes do seu aplicativo baseado em nuvem.

Além disso, pense bem sobre suas estratégias de cache. Se as
informações forem específicas a uma única instância, um cache local é
apropriado, já que sempre é possível recriá-lo se a instância parar de
funcionar. Mas se as informações forem requeridas por usuários que estão
acessando outro servidor da Web, você deve buscar as informações em um
armazenamento persistente compartilhado ou usar um cache distribuído que
trata da atualização do cache em diversas instâncias do servidor da
Web.

Armazenamentos de dados imutáveis

A princípio pode parecer uma contradição, mas é possível obter
benefícios reais ao adotar uma abordagem influenciada por fornecimento
de eventos no design de banco de dados para aplicativos baseados em
nuvem. Um problema comum ao tentar ajustar a escala de aplicativos é
incluir contenção no banco de dados. Uma mudança em um pedido de compra
pode afetar vários itens de pedido e pedidos de compra associados para
os fornecedores.

Naturalmente, qualquer mudança desse tipo deve ser
agrupada em uma transação para assegurar que o banco de dados nunca seja
deixado em um estado inválido, com a efetuação de apenas uma parte das
mudanças. Mas à medida que mais usuários passam a atualizar transações
que afetam pedidos relacionados, pode-se chegar rapidamente a um ponto
em que todas as suas transações falhem por causa de conflitos de
bloqueio otimistas.

Uma abordagem para lidar com esse problema é minimizar o estado
mutável no banco de dados. Em vez de ter um usuário com valores mutáveis
para o nome, sobrenome e outros campos de perfil, deve-se possuir um
único registro no banco de dados com identificadores exclusivos (por
exemplo, ID, endereço de e-mail, UUID), mas nenhum estado adicional.
Deve-se então ter uma tabela separada para ProfileUpdateEvents
em que é armazenado um evento imutável para cada atualização com um
registro de data e hora associado.

Assim, se um usuário atualizasse sua
cidade natal, haveria um ProfileUpdateEvent em que o campo fosse home_city
e o novo valor e a data/hora da atualização seriam armazenados. Para
obter a cidade natal de um usuário, simplesmente leia todos os ProfileUpdateEvents
até encontrar o registro de data e hora mais recente que atualizou o
campo. Esse processo fornece um sistema em que a contenção de gravação
desaparece e você soluciona quaisquer conflitos com uma leitura ou com
uma programação automática, permitindo uma escalabilidade de gravação
muito maior em seu aplicativo.

Naturalmente, agora há um problema com o desempenho de leitura.
Ter que executar operações em um grande conjunto de eventos somente para
obter o estado de uma única entidade não é eficiente, principalmente
nos aplicativos para os quais a escalabilidade de leitura é uma
preocupação.

Mas esse problema é fácil de ser resolvido: recrie os
campos adicionais na tabela de usuário, mas em vez de ser a única
referência autorizada sobre o estado do usuário, os campos são
simplesmente um cache do estado do usuário em um determinado momento.
Assim, dependendo das suas regras de negócios, você pode escrever
scripts para atualizar o cache de estado na tabela de usuário com base
em ProfileUpdateEvents, solucionando conflitos de gravação
com base nas suas regras de negócios.

Vale a pena levar o fornecimento
de eventos em consideração se você tiver um aplicativo para o qual a
contenção de gravação possa realmente ser uma barreira para a
escalabilidade do seu aplicativo baseado em nuvem

Avaliar armazenamentos de dados NoSQL

Outra abordagem para gerenciar a contenção de gravação no banco de
dados consiste em avaliar a possibilidade de usar armazenamentos de
dados NoSQL para alguns ou todos os seus dados de aplicativos. Por
exemplo, o Cassandra é projetado especificamente para fornecer
escalabilidade de gravação linear em vários nós para armazenar enormes
quantidades de dados.

O CouchDB possui uma excelente sincronização de
mestre-para-mestre entre vários nós, e o MongoDB possui o conceito de
campos de “contador”, permitindo atualizações assíncronas de
disparar-e-esquecer aos campos de contador para que você não tenha que
se preocupar com a contenção de gravação se estiver apenas atualizando
regularmente o número de visualizações em uma postagem.

Muitos armazenamentos NoSQL também fornecem recursos de MapReduce,
permitindo acesso eficiente a consultas predefinidas. Se você estiver
executando consultas conhecidas em grandes conjuntos de dados, eles
podem fornecer uma solução escalável à medida que a quantidade de dados
com os quais você está trabalhando cresce.

Criar serviços assíncronos

Outra abordagem arquitetônica importante é a de transferir
trabalhos dos seus servidores de aplicativos principais criando serviços
assíncronos separados. Não há necessidade de bloquear um encadeamento e
fazer com que os visitantes do seu site esperem por uma resposta
enquanto esperam para enviar e-mails ou executar relatórios
sincronicamente.

É muito melhor dividir esses tipos de tarefas em
serviços diferentes que possam ser executados em servidores diferentes
na nuvem. Dessa maneira, é possível fazer o ajuste de escala
independentemente do seu aplicativo principal.

Não é necessário bloquear
um encadeamento em um servidor da Web enquanto espera que as tarefas
sejam concluídas, e seus usuários não precisam esperar que esses
processos sejam concluídos para receber uma resposta dizendo que você
está trabalhando e que irá notificá-los quando o processo for concluído.

Você tipicamente se conecta aos seus serviços assíncronos usando um
mecanismo de transporte com algum grau de capacidade garantida de
entrega, como uma fila de mensagens. Também é importante usar a rede em
vez de comunicação interprocessual. De modo geral, execute esses
serviços em servidores separados. Certamente é desejável ser capaz de
executá-los em servidores separados, se você escolher assim.

Também é
possível usar outras abordagens arquitetônicas, como tratar um banco de
dados como um quadro para compartilhar informações entre o aplicativo
principal e os vários serviços que executam os serviços assíncronos
separados.

Implementação automática

Finalmente, é importante automatizar seu processo de implementação
de nuvem. É necessário possuir imagens de máquina base para os diversos
papeis (servidor da Web, servidor de e-mail, servidor de banco de dados e
assim por diante) e usar ferramentas como Chef ou Puppet para
configurar instâncias automaticamente. Também é uma boa ideia usar algum
tipo de sistema de compilação para implementar seu código
automaticamente.

O monitoramento para aplicativos baseados em nuvem também é uma
preocupação importante. Configure o monitoramento para que você saiba
quando as instâncias ficarão inativas ou altamente carregadas para que
você possa automatizar o processo de criar mais ou menos instâncias
dependendo do desempenho do seu aplicativo.

Lembre-se de ter um remapeamento automático de endereços IP em
script para tratar dos casos em que um datacenter ficar inativo. Além
disso, procure saber como seu provedor de nuvem oferece suporte para
conexões SSL, mapeando-as para várias instâncias.

É geralmente uma boa
prática usar SSL apenas em páginas que requerem absolutamente a
segurança de transporte que o protocolo oferece. Solicitações SSL
incorrem em gastos adicionais, portanto elas só devem ser feitas quando
adicionam valor de negócios.

Para saber mais sobre a automatização de implementação e sobre o
DevOps, um movimento em crescimento, leia o excelente livro sobre
entrega contínua de Jez Humble e David Farley em logo abaixo em Recursos.

Considerar as falhas no design

É particularmente importante considerar as falhas no design para
seus aplicativos baseados em nuvem. Você deve pensar no pior cenário
possível, observando seus pontos singulares de falha e pensando no que
faria caso eles falhassem.

Porém, é igualmente importante decidir qual nível de falha é
aceitável. Se um datacenter se tornar inativo, talvez não haja problema
se você apenas puder fornecer acesso de leitura aos seus servidores por
alguns minutos até ter promovido outro banco de dados a “mestre”. Talvez
não haja problema se, quando um servidor falhar, um usuário tenha que
efetuar login novamente ou perder suas preferências ou carrinho de
compras.

É importante comparar o custo da engenharia de tolerância de
falhas com o valor de negócios que a tolerância de falhas pode oferecer,
para ter certeza de que você não está investindo excessivamente na
redundância de informações que não são suficientemente importantes para
seu negócio.

Em conclusão

Aplicativos baseados em nuvem oferecem muitos benefícios, incluindo
uma colocação mais rápida no mercado e uma escalabilidade mais fácil.
Além disso, o custo inicial é mais baixo, e, usualmente, as soluções
baseadas em nuvem que são bem-projetadas fornecem recursos de
recuperação de desastres muito melhores.

Porém, para aproveitar as
implementações baseadas em nuvem, é necessário fazer a arquitetura de
uma forma que seja consistente com a nuvem, geralmente presumindo que
você trabalhará com sistemas de arquivos locais somente para leitura e
armazenando quaisquer dados importantes em um banco de dados ou em um
sistema de arquivos persistente e compartilhado. Além disso, minimize o
estado mutável dos seus aplicativos e considere armazenamentos de dados
NoSQL para melhorar a escalabilidade dos seus aplicativos.

Procure saber
como você pode transferir qualquer processamento pesado para serviços
assíncronos que possam ser movidos para servidores separados.
Finalmente, automatize seu processo de implementação e considere as
falhas no design dos seus aplicativos, para garantir a obtenção de
características aceitáveis de desempenho de falha com seu aplicativo
baseado em nuvem.

Recursos

Aprender

Obter produtos e tecnologias

  • Consulte as imagens do produto disponíveis para IBM SmartCloud Enterprise.
  • Saiba mais e faça o download do Chef, uma estrutura de integração de sistemas de software livre automatizada.
  • O Puppet pode ajudar você a desenvolver aplicativos grandes baseados em nuvem.

Discutir

Sobre o autor:

Peter Bell faz apresentações internacionalmente e escreve bastante sobre
implementações de nuvem, arquitetura ágil, NoSQL, requisitos e
linguagens específicas de domínio e efetuação de estimativas. Ele ajuda
as equipes a desenvolver bons softwares ao melhorar a coleta de
requisitos, a efetuação de estimativas e os processos de gerenciamento
de projetos, assim como as práticas de engenharia e as ferramentas
utilizadas. Ele faz parte do comitê de programa do Code Generation em
Cambridge, na Inglaterra, e do workshop Domain Specific Modeling na
SPLASH.

é o portal de tecnologia da IBM para profissionais de TI de todo o mundo. Colabore, aprenda a criar aplicativos inovadores e compreenda tecnologias avançadas. Acesse mais artigos e tutoriais em www.ibm.com/developerworks/br

Ver perfil