Comparação correta entre Kubernetes Apply, Replace e Patch

Existem várias maneiras de atualizar recursos para Kubernetes: apply, edit, patch e replace. É comum haver confusão sobre o que cada um faz e quando aplicá-los. Vamos esclarecer.

Comparação correta entre Kubernetes Apply, Replace e Patch

Se pesquisar no Google a frase "kubernetes apply vs replace" encontra resposta no StackOverflow, que não está correta. Ao pesquisar "kubernetes apply vs patch", o primeiro link é a documentação sobre kubectl patch, que não inclui a comparação apply e patch. Este artigo discutirá as várias opções, bem como o uso correto de cada uma delas.

Durante o ciclo de vida de um recurso Kubernetes (serviço, deployment, ingress, etc.), às vezes é necessário alterar, adicionar ou remover algumas propriedades desse recurso. Por exemplo, adicionar uma nota, aumentar ou diminuir o número de réplicas.

CLI do Kubernetes

Se você já trabalha com clusters Kubernetes através da CLI, já está familiarizado com apply e edit. O comando apply lê a especificação do recurso de um arquivo e faz um "upsert" no cluster Kubernetes, ou seja, cria o recurso se ele não existir e o atualiza se já existir. O comando edit lê o recurso através da API, depois escreve a especificação do recurso em um arquivo local, que é então aberto em um editor de texto. Após editar e salvar o arquivo, kubectl enviará as mudanças feitas de volta através da API, que irá aplicar essas alterações ao recurso.

Nem todos conhecem os comandos patch e replace. O comando patch permite alterar parte da especificação do recurso, fornecendo apenas a parte modificada na linha de comando. O comando replace funciona da mesma forma que edit, mas tudo deve ser feito manualmente: é necessário baixar a versão atual da especificação do recurso, por exemplo, usando kubectl get -o yaml, editá-la e depois usar replace para atualizar o recurso com a especificação modificada. O comando replace não funcionará se houver alterações entre a leitura e a substituição do recurso.

API do Kubernetes

Você provavelmente está familiarizado com os métodos CoreV1().Pods().Update(), replaceNamespacedService ou patch_namespaced_deployment, se estiver trabalhando com clusters através da biblioteca cliente para a API do Kubernetes usando alguma linguagem de programação. A biblioteca manipula esses métodos por meio de solicitações HTTP, utilizando métodos PUT e PATCH. Nesse contexto, atualizar e replace utilizam PUT, e patch, por mais banal que seja, utiliza PATCH.

Vale ressaltar que kubectl também trabalha com clusters através da API. Em outras palavras, kubectl– é uma camada sobre a biblioteca cliente para a linguagem Go, que oferece em grande parte a capacidade de fornecer subcomandos de uma maneira mais compacta e legível, além das funcionalidades padrão da API. Por exemplo, como você já pode ter notado, o método apply não foi mencionado acima no parágrafo anterior. Atualmente (maio de 2020, Nota do tradutor) toda a lógica kubectl apply, ou seja, a criação de recursos não existentes e a atualização de recursos existentes, funciona totalmente no lado do código kubectl. Estão sendo feitos esforços para transferir a lógica apply para o lado da API, mas isso ainda está em fase de beta-testing. Explicarei mais abaixo.

O patch por padrão

É melhor aplicá-lo patch, se você deseja atualizar um recurso. Assim funcionam tanto as bibliotecas clientes sobre a API Kubernetes quanto kubectl (não surpreende, pois é uma camada da biblioteca cliente, Nota do tradutor).

Trabalhar de forma estratégica

Todos os comandos kubectl apply, edit e patch utilizam o método PATCH em solicitações HTTP para atualizar um recurso existente. Se analisarmos a implementação dos comandos, todos usam a abordagem strategic-merge patching para atualizar recursos, embora o comando patch possa usar outras abordagens (mais detalhes abaixo). A abordagem strategic-merge patching tenta "fazer tudo certo" ao mesclar a especificação fornecida com a especificação existente. Mais especificamente, tenta mesclar tanto objetos quanto arrays, o que significa que as alterações são geralmente aditivas. Por exemplo, a execução do comando patch com uma nova variável de ambiente na especificação do pod container resulta na adição dessa variável de ambiente às variáveis de ambiente existentes, em vez de sobrescrevê-las. Para remoção com essa abordagem, deve-se forçar o valor do parâmetro para null na especificação fornecida. Quais comandos kubectl para atualização são os melhores para usar?

