.NET Core no Linux, DevOps a cavalo

Desenvolvemos o DevOps como pudemos. Éramos 8 e Vasya era o mais legal do Windows. De repente, Vasya saiu e eu tive a tarefa de lançar um novo projeto que fornece desenvolvimento para Windows. Quando joguei toda a pilha de desenvolvimento do Windows na mesa, percebi que a situação é uma dor ...

Assim começa a história de Alexander Sinchinov no 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 definir um precedente e transferir parte do desenvolvimento do Windows para o Linux usando o exemplo de um projeto concluído para 100.000 usuários finais.



Como entregar um projeto ao RPM com facilidade e sem esforço usando o TFS, Puppet, Linux .NET core? Como manter o controle de versão do banco de dados do projeto se o desenvolvimento ouvir primeiro as palavras Postgres e Flyway e o prazo depois de amanhã? Como se integrar ao Docker? Como motivar os desenvolvedores .NET a abandonar o Windows e os smoothies em favor do Puppet e Linux? Como resolver conflitos ideológicos, se não há forças, desejo ou recursos para servir o Windows em produção? Sobre isso, bem como sobre o Web Deploy, testes, IC, sobre as práticas de uso do TFS em projetos existentes e, é claro, sobre muletas quebradas e soluções de trabalho, na decodificação do relatório de Alexander.


Então, Vasya saiu, a tarefa é para mim, os desenvolvedores estão ansiosos com um forcado . Quando finalmente percebi que Vasya não podia ser devolvida, comecei a trabalhar. Para começar, estimei a porcentagem de Win VM em nosso parque. A pontuação não foi a favor do Windows.



Como estamos desenvolvendo ativamente o DevOps, percebi que algo precisa ser mudado na abordagem de remover um novo aplicativo. A solução foi uma - se possível, transfira tudo para o Linux. O Google me ajudou - naquela época .Net já estava portado para Linux, e eu percebi que essa solução!

Por que o .NET core é fornecido com o Linux?


Havia várias razões para isso. Entre "pagar dinheiro" e "não pagar", a maioria escolherá o segundo - como eu. Uma licença para MSDB custa cerca de US $ 1.000; a manutenção de uma frota de máquinas virtuais Windows custa centenas de dólares. Para uma grande empresa, essa é uma grande despesa. Portanto, salvar é o primeiro motivo . Não é o mais importante, mas um dos mais significativos.

As máquinas virtuais Windows ocupam mais recursos do que seus irmãos Linux - elas são pesadas . Dada a escala de uma grande empresa, escolhemos o Linux.

O sistema é simplesmente integrado ao IC existente . Nós nos consideramos DevOps progressivos, usamos Bamboo, Jenkins e GitLab CI, portanto, a maior parte do nosso trabalho é no Linux.

O último motivo é uma escolta conveniente. Tivemos que diminuir o limite de entrada para "acompanhantes" - pessoas que entendem a parte técnica, garantem serviços ininterruptos de operação e serviço a partir da segunda linha. Eles já estavam familiarizados com a pilha do Linux, portanto, é muito mais fácil para eles entender o novo produto, manter e manter, do que gastar recursos adicionais para lidar com a funcionalidade semelhante do software para a plataforma Windows.

Exigências


Em primeiro lugar, a conveniência de uma nova solução para desenvolvedores . Nem todos estavam prontos para a mudança, especialmente após a palavra falada Linux. Os desenvolvedores querem o seu amado Visual Studio, TFS, com testes de construção e smoothies. Como a entrega ocorre na produção - eles não se importam. Portanto, decidimos não alterar o processo usual e deixar tudo inalterado para o desenvolvimento do Windows.

O novo projeto precisa ser incorporado no IC 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.

Simplicidade no suporte e operação , como condição para um limite mínimo de entrada para todos os novos participantes de diferentes departamentos e departamentos de suporte.

Prazo - ontem .

Win Development Group


Com o que a equipe do Windows trabalhou então?



Agora, posso dizer com segurança que o IdentityServer4 é uma alternativa gratuita gratuita ao ADFS com recursos semelhantes ou que o Entity Framework Core é o paraíso dos desenvolvedores, onde você não pode se preocupar em escrever scripts SQL, mas descreve as consultas no banco de dados em termos OOP. Mas então, ao discutir o plano de ação, olhei para essa pilha como uma escrita cuneiforme suméria, reconhecendo apenas o PostgreSQL e o Git.

