Fusione a 3 vie in Werf: implementazione su Kubernetes con Helm “con steroidi”

Ciò che noi (e non solo noi) aspettavamo da tempo è accaduto: werf, la nostra utility Open Source per la creazione di applicazioni e la loro distribuzione a Kubernetes, ora supporta l'applicazione delle modifiche utilizzando patch di unione a 3 vie! Oltre a ciò, è possibile adottare le risorse K8 esistenti nelle versioni Helm senza ricostruire tali risorse.

Fusione a 3 vie in Werf: implementazione su Kubernetes con Helm “con steroidi”

Se è molto breve, allora mettiamo WERF_THREE_WAY_MERGE=enabled - otteniamo il dispiegamento "come in kubectl apply", compatibile con le installazioni Helm 2 esistenti e anche qualcosa in più.

Ma cominciamo con la teoria: cosa sono esattamente le patch di unione a 3 vie, come è arrivato l'approccio per generarle e perché sono importanti nei processi CI/CD con l'infrastruttura basata su Kubernetes? E dopo, vediamo cos'è la fusione a 3 vie in werf, quali modalità vengono utilizzate per impostazione predefinita e come gestirla.

Che cos'è una patch di unione a 3 vie?

Iniziamo quindi con l'attività di distribuzione delle risorse descritte nei manifest YAML in Kubernetes.

Per utilizzare le risorse, l'API Kubernetes offre le seguenti operazioni di base: creazione, patch, sostituzione ed eliminazione. Si presume che con il loro aiuto sia necessario costruire un comodo lancio continuo di risorse nel cluster. Come?

Comandi kubectl imperativi

Il primo approccio alla gestione degli oggetti in Kubernetes consiste nell'utilizzare i comandi imperativi kubectl per creare, modificare ed eliminare tali oggetti. In poche parole:

  • Il gruppo kubectl run puoi eseguire Deployment o Job:
    kubectl run --generator=deployment/apps.v1 DEPLOYMENT_NAME --image=IMAGE
  • Il gruppo kubectl scale — modificare il numero di repliche:
    kubectl scale --replicas=3 deployment/mysql
  • eccetera

Questo approccio può sembrare conveniente a prima vista. Tuttavia ci sono problemi:

  1. È difficile automatizzare.
  2. Come riflettere la configurazione in Git? Come rivedere le modifiche che si verificano nel cluster?
  3. Come fornire riproducibilità configurazioni al riavvio?
  4. ...

È chiaro che questo approccio non si adatta bene alla memorizzazione di applicazioni e infrastrutture come codice (IaC; o anche GitOps come opzione più moderna, guadagnando popolarità nell'ecosistema Kubernetes). Pertanto questi comandi non hanno ricevuto ulteriore sviluppo in kubectl.

Operazioni di creazione, acquisizione, sostituzione ed eliminazione

Con primaria la creazione di è semplice: invia il manifest all'operazione create kube api e la risorsa è stata creata. La rappresentazione YAML del manifest può essere archiviata in Git e creata utilizzando il comando kubectl create -f manifest.yaml.

С rimozione anche semplice: sostituire lo stesso manifest.yaml da Git al team kubectl delete -f manifest.yaml.

Operazione replace consente di sostituire completamente la configurazione della risorsa con una nuova, senza ricreare la risorsa. Ciò significa che prima di apportare una modifica a una risorsa, è logico interrogare la versione corrente con l'operazione get, modificarlo e aggiornarlo con l'operazione replace. kube apiserver è integrato chiusura ottimistica e, se dopo l'intervento chirurgico get è cambiato l'oggetto, quindi l'operazione replace non passerà.

Per memorizzare la configurazione in Git e aggiornarla utilizzando replace, è necessario eseguire l'operazione get, unisci la configurazione di Git con ciò che abbiamo ricevuto ed esegui replace. Per impostazione predefinita, kubectl ti consente solo di utilizzare il comando kubectl replace -f manifest.yamlDove manifest.yaml — un manifest già completamente preparato (nel nostro caso, unito) che deve essere installato. Si scopre che l'utente deve implementare i manifest di fusione e questa non è una questione banale...

Vale anche la pena notare che sebbene manifest.yaml ed è memorizzato in Git, non possiamo sapere in anticipo se è necessario creare un oggetto o aggiornarlo: ciò deve essere fatto dal software utente.

Totale: possiamo costruire un'implementazione continua utilizzando solo creazione, sostituzione ed eliminazione, assicurando che la configurazione dell'infrastruttura sia archiviata in Git insieme al codice e al comodo CI/CD?

In linea di principio, possiamo... Per questo dovrai implementare l'operazione di unione manifesti e una sorta di vincolo che:

  • verifica la presenza di un oggetto nel cluster,
  • esegue la creazione iniziale delle risorse,
  • lo aggiorna o lo cancella.

Durante l'aggiornamento, tienilo presente la risorsa potrebbe essere cambiata dallo scorso get e gestire automaticamente il caso di blocco ottimistico: effettuare ripetuti tentativi di aggiornamento.

Tuttavia, perché reinventare la ruota quando kube-apiserver offre un altro modo per aggiornare le risorse: l'operazione patch, che solleva l'utente da alcuni dei problemi descritti?

Toppa

Ora arriviamo alle patch.

Le patch rappresentano il modo principale per applicare modifiche agli oggetti esistenti in Kubernetes. Operazione patch funziona così:

  • l'utente kube-apiserver deve inviare una patch in formato JSON e specificare l'oggetto,
  • e lo stesso apiserver si occuperà dello stato corrente dell'oggetto e lo porterà nella forma richiesta.

In questo caso non è necessario il bloccaggio ottimistico. Questa operazione è più dichiarativa che sostitutiva, anche se a prima vista potrebbe sembrare il contrario.

Così:

  • utilizzando un'operazione create creiamo un oggetto secondo il manifest di Git,
  • via delete — eliminare se l'oggetto non è più necessario,
  • via patch — cambiamo l'oggetto, portandolo nella forma descritta in Git.

Tuttavia, per fare ciò, è necessario creare toppa corretta!

Come funzionano le patch in Helm 2: unione a 2 vie

Quando installi una versione per la prima volta, Helm esegue l'operazione create per le risorse cartografiche.

Quando si aggiorna una versione di Helm per ciascuna risorsa:

  • considera la patch tra la versione della risorsa del grafico precedente e la versione del grafico corrente,
  • applica questa patch.

Chiameremo questa patch Patch di unione a 2 vie, perché nella sua creazione sono coinvolti 2 manifesti:

  • manifest delle risorse della versione precedente,
  • manifesto della risorsa dalla risorsa corrente.

Quando si rimuove l'operazione delete in kube apiserver viene chiamato per le risorse dichiarate nella versione precedente, ma non dichiarate in quella attuale.

L'approccio bidirezionale delle patch di unione ha un problema: porta a non sincronizzato con lo stato reale della risorsa nel cluster e con il manifest in Git.

Illustrazione del problema con un esempio

  • In Git, un grafico memorizza un manifest in cui il campo image La distribuzione è importante ubuntu:18.04.
  • Utente tramite kubectl edit ha cambiato il valore di questo campo in ubuntu:19.04.
  • Quando si ridistribuisce il grafico Helm non genera una patch, perché il campo image nella versione precedente del comunicato e nel grafico attuale sono gli stessi.
  • Dopo la ridistribuzione image resti ubuntu:19.04, anche se il grafico dice ubuntu:18.04.

Abbiamo ottenuto la desincronizzazione e perso la dichiaratività.

Cos'è una risorsa sincronizzata?

In generale, pieno È impossibile ottenere una corrispondenza tra il manifest della risorsa in un cluster in esecuzione e il manifest di Git. Perché in un manifest reale potrebbero essere presenti annotazioni/etichette di servizio, contenitori aggiuntivi e altri dati che vengono aggiunti e rimossi dinamicamente dalla risorsa da alcuni controller. Non possiamo e non vogliamo conservare questi dati in Git. Tuttavia, vogliamo che i campi che abbiamo esplicitamente specificato in Git assumano i valori appropriati al momento del rollout.

Risulta così generale regola delle risorse sincronizzate: quando si distribuisce una risorsa, è possibile modificare o eliminare solo i campi esplicitamente specificati nel manifest di Git (o specificati in una versione precedente e ora eliminati).

Patch di unione a 3 vie

idea centrale Patch di unione a 3 vie: genera una patch tra l'ultima versione applicata del manifest da Git e la versione di destinazione del manifest da Git, tenendo conto della versione corrente del manifest dal cluster in esecuzione. La patch risultante deve essere conforme alla regola delle risorse sincronizzate:

  • i nuovi campi aggiunti alla versione di destinazione vengono aggiunti utilizzando una patch;
  • i campi precedentemente esistenti nell'ultima versione applicata e non esistenti nella versione di destinazione vengono ripristinati tramite una patch;
  • i campi nella versione corrente dell'oggetto che differiscono dalla versione di destinazione del manifest vengono aggiornati utilizzando la patch.

È su questo principio che genera le patch kubectl apply:

  • l'ultima versione applicata del manifest è memorizzata nell'annotazione dell'oggetto stesso,
  • target - preso dal file YAML specificato,
  • quello attuale proviene da un cluster in esecuzione.

Ora che abbiamo chiarito la teoria, è tempo di raccontarvi cosa abbiamo fatto in werf.

Applicazione delle modifiche al werf

In precedenza, werf, come Helm 2, utilizzava patch di unione a 2 vie.

Riparazione patch

Per passare a un nuovo tipo di patch, l'unione a 3 vie, come primo passo abbiamo introdotto il cosiddetto toppe di riparazione.

Durante la distribuzione, viene utilizzata una patch standard di unione a 2 vie, ma werf genera inoltre una patch che sincronizzerà lo stato reale della risorsa con ciò che è scritto in Git (tale patch viene creata utilizzando la stessa regola di sincronizzazione delle risorse descritta sopra) .

Se si verifica una desincronizzazione, al termine del deploy l'utente riceve un AVVISO con un messaggio corrispondente e una patch che deve essere applicata per portare la risorsa in una forma sincronizzata. Questa patch è anche registrata in un'annotazione speciale werf.io/repair-patch. Si presuppone che le mani dell'utente se stesso applicherà questa patch: werf non la applicherà affatto.

La generazione di patch di riparazione è una misura temporanea che consente di testare effettivamente la creazione di patch in base al principio di unione a 3 vie, ma non di applicare automaticamente tali patch. Al momento, questa modalità operativa è abilitata per impostazione predefinita.

Patch di unione a 3 vie solo per le nuove versioni

A partire dal 1 dicembre 2019 iniziano le versioni beta e alpha di werf per impostazione predefinita utilizzare patch di unione a 3 vie complete per applicare le modifiche solo alle nuove versioni di Helm implementate tramite werf. Le versioni esistenti continueranno a utilizzare l'approccio di unione a 2 vie + patch di riparazione.

Questa modalità operativa può essere abilitata esplicitamente tramite l'impostazione WERF_THREE_WAY_MERGE_MODE=onlyNewReleases ora.

Nota: la funzionalità è apparsa in werf in diverse versioni: nel canale alpha è diventata pronta con la versione v1.0.5-alfa.19e nel canale beta - con v1.0.4-beta.20.

Patch di unione a 3 vie per tutte le versioni

A partire dal 15 dicembre 2019, le versioni beta e alpha di werf iniziano a utilizzare per impostazione predefinita le patch complete di unione a 3 vie per applicare le modifiche a tutte le versioni.

Questa modalità operativa può essere abilitata esplicitamente tramite l'impostazione WERF_THREE_WAY_MERGE_MODE=enabled ora.

Cosa fare con la scalabilità automatica delle risorse?

Esistono 2 tipi di scalabilità automatica in Kubernetes: HPA (orizzontale) e VPA (verticale).

Orizzontale seleziona automaticamente il numero di repliche, verticale: il numero di risorse. Sia il numero di repliche che i requisiti delle risorse sono specificati nel manifesto della risorsa (vedi Manifesto della risorsa). spec.replicas o spec.containers[].resources.limits.cpu, spec.containers[].resources.limits.memory и altrui).

Problema: se un utente configura una risorsa in un grafico in modo che specifichi determinati valori per le risorse o per questa risorsa siano abilitate repliche e scalatori automatici, ad ogni distribuzione werf reimposterà questi valori su quanto scritto nel manifest del grafico .

Ci sono due soluzioni al problema. Per cominciare, è meglio evitare di specificare esplicitamente valori scalati automaticamente nel manifest del grafico. Se questa opzione non è adatta per qualche motivo (ad esempio perché è conveniente impostare i limiti iniziali delle risorse e il numero di repliche nel grafico), allora werf offre le seguenti annotazioni:

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

Se tale annotazione è presente, werf non reimposterà i valori corrispondenti ad ogni distribuzione, ma li imposterà solo al momento della creazione iniziale della risorsa.

Per maggiori dettagli consultare la documentazione del progetto HPA и VPA.

Proibire l'uso della patch di unione a 3 vie

L'utente può attualmente vietare l'uso di nuove patch in werf utilizzando una variabile d'ambiente WERF_THREE_WAY_MERGE_MODE=disabled. Tuttavia, a partire Dal 1 marzo 2020 tale divieto non sarà più applicabile. e sarà possibile utilizzare solo patch di unione a 3 vie.

Adozione delle risorse in werf

Padroneggiare il metodo di applicazione delle modifiche con le patch di unione a 3 vie ci ha permesso di implementare immediatamente una funzionalità come l'adozione delle risorse esistenti nel cluster nella versione Helm.

Il timone 2 ha un problema: non è possibile aggiungere una risorsa ai manifest del grafico che già esiste nel cluster senza ricreare questa risorsa da zero (vedi. #6031, #3275). Abbiamo insegnato a Werf ad accettare le risorse esistenti per il rilascio. Per fare ciò, è necessario installare un'annotazione sulla versione corrente della risorsa dal cluster in esecuzione (ad esempio, using kubectl edit):

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

Ora la risorsa deve essere descritta nella tabella e la prossima volta che werf distribuirà una versione con il nome appropriato, la risorsa esistente verrà accettata in questa versione e rimarrà sotto il suo controllo. Inoltre, nel processo di accettazione di una risorsa per il rilascio, werf porterà lo stato corrente della risorsa dal cluster in esecuzione allo stato descritto nel grafico, utilizzando le stesse patch di unione a 3 vie e la regola delle risorse sincronizzate.

Nota: ambientazione WERF_THREE_WAY_MERGE_MODE non influisce sull'adozione delle risorse: in caso di adozione viene sempre utilizzata una patch di unione a 3 vie.

Dettagli - dentro documentazione.

Conclusioni e progetti futuri

Spero che dopo questo articolo sia diventato più chiaro cosa sono le patch di unione a 3 vie e perché sono arrivate ad esse. Dal punto di vista pratico dello sviluppo del progetto werf, la loro implementazione è stata un ulteriore passo avanti verso il miglioramento dell’implementazione tipo Helm. Ora puoi dimenticare i problemi con la sincronizzazione della configurazione, che spesso si verificavano durante l'utilizzo di Helm 2. Allo stesso tempo, nella versione Helm è stata aggiunta una nuova utile funzionalità di adozione delle risorse Kubernetes già scaricate.

Ci sono ancora alcuni problemi e sfide con le distribuzioni simili a Helm, come l'uso dei modelli Go, che continueremo ad affrontare.

È inoltre possibile trovare informazioni sui metodi di aggiornamento e adozione delle risorse all'indirizzo questa pagina di documentazione.

Timone 3

Degno di nota speciale rilasciato proprio l'altro giorno è uscita una nuova versione principale di Helm - v3 - che utilizza anche patch di unione a 3 vie e si sbarazza di Tiller. La nuova versione di Helm richiede migrazione installazioni esistenti per convertirle nel nuovo formato di archiviazione della versione.

Werf, da parte sua, ha attualmente eliminato Tiller, è passato alla fusione a 3 vie e ha aggiunto molto di piu, pur rimanendo compatibile con le installazioni Helm 2 esistenti (non è necessario eseguire script di migrazione). Pertanto, finché werf non passa a Helm 3, gli utenti werf non perdono i principali vantaggi di Helm 3 rispetto a Helm 2 (anche werf li ha).

Tuttavia, il passaggio da werf al codice base di Helm 3 è inevitabile e avverrà nel prossimo futuro. Presumibilmente questo sarà werf 1.1 o werf 1.2 (al momento, la versione principale di werf è 1.0; per maggiori informazioni sul dispositivo di controllo delle versioni werf, vedere qui). Durante questo periodo, Helm 3 avrà il tempo di stabilizzarsi.

PS

Leggi anche sul nostro blog:

Fonte: habr.com

Aggiungi un commento