Se você está criando e gerenciando seus recursos com kubectl apply, é sempre melhor usar kubectl apply, para que kubectl possa gerenciar a configuração e acompanhar corretamente as mudanças solicitadas de aplicação para aplicação. A vantagem de sempre usar apply é que ele rastreia a especificação aplicada anteriormente, permitindo que ele saiba quando as propriedades da especificação e os elementos do array são explicitamente removidos. Isso permite utilizar apply para remover propriedades e elementos de um array, enquanto a fusão estratégica padrão não funcionará. Comandos edit e patch não atualizam os comentários que kubectl apply são aplicados para rastrear suas alterações, portanto, quaisquer alterações que sejam rastreadas e feitas através da API do Kubernetes, mas feitas por comandos edit e patch, são invisíveis para comandos subsequentes apply, ou seja, apply não os remove, mesmo que não apareçam na especificação de entrada para apply (A documentação afirma que edit e patch fazem atualizações de comentários utilizados apply, mas na prática – não).

Se você não estiver usando o comando apply, pode-se usar como edit, quanto o patch, escolhendo o comando que se encaixa melhor para a alteração a ser feita. Ao adicionar e alterar propriedades da especificação, ambos os métodos são aproximadamente os mesmos. Ao remover propriedades da especificação ou elementos do array edit comporta-se como uma execução única apply, incluindo rastreamento de como era a especificação antes e depois da edição, permitindo a remoção explícita de propriedades e elementos de um array do recurso. É necessário definir explicitamente o valor da propriedade como null na especificação para patch, para removê-lo do recurso. A remoção de um elemento de array usando o patching de fusão estratégica é mais complexa, pois requer o uso de diretrizes de fusão. Veja outros métodos de atualização abaixo para escolher alternativas mais aceitáveis.

Para implementar na biblioteca cliente métodos de atualização que se comportem de forma semelhante aos comandos acima kubectl, deve-se definir nos pedidos content-type em application/strategic-merge-patch+json. Se você quiser remover propriedades na especificação, precisará definir explicitamente seus valores como null, semelhante a kubectl patch. Se você precisar remover elementos do array, deve incluir diretrizes de fusão na especificação de atualização ou usar outro método para atualizações.

Outros métodos de atualização

O Kubernetes suporta dois outros métodos de atualização: JSON merge patch e JSON patch. A abordagem JSON merge patch aceita uma especificação parcial do Kubernetes como entrada e suporta a fusão de objetos de forma semelhante à abordagem strategic-merge patching. A diferença entre elas é que a primeira suporta apenas a substituição de arrays, incluindo o array de contêineres na especificação do pod. Isso significa que ao usar o JSON merge patch, você precisa fornecer especificações completas para todos os contêineres caso alguma propriedade de qualquer contêiner seja alterada. Assim, essa abordagem é útil para remover elementos de um array na especificação. Na linha de comando, você pode escolher o JSON merge patch utilizando kubectl patch --type=merge. Ao trabalhar com a API do Kubernetes, você deve usar o método de solicitação PATCH e estabelecimento content-type em application/merge-patch+json.

A abordagem JSON patch, em vez de fornecer uma especificação parcial do recurso, utiliza a apresentação das alterações que você deseja fazer no recurso, em forma de um array, onde cada elemento do array representa uma descrição da alteração feita no recurso. Essa abordagem é uma maneira mais flexível e poderosa de expressar as mudanças a serem feitas, mas requer que a lista das mudanças seja enviada em um formato separado, não nativo do Kubernetes, ao invés de enviar uma especificação parcial do recurso. No kubectl você pode optar pelo JSON patch utilizando kubectl patch --type=json. Ao utilizar a API do Kubernetes, essa abordagem funciona com o método de solicitação PATCH e estabelecimento content-type em application/json-patch+json.

É necessário garantir — utilizamos replace

Em alguns casos, é preciso ter certeza de que o recurso não será modificado entre o momento da leitura do recurso e sua atualização. Em outras palavras, é importante garantir que todas as alterações sejam atômicas. Nesse caso, para atualizar os recursos, devemos utilizar replace. Por exemplo, se houver um ConfigMap com um contador, que é atualizado por várias fontes, devemos ter certeza de que duas fontes não atualizarão o contador simultaneamente, o que resultaria na perda da atualização. Para ilustrar, imagine uma sequência de eventos usando a abordagem patch:

  • A e B obtêm o estado atual do recurso da API
  • Cada um deles atualiza localmente a especificação, incrementando o contador em um, além de adicionar 'A' ou 'B' respectivamente na anotação 'updated-by'
  • A atualiza o recurso um pouco mais rápido
  • B atualiza o recurso

