Per Kubernetes ci sono diverse opzioni per aggiornare le risorse: apply, edit, patch e replace. C'è confusione su cosa faccia ciascuna di esse e quando applicarle. Vediamo di fare chiarezza.

Se la frase "kubernetes apply vs replace", si trova , che non è corretta. "kubernetes apply vs patch" il primo risultato è la documentazione su kubectl patch, che non include un confronto applica e patch. In questo articolo verranno esaminate le diverse opzioni, così come il corretto utilizzo di ciascuna di esse.
Nel 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 un'annotazione, aumentare o diminuire il numero di repliche.
Kubernetes CLI
Se stai già lavorando con cluster Kubernetes tramite la CLI, conosci già applica e modifica. Comando applica legge la specifica della risorsa da un file e fa un "upsert" nel cluster Kubernetes, cioè crea la risorsa se non esiste e la aggiorna se esiste. Il comando modifica legge la risorsa tramite l'API, dopodiché scrive la specifica della risorsa in un file locale, che viene poi aperto in un editor di testo. Dopo aver modificato e salvato il file, kubectl invierà le modifiche tramite l'API, che applicherà con attenzione queste modifiche alla risorsa.
Non tutti conoscono i comandi patch e replace. Comando patch permette di modificare una parte della specifica della risorsa, fornendo solo la parte modificata nella riga di comando. Il comando replace funziona allo stesso modo di modifica, ma tutto deve essere fatto manualmente: è necessario scaricare la versione attuale della specifica della risorsa, ad esempio utilizzando kubectl get -o yaml, modificarla, quindi utilizzare replace per aggiornare la risorsa con la specifica modificata. Il comando replace non funzionerà se ci sono state modifiche tra la lettura e la sostituzione della risorsa.
Kubernetes API
Probabilmente conosci i metodi CoreV1().Pods().Update(), replaceNamespacedService o patch_namespaced_deployment, se lavori con cluster tramite utilizzando un linguaggio di programmazione. La libreria gestisce questi metodi tramite richieste HTTP, utilizzando i metodi PUT e PATCH. In questo contesto, update e replace usano PUT, e patch, per quanto possa sembrare ovvio, usa PATCH.
Vale la pena notare che kubectl lavora anche con cluster tramite l'API. In altre parole, kubectlè un wrapper sopra la libreria client per il linguaggio Go, fornendo in gran parte la possibilità di fornire sottocomandi in modo più compatto e leggibile per accompagnare le funzionalità standard dell'API. Ad esempio, come potresti aver notato, il metodo applica non è stato menzionato sopra nel paragrafo precedente. Attualmente (maggio 2020, nota del traduttore) tutta la logica kubectl apply, cioè la creazione di risorse non esistenti e l'aggiornamento di quelle esistenti, funziona completamente dalla parte del codice kubectl. Si stanno compiendo sforzi applica dal lato API, ma è ancora in fase di beta testing. Spiegherò meglio di seguito.
Patch per impostazione predefinita
È meglio utilizzare patch, se desideri aggiornare una risorsa. Così funzionano sia le librerie client sopra l'API di Kubernetes, che kubectl non sorprende, poiché è un wrapper per la libreria client, nota del traduttore).
Lavorare strategicamene
Tutti i comandi kubectl applica, modifica e patch utilizzano il metodo PATCH nelle richieste HTTP per aggiornare una risorsa esistente. Se si analizzano in dettaglio le implementazioni dei comandi, si nota che tutti utilizzano l'approccio per aggiornare le risorse, anche se il comando patch può utilizzare anche altri approcci (ne parlerò meglio di seguito). L'approccio strategic-merge patching cerca di "fare tutto nel modo giusto" quando si unisce la specifica fornita a quella esistente. Più precisamente, cerca di unire sia gli oggetti sia gli array, il che significa che le modifiche sono generalmente additive. Ad esempio, eseguendo il comando patch con una nuova variabile ambientale nella specifica del contenitore del pod, provoca l'aggiunta di questa variabile alle variabili ambientali esistenti, senza sovrascriverle. Per rimuovere utilizzando questo approccio, è necessario impostare forzatamente il valore del parametro su null nella specifica fornita. Quali sono i comandi kubectl migliori per l'aggiornamento da utilizzare?
Se stai creando e gestendo le tue risorse utilizzando kubectl apply, è meglio utilizzare sempre kubectl apply, affinché kubectl può gestire la configurazione e monitorare correttamente le modifiche richieste da un'applicazione all'altra. Il vantaggio di utilizzare sempre applica è che tiene traccia della specifica precedentemente applicata, consentendogli di sapere quando le proprietà della specifica e gli elementi dell'array vengono esplicitamente rimossi. Ciò consente di utilizzare applica per rimuovere proprietà ed elementi dell'array, mentre una normale fusione strategica non funzionerà. I comandi modifica e patch non aggiornano le annotazioni che kubectl apply utilizza per tracciare le sue modifiche, quindi qualsiasi modifica tracciata e effettuata tramite API Kubernetes, ma apportata tramite comandi modifica e patch, è invisibile ai comandi successivi applica, vale a dire applica non le elimina, anche se non compaiono nella specifica di input per applica (Nella documentazione è indicato che modifica e patch effettuano aggiornamenti delle annotazioni utilizzate applica, ma in pratica – no).
Se non si utilizza il comando applica, è possibile utilizzare come modifica, sia patch, scegliendo il comando che si adatta meglio alla modifica apportata. Quando si aggiungono e modificano le proprietà della specifica, entrambi gli approcci sono più o meno equivalenti. Quando si eliminano proprietà della specifica o elementi dell'array modifica si comporta come un'operazione unica applica, inclusa la tracciatura di quale fosse la specifica prima e dopo la 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 rimuoverla dalla risorsa. L'eliminazione di un elemento dell'array utilizzando il strategic-merge patching è più complessa, poiché richiede l'uso di direttive di fusione. Vedi altri approcci agli aggiornamenti qui sotto per scegliere alternative più accettabili.
Per implementare metodi di aggiornamento nella libreria client che si comportano in modo simile ai comandi sopra indicati kubectl, è necessario impostare nelle richieste content-type in application/strategic-merge-patch+json. Se si desidera eliminare le proprietà nella specifica, è necessario impostare esplicitamente i loro valori su null in modo simile a kubectl patch. Se è necessario rimuovere elementi dell'array, occorre includere le direttive di fusione nella specifica di aggiornamento o utilizzare un altro approccio agli aggiornamenti.
Altri approcci agli aggiornamenti
In Kubernetes vengono supportati due ulteriori approcci agli aggiornamenti: e . L'approccio JSON merge patch accetta una specificazione parziale di Kubernetes come input e supporta la fusione degli oggetti in modo simile all'approccio strategic-merge patching. La differenza tra i due è che supporta solo la sostituzione degli array, incluso l'array dei contenitori nella specifica pod. Questo significa che, utilizzando JSON merge patch, è necessario fornire specificazioni complete per tutti i contenitori nel caso in cui si modifichi una qualsiasi proprietà di un contenitore. Pertanto, questo approccio è utile per eliminare elementi dall'array nella specifica. Nella riga di comando, puoi scegliere JSON merge patch utilizzando kubectl patch --type=merge. Quando si lavora con l'API di Kubernetes, è necessario utilizzare il metodo di richiesta PATCH e impostare content-type in application/merge-patch+json.
L'approccio JSON patch, invece di fornire una specificazione parziale della risorsa, utilizza la fornitura delle modifiche che si desidera apportare alla risorsa come array, dove ogni elemento dell'array rappresenta una descrizione della modifica che si sta apportando alla risorsa. Questo approccio è un modo più flessibile e potente di esprimere le modifiche apportate, ma considerato che l'elenco delle modifiche è fornito in un formato separato, non Kubernetes, anziché inviare una specificazione parziale della risorsa. In kubectl puoi scegliere JSON patch utilizzando kubectl patch --type=json. Quando si utilizza l'API Kubernetes, questo approccio funziona utilizzando il metodo di richiesta PATCH e impostare content-type in application/json-patch+json.
Serve certezza — utilizziamo replace
In alcuni casi, è necessaria certezza che non vengano apportate modifiche alla risorsa tra il momento della lettura della risorsa e il suo aggiornamento. In altre parole, è opportuno assicurarsi che tutte le modifiche siano atomiche. In questo caso, per aggiornare le risorse sarebbe opportuno utilizzare replace. Ad esempio, se c'è un ConfigMap con un contatore aggiornato da più fonti, è opportuno assicurarsi che due fonti non aggiornino il contatore contemporaneamente, portando a una perdita di aggiornamento. Per illustrare, immaginiamo una sequenza di eventi utilizzando l'approccio patch:
- A e B ottengono lo stato attuale della risorsa dall'API
- Ognuno di essi aggiorna localmente la specifica, incrementando il contatore di uno e aggiungendo "A" o "B" rispettivamente nell'annotazione "updated-by"
- A aggiorna la risorsa un po' più velocemente
- B aggiorna la risorsa
Di conseguenza, l'aggiornamento di A viene perso. L'ultima operazione patch vince, il contatore aumenta di uno anziché due, e il valore dell'annotazione "updated-by" termina su "B" e non contiene "A". Confrontiamo quanto detto sopra con ciò che avviene quando gli aggiornamenti vengono effettuati utilizzando l'approccio replace:
- A e B ottengono lo stato attuale della risorsa dall'API
- Ognuno di essi aggiorna localmente la specifica, incrementando il contatore di uno e aggiungendo "A" o "B" rispettivamente nell'annotazione "updated-by"
- A aggiorna la risorsa un po' più velocemente
- B tenta di aggiornare la risorsa, ma l'aggiornamento viene rifiutato dall'API, poiché la versione della risorsa nella specifica
replacenon corrisponde alla versione attuale della risorsa in Kubernetes, poiché la versione della risorsa è stata incrementata durante l'operazione di replace da parte di A.
Nel caso sopra riportato, B dovrà estrarre nuovamente la risorsa, apportare le modifiche nel nuovo stato e riprovare. replaceDi conseguenza, il contatore sarà incrementato di due e la nota "updated-by" conterrà "AB" alla fine.
L'esempio sopra implica che durante l'esecuzione replace viene eseguita una sostituzione completa dell'intera risorsa. La specifica utilizzata per replacedeve essere completa, non parziale o frammentaria come in applicama integrale, includendo l'aggiunta di resourceVersion nei metadati della specifica. Se non hai incluso resourceVersion o la versione fornita non è attuale, la sostituzione sarà rifiutata. Pertanto, il miglior approccio da utilizzare replace è leggere la risorsa, aggiornarla e sostituirla immediatamente. Utilizzando kubectlpuò apparire 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, avranno successo poiché deployment.yaml non contiene la proprietà .metadata.resourceVersion
$ kubectl create -f deployment.yaml
$ kubectl replace -f deployment.yamlPotrebbe sembrare contraddittorio rispetto a quanto detto sopra, cioè "aggiunta resourceVersion nei metadati della specifica". È errato affermare così? No, non lo è, poiché se kubectl nota che non hai specificato resourceVersion, leggerà da essa e la aggiungerà alla specifica fornita da te e solo allora eseguirà replace. Dato che questo è potenzialmente pericoloso, se ci si fida dell'atomicità, questa 'magia' funziona completamente sul lato kubectl, non è consigliabile fare affidamento su di essa quando si utilizzano librerie client che interagiscono con l'API. In questo caso, dovrai leggere l'attuale specifica della risorsa, aggiornarla e poi eseguire PUT la richiesta.
Non è possibile fare patch – faremo replace.
A volte è necessario apportare alcune modifiche che non possono essere gestite dall'API. In questi casi, è possibile sostituire forzatamente la risorsa, eliminandola e ricreandola. Ciò avviene tramite kubectl replace --force. L'esecuzione del comando elimina immediatamente le risorse e poi le ricrea con la specifica fornita. Non esiste un gestore "forzare la sostituzione" nell'API, e per farlo tramite API è necessario eseguire due operazioni. In primo luogo, si deve eliminare la risorsa impostando gracePeriodSeconds a zero (0) e propagationPolicy su “Background”, e poi ricreare questa risorsa con la specifica desiderata.
Attenzione: questo approccio è potenzialmente pericoloso, può portare a uno stato indeterminato.
Apply lato server
Come accennato in precedenza, gli sviluppatori di Kubernetes stanno lavorando per implementare la logica applica di kubectl nell'API di Kubernetes. La logica applica è disponibile in Kubernetes 1.18 tramite kubectl apply --server-side o tramite l'API, utilizzando il metodo PATCH con content-type application/apply-patch+YAML.
Nota: anche JSON è un YAML valido, quindi puoi inviare la specifica come JSON, anche se
content-typeverràapplication/apply-patch+yaml.
Inoltre, la logica kubectl diventa disponibile per tutti tramite API, applica lato server tiene traccia dei responsabili dei campi nella specifica, consentendo così un accesso concorrente sicuro per la sua modifica senza conflitti. In altre parole, se applica lato server avrà una diffusione più ampia, emergerà un'interfaccia universale di gestione delle risorse sicura per diversi client, come kubectl, Pulumi o Terraform, GitOps e anche script personalizzati che utilizzano librerie client.
Risultati
Spero che questa breve panoramica sui diversi modi di aggiornare le risorse nei cluster sia stata utile per te. È utile sapere che ci sono più aspetti rispetto a 'apply' contro 'replace', dato che si può aggiornare una risorsa utilizzando apply, edit, patch o replace. Infatti, ogni approccio ha il suo ambito di applicazione. Per le modifiche atomiche, è preferibile il replace, altrimenti è opportuno utilizzare strategic-merge patch tramite apply. In ogni caso, spero che tu abbia capito che non è saggio fidarsi di Google o StackOverflow quando cerchi "kubernetes apply vs replace". Almeno fino a quando questo articolo non sostituirà la risposta corrente.

Fonte: habr.com