Naquela época, usamos o Puppet ativamente como um sistema de gerenciamento de configuração. Na maioria dos nossos projetos, usamos GitLab CI , Elastic , serviços altamente carregados e balanceados usando HAProxy, monitoramos tudo com o Zabbix , vários Grafana e Prometheus , Jaeger , e tudo isso girava no hardware da HP com ESXi no VMware . Todo mundo sabe - um clássico do gênero.



Vamos ver 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 fornece código do desenvolvedor para a máquina de produção final, mas também possui um conjunto de integração muito flexível com vários serviços - para fornecer CI no nível de plataforma cruzada.


Anteriormente, essas eram janelas sólidas. O TFS usou vários agentes de Compilação, que reuniram muitos projetos. Cada agente possui de 3 a 4 trabalhadores-a para paralelizar tarefas e otimizar o processo. Além disso, de acordo com os planos de lançamento, o TFS entregou o Build recém-instalado no servidor de aplicativos do Windows.

O que queríamos chegar


Para entrega e desenvolvimento, usamos o TFS e lançamos o aplicativo no servidor de aplicativos Linux, e há algum tipo de mágica entre eles. Esta Caixa Mágica é o sal do próximo trabalho. Antes de desmontá-lo em partes, darei um passo para o lado e direi duas palavras sobre o aplicativo.

Projeto


O aplicativo fornece funcionalidade para lidar com cartões pré-pagos.



Cliente


Havia dois tipos de usuários. O primeiro obteve acesso efetuando login com o certificado SSL SHA-2. O segundo teve acesso por login e senha.

HAProxy


Além disso, a solicitação do cliente caiu no HAProxy, que resolveu as seguintes tarefas:

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

A verificação do certificado do cliente passou pela cadeia. Somos autoridade e podemos pagar, pois emitimos certificados para atender os clientes.

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

Backend


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

Salvando com HAProxy


Além dos dois contextos pelos quais cada cliente passava, havia também um contexto de identidade. O IdentityServer4 apenas permite que você efetue login, é um análogo gratuito e poderoso para o ADFS - Serviços de Federação do Active Directory .

A solicitação na identidade foi processada em várias etapas. A primeira etapa - o cliente caiu no back-end , que trocou dados com este servidor e verificou a presença de um token para o cliente. Se não o encontrei, a solicitação retornou ao contexto de origem, mas com um redirecionamento e com um redirecionamento foi para a identidade.

A segunda etapa - a solicitação foi para a página de autenticação no IdentityServer, onde o cliente estava registrado, e o token muito aguardado apareceu no banco de dados do IdentityServer.

A terceira etapa - o cliente redirecionado de volta ao contexto de onde ele veio.



IdentityServer4 tem uma peculiaridade: retorna a resposta à solicitação de retorno via HTTP . Não importa o quanto lutamos com a configuração do servidor, não importamos o quanto ficamos esclarecidos com a documentação, cada vez que recebíamos uma solicitação inicial do cliente com uma URL que via HTTPS, e o IdentityServer retornava o mesmo contexto, mas com HTTP. Nós estávamos em choque! E tudo isso foi transferido 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 eles salvaram?

Economizamos dinheiro usando uma solução gratuita para autorizar um grupo de usuários, recursos, pois não consideramos o IdentityServer4 como uma nota separada em um segmento separado, mas o usamos junto com um back-end no mesmo servidor em que o back-end do aplicativo está girando.

Como deve funcionar


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



Manifestações de marionetes. Para entregar e gerenciar a configuração do serviço e aplicativo, você precisava escrever receitas legais. Um rolo de lápis mostra eloquentemente com que rapidez e eficiência isso foi feito.

Método de entrega. O padrão é RPM. Todo mundo entende que no Linux não há como, mas o próprio projeto após a montagem era um conjunto de arquivos DLL executáveis. Havia cerca de 150 deles, o projeto é bastante difícil. A única solução harmoniosa é empacotar esses binários no RPM e implantar o aplicativo a partir dele.

Versionamento Tivemos que lançar com muita freqüência e tivemos que decidir como formar o nome do pacote. Essa é uma questão de nível de integração do TFS. Tínhamos um agente de construção no Linux. Quando o TFS envia a tarefa para o manipulador - trabalhador - para o agente Build, ele também envia várias variáveis ​​que se enquadram no ambiente do processo do manipulador. Essas variáveis ​​de ambiente recebem o nome Build, o nome da versão e outras variáveis. Leia mais sobre isso na seção “Montando um pacote RPM”.

A configuração do TFS se resumiu à configuração do Pipeline. Anteriormente, coletamos todos os projetos do Windows nos agentes do Windows e agora existe um agente do Linux - um agente do Build que precisa ser incluído no grupo de montagem, enriquecido com alguns artefatos, informando que tipo de projetos serão criados nesse agente do Build, e de alguma forma modificar o Pipeline.

IdentityServer. O ADFS não é o nosso caminho, nós nos afogamos no Código Aberto.

Vamos analisar os componentes.

Caixa mágica


Consiste em quatro partes.



Agente de construção do Linux. O Linux, porque o compilamos, é lógico. Esta parte foi realizada em três etapas.

  • Configure trabalhadores e mais de um, pois foi assumido trabalho distribuído no projeto.
  • Instale o .NET Core 1.x. Por que 1.x quando o 2.0 já está disponível no repositório padrão? Porque quando começamos o desenvolvimento, a versão estável era 1.09, e foi decidido fazer o projeto para ele.
  • Git 2.x.

Repositório RPM. Pacotes RPM precisavam ser armazenados em algum lugar. Supunha-se que usássemos o mesmo repositório corporativo de RPM disponível para todos os hosts Linux. E assim eles fizeram. Um webhook é configurado no servidor de repositório que baixou o pacote RPM necessário do local especificado. A versão do pacote foi relatada ao webhook pelo agente Build.

Gitlab Atenção! O GitLab é usado aqui não pelos 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 armazenar a receita - todos os manifestos do Puppet.

Puppet - resolve todos os problemas controversos e fornece exatamente a configuração que queremos do Gitlab.

Começamos a mergulhar. Como a DLL é entregue no RPM?

Entrega de DDL para RPM


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



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

O agente de construção recebe as fontes, baixa as dependências necessárias do .NET, repositório npm, etc. e depois de criar o próprio aplicativo e o empacotamento subsequente, ele envia o pacote RPM para o repositório do RPM.

Por outro lado, ocorre o seguinte. O engenheiro de manutenção está diretamente envolvido na implementação do projeto: ele altera a versão dos pacotes no Hiera no repositório em que a receita do aplicativo está armazenada, após o que o Puppet aciona o Yum , pega o novo pacote no repositório e a nova versão do aplicativo está pronta para uso.



Em palavras, tudo é simples, mas o que acontece no próprio agente Build?

DLL RPM de empacotamento


As fontes do projeto e a tarefa de construção do TFS foram recebidas. O agente de construção começa a construir o projeto a partir da origem . O projeto montado está disponível na forma de muitos arquivos DLL compactados em um arquivo zip para reduzir a carga no sistema de arquivos.

O arquivo ZIP é lançado no diretório de compilação do pacote RPM. Em seguida, o script Bash inicializa as variáveis ​​de ambiente, localiza a versão Build, a versão do projeto, o caminho para o diretório de build e inicia o RPM-build. No final do assembly, o pacote é publicado no repositório local , localizado no agente Build.

Além disso, uma solicitação JSON é enviada do agente Build para o servidor no repositório RPM com 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.



Por que esse esquema para entregar um pacote a um repositório RPM? Por que não consigo enviar imediatamente o pacote montado para o repositório? O fato é que essa é uma condição para segurança. Esse cenário restringe a possibilidade de download não autorizado de pacotes RPM por terceiros para um servidor acessível a todas as máquinas Linux.

Controle de versão do banco de dados


Na consulta com o desenvolvimento, descobriu-se que os caras estão mais próximos do MS SQL, mas na maioria dos projetos que não são Windows, já usamos o PostgreSQL com poder e principal. Como já decidimos abandonar tudo pago, começamos a usar o PostgreSQL aqui.



Nesta parte, quero falar sobre como implementamos a versão do banco de dados e como escolher entre o Flyway e o Entity Framework Core. Considere seus prós e contras.

Contras


Flyway segue apenas uma maneira, não podemos reverter - esse é um sinal de menos. A comparação com o Entity Framework Core pode ser feita de acordo com outros parâmetros - do ponto de vista da conveniência do desenvolvedor. Você se lembra de que colocamos isso em primeiro plano, e o principal critério não foi alterar nada no desenvolvimento do Windows.

Para o Flyway, precisávamos de algum tipo de invólucro para que os caras não escrevessem consultas SQL . Eles estão muito mais perto de operar em termos de POO. Escrevemos instruções para trabalhar com objetos de banco de dados, formamos uma consulta SQL e executamos. A nova versão do banco de dados está pronta, rolada - está tudo bem, tudo funciona.

O Entity Framework Core tem menos - sob cargas pesadas, ele cria consultas SQL não ideais , e o rebaixamento do banco de dados pode ser significativo. Mas como não temos um serviço de alta carga, não calculamos a carga com centenas de RPS, assumimos esses riscos e delegamos o problema para o futuro.

Prós


O Entity Framework Core funciona imediatamente e é fácil de desenvolver , e o Flyway se integra perfeitamente ao IC existente . Mas fazemos isso convenientemente para desenvolvedores :)

Procedimento de enrolamento


O Puppet vê que há uma alteração na versão dos pacotes, entre os quais o responsável pela migração. Primeiro, ele instala um pacote que contém scripts de migração e funcionalidade vinculada ao banco de dados. Depois disso, o aplicativo que funciona com o banco de dados é reiniciado. A seguir, é a instalação dos componentes restantes. A ordem na qual os pacotes são instalados e os aplicativos iniciados é descrita no manifesto do Puppet.

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

Problemas de TFS


Depois que decidimos e percebemos que tudo realmente funciona para nós, decidi ver o que estava acontecendo com os assemblies no TFS como um todo para o departamento de desenvolvimento Win para outros projetos - rapidamente ou não, estávamos indo / liberando e encontrei problemas significativos com a velocidade .

Um dos principais projetos levará de 12 a 15 minutos - é muito tempo, você não pode viver assim. Uma análise rápida mostrou uma queda terrível na E / S, e isso está nas matrizes.

Tendo analisado os componentes, identifiquei três focos. O primeiro é o antivírus Kaspersky , que verifica o código-fonte em todos os agentes do Windows Build. O segundo é o Windows Indexer. Não foi desconectado e, nos agentes Build em tempo real, tudo foi indexado durante o processo de implantação.

O terceiro é a instalação do Npm. Aconteceu que na maioria dos pipelines usamos esse cenário específico. Por que ele é ruim? O procedimento de instalação do Npm inicia quando a árvore de dependência é formada em package-lock.json , onde as versões dos pacotes que serão usadas para construir o projeto são corrigidas. O ponto negativo é que a instalação do Npm extrai as versões mais recentes dos pacotes da Internet todas as vezes, e esse é um tempo considerável no caso de um projeto grande.

Às vezes, os desenvolvedores experimentam na máquina local para testar a operação de uma peça ou projeto individual como um todo. Às vezes, localmente, tudo era legal, mas montado, desenrolado - nada funcionava. Começamos a entender qual é o problema - sim, diferentes versões dos pacotes de dependência.

Solução


  • Fontes para exceções de AV.
  • Desabilitando a indexação.
  • Mude para npm ci .

A vantagem do npm ci é que coletamos a árvore de dependência uma vez e temos a oportunidade de fornecer ao desenvolvedor uma lista atualizada de pacotes com os quais ele pode experimentar localmente o quanto quiser. Isso economiza tempo para desenvolvedores que escrevem código.

Configuração


Agora um pouco sobre a configuração do repositório. Historicamente, usamos o Nexus para gerenciar repositórios, incluindo o REPO interno . Todos os componentes que usamos para fins internos, por exemplo, monitoramento auto-escrito, são entregues neste repositório interno.



Também usamos o NuGet , pois ele armazena em cache melhor que outros gerenciadores de pacotes.

Resultado


Depois de otimizar os agentes de compilação, o tempo médio de compilação foi reduzido de 12 minutos para 7.

, Windows, Linux , $10 000. , — .


.

Docker- . TFS — , Pipeline, , , , Docker-. package-lock.json . - , — Docker-. . , Kubernetes, .

Sumário


Windows, , . , Opensource- — Linux- . . , Open Source Linux .

GitHub .

DevOps Conf — , . , ? , . DevOps Conf ++ 27 28 . . !

Source: https://habr.com/ru/post/pt448496/


All Articles