Raggiungimento di zero tempi di fermo macchina


40

Sto cercando di ottenere zero tempi di fermo macchina in modo da poterlo distribuire meno durante le ore di riposo 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)
  • Web Server B (app .NET)
  • Server database (SQL Server)

Il mio attuale processo di distribuzione:

  1. "Arresta" i siti su entrambi i server Web A e B.
  2. Aggiorna lo schema del database per la versione dell'app da distribuire
  3. Aggiorna server Web A
  4. Aggiorna server Web B
  5. Riporta tutto online

Problema attuale

Ciò porta a una piccola quantità di inattività ogni mese - circa 30 minuti. Lo faccio durante le ore di riposo, quindi non è un grosso problema, ma è qualcosa da cui vorrei allontanarmi.

Inoltre, non c'è modo di tornare "indietro". In genere non eseguo script di rollback DB, ma solo script di aggiornamento.

Sfruttando il bilanciamento del carico

Mi piacerebbe poter aggiornare un Web Server alla volta. Rimuovere Web Server A dal bilanciamento del carico, aggiornarlo, rimetterlo online, quindi ripetere 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".

Possibile soluzione

Una soluzione attuale che sto prendendo in considerazione è l'adozione delle seguenti regole:

  • Non eliminare mai una tabella del database.
  • Non eliminare mai una colonna del database.
  • Non rinominare mai una colonna del database.
  • Non riordinare mai una colonna.
  • Ogni procedura memorizzata deve essere versionata.
    • Significato: 'spFindAllThings' diventerà 'spFindAllThings_2' quando viene modificato.
    • Quindi diventa 'spFindAllThings_3' quando modificato di nuovo.
    • La stessa regola si applica alle viste.

Anche se questo sembra un po 'estremo - penso che risolva il problema. Ogni versione dell'applicazione colpirà il DB in modo non-break. Il codice prevede alcuni risultati dalle viste / stored procedure - e questo mantiene valido quel "contratto". Il problema è che sembra solo sciatto. So di poter ripulire le vecchie procedure memorizzate dopo che l'app è stata distribuita per un po ', ma mi sembra solo sporca. Inoltre, dipende dal fatto che tutti gli sviluppatori seguano queste regole, il che accadrà principalmente, ma immagino che qualcuno commetterà un errore.

Finalmente - La mia domanda

  • Questo è sciatto o confuso?
  • Qualcun altro lo sta facendo in questo modo?
  • In che modo altre persone risolvono questo problema?

2
Dov'è il tuo piano di backout? Come testate che tutto funzioni e non ci sono regressioni?
Deer Hunter,

3
Non hai bisogno di "mai": devi "solo" assicurarti che ogni due versioni adiacenti possano essere eseguite contemporaneamente. Ciò limita i percorsi di aggiornamento, ma non così gravemente come non riuscire mai a modificare in modo significativo lo schema DB.
Joachim Sauer,

Grazie Joachim ... Mi piace parlare in modo assoluto in modo che l'idea di base sia chiara - ma hai ragione, potremmo avere una politica di compatibilità retroattiva con N release, a quel punto possiamo rimuovere oggetti DB non necessari.
Matt

2
Ti consigliamo di disporre di un piano di rollback. Un giorno ne avrai bisogno.
Thorbjørn Ravn Andersen,

1
Nella mia esperienza, per la maggior parte dei siti Web, la tua possibile soluzione è peggiore del problema che risolve. La complessità che aggiungerà sarà più costosa di quanto tu possa prevedere ora. Forse molte volte più tempo / sforzi per apportare modifiche e aggiungere funzionalità. Mi piacerebbe prendere in considerazione solo per i siti web, che non può assolutamente alcun avere tempi di inattività, mai .
MGOwen

Risposte:


14

Questo è un approccio molto pragmatico agli aggiornamenti software supportati da database. È stato descritto da Martin Fowler e Pramod Sadalage nel 2003 e successivamente scritto in Database di refactoring: Evolutionary Database Design .

Capisco cosa intendi quando dici che sembra sciatto, ma quando fatto intenzionalmente e con lungimiranza, e prendendo il tempo di refactoring delle strutture inutilizzate fuori dalla base di codice e dal database quando non sono più dimostrabili, è molto più robusto di soluzioni più semplici basate su script di aggiornamento e rollback.


