Lidar com vários ramos em integração contínua

86

Tenho lidado com o problema de escalonamento de CI na minha empresa e, ao mesmo tempo, tentando descobrir qual abordagem tomar quando se trata de CI e várias filiais. Há uma questão semelhante em stackoverflow, Multiple feature branches e integração contínua . Comecei um novo porque gostaria de obter mais discussão e fornecer algumas análises sobre a questão.

Até agora descobri que existem 2 abordagens principais que posso adotar (ou talvez algumas outras ???).

Então, parece que se eu quiser fornecer aos desenvolvedores CI para seus próprios branches personalizados, preciso de ferramentas especiais para Jenkins (API ou shellscripts ou algo assim?) E lidar com o dimensionamento. Ou posso dizer a eles para mesclar com mais frequência ao DEV e viver sem CI em branches personalizados. Qual você escolheria ou existem outras opções?

Toomasr
fonte

Respostas:

70

Quando você fala sobre escalonamento de CI, você está realmente falando sobre escalonar o uso de seu servidor de CI para lidar com todos os seus branches de recursos junto com sua linha principal. Inicialmente, isso parece uma boa abordagem, já que os desenvolvedores em uma filial obtêm todas as vantagens do teste automatizado que as tarefas de CI incluem. No entanto, você encontra problemas para gerenciar as tarefas do servidor de CI (como descobriu) e, mais importante, você não está realmente fazendo CI. Sim, você está usando um servidor de CI, mas não está integrando continuamente o código de todos os seus desenvolvedores.

Executar CI real significa que todos os seus desenvolvedores estão se comprometendo regularmente com a linha principal. Fácil de falar, mas o difícil é fazê-lo sem quebrar seu aplicativo. Recomendo enfaticamente que você dê uma olhada na Entrega contínua , especialmente na seção Mantendo seu aplicativo liberável no Capítulo 13: Gerenciando componentes e dependências . Os pontos principais são:

  • Oculte a nova funcionalidade até que seja concluída (AKA Feature Toggles )
  • Faça todas as alterações de forma incremental como uma série de pequenas alterações, cada uma delas liberável.
  • Use ramificação por abstração para fazer alterações em grande escala na base de código.
  • Use componentes para desacoplar partes de seu aplicativo que mudam em taxas diferentes.

Eles são bastante autoexplicativos, exceto ramo por abstração. Este é apenas um termo chique para:

  1. Crie uma abstração sobre a parte do sistema que você precisa alterar.
  2. Refatore o resto do sistema para usar a camada de abstração.
  3. Crie uma nova implementação, que não faça parte do caminho do código de produção até ser concluída.
  4. Atualize sua camada de abstração para delegar à sua nova implementação.
  5. Remova a implementação antiga.
  6. Remova a camada de abstração se ela não for mais apropriada.

O parágrafo a seguir da seção Ramificações, fluxos e integração contínua no Capítulo 14: Controle de versão avançado resume os impactos.

A abordagem incremental certamente requer mais disciplina e cuidado - e de fato mais criatividade - do que criar uma filial e mergulhar de cabeça na reconstrução e no desenvolvimento de novas funcionalidades. Mas isso reduz significativamente o risco de suas alterações interromperem o aplicativo e economizará muito tempo para você e sua equipe mesclar, consertar quebras e colocar seu aplicativo em um estado implantável.

É preciso uma grande mudança de mentalidade para desistir dos branches de recursos e você sempre terá resistência. Na minha experiência, essa resistência é baseada em desenvolvedores que não se sentem seguros ao comprometer o código na linha principal e essa é uma preocupação razoável. Isso, por sua vez, geralmente resulta da falta de conhecimento, confiança ou experiência com as técnicas listadas acima e, possivelmente, com a falta de confiança com seus testes automatizados. O primeiro pode ser resolvido com treinamento e suporte do desenvolvedor. O último é um problema muito mais difícil de lidar, porém a ramificação não fornece nenhuma segurança real extra, ela apenas adia o problema até que os desenvolvedores se sintam confiantes o suficiente com seu código.

Tom Howard
fonte
4
Tom, isso só funciona bem se 1) tanto o lançamento quanto a atualização forem comparativamente fáceis 2) a maioria de suas alterações forem bem isoladas. Isso é verdade para desenvolvimento web, mas se você estiver lançando produtos em caixas, as versões estáveis ​​devem permanecer estáveis ​​a todo custo, porque os hotfixes são realmente caros ou até mesmo impossíveis em um grande ambiente corporativo.
Jevgeni Kabanov
13
A CI real não é apenas sobre integração, é também sobre o feedback
Anton Arhipov,
3
Escolhi esta como resposta (pelo menos deu a recompensa, por favor me avise se de alguma forma eu ainda precisar marcá-la correta) mas acho que esta não é uma solução para o meu problema. Escrevi um acompanhamento em zeroturnaround.com/blog/…
toomasr
1
@Jevgeni Kabanov e @toomasr Ambos parecem presumir que fazer CI verdadeiro significa abrir mão da qualidade e funciona apenas para desenvolvimento da Web, porque é muito fácil fazer correções. Suponho que você esteja preocupado com um commit duvidoso pouco antes do lançamento. Sim, isso pode resultar em um lançamento ruim que pode ser caro para consertar. No entanto, um commit duvidoso em um branch de recurso pouco antes de ser lançado é tão ruim quanto. Se você sentir que há uma diferença, compartilhe seu raciocínio. Uma maneira de combater isso (se o commit foi para a linha principal ou um branch de recurso) é usar a abordagem de Entrega Contínua.
Tom Howard,
1
Ah, e a propósito, nos últimos 4 anos, minha principal experiência de desenvolvimento foi em instituições financeiras. O imperativo de ter versões estáveis ​​e o custo de errar (sem mencionar o processo de controle de mudanças que você precisa passar para lançar um hotfix) não fica muito maior do que isso. Um produto embalado seria uma mudança relaxante para mim.
Tom Howard,
4

