Un confronto corretto tra Kubernetes Apply, Replace e Patch

Per Kubernetes ci sono diverse opzioni per aggiornare le risorse: apply, edit, patch e replace. C'è confusione su cosa faccia ciascuno di essi e quando usarli. Facciamo chiarezza.

Un confronto corretto tra Kubernetes Apply, Replace e Patch

Se cercare su Google la frase "kubernetes apply vs replace", si trova una risposta su StackOverflow, che non è corretta. Quando si cerca "kubernetes apply vs patch" il primo link è la documentazione su kubectl patch, che non include il confronto apply e patch. In questo articolo verranno esaminati i vari metodi, così come l'uso corretto di ciascuno di essi.

Durante il ciclo di vita di una risorsa Kubernetes (servizio, deployment, ingress, ecc.) a volte è necessario modificare, aggiungere o rimuovere alcune proprietà di questa risorsa. Ad esempio, aggiungere una nota, aumentare o diminuire il numero di repliche.

Kubernetes CLI

Se stai già lavorando con cluster Kubernetes tramite CLI, già conosci apply e edit. Il comando apply legge la specifica della risorsa da un file e fa un 'upsert' nel cluster Kubernetes, ovvero crea la risorsa se non esiste e la aggiorna se esiste. Il comando edit legge la risorsa tramite API, dopo di che scrive la specifica della risorsa in un file locale, che viene poi aperto in un editor di testo. Dopo che hai modificato e salvato il file, kubectl invierà le modifiche effettuate tramite l'API, che applicherà queste modifiche alla risorsa.

Non tutti conoscono i comandi patch e replace. Il comando patch consente di modificare una parte della specifica della risorsa, fornendo solo la parte modificata nella riga di comando. Il comando replace funziona in modo simile a edit, ma tutto deve essere fatto manualmente: è necessario scaricare la versione corrente della specifica della risorsa, ad esempio, utilizzando kubectl get -o yaml, modificarla e poi utilizzare replace per aggiornare la risorsa secondo la specifica modificata. Il comando replace non funzionerà se ci sono state modifiche tra la lettura e la sostituzione della risorsa.

Kubernetes API

Probabilmente sei familiare con i metodi CoreV1().Pods().Update(), replaceNamespacedService o patch_namespaced_deployment, se lavori con cluster tramite la libreria client per l'API Kubernetes usando un linguaggio di programmazione. La libreria gestisce questi metodi tramite richieste HTTP, utilizzando i metodi richiesta PUT: e PATCH. In questo modo aggiornamento e replace utilizzano richiesta PUT:, ma patch, per quanto possa sembrare banale, utilizza PATCH.

Vale la pena notare che kubectl funziona anche con cluster tramite API. In altre parole, kubectl– è un wrapper sopra la libreria client per il linguaggio Go, che fornisce in significativa misura la possibilità di offrire sottocomandi in una forma più compatta e leggibile in aggiunta alle funzionalità standard dell'API. Ad esempio, come avete già potuto notare, il metodo apply non è stato menzionato sopra nel paragrafo precedente. Attualmente (maggio 2020, nota del traduttore) tutta la logica kubectl apply, ovvero la creazione di risorse inesistenti e l'aggiornamento di quelle esistenti, funziona completamente dal lato del codice kubectl. Si stanno compiendo sforzi per trasferire la logica apply dal lato API, ma è ancora in fase di beta testing. Spiegherò meglio di seguito.

Il patch predefinito

È meglio applicare patch, se desiderate aggiornare una risorsa. Questo è il modo in cui funzionano sia le librerie client sopra l'API Kubernetes, sia kubectl (non sorprende, dato che è un wrapper della libreria client, nota del traduttore).

Operare strategicamente

Tutti i comandi kubectl apply, edit e patch utilizzano il metodo PATCH nelle richieste HTTP per aggiornare una risorsa esistente. Se si approfondisce l'implementazione dei comandi, si utilizza in tutti l'approccio strategic-merge patching per l'aggiornamento delle risorse, anche se il comando patch può utilizzare anche altri approcci (maggiori dettagli su questo più avanti). L'approccio strategic-merge patching cerca di "fare tutto correttamente" quando si unisce la specifica fornita con la specifica esistente. In modo più specifico, cerca di unire sia oggetti che array, il che significa che le modifiche sono generalmente additive. Ad esempio, l'esecuzione del comando patch con una nuova variabile d'ambiente nella specifica del contenitore del pod comporta che questa variabile d'ambiente venga aggiunta alle variabili d'ambiente esistenti, e non le sovrascrive. Per rimuovere utilizzando questo approccio, è necessario forzare il valore del parametro a null nella specifica fornita. Quali sono quindi i comandi kubectl da utilizzare per aggiornare meglio?

Se create e gestite le vostre risorse utilizzando kubectl apply, è sempre meglio utilizzare kubectl apply, in modo che kubectl possa gestire la configurazione e monitorare correttamente le modifiche richieste dall'applicazione all'applicazione. Il vantaggio di utilizzare sempre apply è che tiene traccia della specifica precedentemente applicata, consentendo di sapere quando le proprietà della specifica e gli elementi dell'array vengono esplicitamente rimossi. Questo consente di utilizzare apply per rimuovere proprietà ed elementi dell'array, mentre una fusione strategica normale non funzionerà. I comandi edit e patch non aggiornano le annotazioni che kubectl apply viene utilizzato per tenere traccia delle proprie modifiche, quindi tutte le modifiche che vengono tracciate e apportate tramite l'API di Kubernetes, ma effettuate tramite i comandi edit e patch, sono invisibili ai comandi successivi apply, cioè apply non li rimuovono, anche se non compaiono nella specifica di ingresso per apply (La documentazione dice che edit e patch fanno aggiornamenti delle annotazioni utilizzate apply, ma nella pratica - no).

Se non utilizzi il comando apply, puoi usare come edit, sia patch, scegliendo il comando che si adatta meglio alla modifica apportata. Quando si aggiungono e si modificano le proprietà della specifica, entrambi gli approcci sono più o meno equivalenti. Quando si rimuovono le proprietà della specifica o gli elementi dell'array edit si comporta come un'esecuzione una tantum apply, compreso il monitoraggio di quale fosse la specifica prima e dopo la sua modifica, quindi è possibile rimuovere esplicitamente le proprietà e gli elementi dell'array dalla risorsa. È necessario impostare esplicitamente il valore della proprietà su null nella specifica per patch, per rimuoverlo dalla risorsa. La rimozione di un elemento dell'array utilizzando il patching con fusione strategica è più complessa, poiché richiede l'uso di direttive di fusione. Consulta gli altri approcci per gli aggiornamenti qui sotto per scegliere alternative più accettabili.

Per implementare nella libreria client metodi di aggiornamento che si comportano in modo simile ai comandi sopra menzionati kubectl, è necessario specificare nelle richieste content-type in application/strategic-merge-patch+json. Se desideri rimuovere proprietà nella specifica, devi impostare esplicitamente i loro valori su null in modo simile a kubectl patch. Se devi rimuovere elementi dell'array, dovresti includere le direttive di fusione nella specifica di aggiornamento o utilizzare un altro approccio per gli aggiornamenti.

Altri approcci agli aggiornamenti

In Kubernetes sono supportati altri due approcci per gli aggiornamenti: JSON merge patch e JSON patch. L'approccio JSON merge patch accetta una specifica parziale di Kubernetes come input e supporta la fusione degli oggetti simile all'approccio strategic-merge patching. La differenza tra i due è che supporta solo la sostituzione degli array, incluso l'array dei container nella specifica del pod. Ciò significa che quando si utilizza JSON merge patch, è necessario fornire specifiche complete per tutti i container nel caso si modifichi una qualsiasi proprietà di un container. Questo approccio è utile per rimuovere elementi da un array nella specifica. Nella riga di comando, è possibile scegliere JSON merge patch utilizzando kubectl patch --type=merge. Quando si lavora con l'API Kubernetes, si dovrebbe utilizzare il metodo di richiesta PATCH e impostazione content-type in application/merge-patch+json.

L'approccio JSON patch, invece di fornire una specifica parziale della risorsa, utilizza la fornitura delle modifiche che si desidera apportare alla risorsa sotto forma di array, in cui ogni elemento dell'array rappresenta una descrizione della modifica apportata alla risorsa. Questo approccio è un modo più flessibile e potente per esprimere le modifiche apportate, ma a scapito del fatto che l'elenco delle modifiche è in un formato separato, non Kubernetes, invece di inviare una specifica parziale della risorsa. In kubectl è possibile scegliere JSON patch utilizzando kubectl patch --type=json. Utilizzando l'API Kubernetes, questo approccio funziona utilizzando il metodo di richiesta PATCH e impostazione content-type in application/json-patch+json.

Necessaria certezza — utilizziamo replace

In alcuni casi è necessaria certezza che non ci siano modifiche alla risorsa tra il momento della lettura della risorsa e il suo aggiornamento. In altre parole, bisogna assicurarsi che tutte le modifiche siano atomiche. In tal caso, per aggiornare le risorse si dovrebbe utilizzare replace. Ad esempio, se c'è un ConfigMap con un contatore, aggiornato da più fonti, è necessario essere certi che due fonti non aggiornino il contatore contemporaneamente, il che porterebbe alla perdita dell'aggiornamento. Per dimostrare, immagina una sequenza di eventi utilizzando l'approccio patch:

  • A e B ottengono lo stato attuale della risorsa dall'API
  • Ognuno di loro aggiorna localmente la specifica, aumentando il contatore di uno e aggiungendo "A" o "B" rispettivamente nella nota "updated-by"
  • A aggiorna la risorsa un po' più velocemente
  • B aggiorna la risorsa

Di conseguenza, l'aggiornamento A è andato perso. Ultima operazione patch vincente, il contatore aumenta di uno invece che di due, e il valore della nota "updated-by" termina con "B" e non contiene "A". Confrontiamo quanto sopra con ciò che accade quando gli aggiornamenti vengono eseguiti utilizzando l'approccio replace:

  • A e B ottengono lo stato attuale della risorsa dall'API
  • Ognuno di loro aggiorna localmente la specifica, aumentando il contatore di uno e aggiungendo "A" o "B" rispettivamente nella nota "updated-by"
  • A aggiorna la risorsa un po' più velocemente
  • B tenta di aggiornare la risorsa, ma l'aggiornamento viene rifiutato dall'API, perché la versione della risorsa nella specifica replace non corrisponde alla versione attuale della risorsa in Kubernetes, poiché la versione della risorsa è stata incrementata durante l'operazione di sostituzione da parte di A.

Nel caso sopra, B dovrà riottenere la risorsa, apportare modifiche al nuovo stato e riprovare replace. Di conseguenza, il contatore sarà aumentato di due e la nota "updated-by" conterrà "AB" alla fine.

L'esempio di cui sopra implica che durante l'esecuzione replace viene eseguita una sostituzione completa dell'intera risorsa. La specifica utilizzata per replace, deve essere non parziale, o in parti come in apply, ma completa, inclusa l'aggiunta di resourceVersion metadati della specifica. Se non l'hai inclusa resourceVersion o la versione fornita non è quella attuale, la sostituzione verrà rifiutata. Pertanto, il miglior approccio da utilizzare replace è leggere la risorsa, aggiornarla e sostituirla immediatamente. Usando kubectl, potrebbe sembrare così:

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

Vale la pena notare che le seguenti due comandi, eseguiti in sequenza, verranno eseguiti con successo, poiché deployment.yaml non contiene la proprietà .metadata.resourceVersion

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

Sembra contraddire quanto detto sopra, cioè "aggiungere resourceVersion metadati della specifica". Affermarlo è errato? No, non lo è, poiché se kubectl nota che non hai specificato resourceVersion, la leggerà dalla risorsa e la aggiungerà alla specifica che hai fornito e solo dopo eseguirà replace. Poiché questa è potenzialmente pericolosa, se ci si affida all'atomicità, la magia funziona completamente da parte di kubectl, non vale la pena fidarsi di essa usando le librerie client che operano con l'API. In questo caso dovrai leggere la specifica corrente della risorsa, aggiornarla e poi eseguire richiesta PUT: la richiesta.

Non si può fare un patch – si fa una sostituzione

A volte è necessario apportare alcune modifiche che non possono essere gestite dall'API. In questi casi è possibile forzare la sostituzione della risorsa, rimuovendo e creando di nuovo. Questo avviene tramite kubectl replace --force. L'esecuzione del comando rimuove immediatamente le risorse, per poi ricrearle con la specifica fornita. Nell'API non esiste un gestore "sostituisci forzatamente", e per fare ciò tramite API è necessario eseguire due operazioni. Per prima cosa bisogna rimuovere la risorsa impostando per essa gracePeriodSeconds a zero (0) e propagationPolicy a “Background”, per poi ricreare questa risorsa con la specifica desiderata.

Attenzione: questo approccio è potenzialmente pericoloso, potrebbe portare a uno stato non definito.

Apply sul lato server

Come accennato in precedenza, gli sviluppatori di Kubernetes stanno lavorando all'implementazione della logica apply da kubectl nell'API di Kubernetes. La logica apply è disponibile in Kubernetes 1.18 tramite kubectl apply --server-side o tramite API, utilizzando il metodo PATCH con content-type application/apply-patch+YAML.

Nota: JSON è anche un YAML valido, quindi è possibile inviare la specifica in formato JSON, anche se content-type si applicherà application/apply-patch+yaml.

Inoltre, la logica kubectl diventa disponibile per tutti tramite API, apply lato server tiene traccia dei responsabili per i campi nella specifica, consentendo così un accesso simultaneo sicuro per la sua modifica senza conflitti. In altre parole, se apply lato server ottiene una diffusione più ampia, apparirà un'interfaccia di gestione delle risorse universale e sicura per diversi client, ad esempio kubectl, Pulumi o Terraform, GitOps e anche script personalizzati che utilizzano librerie client.

Conclusioni

Spero che questa breve panoramica sui diversi modi per aggiornare le risorse nei cluster ti sia stata utile. È utile sapere che ci sono argomenti di discussione tra apply e replace, poiché è possibile aggiornare una risorsa tramite apply, edit, patch o replace. Infatti, ogni approccio ha il suo ambito di applicazione. Per modifiche atomiche è preferibile usare replace, altrimenti si deve utilizzare strategic-merge patch tramite apply. In ogni caso, spero che tu abbia capito che non bisogna fidarsi di Google o StackOverflow quando si cerca "kubernetes apply vs replace". Almeno finché questo articolo non sostituisce la risposta attuale.

Un confronto corretto tra Kubernetes Apply, Replace e Patch

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS - ProHoster