.NET Core no Linux, DevOps a cavalo

Desenvolvemos DevOps da melhor maneira que pudemos. Éramos 8, e Vasya era o mais legal no Windows. De repente, Vasya saiu e eu tive a tarefa de lançar um novo projeto que foi fornecido pelo desenvolvimento do Windows. Quando joguei toda a pilha de desenvolvimento do Windows na mesa, percebi que a situação era uma dor...

É assim que a história começa Alexandra Sinchinova em DevOpsConf. Quando o principal especialista em Windows deixou a empresa, Alexander se perguntou o que fazer agora. Mude para o Linux, é claro! Alexander contará como ele conseguiu criar um precedente e transferir parte do desenvolvimento do Windows para Linux usando o exemplo de um projeto concluído para 100 usuários finais.

.NET Core no Linux, DevOps a cavalo

Como entregar um projeto para RPM de maneira fácil e sem esforço usando TFS, Puppet, Linux .NET core? Como dar suporte ao versionamento de um banco de dados de projeto se a equipe de desenvolvimento ouve as palavras Postgres e Flyway pela primeira vez e o prazo é depois de amanhã? Como integrar com Docker? Como motivar os desenvolvedores .NET a abandonar o Windows e os smoothies em favor do Puppet e do Linux? Como resolver conflitos ideológicos se não há força, nem vontade, nem recursos para manter o Windows em produção? Sobre isso, bem como sobre Web Deploy, testes, CI, sobre as práticas de uso do TFS em projetos existentes e, claro, sobre muletas quebradas e soluções de trabalho, na transcrição do relatório de Alexander.


Então, Vasya saiu, a tarefa é minha, os desenvolvedores estão esperando impacientemente com forcados. Quando finalmente percebi que Vasya não poderia ser devolvida, comecei a trabalhar. Para começar, avaliei a porcentagem de VMs Win em nossa frota. O placar não foi a favor do Windows.

.NET Core no Linux, DevOps a cavalo

Como estamos desenvolvendo ativamente o DevOps, percebi que algo precisa ser mudado na abordagem de entrega de um novo aplicativo. Só havia uma solução - se possível, transferir tudo para o Linux. O Google me ajudou - naquela época o .Net já havia sido portado para Linux, e percebi que essa era a solução!

Por que o .NET core em conjunto com o Linux?

Houve vários motivos para isso. Entre “pagar em dinheiro” e “não pagar”, a maioria escolherá a segunda - como eu. Uma licença para MSDB custa cerca de US$ 1; manter uma frota de máquinas virtuais Windows custa centenas de dólares. Para uma grande empresa esta é uma grande despesa. É por isso poupança - primeira razão. Não é o mais importante, mas um dos mais significativos.

As máquinas virtuais Windows consomem mais recursos do que suas irmãs Linux - eles são pesados. Dada a dimensão da grande empresa, escolhemos o Linux.

O sistema é simplesmente integrado ao CI existente. Nós nos consideramos DevOps progressivos, usamos Bamboo, Jenkins e GitLab CI, então a maior parte do nosso trabalho é executado em Linux.

A última razão é acompanhamento conveniente. Precisávamos diminuir a barreira de entrada dos “acompanhantes” – os caras que entendem da parte técnica, garantem o atendimento ininterrupto e mantêm o atendimento de segunda linha. Eles já estavam familiarizados com a pilha Linux, então é muito mais fácil para eles entender, dar suporte e manter um novo produto do que gastar recursos adicionais para entender a mesma funcionalidade do software para a plataforma Windows.

Requisitos

Em primeiro lugar - conveniência da nova solução para desenvolvedores. Nem todos estavam prontos para a mudança, especialmente depois que a palavra Linux foi falada. Os desenvolvedores desejam seu Visual Studio favorito, TFS, com autotestes para montagens e smoothies. Como ocorre a entrega à produção não é importante para eles. Portanto, decidimos não alterar o processo usual e deixar tudo inalterado para o desenvolvimento do Windows.

Novo projeto necessário integrar-se ao CI existente. Os trilhos já estavam lá e todo o trabalho teve que ser feito levando em consideração os parâmetros do sistema de gerenciamento de configuração, padrões de entrega aceitos e sistemas de monitoramento.

Facilidade de suporte e operação, como condição para o limite mínimo de entrada para todos os novos participantes das diferentes divisões e do departamento de suporte.

Prazo - ontem.

Grupo de Desenvolvimento Win

