Compartilhar via


DevOps

Dica

Esse conteúdo é um trecho do eBook, Architecting Cloud Native .NET Applications for Azure, disponível no .NET Docs ou como um PDF para download gratuito que pode ser lido offline.

Miniatura de capa do eBook

O mantra favorito dos consultores de software é responder "Depende" de qualquer pergunta colocada. Não é porque os consultores de software gostam de não tomar uma posição. É porque não há uma resposta verdadeira para nenhuma pergunta no software. Não há nenhum certo e errado absoluto, mas sim um equilíbrio entre opostos.

Veja, por exemplo, as duas principais escolas de desenvolvimento de aplicativos Web: SPAs (Aplicativos de Página Única) versus aplicativos do lado do servidor. Por um lado, a experiência do usuário tende a ser melhor com SPAs e a quantidade de tráfego para o servidor Web pode ser minimizada, tornando possível hospedá-los em algo tão simples quanto a hospedagem estática. Por outro lado, os SPAs tendem a ser mais lentos para serem desenvolvidos e mais difíceis de testar. Qual é a escolha certa? Depende da sua situação.

As aplicações nativas da nuvem não são imunes à mesma dicotomia. Eles têm vantagens claras em termos de velocidade de desenvolvimento, estabilidade e escalabilidade, mas gerenciá-los pode ser um pouco mais difícil.

Anos atrás, não era incomum que o processo de migração de um aplicativo do desenvolvimento para a produção demorasse um mês ou até mais. As empresas lançaram software em uma cadência de 6 meses ou até mesmo todos os anos. Não é necessário procurar além do Microsoft Windows para ter uma ideia do ritmo de lançamento das versões que era aceitável antes do Windows 10. Cinco anos se passaram entre o Windows XP e o Vista, outros três entre o Vista e o Windows 7.

Agora está bem estabelecido que ser capaz de lançar software rapidamente dá às empresas ágeis uma enorme vantagem de mercado sobre seus concorrentes mais preguiçosos. É por esse motivo que as atualizações principais do Windows 10 agora são aproximadamente a cada seis meses.

Os padrões e práticas que permitem versões mais rápidas e confiáveis para fornecer valor para a empresa são coletivamente conhecidos como DevOps. Eles consistem em uma ampla gama de ideias que abrangem todo o ciclo de vida de desenvolvimento de software, desde a especificação de um aplicativo até a entrega e a operação desse aplicativo.

O DevOps surgiu antes dos microsserviços e é provável que o movimento em direção a serviços menores e mais adequados à finalidade não teria sido possível sem o DevOps para facilitar a liberação e a operação não apenas de um, mas de muitos aplicativos em produção.

A Figura 10-1 Tendências de pesquisa mostram que o crescimento dos microsserviços só começa depois que o DevOps é uma ideia bastante bem estabelecida.

Figura 10-1 – DevOps e microsserviços.

Por meio de boas práticas de DevOps, é possível aproveitar as vantagens de aplicativos nativos de nuvem sem se afogar sob uma avalanche de trabalho ao operar os aplicativos.

Não há martelo dourado quando se trata de DevOps. Ninguém pode vender uma solução completa e abrangente para liberar e operar aplicativos de alta qualidade. Isso ocorre porque cada aplicativo é extremamente diferente de todos os outros. No entanto, há ferramentas que podem tornar o DevOps uma proposta muito menos assustadora. Uma dessas ferramentas é conhecida como Azure DevOps.

Azure DevOps

O Azure DevOps tem um longo pedigree. Ele pode rastrear suas origens até quando o Team Foundation Server foi para o ambiente online pela primeira vez e por meio das várias alterações de nome: Visual Studio Online e Visual Studio Team Services. Ao longo dos anos, no entanto, tornou-se muito mais do que seus antecessores.

O Azure DevOps é dividido em cinco componentes principais:

Figura 10-2 As cinco principais áreas do Azure DevOps

Figura 10-2 – Azure DevOps.

Azure Repos – Gerenciamento de código-fonte que dá suporte ao venerável TFVC (Team Foundation Version Control) e ao Git favorito do setor. As solicitações de pull fornecem uma maneira de habilitar a codificação social promovendo a discussão das mudanças conforme elas são feitas.

Azure Boards – Fornece uma ferramenta de acompanhamento de item de trabalho e problemas que se esforça para permitir que os usuários escolham os fluxos de trabalho que funcionam melhor para eles. Ele vem com vários modelos pré-configurados, incluindo os que dão suporte a estilos de desenvolvimento SCRUM e Kanban.

Azure Pipelines – Um sistema de gerenciamento de build e lançamento que dá suporte a uma integração rígida com o Azure. Os builds podem ser executados em várias plataformas, do Windows ao Linux ao macOS. Os agentes de build podem ser provisionados na nuvem ou no local.

Planos de Teste do Azure – Nenhuma pessoa de QUALIDADE será deixada para trás com o gerenciamento de testes e o suporte a testes exploratórios oferecidos pelo recurso Planos de Teste.

Azure Artifacts - Um feed de artefatos que permite que as empresas criem suas próprias versões internas do NuGet, npm e outras. Ele tem dupla finalidade de atuar como um cache para pacotes upstream caso ocorra uma falha em um repositório centralizado.

A unidade organizacional de nível superior no Azure DevOps é conhecida como projeto. Em cada projeto, os vários componentes, como o Azure Artifacts, podem ser ativados e desativados. Cada um desses componentes oferece vantagens diferentes para aplicativos nativos de nuvem. Os três mais úteis são repositórios, quadros e pipelines. Se os usuários quiserem gerenciar seu código-fonte em outra pilha de repositório, como o GitHub, mas ainda tirarem proveito do Azure Pipelines e outros componentes, isso é perfeitamente possível.

Felizmente, as equipes de desenvolvimento têm muitas opções ao selecionar um repositório. Um deles é o GitHub.

Ações do GitHub

Fundado em 2009, o GitHub é um repositório amplamente popular baseado na Web para hospedar projetos, documentação e código. Muitas grandes empresas de tecnologia, como Apple, Amazon, Google e grandes corporações, usam o GitHub. O GitHub usa o sistema de controle de versão distribuída de software livre chamado Git como sua base. Além disso, ele adiciona seu próprio conjunto de recursos, incluindo acompanhamento de defeitos, solicitações de recurso e pull, gerenciamento de tarefas e wikis para cada base de código.

À medida que o GitHub evolui, ele também está adicionando recursos de DevOps. Por exemplo, o GitHub tem seu próprio pipeline de CI/CD (integração contínua/entrega contínua), chamado GitHub Actions. O GitHub Actions é uma ferramenta de automação de fluxo de trabalho da comunidade. Ele permite que as equipes DevOps se integrem às ferramentas existentes, combinem novos produtos e se conectem ao ciclo de vida do software, incluindo os parceiros de CI/CD existentes.

O GitHub tem mais de 40 milhões de usuários, tornando-se o maior host de código-fonte do mundo. Em outubro de 2018, a Microsoft comprou o GitHub. A Microsoft prometeu que o GitHub continuará sendo uma plataforma aberta na qual qualquer desenvolvedor pode se conectar e estender. Continua operando como uma empresa independente. O GitHub oferece planos para contas corporativas, de equipe, profissionais e gratuitas.

Controle do código-fonte

Organizar o código para um aplicativo nativo de nuvem pode ser desafiador. Em vez de um único aplicativo gigante, os aplicativos nativos de nuvem tendem a ser compostos por uma Web de aplicativos menores que conversam entre si. Assim como acontece com todas as coisas na computação, a melhor disposição do código continua sendo uma questão aberta. Há exemplos de aplicativos bem-sucedidos usando diferentes tipos de layouts, mas duas variantes parecem ter a maior popularidade.

Antes de entrar no próprio controle do código-fonte, provavelmente vale a pena decidir quantos projetos são apropriados. Em um único projeto, há suporte para múltiplos repositórios e pipelines de compilação. Os quadros são um pouco mais complicados, mas também facilitam atribuir as tarefas a várias equipes em um único projeto. É possível dar suporte a centenas, até milhares de desenvolvedores, de um único projeto do Azure DevOps. Fazer isso provavelmente é a melhor abordagem, pois fornece um único lugar para todos os desenvolvedores trabalharem e reduz a confusão de descobrir que um aplicativo quando os desenvolvedores não têm certeza em qual projeto ele reside.

Dividir o código para microsserviços no projeto do Azure DevOps pode ser um pouco mais desafiador.

Figura 10-3 Único versus Vários Repositórios

Figura 10-3 – Um versus muitos repositórios.

Repositório por microsserviço

À primeira vista, essa abordagem parece ser a abordagem mais lógica para dividir o código-fonte para microsserviços. Cada repositório pode conter o código necessário para criar um microsserviço. As vantagens dessa abordagem são prontamente visíveis:

  1. Instruções para criar e manter o aplicativo podem ser adicionadas a um arquivo README na raiz de cada repositório. Ao folhear os repositórios, é fácil encontrar essas instruções, reduzindo o tempo de inicialização para os desenvolvedores.
  2. Cada serviço está localizado em um local lógico, facilmente encontrado sabendo o nome do serviço.
  3. Os builds podem ser facilmente configurados de modo que só sejam disparados quando uma alteração é feita no repositório proprietário.
  4. O número de alterações que chegam a um repositório é limitado ao pequeno número de desenvolvedores que trabalham no projeto.
  5. A segurança é fácil de configurar restringindo os repositórios aos quais os desenvolvedores têm permissões de leitura e gravação.
  6. As configurações de nível de repositório podem ser alteradas pela equipe proprietária com o mínimo de discussão com outras equipes.

Uma das principais ideias por trás dos microsserviços é que os serviços devem ser silos e separados uns dos outros. Ao usar o Design Controlado por Domínio para decidir sobre os limites dos serviços, os serviços atuam como limites transacionais. As atualizações de banco de dados não devem abranger vários serviços. Essa coleção de dados relacionados é conhecida como um contexto limitado. Essa ideia é refletida pelo isolamento de dados de microsserviço para um banco de dados separado e autônomo do restante dos serviços. Faz muito sentido levar essa ideia até o código-fonte.

No entanto, essa abordagem não é sem seus problemas. Um dos problemas de desenvolvimento mais recorrentes do nosso tempo é o gerenciamento de dependências. Considere o número de arquivos que compõem o diretório médio node_modules . Uma nova instalação de algo como create-react-app provavelmente trará consigo milhares de pacotes. A questão de como gerenciar essas dependências é difícil.

Se uma dependência for atualizada, os pacotes downstream também deverão atualizar essa dependência. Infelizmente, isso requer um trabalho de desenvolvimento para que, invariavelmente, o node_modules diretório acabe com várias versões de um único pacote, cada uma, sendo uma dependência de algum outro pacote que está sendo versionado em uma cadência um pouco diferente. Ao implantar um aplicativo, qual versão de uma dependência deve ser usada? A versão que está em produção no momento? A versão que está atualmente em Beta, mas provavelmente estará em produção quando o consumidor chegar à produção? Problemas difíceis que não são resolvidos apenas usando microsserviços.

Há bibliotecas que dependem de uma ampla variedade de projetos. Dividindo os microsserviços com um em cada repositório, as dependências internas podem ser melhor resolvidas usando o repositório interno, a Azure Artifacts. Compilações para bibliotecas publicarão suas versões mais recentes no Azure Artifacts para consumo interno. O projeto em downstream ainda deve ser atualizado manualmente para assumir uma dependência dos pacotes recém-atualizados.

Outra desvantagem se apresenta ao mover o código entre serviços. Embora seja bom acreditar que a primeira divisão de um aplicativo em microsserviços está 100% correta, a realidade é que raramente conseguimos prever tudo a ponto de não cometer erros de divisão de serviço. Portanto, a funcionalidade e o código que o impulsiona precisarão mudar de serviço para serviço: repositório para repositório. Ao saltar de um repositório para outro, o código perde seu histórico. Há muitos casos, especialmente no caso de uma auditoria, em que ter um histórico completo em um pedaço de código é inestimável.

A desvantagem final e mais importante é coordenar as alterações. Em um aplicativo de microsserviços verdadeiro, não deve haver dependências de implantação entre serviços. Deve ser possível implantar os serviços A, B e C em qualquer ordem, pois eles têm acoplamento flexível. Na realidade, no entanto, há momentos em que é desejável fazer uma alteração que cruza vários repositórios ao mesmo tempo. Alguns exemplos incluem atualizar uma biblioteca para fechar uma falha de segurança ou alterar um protocolo de comunicação usado por todos os serviços.

Para fazer uma alteração entre repositórios, é necessário obter uma confirmação sucessiva de cada repositório. Cada alteração em cada repositório precisará ser solicitada e revisada separadamente. Essa atividade pode ser difícil de coordenar.

Uma alternativa ao uso de muitos repositórios é juntar todo o código-fonte em um repositório único gigante, onisciente.

Repositório único

Nessa abordagem, às vezes conhecida como um monorepositório, todo o código-fonte de cada serviço é colocado no mesmo repositório. No início, essa abordagem parece uma ideia terrível que pode tornar o manuseio do código-fonte difícil de manejar. No entanto, há algumas vantagens marcantes em trabalhar dessa maneira.

A primeira vantagem é que é mais fácil gerenciar dependências entre projetos. Em vez de depender de algum repositório externo de artefatos, os projetos podem importar uns aos outros diretamente. Isso significa que as atualizações são instantâneas e versões conflitantes provavelmente serão encontradas em tempo de compilação na estação de trabalho do desenvolvedor. Na verdade, deslocando alguns dos testes de integração restantes.

Ao mover o código entre projetos, agora é mais fácil preservar o histórico, pois os arquivos serão detectados como sendo movidos em vez de reescritos.

Outra vantagem é que alterações abrangentes que ultrapassam os limites de serviço podem ser feitas com uma única confirmação. Essa atividade reduz a sobrecarga de ter potencialmente dezenas de alterações a serem revisadas individualmente.

Há muitas ferramentas que podem executar a análise estática do código para detectar práticas de programação inseguras ou uso problemático de APIs. Em um mundo de vários repositórios, cada repositório precisará ser iterado para encontrar os problemas neles. O repositório único permite executar a análise em um só lugar.

Também há muitas desvantagens na abordagem de repositório único. Um dos mais preocupantes é que ter um único repositório levanta preocupações de segurança. Se o conteúdo de um repositório for vazado em um repositório por modelo de serviço, a quantidade de código perdido será mínima. Com um único repositório, tudo o que a empresa possui pode ser perdido. Houve muitos exemplos no passado disso acontecendo e descarrilando todos os esforços de desenvolvimento de jogos. Ter vários repositórios expõe menos área de superfície, o que é uma característica desejável na maioria das práticas de segurança.

O tamanho do repositório único provavelmente se tornará incontrolável rapidamente. Isso apresenta algumas implicações de desempenho interessantes. Pode se tornar necessário usar ferramentas especializadas, como o Virtual File System for Git, que foi originalmente projetado para melhorar a experiência dos desenvolvedores na equipe do Windows.

Frequentemente, o argumento para usar um único repositório se resume a um argumento de que o Facebook ou o Google usam esse método para a disposição do código-fonte. Se a abordagem é boa o suficiente para essas empresas, então, certamente, é a abordagem correta para todas as empresas. A verdade é que poucas empresas operam em algo como a escala do Facebook ou do Google. Os problemas que ocorrem nessas escalas são diferentes daqueles que a maioria dos desenvolvedores enfrentará. O que é bom para o ganso pode não ser bom para o gander.

No final, qualquer solução pode ser usada para hospedar o código-fonte para microsserviços. No entanto, na maioria dos casos, a sobrecarga de gerenciamento e engenharia de operar em um único repositório não vale as vantagens escassas. Dividir o código em vários repositórios incentiva a melhor separação de preocupações e incentiva a autonomia entre as equipes de desenvolvimento.

Estrutura de diretório padrão

Independentemente do debate único versus vários repositórios, cada serviço terá seu próprio diretório. Uma das melhores otimizações para permitir que os desenvolvedores se cruzem rapidamente entre projetos é manter uma estrutura de diretório padrão.

Figura 10-4 Uma estrutura de diretório padrão para os serviços de email e de entrada

Figura 10-4 – Estrutura de diretório standard.

Sempre que um novo projeto é criado, um modelo que coloca em vigor a estrutura correta deve ser usado. Esse modelo também pode incluir itens úteis como um arquivo README esqueleto e um azure-pipelines.yml. Em qualquer arquitetura de microsserviço, um alto grau de variação entre projetos dificulta as operações em massa em relação aos serviços.

Há muitas ferramentas que podem fornecer modelagem para um diretório inteiro, contendo vários diretórios de código-fonte. Yeoman é popular no mundo JavaScript e o GitHub lançou recentemente modelos de repositório, que fornecem grande parte da mesma funcionalidade.

Gerenciamento de tarefas

O gerenciamento de tarefas em qualquer projeto pode ser difícil. Na frente, há inúmeras perguntas a serem respondidas sobre o tipo de fluxos de trabalho a serem configurados para garantir a produtividade ideal do desenvolvedor.

Os aplicativos nativos de nuvem tendem a ser menores do que os produtos de software tradicionais ou pelo menos são divididos em serviços menores. O acompanhamento de problemas ou tarefas relacionadas a esses serviços permanece tão importante quanto com qualquer outro projeto de software. Ninguém quer perder o controle de algum item de trabalho ou explicar a um cliente que seu problema não foi registrado corretamente. As placas são configuradas no nível do projeto, mas em cada projeto, as áreas podem ser definidas. Isso permite a divisão de questões em vários componentes. A vantagem de manter todo o trabalho para todo o aplicativo em um só lugar é que é fácil mover itens de trabalho de uma equipe para outra, pois elas são compreendidas melhor.

O Azure DevOps vem com vários modelos populares pré-configurados. Na configuração mais básica, tudo o que é necessário saber é o que está na lista de pendências, no que as pessoas estão trabalhando e no que é feito. É importante ter essa visibilidade no processo de criação de software, para que o trabalho possa ser priorizado e as tarefas concluídas sejam relatadas ao cliente. Claro, poucos projetos de software mantêm um processo tão simples quanto to do, doinge done. Não demora muito para que as pessoas comecem a adicionar etapas como QA ou Detailed Specification ao processo.

Uma das partes mais importantes das metodologias Agile é a auto-introspecção em intervalos regulares. Essas revisões destinam-se a fornecer informações sobre quais problemas a equipe está enfrentando e como elas podem ser melhoradas. Frequentemente, isso significa alterar o fluxo de problemas e funcionalidades por meio do processo de desenvolvimento. Assim, a integridade não é afetada com a expansão dos layouts das placas com estágios adicionais.

Os estágios nos quadros não são a única ferramenta organizacional. Dependendo da configuração do quadro, há uma hierarquia de itens de trabalho. O item mais granular que pode aparecer em uma placa é uma tarefa. Fora da caixa, uma tarefa contém campos para título, descrição, prioridade, uma estimativa da quantidade de trabalho restante e a capacidade de se vincular a outros itens de trabalho ou de desenvolvimento (branches, confirmações, solicitações de pull, builds e assim por diante). Os itens de trabalho podem ser classificados em diferentes áreas do aplicativo e iterações diferentes (sprints) para facilitar a localização deles.

Figura 10-5 Uma tarefa de exemplo no Azure DevOps

Figura 10-5 – Tarefa no Azure DevOps.

O campo de descrição dá suporte aos estilos normais que você esperaria (negrito, itálico, sublinhado e tachado) e à capacidade de inserir imagens. Isso a torna uma ferramenta poderosa para ser usada ao especificar tarefas ou bugs.

As tarefas podem ser agrupadas em funcionalidades, que definem uma unidade de trabalho maior. Por sua vez, os recursos podem ser acumulados em épicos. Classificar tarefas nessa hierarquia torna muito mais fácil entender o quão próximo um recurso grande está de ser implantado.

Figura 10-6 Tipos de item de trabalho configurados por padrão no modelo de processo Básico

Figura 10-6 – Item de trabalho no Azure DevOps.

Há diferentes tipos de visões dos tópicos no Azure Boards. Os itens que ainda não estão agendados aparecem na lista de pendências. A partir daí, eles podem ser atribuídos a um sprint. Um sprint é uma caixa de tempo durante a qual se espera que alguma quantidade de trabalho seja concluída. Esse trabalho pode incluir tarefas, mas também a resolução de tíquetes. Uma vez lá, todo o sprint inteiro pode ser gerenciado na seção do quadro Sprint. Este modo de exibição mostra como o trabalho está progredindo e inclui um gráfico de burndown para fornecer uma estimativa de sucesso para o sprint.

Figura 10-7 Uma placa com um sprint definido

Figura 10-7 – Quadro no Azure DevOps.

Agora, deve ser evidente que há uma grande quantidade de poder nos Boards no Azure DevOps. Para os desenvolvedores, há visualizações fáceis do trabalho em andamento. Os gerentes de projeto conseguem visualizar o próximo trabalho e têm uma visão geral do trabalho existente. Para os gerentes, há muitos relatórios sobre resourcing e capacidade. Infelizmente, não há nada mágico em aplicativos nativos de nuvem que eliminem a necessidade de acompanhar o trabalho. Mas se você precisar acompanhar o trabalho, há alguns locais em que a experiência é melhor do que no Azure DevOps.

Pipelines de CI/CD

Quase nenhuma alteração no ciclo de vida de desenvolvimento de software tem sido tão revolucionária quanto o advento da CI (integração contínua) e da CD (entrega contínua). Compilar e executar testes automatizados no código-fonte de um projeto assim que uma alteração for verificada captura erros antecipadamente. Antes do advento das builds de integração contínua, não seria incomum puxar código do repositório e descobrir que ele não passou nos testes ou nem sequer pôde ser compilado. Isso resultou no rastreamento da origem da quebra.

Tradicionalmente, o envio de software para o ambiente de produção exigia documentação abrangente e uma lista de etapas. Cada uma dessas etapas precisava ser concluída manualmente em um processo muito propenso a erros.

Figura 10-8 Uma lista de verificação

Figura 10-8 – Lista de verificação.

A irmã da integração contínua é a entrega contínua na qual os pacotes recém-criados são implantados em um ambiente. A incapacidade do processo manual de ser dimensionado para acompanhar a velocidade do desenvolvimento torna a automação ainda mais importante. As listas de verificação são substituídas por scripts que podem executar as mesmas tarefas com mais rapidez e precisão do que qualquer humano.

O ambiente para o qual a entrega contínua é feita pode ser um ambiente de teste ou pode ser o ambiente de produção, como é o caso de grandes empresas de tecnologia. Este último requer um investimento em testes de alta qualidade que podem dar confiança de que uma mudança não interromperá a produção para os usuários. Da mesma forma que a integração contínua detectou problemas no código, a entrega contínua antecipada captura problemas no processo de implantação antecipadamente.

A importância de automatizar o processo de build e entrega é acentuada por aplicativos nativos de nuvem. As implantações ocorrem com mais frequência e em mais ambientes, tornando praticamente impossível realizar implantações manuais entre diferentes ambientes.

Azure Builds

O Azure DevOps fornece um conjunto de ferramentas para facilitar a integração e a implantação contínuas do que nunca. Essas ferramentas estão localizadas no Azure Pipelines. O primeiro deles é o Azure Builds, que é uma ferramenta para executar definições de build baseadas em YAML em escala. Os usuários podem trazer seus próprios computadores de build (ótimo para se o build exigir um ambiente meticulosamente configurado) ou usar um computador de um pool constantemente atualizado de máquinas virtuais hospedadas no Azure. Esses agentes de build hospedados vêm pré-instalados com uma ampla gama de ferramentas de desenvolvimento para não apenas o desenvolvimento do .NET, mas para tudo, do Java ao Python ao desenvolvimento do iPhone.

O DevOps inclui uma ampla gama de definições de build prontas para uso que podem ser personalizadas para qualquer build. As definições de build são definidas em um arquivo chamado azure-pipelines.yml e verificadas no repositório para que possam ser colocadas em versão junto com o código-fonte. Dessa forma, fica mais fácil fazer alterações no pipeline de build em um branch, pois é possível verificar as alterações apenas nessa ramificação. Um exemplo azure-pipelines.yml para a criação de um aplicativo Web ASP.NET na estrutura completa é mostrado na Figura 10-9.

name: $(rev:r)

variables:
  version: 9.2.0.$(Build.BuildNumber)
  solution: Portals.sln
  artifactName: drop
  buildPlatform: any cpu
  buildConfiguration: release

pool:
  name: Hosted VisualStudio
  demands:
  - msbuild
  - visualstudio
  - vstest

steps:
- task: NuGetToolInstaller@0
  displayName: 'Use NuGet 4.4.1'
  inputs:
    versionSpec: 4.4.1

- task: NuGetCommand@2
  displayName: 'NuGet restore'
  inputs:
    restoreSolution: '$(solution)'

- task: VSBuild@1
  displayName: 'Build solution'
  inputs:
    solution: '$(solution)'
    msbuildArgs: '-p:DeployOnBuild=true -p:WebPublishMethod=Package -p:PackageAsSingleFile=true -p:SkipInvalidConfigurations=true -p:PackageLocation="$(build.artifactstagingdirectory)\\"'
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'

- task: VSTest@2
  displayName: 'Test Assemblies'
  inputs:
    testAssemblyVer2: |
     **\$(buildConfiguration)\**\*test*.dll
     !**\obj\**
     !**\*testadapter.dll
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'

- task: CopyFiles@2
  displayName: 'Copy UI Test Files to: $(build.artifactstagingdirectory)'
  inputs:
    SourceFolder: UITests
    TargetFolder: '$(build.artifactstagingdirectory)/uitests'

- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact'
  inputs:
    PathtoPublish: '$(build.artifactstagingdirectory)'
    ArtifactName: '$(artifactName)'
  condition: succeededOrFailed()

Figura 10-9 - Um exemplo azure-pipelines.yml

Essa definição de build usa uma série de tarefas internas que tornam a criação de builds tão simples quanto a criação de um conjunto de Lego (mais simples que o gigante Millennium Falcon). Por exemplo, a tarefa NuGet restaura pacotes NuGet, enquanto a tarefa VSBuild chama as ferramentas de build do Visual Studio para executar a compilação real. Há centenas de tarefas diferentes disponíveis no Azure DevOps, com milhares a mais que são mantidas pela comunidade. É provável que não importa quais tarefas de build você está procurando executar, alguém já criou uma.

Os builds podem ser disparados manualmente, por um check-in, por agendamento ou pela conclusão de outro build. Na maioria dos casos, é desejável o build a cada check-in. Os builds podem ser filtrados para que diferentes builds sejam executados em diferentes branches ou partes do repositório. Isso possibilita criar cenários para executar builds rápidos com testes reduzidos em solicitações de pull e para executar um pacote de regressão completo no tronco todas as noites.

O resultado final de um build é uma coleção de arquivos conhecidos como artefatos de build. Esses artefatos podem ser passados para a próxima etapa no processo de build ou adicionados a um feed do Azure Artifacts, para que possam ser consumidos por outras compilações.

Versões do Azure DevOps

Builds cuidam da compilação do software em um pacote que pode ser enviado, mas os artefatos ainda precisam ser enviados por push para um ambiente de teste a fim de concluir a entrega contínua. Para isso, o Azure DevOps usa uma ferramenta separada chamada Versões. A ferramenta Versões usa a biblioteca das mesmas tarefas que estavam disponíveis para o Build, mas introduz o conceito de "estágios". Um estágio é um ambiente isolado no qual o pacote é instalado. Por exemplo, um produto pode usar um ambiente de desenvolvimento, de teste e de produção. O código é entregue continuamente no ambiente de desenvolvimento em que os testes automatizados podem ser executados nele. Depois que esses testes passarem, a versão passará para o ambiente de QA para teste manual. Por fim, o código é implantado na produção, onde é visível a todos.

Figura 10-10 Um exemplo de pipeline de lançamento com as fases de Desenvolvimento, Garantia de Qualidade e Produção

Figura 10-10 – Pipeline de lançamento

Cada estágio do build pode ser disparado automaticamente pela conclusão da fase anterior. Em muitos casos, no entanto, isso não é desejável. Mover código para produção pode exigir aprovação de alguém. Há suporte para isso na ferramenta Versões, que permite aprovadores em cada etapa do pipeline de lançamento. As regras podem ser configuradas de forma que uma pessoa ou um grupo específico de pessoas deva aprovar uma versão antes de ela entrar em produção. Esses portões permitem verificações manuais de qualidade e também para conformidade com quaisquer requisitos regulatórios relacionados a controlar o que entra em produção.

Todo mundo obtém um pipeline de build

Não há custo para configurar vários pipelines de build, portanto, é vantajoso ter pelo menos um pipeline de build por microsserviço. O ideal é que os microsserviços sejam implantados independentemente em qualquer ambiente, portanto, ter cada um deles capaz de ser lançado por meio de seu próprio pipeline sem liberar uma massa de código não relacionado é perfeito. Cada pipeline pode ter seu próprio conjunto de aprovações, permitindo variações no processo de build para cada serviço.

Lançamentos de versões

Uma desvantagem de usar a funcionalidade de Versões é que ela não pode ser configurada como um arquivo azure-pipelines.yml com check-in. Há muitos motivos pelos quais você pode querer fazer isso, que vão de ter definições de versão por branch até incluir um esqueleto de versão em seu modelo de projeto. Por sorte, o trabalho está em andamento para transferir alguns dos estágios de suporte para o componente Build. Isso será conhecido como build de vários estágios e a primeira versão já está disponível!