
Com o aumento da experiência no setor de TI, você começa a perceber que os sistemas têm seu próprio caráter. Eles podem ser obedientes, silenciosos, temperamentais ou severos. Podem atrair ou repelir. De uma forma ou de outra, você precisa 'negociar' com eles, manobrando entre os 'perigos ocultos' e construindo cadeias de interação.
E chegou a nossa vez de construir uma plataforma em nuvem, e para isso foi necessário 'persuadir' algumas subsistemas a trabalharem conosco. Felizmente, temos nossa 'linguagem API', mãos habilidosas e uma grande dose de entusiasmo.
Neste artigo não haverá hardcore técnico, mas sim uma descrição dos problemas que enfrentamos ao construir a nuvem. Decidi descrever nosso caminho como uma leve ficção técnica sobre como buscamos um idioma comum com os sistemas e o que resultou disso.
Bem-vindo ao conteúdo.
Início da jornada
Há algum tempo, nossa equipe recebeu a tarefa de lançar uma plataforma em nuvem para nossos clientes. Tínhamos o apoio da direção, recursos, uma pilha de hardware e liberdade na escolha das tecnologias para implementar a parte de software do serviço.
Havia também uma série de requisitos:
- o serviço precisava de um painel de controle amigável;
- a plataforma deve ser integrada ao sistema de faturamento existente;
- a parte software-hardware: OpenStack + Tungsten Fabric (Open Contrail), que nossos engenheiros aprenderam a 'preparar' bastante bem.
Falaremos sobre como a equipe foi formada, como a interface do painel foi desenvolvida e como as decisões de design foram tomadas em outra ocasião, se a comunidade do Habr tiver interesse.
As ferramentas que decidimos usar:
- Python + Flask + Swagger + SQLAlchemy — uma combinação padrão de Python;
- Vue.js para o frontend;
- decidimos realizar a interação entre componentes e serviços usando Celery sobre AMQP.
Antecipando perguntas sobre a escolha do Python, explicarei. A linguagem encontrou seu espaço em nossa empresa e ao seu redor se formou uma pequena, mas significativa cultura. Portanto, decidimos começar a construir o serviço exatamente com ela. Especialmente porque a velocidade de desenvolvimento em tais tarefas normalmente é um fator decisivo.
Então, vamos começar nosso conhecimento.
Bill Silencioso — faturamento
Nós conhecíamos esse cara há muito tempo. Ele sempre sentava ao nosso lado e contava algo em silêncio. Às vezes, ele nos encaminhava solicitações de usuários, gerava faturas para clientes, gerenciava serviços. Um cara trabalhador comum. No entanto, havia algumas dificuldades. Ele era silencioso, às vezes pensativo e frequentemente — introspectivo.

O billing foi o primeiro sistema que tentamos integrar. E a primeira dificuldade apareceu ao processar serviços.
Por exemplo, ao criar ou deletar, a tarefa vai para a fila interna do billing. Assim, foi implementado um sistema de trabalho assíncrono com serviços. Para processar nossos tipos de serviços, precisávamos 'enfileirar' nossas tarefas nessa fila. E aqui nos deparamos com o problema: a falta de documentação.

De acordo com a descrição da API, essa tarefa poderia ser resolvida, mas não tínhamos tempo para engenharia reversa, então levamos a lógica para fora e organizamos uma fila de tarefas sobre o RabbitMQ. A operação sobre o serviço é iniciada pelo cliente no painel de controle, envolta em uma 'tarefa' Celery no backend e executada do lado do billing e OpenStack. O Celery permite gerenciar tarefas de forma bastante conveniente, organizando repetições e monitorando o estado. Para mais informações sobre o 'celery', você pode ler, por exemplo, .
Além disso, o billing não interrompeu o projeto quando o dinheiro acabou. Conversando com os desenvolvedores, descobrimos que ao calcular estatísticas (e precisamos implementar essa lógica), há uma complexa interconexão de regras de interrupção. Mas esses modelos não se ajustam bem à nossa realidade. Também realizamos isso através de tarefas no Celery, transferindo a lógica de gerenciamento de serviços para o backend.
Ambos os problemas mencionados levaram a um código um pouco inflado e no futuro teremos que nos dedicar a refatorar para extrair a lógica de trabalho com tarefas para um serviço separado. Também precisamos armazenar parte das informações dos usuários e seus serviços em nossas tabelas para manter essa lógica.
Mais um problema — a falta de comunicação.
Para algumas solicitações à API, o Billy responde silenciosamente com 'Ok'. Por exemplo, isso aconteceu quando fizemos lançamentos de pagamentos prometidos durante o teste (sobre o qual falaremos mais tarde). As solicitações foram executadas corretamente e não vimos erros.