Com o que a equipe do Windows estava trabalhando naquela época?

.NET Core no Linux, DevOps a cavalo

Agora posso dizer com segurança que Servidor de Identidade4 é uma alternativa gratuita e interessante ao ADFS com recursos semelhantes, ou o que Núcleo do Entity Framework - um paraíso para um desenvolvedor, onde você não precisa se preocupar em escrever scripts SQL, mas descrever consultas no banco de dados em termos OOP. Mas então, durante a discussão do plano de ação, olhei para essa pilha como se fosse o cuneiforme sumério, reconhecendo apenas PostgreSQL e Git.

Naquela época, estávamos usando ativamente Fantoche como um sistema de gerenciamento de configuração. Na maioria dos nossos projetos usamos CI do GitLab, Elástico, serviços balanceados de alta carga usando HAProxy monitorou tudo com Zabbix, ligamentos grafana и Prometeu, Jaeger, e tudo isso girava em pedaços de ferro HPESXi em VMware. Todo mundo sabe disso - um clássico do gênero.

.NET Core no Linux, DevOps a cavalo

Vamos olhar e tentar entender o que aconteceu antes de iniciarmos todas essas intervenções.

O que foi

O TFS é um sistema bastante poderoso que não apenas entrega o código do desenvolvedor até a máquina de produção final, mas também possui um conjunto para integração muito flexível com vários serviços - para fornecer CI em nível de plataforma cruzada.

.NET Core no Linux, DevOps a cavalo
Anteriormente, estas eram janelas sólidas. O TFS usou vários agentes Build, que foram usados ​​para montar muitos projetos. Cada agente possui de 3 a 4 trabalhadores para paralelizar tarefas e otimizar o processo. Então, de acordo com os planos de lançamento, o TFS entregou o Build recém-preparado ao servidor de aplicativos Windows.

O que queríamos alcançar?

Usamos TFS para entrega e desenvolvimento e executamos o aplicativo em um servidor de aplicativos Linux, e há algum tipo de mágica entre eles. Esse caixa Mágica e aí está o sal do trabalho que temos pela frente. Antes de desmontá-lo, darei um passo para o lado e direi algumas palavras sobre o aplicativo.

Projeto

O aplicativo oferece funcionalidade para manuseio de cartões pré-pagos.

.NET Core no Linux, DevOps a cavalo

Cliente

Havia dois tipos de usuários. Primeiro obteve acesso fazendo login usando um certificado SSL SHA-2. você segundo houve acesso através de login e senha.

HAPROxy

Em seguida, a solicitação do cliente foi para o HAProxy, que resolveu os seguintes problemas:

  • autorização primária;
  • Rescisão SSL;
  • ajuste de solicitações HTTP;
  • solicitações de transmissão.

O certificado do cliente foi verificado ao longo da cadeia. Nós - autoridade e podemos arcar com isso, já que nós mesmos emitimos certificados para atender clientes.

Preste atenção ao terceiro ponto, voltaremos a ele um pouco mais tarde.

Backend

Eles planejaram fazer o backend no Linux. O backend interage com o banco de dados, carrega a lista de privilégios necessária e então, dependendo dos privilégios que o usuário autorizado possui, fornece acesso para assinar documentos financeiros e enviá-los para execução, ou gerar algum tipo de relatório.

Economia com HAProxy

Além dos dois contextos que cada cliente navegava, havia também um contexto de identidade. Servidor de Identidade4 apenas permite que você faça login, este é um análogo gratuito e poderoso para ADFS - Serviços de Federação do Active Directory.

A solicitação de identidade foi processada em diversas etapas. Primeiro passo - cliente entrou no back-end, que se comunicou com este servidor e verificou a presença de um token para o cliente. Caso não fosse encontrado, a solicitação era devolvida ao contexto de onde veio, mas com redirecionamento, e com o redirecionamento ia para a identidade.

Segunda etapa - a solicitação foi recebida para a página de autorização no IdentityServer, onde o cliente se registrou, e aquele token tão esperado apareceu no banco de dados do IdentityServer.

Terceiro passo - o cliente foi redirecionado de volta ao contexto de onde veio.

.NET Core no Linux, DevOps a cavalo

