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:
Essa abordagem pode parecer conveniente à primeira vista. No entanto, existem problemas:
- É difícil de automatizar .
- Como refletir a configuração no Git? Como revisar as alterações que ocorrem em um cluster?
- Como garantir a reprodutibilidade da configuração na reinicialização?
- ...
É 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: