Mesclagem de 3 vias no werf: implantação no Kubernetes com Helm "on steroids"

Aconteceu algo que nós (e não apenas nós) estávamos esperando: werf , nosso utilitário de código aberto para criar aplicativos e entregá-los ao Kubernetes, agora suporta a aplicação de alterações usando patches de mesclagem de 3 vias! Além disso, tornou-se possível adotar os recursos existentes do K8s nas versões Helm sem recriar esses recursos.



Se for muito curto, defina WERF_THREE_WAY_MERGE=enabled - obtemos a implantação "como no kubectl apply ", compatível com as instalações existentes no Helm 2 e até um pouco mais.

Mas vamos começar com a teoria: o que são patches de 3 vias em geral, como as pessoas chegaram à abordagem com sua geração e por que elas são importantes nos processos de CI / CD com infraestrutura baseada em Kubernetes? E depois disso - vamos ver o que é a fusão de três vias no werf, quais modos são usados ​​por padrão e como gerenciá-lo.

O que é um patch de 3 vias de mesclagem?


Então, vamos começar com a tarefa de implantar os recursos descritos nos manifestos YAML no Kubernetes.

Para trabalhar com recursos, a API do Kubernetes oferece as seguintes operações básicas: criar, corrigir, substituir e excluir. Supõe-se que, com a ajuda deles, seja necessário construir uma implementação contínua e conveniente de recursos para o cluster. Como

Equipas imperativas do kubectl


A primeira abordagem para gerenciar objetos no Kubernetes é usar os comandos imperativos do kubectl para criar, modificar e excluir esses objetos. Simplificando:

  • kubectl run comando kubectl run pode executar Deployment ou Job:

     kubectl run --generator=deployment/apps.v1 DEPLOYMENT_NAME --image=IMAGE 
  • kubectl scale - altere o número de réplicas:

     kubectl scale --replicas=3 deployment/mysql 
  • etc.

Essa abordagem pode parecer conveniente à primeira vista. No entanto, existem problemas:

  1. É difícil de automatizar .
  2. Como refletir a configuração no Git? Como revisar as alterações que ocorrem em um cluster?
  3. Como garantir a reprodutibilidade da configuração na reinicialização?
  4. ...

É claro que essa abordagem não se encaixa bem em armazenar código e infraestrutura de aplicativos como código (IaC; ou mesmo GitOps como uma opção mais moderna, ganhando popularidade no ecossistema Kubernetes). Portanto, essas equipes não receberam mais desenvolvimentos no kubectl.

Criar, obter, substituir e excluir operações


Com a criação primária , tudo é simples: enviamos o manifesto para a operação de criação do kube api e o recurso é criado. A representação YAML do manifesto pode ser armazenada no Git e, para criar, use o comando kubectl create -f manifest.yaml .

A exclusão também é simples: substituímos o mesmo manifest.yaml do Git pelo comando kubectl delete -f manifest.yaml .

A operação de replace permite substituir completamente a configuração do recurso por uma nova, sem recriar o recurso. Isso significa que, antes de fazer uma alteração em um recurso, é lógico solicitar a versão atual com a operação get , alterá-la e atualizar com a operação de replace . O bloqueio otimista é incorporado ao kube apiserver e, se o objeto tiver sido alterado após a operação get , a operação de replace falhará.

Para armazenar a configuração no Git e atualizar usando o replace, você precisa get uma operação get , manter a configuração do Git com o que obtivemos e executar a replace . Normalmente, o kubectl permite apenas usar o kubectl replace -f manifest.yaml , em que manifest.yaml é o manifest.yaml totalmente preparado (no nosso caso, anexo) que você precisa instalar. Acontece que o usuário precisa implementar manifestos de mesclagem, mas isso não é uma questão trivial ...

Também é importante notar que, embora manifest.yaml esteja armazenado no Git, não podemos saber antecipadamente se precisamos criar um objeto ou atualizá-lo - isso deve ser feito pelo software do usuário.