Foi necessário estudar os logs ao trabalhar com o sistema através da interface. Descobriu-se que o billing realiza essas solicitações, alterando o escopo para um usuário específico, por exemplo, admin, passando-o no parâmetro su.
No geral, apesar das lacunas na documentação e algumas falhas na API, tudo correu bastante bem. Os logs podem ser lidos mesmo sob alta carga, se entender como estão estruturados e o que se deve buscar. A estrutura do banco de dados é complexa, mas bastante lógica e até atraente em certos aspectos.
Assim, resumindo, os principais problemas que enfrentamos na fase de interação estão relacionados às particularidades de implementação do sistema específico:
- funcionalidades não documentadas que nos afetaram de alguma forma;
- código-fonte fechado (o billing foi escrito em C++), o que significa que não foi possível resolver o problema 1 de nenhuma maneira além do "método de tentativa e erro".
Felizmente, o produto possui uma API bastante robusta e integramos as seguintes subsistemas em nosso painel de controle:
- módulo de suporte técnico — solicitações do painel de controle são "proxies" para o billing de forma transparente para os clientes do serviço;
- módulo financeiro — permite a emissão de faturas para clientes atuais, realizar cobranças e gerar documentos de pagamento;
- módulo de gerenciamento de serviços — para isso, tivemos que implementar nosso próprio manipulador. A expansibilidade do sistema nos favoreceu e "ensinamos" o Billing a um novo tipo de serviço.
Deu trabalho, mas, de qualquer forma, acho que conseguiremos nos entender com o Billing.
Caminhadas pelos campos de tungstênio — Tungsten Fabric
Campos de tungstênio, repletos de centenas de fios, que transmitem milhares de bits de informação. A informação é coletada em "pacotes", analisada e complexas rotas são traçadas, como que por mágica.

Este é o domínio do segundo sistema com o qual tivemos que nos familiarizar — Tungsten Fabric (TF), anteriormente conhecido como OpenContrail. Sua tarefa é gerenciar os equipamentos de rede, fornecendo uma abstração de software para nós, como usuários. TF é uma SDN que encapsula a lógica complexa de operação com os equipamentos de rede. Há um bom artigo sobre a tecnologia em si, por exemplo, .
O sistema está integrado ao OpenStack (de que falaremos a seguir) através do plugin Neutron.

Interação dos serviços OpenStack.
Essa sistema foi apresentado a nós pela equipe de operações. Utilizamos a API do sistema para gerenciar a pilha de rede de nossos serviços. Até agora, não tivemos problemas sérios ou inconvenientes (não posso falar pelos colegas de operações), mas houve algumas situações curiosas na interação.
A primeira parecia assim: comandos que exigiam a saída de grandes quantidades de dados no console da instância ao conectar via SSH simplesmente "congelavam" a conexão, enquanto pelo VNC tudo funcionava corretamente.

Para quem não está familiarizado com o problema, isso pode parecer engraçado: ls /root funciona corretamente, enquanto, por exemplo, top "congela" completamente. Felizmente, já enfrentamos problemas semelhantes. Isso foi resolvido ajustando o MTU no caminho das nós de computação até os roteadores. A propósito, isso não é um problema do TF.
O próximo problema estava logo à frente. Em um "belo" momento, a mágica do roteamento desapareceu, assim, de repente. O TF parou de gerenciar o roteamento no hardware.

Trabalhamos com OpenStack a partir do nível de admin e, em seguida, mudamos para o nível do usuário necessário. O SDN parece "interceptar" o escopo do usuário sob o qual as ações são executadas. O problema é que essa mesma conta de administrador é usada para a conexão entre o TF e o OpenStack. No momento da mudança para o usuário, a "mágica" desaparecia. A solução foi criar uma conta separada para trabalhar com o sistema. Isso permitiu operar sem quebrar a funcionalidade da integração.
Formas de vida de silicone — OpenStack
Uma criatura de silicone de forma peculiar habita perto dos campos de tungstênio. Ela mais se parece com uma criança gigante, que com um único golpe pode nos esmagar, mas não exibe agressividade perceptível. Não causa medo, mas seu tamanho inspira preocupação. Assim como a complexidade do que acontece ao seu redor.