IdentityServer4 tem um recurso: ele retorna a resposta à solicitação de retorno via HTTP. Por mais que tenhamos lutado para configurar o servidor, por mais que nos esclareçamos com a documentação, cada vez que recebíamos uma solicitação inicial do cliente com uma URL que vinha via HTTPS, e o IdentityServer retornava o mesmo contexto, mas com HTTP. Ficamos chocados! E transferimos tudo isso através do contexto de identidade para o HAProxy, e nos cabeçalhos tivemos que modificar o protocolo HTTP para HTTPS.

Qual é a melhoria e onde você economizou?

Economizamos dinheiro usando uma solução gratuita para autorizar um grupo de usuários, recursos, pois não colocamos o IdentityServer4 como um nó separado em um segmento separado, mas o usamos junto com o backend no mesmo servidor onde o backend do aplicativo é executado .

Como deveria funcionar

Então, como prometi - Magic Box. Já entendemos que estamos garantidos para avançar para o Linux. Vamos formular tarefas específicas que exigem soluções.

.NET Core no Linux, DevOps a cavalo

Manifestações de fantoches. Para fornecer e gerenciar a configuração de serviços e aplicativos, foi necessário escrever receitas interessantes. Um rolo de lápis mostra eloquentemente como isso foi feito com rapidez e eficiência.

Método de Entrega. O padrão é RPM. Todo mundo entende que no Linux não dá para ficar sem ele, mas o projeto em si, após a montagem, era um conjunto de arquivos DLL executáveis. Eram cerca de 150, o projeto foi bastante difícil. A única solução harmoniosa é empacotar esse binário no RPM e implantar o aplicativo a partir dele.

Versionamento. Tínhamos que lançar com frequência e decidir como formar o nome do pacote. Esta é uma questão do nível de integração com o TFS. Tínhamos um agente de compilação no Linux. Quando o TFS envia uma tarefa para um manipulador - trabalhador - para o agente Build, ele também passa um monte de variáveis ​​que acabam no ambiente do processo manipulador. Essas variáveis ​​de ambiente contêm o nome da compilação, o nome da versão e outras variáveis. Leia mais sobre isso na seção “Construindo um pacote RPM”.

Configurando o TFS se resumiu a configurar o Pipeline. Anteriormente, coletávamos todos os projetos do Windows em agentes do Windows, mas agora aparece um agente Linux - um agente Build, que precisa ser incluído no grupo de construção, enriquecido com alguns artefatos e informado que tipo de projetos serão construídos neste agente Build e, de alguma forma, modificar o Pipeline.

Servidor de Identidade. ADFS não é o nosso caminho, estamos optando pelo código aberto.

Vamos examinar os componentes.

caixa Mágica

Consiste em quatro partes.

.NET Core no Linux, DevOps a cavalo

Agente de compilação Linux. Linux, porque construímos para ele – é lógico. Esta parte foi feita em três etapas.

  • Configurar trabalhadores e não sozinho, já que se esperava um trabalho distribuído no projeto.
  • Instale o .NET Core 1.x. Por que 1.x quando 2.0 já está disponível no repositório padrão? Porque quando iniciamos o desenvolvimento, a versão estável era a 1.09, e foi decidido fazer o projeto baseado nela.
  • Git 2.x.

Repositório RPM. Os pacotes RPM precisavam ser armazenados em algum lugar. Presumiu-se que usaríamos o mesmo repositório RPM corporativo que está disponível para todos os hosts Linux. Foi isso que eles fizeram. O servidor do repositório está configurado webhooks que baixou o pacote RPM necessário do local especificado. A versão do pacote foi informada ao webhook pelo agente Build.

GitLab. Atenção! O GitLab aqui não é usado por desenvolvedores, mas pelo departamento de operações para controlar versões de aplicativos, versões de pacotes, monitorar o status de todas as máquinas Linux e armazena a receita - todos os manifestos do Puppet.

Fantoche — resolve todas as questões controversas e entrega exatamente a configuração que queremos do Gitlab.

Começamos a mergulhar. Como funciona a entrega de DLL para RPM?

Entrega DDL para RPM

Digamos que temos uma estrela do rock no desenvolvimento .NET. Ele usa o Visual Studio e cria um branch de lançamento. Depois disso, ele faz o upload para o Git, e o Git aqui é uma entidade TFS, ou seja, é o repositório da aplicação com o qual o desenvolvedor trabalha.

.NET Core no Linux, DevOps a cavalo

Depois disso, o TFS vê que um novo commit chegou. Qual aplicativo? Nas configurações do TFS há um rótulo que indica quais recursos um agente Build específico possui. Nesse caso, ele vê que estamos construindo um projeto .NET Core e seleciona um agente Linux Build do pool.

O agente Build recebe as fontes e baixa os arquivos necessários dependências do repositório .NET, npm, etc. e após construir o próprio aplicativo e posterior empacotamento, envia o pacote RPM para o repositório RPM.

Por outro lado, acontece o seguinte. O engenheiro do departamento de operações está diretamente envolvido na implementação do projeto: ele altera as versões dos pacotes em Hiera no repositório onde a receita do aplicativo está armazenada, após o qual o Puppet é acionado yum, busca o novo pacote no repositório e a nova versão do aplicativo está pronta para uso.

.NET Core no Linux, DevOps a cavalo

Tudo é simples em palavras, mas o que acontece dentro do próprio agente Build?

Empacotando DLL RPM

Fontes de projeto recebidas e tarefas de construção do TFS. Agente de construção começa a construir o próprio projeto a partir das fontes. O projeto montado está disponível em conjunto Arquivos DLL, que são empacotados em um arquivo zip para reduzir a carga no sistema de arquivos.

O arquivo ZIP é jogado fora para o diretório de construção do pacote RPM. Em seguida, o script Bash inicializa as variáveis ​​de ambiente, encontra a versão Build, a versão do projeto, o caminho para o diretório build e executa o RPM-build. Assim que a compilação for concluída, o pacote será publicado em repositório local, que está localizado no agente Build.

Em seguida, do agente Build para o servidor no repositório RPM A solicitação JSON é enviada indicando o nome da versão e build. O Webhook, sobre o qual falei anteriormente, baixa esse mesmo pacote do repositório local no agente Build e disponibiliza o novo assembly para instalação.

.NET Core no Linux, DevOps a cavalo

Por que esse esquema específico de entrega de pacotes para o repositório RPM? Por que não consigo enviar imediatamente o pacote montado para o repositório? O fato é que esta é uma condição para garantir a segurança. Este cenário limita a possibilidade de pessoas não autorizadas fazerem upload de pacotes RPM para um servidor acessível a todas as máquinas Linux.

Versionamento de banco de dados

Em consulta com a equipe de desenvolvimento, descobriu-se que os caras estavam mais próximos do MS SQL, mas na maioria dos projetos não Windows já estávamos usando o PostgreSQL com toda a força. Como já havíamos decidido abandonar tudo pago, começamos a usar o PostgreSQL aqui também.

.NET Core no Linux, DevOps a cavalo

Nesta parte quero contar como versionamos o banco de dados e como escolhemos entre Flyway e Entity Framework Core. Vejamos seus prós e contras.

Contras

Flyway só vai em uma direção, nós não podemos reverter - esta é uma desvantagem significativa. Você pode compará-lo com o Entity Framework Core de outras maneiras - em termos de conveniência para o desenvolvedor. Você lembra que colocamos isso em primeiro plano, e o principal critério era não mudar nada para o desenvolvimento do Windows.

Para Flyway nós algum tipo de invólucro era necessáriopara que os caras não escrevam Consultas SQL. Eles estão muito mais próximos de operar em termos de OOP. Escrevemos instruções para trabalhar com objetos de banco de dados, geramos uma consulta SQL e a executamos. A nova versão do banco de dados está pronta, testada - está tudo bem, tudo funciona.

O Entity Framework Core tem um sinal de menos - sob cargas pesadas, ele cria consultas SQL abaixo do ideal, e o rebaixamento no banco de dados pode ser significativo. Mas como não temos um serviço de alta carga, não calculamos a carga em centenas de RPS, aceitamos esses riscos e delegamos o problema para nós futuros.

Prós

Núcleo do Entity Framework funciona imediatamente e é fácil de desenvolvere Flyway Integra-se facilmente ao CI existente. Mas tornamos isso conveniente para os desenvolvedores :)

Procedimento de roll-up

O Puppet vê que uma mudança na versão do pacote está chegando, incluindo aquela que é responsável pela migração. Primeiro, ele instala um pacote que contém scripts de migração e funcionalidades relacionadas ao banco de dados. Após isso, a aplicação que funciona com o banco de dados é reiniciada. Em seguida vem a instalação dos componentes restantes. A ordem em que os pacotes são instalados e os aplicativos são iniciados é descrita no manifesto do Puppet.

Os aplicativos usam dados confidenciais, como tokens, senhas de banco de dados, tudo isso é puxado para a configuração do Puppet master, onde são armazenados de forma criptografada.

Problemas de TFS