Eu criaria trabalhos separados para cada filial. Já fiz isso antes e não é difícil de gerenciar e configurar se você configurou Hudson / Jenkins corretamente. Uma maneira rápida de criar vários trabalhos é copiar de um trabalho existente que tenha requisitos semelhantes e modificá-los conforme necessário. Não tenho certeza se você deseja permitir que cada desenvolvedor configure seus próprios trabalhos para seus próprios ramos, mas não é muito trabalho para uma pessoa (ou seja, um gerente de compilação) gerenciar. Uma vez que as ramificações personalizadas foram mescladas em ramificações estáveis, os trabalhos correspondentes podem ser removidos quando não forem mais necessários.

Se estiver preocupado com a carga no servidor de CI, você pode configurar instâncias separadas do CI ou mesmo escravos separados para ajudar a equilibrar a carga em vários servidores. Certifique-se de que o servidor em que está executando o Hudson / Jenkins é adequado. Eu usei o Apache Tomcat e apenas tive que garantir que ele tivesse memória e capacidade de processamento suficientes para processar a fila de construção.

É importante deixar claro o que você deseja alcançar usando CI e então descobrir uma maneira de implementá-lo sem muito esforço manual ou duplicação. Não há nada de errado em usar outras ferramentas externas ou scripts que são executados por seu servidor de CI que ajudam a simplificar seu processo geral de gerenciamento de build.

Bernard
fonte
Acho que essa falta de ferramentas significa que há espaço para alguns plug-ins / produtos neste departamento. Não gostaria de escrever o meu próprio.
toomasr
1
Há um utilitário para Jenkins que cria a configuração de compilação para cada filial automaticamente: entagen.github.com/jenkins-build-per-branch
kolen
3

Eu escolheria dev + branches estáveis. E se você ainda quer ramificações personalizadas e tem medo da carga, então por que não mover essas ramificações personalizadas para a nuvem e deixar que os desenvolvedores gerenciem eles mesmos, por exemplo http://cloudbees.com/dev.cb Esta é a empresa onde Kohsuke está agora . Há um Eclipse Tooling também, então se você estiver no Eclipse, você o terá totalmente integrado ao dev env.

Anton Safonov
fonte
Estarei trocando a falta de ferramentas de gerenciamento de várias filiais para ter o mesmo problema, mas na nuvem? Quer dizer, poderei gerenciar a carga agora, mas ainda não os ramos?
toomasr
Eu quis dizer esquecer o conjunto de ferramentas e distribuir o gerenciamento entre os desenvolvedores - "se você quiser um build personalizado, aqui está sua conta CB". Sem afetar o desempenho de construção do servidor principal. Embora sua API seja muito simples, criar utilitários de gerenciamento levaria provavelmente uma ou duas semanas, e então você faz o que quiser. Como é normal na vida, se você quer algo especial, é melhor fazer você mesmo. Ao mesmo tempo, eles estão crescendo rapidamente e ouvindo a comunidade, então preencha uma solicitação de recurso e pode ser que apareça em breve.
Anton Safonov de
Oh, entendido. Diga ao proprietário da filial que você deve escolher os empregos nos quais está interessado e configurá-los para sua filial personalizada como ele deseja. Eu gosto desta ideia.
toomasr
1

Na verdade, o que é realmente problemático é o isolamento de construção com ramificações de recursos. Em nossa empresa, temos um conjunto de projetos maven separados, todos parte de uma distribuição maior. Esses projetos são mantidos por equipes diferentes, mas para cada distribuição, todos os projetos precisam ser liberados. Uma featurebranch agora pode se sobrepor de um projeto a outro e é aí que o isolamento da construção fica doloroso. Existem várias soluções que tentamos:

  • criar repositórios de instantâneos separados no nexo para cada ramo de recurso
  • compartilhar repositórios locais em escravos dedicados
  • use o repository-server-plugin com os repositórios upstream
  • construir tudo dentro de um trabalho com um repositório privado

Na verdade, a última solução é a mais promissora. Todas as outras soluções carecem de uma ou de outra forma. Junto com o plugin job-dsl, é fácil configurar um novo branch de recurso. simplesmente copie e cole o script bacana, adapte os ramos e deixe o trabalho inicial criar os novos trabalhos. Certifique-se de que a tarefa de propagação remova tarefas não gerenciadas. Em seguida, você pode escalar facilmente com ramificações de recursos em diferentes projetos de maven.

Mas, como Tom disse bem acima, seria melhor superar a necessidade de branches de recursos e ensinar os desenvolvedores a integrar de forma limpa, mas esse é um processo mais longo e o resultado não é claro com muitas partes do sistema legado que você não vai tocar mais.

meus 2 centavos

prosailor
fonte