Conclusão: podemos criar uma implementação contínua apenas com criar, substituir e excluir, garantindo que a configuração da infraestrutura seja armazenada no Git, juntamente com o código e um CI / CD conveniente?

Basicamente, podemos ... Para fazer isso, precisamos implementar a operação de mesclagem dos manifestos e algum tipo de ligação que:

  • verifica a presença de um objeto no cluster,
  • executa a criação inicial do recurso,
  • atualiza ou exclui.

Ao atualizar, é necessário considerar que o recurso pode ter sido alterado desde a última get e lidar automaticamente com o caso de bloqueio otimista - faça repetidas tentativas de atualização.

No entanto, por que reinventar a roda quando o kube-apiserver oferece outra maneira de atualizar os recursos: a operação de patch , que remove alguns dos problemas descritos pelo usuário?

Patch


Então chegamos aos remendos.

Os patches são a principal maneira de aplicar alterações aos objetos existentes no Kubernetes. A operação de patch funciona para que:

  • O usuário do kube-apiserver precisa enviar o patch no formato JSON e especificar o objeto,
  • e o próprio apiserver irá lidar com o estado atual do objeto e trazê-lo para a forma desejada.

O bloqueio otimista neste caso não é necessário. Essa operação é mais declarativa em comparação à substituição, embora a princípio possa parecer o contrário.

Desta forma:

  • usando a operação create , criamos um objeto a partir do manifesto do Git,
  • using delete - delete se o objeto não for mais necessário,
  • using patch - modificamos o objeto, trazendo-o para o formulário descrito no Git.

No entanto, para fazer isso, você deve criar o patch correto !

Como os patches funcionam no Helm 2: mesclagem bidirecional


Na primeira vez que um release é instalado, o Helm executa uma operação de create nos recursos do gráfico.

Ao atualizar o release do Helm para cada recurso:

  • conta o patch entre a versão do recurso do gráfico anterior e a versão atual do gráfico,
  • aplica esse patch.

Vamos chamar esse patch de patch de mesclagem de 2 vias , porque 2 manifestos participam de sua criação:

  • Manifesto de recursos da versão anterior,
  • O manifesto do recurso do recurso atual.

Ao excluir, a operação de delete no kube apiserver é chamada para recursos que foram declarados no release anterior, mas não declarados no atual.

A abordagem com o patch de mesclagem bidirecional tem um problema: leva à dessincronização do estado real do recurso no cluster e do manifesto no Git .

Um exemplo de um problema


  • No Git, um manifesto é armazenado no gráfico no qual o campo image Implantação possui o valor do ubuntu:18.04 .
  • O usuário através do kubectl edit alterou o valor desse campo para ubuntu:19.04 .
  • Quando você reimplanta o gráfico, o Helm não gera um patch , porque o campo de image na versão anterior da liberação e no gráfico atual é o mesmo.
  • Após a implantação repetida da image , o ubuntu:19.04 permanece, embora o ubuntu:18.04 esteja escrito no gráfico.

Temos dessincronização e perdemos a declaratividade.

O que é um recurso sincronizado?


De um modo geral, é impossível obter uma correspondência completa entre um manifesto de recurso em um cluster em execução e um manifesto do Git. Como no manifesto real, pode haver anotações / rótulos de serviço, contêineres adicionais e outros dados adicionados e excluídos dinamicamente por alguns controladores do recurso. Não podemos e não queremos manter esses dados no Git. No entanto, queremos que, ao lançar, os campos especificados explicitamente no Git tenham valores apropriados.

Acontece esta regra geral de um recurso sincronizado : quando você lança um recurso, pode alterar ou excluir apenas os campos explicitamente especificados no manifesto do Git (ou foram registrados na versão anterior, mas agora são excluídos).

Patch de 3 vias para mesclagem