Depois que decidimos e percebemos que tudo estava realmente funcionando para nós, decidi olhar o que estava acontecendo com as montagens no TFS como um todo para o departamento de desenvolvimento do Win em outros projetos - se estávamos construindo/lançando rapidamente ou não, e descobriu problemas significativos com velocidade.

Um dos projetos principais leva de 12 a 15 minutos para ser montado - é muito tempo, você não pode viver assim. Uma análise rápida mostrou uma redução terrível na E/S, e isso ocorreu nos arrays.

Depois de analisar componente por componente, identifiquei três focos. Primeiro - "Antivírus Kaspersky", que verifica fontes em todos os agentes do Windows Build. Segundo - Windows Indexador. Não foi desabilitado e tudo foi indexado em tempo real nos agentes Build durante o processo de implantação.

Terceiro - Instalação do Npm. Acontece que na maioria dos Pipelines usamos exatamente esse cenário. Por que ele é ruim? O procedimento de instalação do Npm é executado quando a árvore de dependências é formada em Pacote-lock.json, onde são registradas as versões dos pacotes que serão utilizados para construir o projeto. A desvantagem é que o Npm install sempre obtém as versões mais recentes dos pacotes da Internet, e isso leva muito tempo no caso de um projeto grande.

Às vezes, os desenvolvedores fazem experiências em uma máquina local para testar como funciona uma parte específica ou todo o projeto. Às vezes acontecia que tudo estava legal localmente, mas eles montavam, desenrolavam e nada funcionava. Começamos a descobrir qual é o problema - sim, diferentes versões de pacotes com dependências.

Solução

  • Fontes em exceções AV.
  • Desative a indexação.
  • Vamos para npmci.

As vantagens do npm ci são que nós Coletamos a árvore de dependências uma vez, e temos a oportunidade de fornecer ao desenvolvedor lista atual de pacotes, com o qual ele pode experimentar localmente o quanto quiser. Esse poupa tempo desenvolvedores que escrevem código.

Configuração

Agora um pouco sobre a configuração do repositório. Historicamente usamos Nexus para gerenciar repositórios, incluindo REPO interno. Este repositório interno contém todos os componentes que usamos para fins internos, por exemplo, monitoramento auto-escrito.

.NET Core no Linux, DevOps a cavalo

Nós também usamos NuGet, pois possui melhor cache em comparação com outros gerenciadores de pacotes.

resultado

Depois de otimizarmos os Build Agents, o tempo médio de construção foi reduzido de 12 minutos para 7.

Se contarmos todas as máquinas que poderíamos ter usado para Windows, mas mudamos para Linux neste projeto, economizamos cerca de US$ 10 mil. E isso é só em licenças, e mais se levarmos em conta o conteúdo.

Planos

Para o próximo trimestre, planejamos trabalhar na otimização da entrega de código.

Mudando para uma imagem Docker pré-construída. TFS é uma coisa legal com muitos plug-ins que permitem integração ao Pipeline, incluindo montagem baseada em gatilho de, digamos, uma imagem Docker. Queremos fazer esse gatilho para o mesmo Pacote-lock.json. Se a composição dos componentes usados ​​para construir o projeto mudar de alguma forma, construímos uma nova imagem Docker. Posteriormente, é usado para implantar o contêiner com o aplicativo montado. Este não é o caso agora, mas estamos planejando mudar para uma arquitetura de microsserviços no Kubernetes, que está se desenvolvendo ativamente em nossa empresa e servindo soluções de produção há muito tempo.

Resumo

Encorajo todos a jogarem fora o Windows, mas não é porque não sei cozinhá-lo. A razão é que a maioria das soluções Opensource são Pilha Linux. você está bem economizar em recursos. Na minha opinião, o futuro pertence às soluções Open Source em Linux com uma comunidade poderosa.

Perfil do palestrante de Alexander Sinchinov no GitHub.

Conferência DevOps é uma conferência sobre a integração de processos de desenvolvimento, teste e operação para profissionais por profissionais. É por isso que o projeto de que Alexander falou? implementado e funcionando, e no dia da apresentação houve dois lançamentos de sucesso. Sobre Conferência DevOps na RIT++ Nos dias 27 e 28 de maio haverá ainda mais casos semelhantes de profissionais. Você ainda pode pular na última carruagem e enviar um relatório ou não tenha pressa agendar bilhete. Encontre-nos em Skolkovo!

Fonte: habr.com

Adicionar um comentário