5

"Zero downtime" è solo uno dei tanti motivi possibili per questo tipo di approccio. Mantenere un modello di dati compatibile all'indietro in questo modo consente di affrontare molti problemi diversi:

  • se hai molti pacchetti software che accedono al tuo database, non dovrai controllarli tutti se una modifica dello schema li influenza (nelle organizzazioni più grandi con più team che scrivono programmi che accedono tutti allo stesso database, le modifiche allo schema potrebbero diventare molto difficili)

  • se è necessario, è possibile controllare una versione precedente di uno dei programmi e molto probabilmente eseguirà nuovamente un database più recente (purché non ci si aspetti che il vecchio programma gestisca correttamente le colonne più recenti)

  • l'importazione / esportazione dei dati archiviati nella versione corrente 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 programmi più vecchi che non conoscono le nuove colonne non rompano nulla quando creano nuovi record nel database).

Ovviamente, questo approccio ha un vero svantaggio: la qualità del modello di dati potrebbe deteriorarsi nel tempo. E se hai il controllo completo su tutte le applicazioni che accedono al tuo database e puoi refactificare facilmente tutte quelle applicazioni quando, ad esempio, rinominerai una colonna, allora potresti considerare di riformattare le cose in modo più pulito.


4

Si varia da una distribuzione all'altra.

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

La domanda che devi porsi è: ogni singola versione modifica lo schema in modo tale che non sia compatibile con le versioni precedenti?

Se pochissime versioni cambiano lo schema in quel modo, il problema con il database è muto. Basta eseguire una distribuzione continua dei server applicazioni.

Le due cose che ho visto sono di grande aiuto nella distribuzione con tempi di inattività minimi:

  1. Cerca la retrocompatibilità, almeno all'interno di una singola versione. Non lo raggiungerai sempre, ma posso scommettere che puoi ottenerlo sul 90% o più delle tue versioni, specialmente se ogni versione è piccola.
  2. Avere uno script di database pre-release e post-release. Ciò ti consente di gestire le modifiche di nomi o interfacce creando il tuo nuovo oggetto prima della distribuzione del codice dell'app, quindi rilasciando quello vecchio dopo la distribuzione del codice dell'app. Se aggiungi una nuova colonna non annullabile, puoi aggiungerla come nullable nello script pre-release con un trigger che riempie un valore predefinito. Quindi, nel tuo post-rilascio, puoi rilasciare il grilletto.

Spero che il resto dei tuoi schieramenti possa essere salvato per finestre di manutenzione.

Altre idee che possono aiutare a gestire le poche distribuzioni che richiedono tempi di inattività:

  • Puoi integrare la retrocompatibilità nel tuo codice? Ad esempio, esiste un modo in cui il codice può supportare più tipi di set di risultati? Se devi modificare una colonna da un int a un doppio, il codice dell'app potrebbe leggerlo come una stringa e analizzarlo. Tipo di hacky, ma se è un codice temporaneo a superare 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 può andare solo così lontano, ma aiuta un po '.

2

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

  1. Eseguire il backup del database eseguendo un'esportazione
  2. Importa il backup ma rinominalo con una versione di rilascio, ad esempio myDb_2_1
  3. Eseguire la versione del database su myDB_2_1
  4. "Arresta" il pool di app sul server Web A o estrailo dal bilanciamento del carico
  5. Aggiorna il server Web A, esegui i test post implementazione e, se necessario, esegui il rollback
  6. La sessione scarica Web Server B e rimette in loop il Web Server A
  7. Aggiornare il server Web B e quindi rimetterlo nel bilanciamento del carico

Naturalmente gli aggiornamenti web avrebbero bisogno di nuove voci di configurazione per puntare al nuovo schema Db. Il fatto è che stai rilasciando una volta al mese ed è un piccolo team quante modifiche al DB stai davvero facendo che non sono compatibili con le versioni precedenti? Se riesci a controllarlo testandolo, potresti ottenere una distribuzione automatica senza tempi di inattività o, nel peggiore dei casi, solo 5 minuti di inattività.


1
Cosa succede se (quando) l'app sul server A scrive nel suo database dopo aver archiviato il backup, ma prima di interrompere il server A? C'è sempre una "finestra di vulnerabilità". Queste scritture andranno perse, il che potrebbe non essere accettabile.
sleske,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.