A ideia principal do patch de mesclagem de 3 vias : geramos um patch entre a última versão aplicada do manifesto do Git e a versão de destino do manifesto do Git, levando em consideração a versão atual do manifesto do cluster de trabalho. O patch final deve estar em conformidade com a regra de recurso sincronizado:

  • novos campos adicionados à versão de destino são adicionados usando o patch;
  • campos anteriormente existentes na última versão aplicada e não existentes no campo de destino são redefinidos usando o patch;
  • Os campos na versão atual do objeto que diferem da versão de destino do manifesto são atualizados usando o patch.

É por esse princípio que o kubectl apply patches é gerado:

  • a última versão aplicada do manifesto é armazenada na anotação do próprio objeto,
  • target - extraído do arquivo YAML especificado,
  • atual - de um cluster de trabalho.

Agora que descobrimos a teoria, é hora de contar o que fizemos no werf.

Aplicar alterações ao werf


Antes, o werf, como o Helm 2, usava patches de duas vias.

Patch de reparo


Para mudar para um novo tipo de patches - 3-way-merge - a primeira etapa, introduzimos os chamados patches de reparo .

Ao implantar, o patch padrão de mesclagem bidirecional é usado, mas o werf gera adicionalmente um patch que sincroniza o estado real do recurso com o que está escrito no Git (esse patch é criado usando a mesma regra de recurso sincronizado descrita acima).

No caso de um rassynchron, no final da implantação, o usuário recebe um WARNING com a mensagem e o patch apropriados, que devem ser aplicados para levar o recurso a um formulário sincronizado. Além disso, esse patch é gravado em uma anotação especial werf.io/repair-patch . Supõe-se que o próprio usuário aplicará esse patch com as mãos: werf não o aplicará em princípio.

A geração de patches de reparo é uma medida temporária que permite testar a criação de patches com base no princípio da mesclagem de 3 vias, mas não os aplica automaticamente. No momento, esse modo de operação está ativado por padrão.

Patch de 3 vias para mesclagem apenas para novos lançamentos


A partir de 1º de dezembro de 2019, as versões beta e alfa do werf começam por padrão a usar patches completos de mesclagem de 3 vias para aplicar alterações somente para novas versões do Helm lançadas via werf. As versões existentes continuarão a usar a abordagem de correção de mesclagem bidirecional + reparo.

Você pode ativar esse modo de operação explicitamente, definindo WERF_THREE_WAY_MERGE_MODE=onlyNewReleases agora.

Nota : o recurso apareceu no werf em várias versões: no canal alfa, ficou pronto a partir da versão v1.0.5-alpha.19 , e no canal beta com a v1.0.4-beta.20 .

Patch de 3 vias para todas as versões


A partir de 15 de dezembro de 2019, as versões beta e alfa do werf começam a usar patches completos de 3 vias de mesclagem por padrão para aplicar alterações em todas as versões.

Este modo de operação pode ser WERF_THREE_WAY_MERGE_MODE=enabled explicitamente WERF_THREE_WAY_MERGE_MODE=enabled definindo WERF_THREE_WAY_MERGE_MODE=enabled agora.

O que fazer com os recursos de dimensionamento automático?


O Kubernetes possui 2 tipos de dimensionamento automático: HPA (horizontal) e VPA (vertical).

Horizontal seleciona automaticamente o número de réplicas, vertical - o número de recursos. O número de réplicas e os requisitos de recursos são especificados no manifesto do recurso (consulte spec.replicas ou spec.containers[].resources.limits.cpu , spec.containers[].resources.limits.memory e outros ).

Problema: se um usuário configurar um recurso no gráfico para que ele exiba valores específicos para recursos ou réplicas e auto-scalers sejam ativados para esse recurso, em cada implantação o werf redefinirá esses valores para o que está escrito no manifesto do gráfico.