OpenStack é o núcleo de nossa plataforma.
OpenStack possui várias subsistemas, entre os quais utilizamos ativamente Nova, Glance e Cinder. Cada um deles tem sua própria API. Nova é responsável pelos recursos de computação e criação de instâncias, Cinder — gerenciamento de volumes e suas cópias de segurança, Glance — serviço de imagens, que gerencia os templates do sistema operacional e suas metainformações.
Cada serviço é executado em um contêiner, e o corretor de mensagens é o "coelho branco" — RabbitMQ.
Esse sistema nos trouxe mais problemas inesperados.
O primeiro problema não tardou a aparecer quando tentamos conectar um volume adicional ao servidor. A API do Cinder se recusava categoricamente a executar essa tarefa. Para ser mais preciso, se acreditarmos no próprio OpenStack, a conexão é estabelecida, mas dentro do servidor virtual, o dispositivo de disco está ausente.

Decidimos "contornar" a situação e solicitamos a mesma ação à API do Nova. O resultado — o dispositivo é corretamente conectado e está disponível dentro do servidor. Parece que o problema surge quando o armazenamento em bloco não responde ao Cinder.
Uma nova dificuldade nos aguardava ao trabalhar com discos. Não conseguimos desconectar o volume do sistema do servidor.
Novamente, o próprio OpenStack "garante" que a conexão foi encerrada e que agora é possível trabalhar corretamente com o volume separadamente. Mas a API se recusava categoricamente a realizar operações sobre o disco.

Aqui decidimos não entrar em conflito e mudar a perspectiva sobre a lógica de funcionamento do serviço. Já que existe uma instância, deve haver também um volume de sistema. Portanto, o usuário não pode, por enquanto, excluir ou desconectar o "disco" do sistema sem remover o "servidor".
O OpenStack é um complexo sistema bastante complicado com sua lógica de interação e uma API intrincada. A documentação bastante detalhada e, claro, o método de tentativas e erros (como poderia ser diferente) nos socorrem.
Teste inicial
Realizamos o teste inicial em dezembro do ano passado. O objetivo principal era verificar o funcionamento do nosso projeto em modo real, tanto do lado técnico quanto do lado UX. A audiência foi convidada de forma seletiva e o teste foi fechado. No entanto, também deixamos a possibilidade de solicitar acesso ao teste em nosso site.
O próprio teste, claro, não aconteceu sem momentos cômicos, pois nossas aventuras estão apenas começando.
Primeiro, subestimamos o interesse no projeto e tivemos que adicionar nós de computação rapidamente durante o teste. Um caso comum para um cluster, mas aqui também havia nuances. Na documentação para a versão específica do TF, está indicada uma versão específica do núcleo, na qual o funcionamento com o vRouter foi testado. Decidimos iniciar nós com núcleos mais recentes. Como resultado, o TF não recebeu rotas dos nós. Tive que reverter os núcleos de emergência.

Outro detalhe curioso está relacionado à funcionalidade do botão "mudar senha" na área do usuário.
Decidimos usar JWT para gerenciar o acesso ao painel pessoal, para evitar trabalhar com sessões. Como os sistemas são diversificados e amplamente dispersos, gerenciamos nosso próprio token, no qual 'embutimos' as sessões da bilhetagem e o token do OpenStack. Ao alterar a senha, o token, obviamente, 'expira', uma vez que os dados do usuário já se tornam inválidos e é necessário reemitir.

Perdemos este ponto de vista e não tínhamos recursos para rapidamente completar essa parte. Tivemos que cortar a funcionalidade logo antes do lançamento do teste.
Atualmente, fazemos logout do usuário se a senha for alterada.
Apesar desses detalhes, os testes correram bem. Em algumas semanas, cerca de 300 pessoas nos visitaram. Conseguimos ver o produto pelos olhos dos usuários, testá-lo em ação e coletar feedback de qualidade.
Continua
Para muitos de nós, este é o primeiro projeto de tal escala. Aprendemos várias lições valiosas sobre como trabalhar em equipe, tomar decisões arquitetônicas e de design. Como integrar sistemas complexos com recursos limitados e levá-los para produção.
É claro que há muito a se aprimorar tanto no código quanto nas interfaces de integração dos sistemas. O projeto é bastante jovem, mas estamos cheios de ambições para transformá-lo em um serviço confiável e conveniente.
Já conseguimos persuadir os sistemas. O Bill está calmamente cuidando do cálculo, faturamento e solicitações dos usuários em seu canto. A 'mágica' dos campos de tungstênio nos proporciona uma conexão estável. E apenas o OpenStack às vezes fica temperamental, gritando algo como 'WSREP ainda não preparou o nó para uso da aplicação'. Mas essa é outra história…
Recentemente, lançamos o serviço.
Você pode descobrir todos os detalhes em nosso .

Equipe de desenvolvimento CLO
Links Úteis
OpenStack
Tungsten Fabric
Fonte: habr.com
