Olá colegas. Hoje publicamos uma tradução da próxima resenha no site de Ben Neidel - este site certamente lhe interessará no original. Desta vez, falaremos sobre o livro "
Distributed Systems. Design Patterns ", que complementa o livro "
Master Kubernetes ", publicado no início deste ano e, em essência, é um análogo do GoF para o design de sistemas distribuídos.

Boa leitura.
No fim de semana, li o livro Sistemas Distribuídos. Design Patterns, de
Brendan Burns . Gostei muito do livro, embora, devo admitir, esperava encontrar um pouco de material diferente nele. Assim, na descrição do livro, os contêineres são mencionados apenas de passagem. Ao mesmo tempo, embora os padrões descritos no livro sejam aplicáveis não apenas à conteinerização, quase todos os padrões descritos aqui são fornecidos no contexto do contêiner e, depois disso, são considerados no pipeline de implantação do Kubernetes. Descobriu-se pelo caminho. Estou começando a me familiarizar com o desenvolvimento e a implantação orientados a contêineres, e discutir padrões arquiteturais desse ponto de vista do "contêiner" foi uma revelação para mim. Essa abordagem me ajudou a entender como, no cenário dos microsserviços, os pequenos serviços são distinguidos corretamente, cada um dos quais com uma oportunidade específica.
O autor do livro, Brendan Burns, é o co-fundador do projeto de código aberto Kubernetes. Portanto, não é de surpreender que todos os seus exemplos de aplicativos sejam criados em torno da implantação de contêineres com base nos arquivos de configuração do Kubernetes. Na hora de ler o livro, eu era um pouco versado em Docker e não sabia nada sobre Kubernetes. Assim, tentei descobrir o "objetivo" dos arquivos de configuração do Kubernetes simplesmente lendo-os. No entanto, acredito que o livro será mais útil se o leitor tiver pelo menos alguma experiência com o Kubernetes.
Ao ler o livro, não consegui me livrar das associações com o trabalho “
Modelos de Integração para Aplicativos Corporativos ”, de Gregor Hope e Bobby Wolf. Muitos dos padrões discutidos por Burns são muito semelhantes aos padrões da fila de mensagens discutidos por Hope e Wolfe. De fato, devo dizer que muitos padrões são mencionados nos dois livros da mesma maneira (por exemplo, Scatter / Gather). Isso é lógico, uma vez que o tema de ambos os livros é a partição de sistemas monolíticos complexos em um conjunto de pequenos serviços reutilizáveis e bem ajustados. Acho que pode até argumentar-se que Burns descreve abordagens específicas que serão úteis na implementação de Fabricantes de Serviços e Consumidores de Serviços que participam do funcionamento de fluxos de trabalho baseados em mensagens, os mesmos descritos no livro "Modelos de Integração de Aplicativos Corporativos".
Mais uma vez, acredito que os paralelos traçados entre esses dois livros testemunham o poder dos padrões de design. Tanto quanto eles nos levam a soluções comprovadas, e no fato de que os padrões de design facilitam a comunicação técnica, pois nos permitem raciocinar em um idioma comum.
Ao mesmo tempo, uma das abordagens que mais gostei é descrita no livro “Sistemas Distribuídos. Padrões de design ”está precisamente relacionado ao consumo da fila de mensagens. Burns aconselha não forçar o contêiner de trabalho a receber mensagens diretamente do sistema (semelhante à maneira como isso é feito no sistema Simple Queue Service (SQS) da Amazon), mas a criar um "embaixador" (padrão Ambassador). Esse contêiner de "embaixador" será implantado junto com o contêiner de trabalho e fornecerá uma API generalizada para manipulação de filas. O contêiner Ambassador permite abstrair detalhes da implementação relacionados ao armazenamento permanente de elementos na fila de mensagens, para que o contêiner de trabalho seja completamente independente de qualquer solução tecnológica específica.
“O embaixador da fonte do contêiner da fila de trabalho” é apenas um exemplo de um tópico transversal que percorre o livro como um fio vermelho: use conjuntos de pequenos contêineres para que cada contêiner individual possa se concentrar o máximo possível em uma tarefa específica e se tornar o mais reutilizável possível. Burns explora esse conceito no nível de contêiner individual, falando sobre como parametrizar contêineres usando argumentos de linha de comando e variáveis de ambiente. Então ele sobe um nível, fala sobre os padrões de carros laterais e embaixadores de contêineres múltiplos em um contexto de nó único. Por fim, ele mostra como criar uma arquitetura de microsserviço poderosa usando padrões de vários contêineres.
Eu acho que Burns conseguiu articular perfeitamente o "sonho" de todo o cenário dos microsserviços:
A abordagem de microsserviço tem muitas vantagens, muitas das quais relacionadas à confiabilidade e flexibilidade. Os microsserviços dividem o aplicativo em pequenas partes, cada uma das quais é responsável pela prestação de um serviço específico. Ao restringir o escopo dos serviços, cada serviço é capaz de desenvolver e manter uma equipe que pode ser alimentada com duas pizzas.
Reduzir o tamanho da equipe também reduz o custo de manutenção de suas atividades.
Além disso, o surgimento de uma interface formal entre microsserviços enfraquece a interdependência das equipes e estabelece um contrato confiável entre os serviços. Esse contrato formal reduz a necessidade de uma sincronização rígida da equipe, porque a equipe que fornece a API entende até que ponto é necessário garantir a compatibilidade e a equipe que consome a API pode contar com um serviço estável sem se preocupar com os detalhes da implementação do serviço consumido. Essa decomposição permite que as equipes controlem independentemente o ritmo do desenvolvimento e o cronograma para o lançamento de novas versões, o que lhes dá a oportunidade de iterar, melhorando assim o código de serviço.
Por fim, dividir em microsserviços aumenta a escalabilidade. Como cada componente é alocado para um serviço separado, ele pode ser escalado independentemente dos outros
(p. 79-80 em tradução russa)
Estou falando do "sonho" porque, como o próprio Burns admite, é difícil projetar um sistema de microsserviço e sua arquitetura. E monitorar e depurar esse sistema é muito mais complicado do que monitorar e depurar um análogo monolítico. Só com isso eu posso concordar facilmente. De acordo com minha experiência limitada em trabalhar com microsserviços, confirmo que os serviços compartilhados rapidamente se tornam altamente conectados e os "contratos confiáveis" entre os serviços rapidamente se tornam um alvo em movimento, passando por mais e mais novas mudanças.
Concluindo, gostaria de abordar o conceito do FaaS - "funciona como serviços". Sistemas como o Serviço Lambda da Amazon me fazem sentir confuso. Em um sentido extremamente abstrato, gosto de tais sistemas, mas não tenho idéia de como eles se manifestam em uma aplicação específica. Burns toca no FaaS na Parte II, em "Padrões de design de sistemas de serviço", mas infelizmente não esclarece completamente o problema do Faas para mim.
Gostei muito do fato de o Burns recomendar o uso do FaaS para resolver apenas um subconjunto dos problemas conhecidos:
Como em outras ferramentas para o desenvolvimento de sistemas distribuídos, convém usar uma solução específica (por exemplo, processamento orientado a eventos) como uma ferramenta universal. A verdade é que uma solução específica geralmente resolve problemas específicos. Em um determinado contexto, ele provará ser uma ferramenta poderosa, mas puxá-lo pelos ouvidos para resolver problemas comuns cria arquiteturas complexas e frágeis.
(p. 135 em tradução russa)
Além disso, muito obrigado a ele por mencionar as dificuldades que surgem ao usar o FaaS:
Conforme mencionado na seção anterior, o desenvolvimento do sistema usando a abordagem FaaS obriga você a tornar os componentes do sistema fracamente acoplados. Cada função é independente por definição. Toda a interação é realizada através da rede. As instâncias de função não possuem memória própria e, portanto, requerem armazenamento compartilhado para armazenar o estado. O enfraquecimento forçado da conectividade dos elementos do sistema pode aumentar a flexibilidade e a velocidade do desenvolvimento de serviços, mas ao mesmo tempo pode complicar significativamente seu suporte.
Em particular, é bastante difícil ver a estrutura abrangente do serviço, determinar como as funções são integradas entre si, entender o que e por que deu errado no caso de uma falha. Além disso, a natureza das funções orientadas a consulta e sem servidor significa que alguns problemas serão difíceis de detectar.
(p. 136-137 em tradução para o russo)
Mais uma vez, fiquei surpreso com o fato de a maioria dos sistemas FaaS não ser boa demais para resolver tarefas que exigem processamento ativo:
... além disso, devido à implementação de serviços sem servidor, o tempo de execução de uma instância de função geralmente é limitado. Isso significa que a abordagem FaaS geralmente não é adequada para aplicativos que exigem um longo processamento de dados em segundo plano. (P. 138 em tradução russa)
Por fim, fiquei satisfeito com a observação de que o FaaS está se tornando economicamente inconveniente se for impossível garantir uma operação ininterrupta e prolongada do processador:
Mas se você tiver tantas solicitações que a função esteja constantemente ativa, é provável que pague demais pelo número de solicitações processadas.
... à medida que o serviço cresce, o número de solicitações processadas aumenta a um nível tal que o processador está constantemente ocupado processando-as. Nesse ponto, a taxa pelo número de solicitações começa a se tornar inútil. O custo por unidade de tempo de CPU das máquinas virtuais em nuvem diminui à medida que os núcleos são adicionados, além de reservar recursos e descontos para uso a longo prazo. O custo do pagamento pelo número de solicitações geralmente aumenta com o número de solicitações.
(p. 139-140 em tradução russa)
Como resultado, ainda não entendi: quando é melhor usar "funções como serviços"? Observo que Burns descreve brevemente o trabalho com tarefas de curto prazo orientadas a eventos que não carregam muito o processador, como autenticação de dois fatores (2FA). No entanto, considerando que estamos falando de pequenas tarefas de curto prazo e com baixos custos, surge a pergunta: por que elas deveriam ser escaladas de forma independente? Por que não incluir esses recursos em outro serviço de contêiner estreitamente relacionado ao primeiro?
Espero lidar melhor com esses problemas quando usar as tecnologias FaaS na prática.
Além de alguma confusão com o FaaS, gostei muito do livro. É lido rapidamente, facilmente percebido. Mais uma vez, ele nos lembra o enorme potencial de enfraquecer a conexão entre componentes em todos os níveis de desenvolvimento de aplicativos. Finalmente, agora será muito mais fácil manter uma conversa com meus colegas sobre tópicos como "contêineres laterais".