Como resultado, a atualização A foi perdida. A última operação patch ganha, o contador aumenta em uma unidade em vez de duas, e o valor da anotação "updated-by" termina em "B" e não contém "A". Vamos comparar o que foi dito acima com o que acontece quando as atualizações são realizadas usando a abordagem replace:

  • A e B obtêm o estado atual do recurso da API
  • Cada um deles atualiza localmente a especificação, incrementando o contador em um, além de adicionar 'A' ou 'B' respectivamente na anotação 'updated-by'
  • A atualiza o recurso um pouco mais rápido
  • B tenta atualizar o recurso, mas a atualização é rejeitada pela API, pois a versão do recurso na especificação replace não coincide com a versão atual do recurso no Kubernetes, uma vez que a versão do recurso foi aumentada durante a execução da operação replace do lado A.

No caso acima, B terá que reextrair o recurso, fazer alterações no novo estado e tentar novamente replace. Como resultado, o contador será aumentado em dois, e a anotação "updated-by" conterá "AB" no final.

O exemplo acima pressupõe que durante a execução replace uma substituição completa de todo o recurso é realizada. A especificação usada para replace, deve ser completa, não parcial, ou em partes como em apply, incluindo a adição de resourceVersion nos metadados da especificação. Se você não incluir resourceVersion ou a versão fornecida por você não for atual, a substituição será rejeitada. Assim, a melhor abordagem a ser utilizada replace é ler o recurso, atualizá-lo e imediatamente substituir. Usando kubectl, pode parecer assim:

$ kubectl get deployment my-deployment -o json 
    | jq '.spec.template.spec.containers[0].env[1].value = "new value"' 
    | kubectl replace -f -

Vale a pena notar que os dois comandos seguintes, executados sequencialmente, serão bem-sucedidos, pois deployment.yaml não contém a propriedade .metadata.resourceVersion

$ kubectl create -f deployment.yaml
$ kubectl replace -f deployment.yaml

Isso pode parecer contraditório ao que foi dito acima, ou seja, "adicionar resourceVersion nos metadados da especificação". Isso é uma afirmação incorreta? Não, não é, pois se kubectl perceber que você não forneceu resourceVersion, ele a lerá do recurso e a adicionará na especificação que você forneceu e só então executará replace. Como isso é potencialmente arriscado, se depender da atomicidade, essa mágica funciona totalmente do lado kubectl, não é aconselhável confiar nela ao usar bibliotecas cliente que interagem com a API. Nesse caso, você terá que ler a especificação atual do recurso, atualizá-la e, em seguida, fazer PUT a solicitação.

Não é possível fazer patch – fazemos replace

Às vezes, é necessário fazer algumas alterações que não podem ser processadas pela API. Nesses casos, é possível forçar a substituição do recurso, removendo-o e recriando-o. Isso é feito com kubectl replace --force. A execução do comando remove imediatamente os recursos e, em seguida, os recria com a especificação fornecida. Não há um manipulador de "substituição forçada" na API, e para fazer isso através da API, é necessário realizar duas operações. Primeiro, é preciso excluir o recurso, definindo gracePeriodSeconds como zero (0) e propagationPolicy como “Background”, e depois recriar esse recurso com a especificação desejada.

Atenção: esse método pode ser potencialmente perigoso e pode levar a um estado indefinido.

Apply no lado do servidor

Como mencionado anteriormente, os desenvolvedores do Kubernetes estão trabalhando na implementação da lógica apply do kubectl na API do Kubernetes. A lógica apply está disponível no Kubernetes 1.18 através de kubectl apply --server-side ou através da API, utilizando o método PATCH com content-type application/apply-patch+YAML.

Nota: JSON também é um YAML válido, então é possível enviar a especificação em formato JSON, mesmo se content-type será application/apply-patch+yaml.

Além de que a lógica kubectl se torna disponível para todos através da API, apply do lado do servidor rastreia os responsáveis pelos campos na especificação, permitindo assim um acesso seguro e múltiplo para a edição sem conflitos. Em outras palavras, se apply do lado do servidor ganhar uma adoção mais ampla, haverá uma interface de gerenciamento de recursos segura e universal para diferentes clientes, como kubectl, Pulumi ou Terraform, GitOps, bem como scripts personalizados que utilizam bibliotecas do cliente.

Resultados

Espero que esta breve visão geral das várias maneiras de atualizar recursos em clusters tenha sido útil para você. É bom saber que não se trata apenas de apply contra replace, pois é possível atualizar um recurso usando apply, edit, patch ou replace. Cada abordagem tem sua aplicação específica. Para alterações atômicas, preferencialmente use replace; caso contrário, deve-se utilizar o patch de mesclagem estratégica através de apply. No pior dos casos, espero que você tenha entendido que não se pode confiar no Google ou StackOverflow ao pesquisar "kubernetes apply vs replace". Pelo menos até esta artigo substituir a resposta atual.

Comparação correta entre Kubernetes Apply, Replace e Patch

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster