Raggiungere la Zero downtime Deployment

40

Sto cercando di ottenere zero implementazioni di downtime in modo da poter distribuire meno durante le ore di pausa e di più durante le ore "più lente" - o in qualsiasi momento, in teoria.

La mia configurazione attuale, in qualche modo semplificata:

  • Server Web A (app .NET)
  • Server Web B (app .NET)
  • Server database (SQL Server)

La mia attuale procedura di distribuzione:

  1. "Interrompi" i siti su entrambi i server Web A e B
  2. Aggiorna lo schema del database per la versione dell'app che viene distribuita
  3. Aggiorna server Web A
  4. Aggiorna server Web B
  5. Ripristina tutto online

Problema corrente

Questo porta a una piccola quantità di tempo di inattività ogni mese - circa 30 minuti. Lo faccio durante le ore libere, quindi non è un problema enorme, ma è qualcosa che mi piacerebbe fuggire.

Inoltre, non c'è modo di tornare veramente indietro. Generalmente non eseguo gli script di rollback del DB, ma solo gli script di aggiornamento.

Utilizzo del Load Balancer

Mi piacerebbe poter aggiornare un server Web alla volta. Prendi il server Web A fuori dal servizio di bilanciamento del carico, aggiornalo, rimettilo in linea, quindi ripetilo per Web Server B.

Il problema è il database. Ogni versione del mio software dovrà essere eseguita su una versione diversa del database, quindi sono un po '"bloccato".

Soluzione possibile

Una soluzione attuale che sto considerando è l'adozione delle seguenti regole:

  • Non eliminare mai una tabella di database.
  • Non eliminare mai una colonna del database.
  • Non rinominare mai una colonna del database.
  • Non riordina mai una colonna.
  • Ogni stored procedure deve essere versionata.
    • Significato - "spFindAllThings" diventerà "spFindAllThings_2" quando viene modificato.
    • Quindi diventa "spFindAllThings_3" quando viene modificato di nuovo.
    • La stessa regola si applica alle viste.

Anche se sembra un po 'estremo - penso che risolva il problema. Ogni versione dell'applicazione colpirà il DB in modo non distruttivo. Il codice si aspetta determinati risultati dalle viste / stored procedure - e questo mantiene valido quel 'contratto'. Il problema è che si infiltra solo in modo approssimativo. So che posso ripulire le vecchie stored procedure dopo che l'app è stata distribuita per un po ', ma sembra solo sporca. Inoltre, dipende da tutti gli sviluppatori che seguono questa regola, che avverrà principalmente, ma immagino che qualcuno commetta un errore.

Infine - La mia domanda

  • Questo è sciatto o hacky?
  • Qualcun altro lo fa in questo modo?
  • In che modo altre persone risolvono questo problema?
posta MattW 24.06.2013 - 14:46
fonte

4 risposte

14

Questo è un approccio molto pragmatico agli aggiornamenti del software supportati da database. È stato descritto da Martin Fowler e Pramod Sadalage nel 2003 e successivamente redatto in Refactoring Databases: Evolution Database Design .

Riesco a vedere cosa intendi quando dici che sembra sciatto, ma quando lo fai intenzionalmente e con prudenza, e prenditi il tempo di rifattorizzare le strutture inutilizzate fuori dal database e dal database quando sono dimostrabilmente non più usate, è molto più robusto rispetto a soluzioni più semplici basate su script di upgrade e rollback.

    
risposta data 24.06.2013 - 14:57
fonte
5

"Zero downtime" è solo una delle molte possibili ragioni per questo tipo di approccio. Mantenere un retrocompatibile dei datamodel compatibile in questo modo ti aiuta a gestire molti problemi diversi:

  • se hai un sacco di pacchetti software che accedono al tuo database, non dovrai controllarli tutti se un cambiamento dello schema li riguarda (in organizzazioni più grandi con più team che scrivono programmi che accedono allo stesso database, modifiche dello schema può diventare molto difficile)

  • se è necessario, puoi provare una versione precedente di uno dei tuoi programmi e probabilmente eseguirà di nuovo un database più recente (a patto che non ti aspetti che il vecchio programma gestisca correttamente le colonne più recenti) )

  • l'importazione / esportazione dei dati archiviati nella versione attuale del database è molto più semplice

Ecco una regola aggiuntiva per il tuo elenco

  • ogni nuova colonna deve essere NULLable o fornire un valore predefinito significativo

(questo assicura che anche i vecchi programmi che non conoscono le nuove colonne non si romperanno nulla quando creano nuovi record nel database).

Ovviamente, questo approccio ha un vero svantaggio: la qualità della vostra datamodel potrebbe peggiorare nel tempo. E se hai il controllo completo su tutte le applicazioni che accedono al tuo database, e puoi rifattorizzare facilmente tutte quelle applicazioni quando, ad esempio, stai per rinominare una colonna, allora potresti considerare di refactoring le cose in un modo più pulito.

    
risposta data 24.06.2013 - 15:21
fonte
3

È un tipo variabile da una distribuzione all'altra.

Certo, non potresti mai cancellare una tabella o una colonna. Non potresti mai cambiare nulla che rompesse la compatibilità dell'interfaccia. Puoi sempre aggiungere uno strato di astrazione. Ma poi devi fare la versione dell'astrazione e la versione del controllo delle versioni.

La domanda che devi porci è che ogni singola versione modifica lo schema in modo tale che non sia retrocompatibile?

Se pochissime versioni cambiano lo schema in quel modo, il problema del database è muto. Fai una semplice distribuzione dei server delle applicazioni.

Le due cose che ho visto aiutano di più con la minima implementazione del downtime sono:

  1. Impegnarsi per la compatibilità con le versioni precedenti, almeno in una singola versione. Non lo realizzerai sempre, ma posso scommettere che puoi ottenerlo con il 90% o più delle tue versioni, specialmente se ogni versione è piccola.
  2. Avere uno script di database pre-rilascio e post-rilascio. Ciò consente di gestire i nomi e le modifiche dell'interfaccia creando il nuovo oggetto prima che il codice dell'app venga distribuito, quindi rilasciando quello vecchio dopo che il codice dell'app è stato distribuito. Se si aggiunge una nuova colonna non annullabile, è possibile aggiungerla come annullabile nello script di pre-rilascio con un trigger che riempie un valore predefinito. Quindi nel tuo post-rilascio, puoi rilasciare il trigger.

Si spera che il resto dei tuoi deploys possa essere salvato per le finestre di manutenzione.

Altre idee che potrebbero aiutare a gestire i pochi spiegamenti che richiedono tempi di inattività:

  • Puoi creare compatibilità con le versioni precedenti nel tuo codice? Ad esempio, esiste un modo in cui il codice può supportare più tipi di set di risultati? Se hai bisogno di cambiare una colonna da una int a una doppia, il tuo codice app potrebbe leggerlo come stringa e analizzarlo. Tipo di hacky, ma se è un codice temporaneo per ottenere te stesso attraverso il processo di rilascio, potrebbe non essere la fine del mondo.
  • Le stored procedure possono aiutare a isolare il codice dell'app dalle modifiche dello schema. Questo non può che andare così lontano, ma aiuta un po '.
risposta data 25.06.2013 - 03:58
fonte
2

Potresti potenzialmente farlo in questo modo per un po 'di sforzo in più.

  1. Esegui il backup del database eseguendo un'esportazione
  2. Importa il backup ma rinominalo con una versione di rilascio, ad es. myDb_2_1
  3. Esegui la versione del database su myDB_2_1
  4. "Interrompi" il pool di app sul server Web A o estrailo dal servizio di bilanciamento del carico
  5. Aggiorna server Web A, esegui test di implementazione post e esegui il rollback se necessario
  6. La sessione ha interrotto il server Web B e ha inserito il server Web in loop
  7. Aggiorna il server Web B e quindi ripristina il bilanciamento del carico

Ovviamente gli aggiornamenti web richiederebbero nuove voci di configurazione per puntare al nuovo schema Db. Il fatto è che se si eseguono rilasci una volta al mese ed è una piccola squadra quante modifiche al DB si stanno facendo davvero che non sono retrocompatibili? Se riesci a controllarlo testando, potresti ottenere una distribuzione automatizzata senza tempi morti o, nel peggiore dei casi, solo 5 minuti in meno.

    
risposta data 25.06.2013 - 00:39
fonte

Leggi altre domande sui tag