Existem duas soluções para o problema. Para iniciantes, é melhor descartar os valores de escala automática especificados explicitamente no manifesto do gráfico. Se por algum motivo essa opção não se encaixar (por exemplo, porque é conveniente definir os limites iniciais de recursos e o número de réplicas no gráfico), o werf oferece as seguintes anotações:

  • werf.io/set-replicas-only-on-creation=true
  • werf.io/set-resources-only-on-creation=true

Se essa anotação estiver presente, o werf não redefinirá os valores correspondentes em cada implementação, mas somente os definirá na criação inicial do recurso.

Para obter mais informações, consulte a documentação do projeto para HPA e VPA .

Negar o uso do patch de 3 vias


O usuário ainda pode proibir o uso de novas correções no werf usando a variável de ambiente WERF_THREE_WAY_MERGE_MODE=disabled . No entanto, a partir de 1º de março de 2020, essa proibição deixará de funcionar e só será possível usar patches de 3 vias.

Adoção de recursos no werf


O domínio do método de aplicação de alterações nos patches de mesclagem de 3 vias nos permitiu implementar imediatamente um recurso como a adoção de recursos existentes no cluster no Helm-release.

O leme 2 tem um problema: você não pode adicionar a um manifesto de gráfico um recurso que já existe no cluster sem recriar esse recurso do zero (consulte # 6031 , # 3275 ). Ensinamos o werf a aceitar os recursos existentes em um release. Para fazer isso, você precisa definir uma anotação na versão atual do recurso de um cluster em execução (por exemplo, usando o kubectl edit ):

 "werf.io/allow-adoption-by-release": RELEASE_NAME 

Agora, o recurso precisa ser descrito no gráfico e, na próxima implantação, pelo werf release do release com o nome correspondente, o recurso existente será aceito neste release e permanecerá sob seu controle. Além disso, no processo de aceitação do recurso para liberação, o werf levará o estado atual do recurso do cluster de trabalho para o estado descrito no gráfico usando os mesmos patches de mesclagem de 3 vias e a regra do recurso sincronizado.

Nota : a configuração de WERF_THREE_WAY_MERGE_MODE não afeta a adoção de recursos - no caso de adoção, sempre é usado um patch de mesclagem de 3 vias.

Os detalhes estão na documentação .

Conclusões e planos futuros


Espero que, após este artigo, tenha ficado mais claro o que são os patches de 3 vias e por que eles vieram para eles. Do ponto de vista prático do desenvolvimento do projeto werf, sua implementação foi mais um passo no sentido de melhorar a implantação do tipo Helm. Agora você pode esquecer os problemas com a sincronização da configuração, que costumavam ocorrer ao usar o Helm 2. Ao mesmo tempo, foi adicionado um novo recurso útil da adoção dos recursos do Kubernetes já carregados na versão do Helm.

Ainda existem alguns problemas e dificuldades na implantação do Helm, como o uso de modelos Go, e continuaremos a resolvê-los.

Informações sobre métodos de atualização de recursos e adoção também podem ser encontradas nesta página de documentação .

Elmo 3


Uma observação especial é digna da nova versão principal do Helm - v3 - lançada recentemente, que também usa patches de 3 vias e se livra do Tiller. A nova versão do Helm requer a migração de instalações existentes para convertê-las em um novo formato de armazenamento de release.

A Werf, por sua vez, agora eliminou o uso do Tiller, mudou para a combinação de três vias e adicionou muito mais , mantendo-se compatível com as instalações existentes no Helm 2 (nenhum script de migração é necessário). Portanto, até que o werf seja alternado para o Helm 3, os usuários do werf não perdem as principais vantagens do Helm 3 sobre o Helm 2 (eles também existem no werf).

No entanto, a mudança do werf para a base de código do Helm 3 é inevitável e ocorrerá em um futuro próximo. Presumivelmente, será werf 1.1 ou werf 1.2 (no momento, a versão principal do werf é 1.0; para mais detalhes sobre o dispositivo de versão do werf, veja aqui ). Durante esse período, o Leme 3 terá tempo para se estabilizar.

PS


Leia também em nosso blog:

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


All Articles