Come distribuire un'applicazione ASP.NET senza tempi di inattività


127

Per distribuire una nuova versione del nostro sito Web, facciamo quanto segue:

  1. Comprimi il nuovo codice e caricalo sul server.
  2. Sul server live, eliminare tutto il codice live dalla directory del sito Web IIS.
  3. Estrarre il nuovo codice zipfile nella directory IIS ora vuota

Questo processo è tutto scritto e avviene abbastanza rapidamente, ma possono esserci ancora 10-20 secondi di inattività quando i vecchi file vengono eliminati e i nuovi file vengono distribuiti.

Qualche suggerimento su un metodo di downtime di 0 secondi?


Questo non dovrebbe essere su ServerFault?
Daniel Rodriguez,

49
Forse, ma ServerFault non esisteva nel settembre '08
Karl Glennon,

3
IIS può puntare a una cartella symlink? La modifica del collegamento simbolico provoca il riciclo del processo IIS?
Neil McGuigan

qualche soluzione finale con esempio di script di codice sorgente completo?
Kiquenet,

Non è possibile avere più pool di app e passare il traffico da un pool di app a un altro?
Luca,

Risposte:


79

Sono necessari 2 server e un bilanciamento del carico. Ecco i passaggi:

  1. Attiva tutto il traffico sul Server 2
  2. Distribuire sul server 1
  3. Test server 1
  4. Attiva tutto il traffico sul Server 1
  5. Distribuire sul server 2
  6. Test server 2
  7. Trasforma il traffico su entrambi i server

Il fatto è che, anche in questo caso, avrai comunque il riavvio dell'applicazione e la perdita di sessioni se stai usando "sessioni appiccicose". Se hai sessioni di database o un server di stato, allora tutto dovrebbe andare bene.


4
È inoltre possibile configurare il bilanciamento del carico in modo che serva le sessioni esistenti per un determinato server, ma non ne accetti di nuove. Ciò ti consente di evitare di abbandonare le sessioni. Tuttavia, questa tecnica richiede l'attesa della fine delle sessioni e, in generale, ti consigliamo di copiarlo.

35
Questo metodo tende a cadere quando il rotolo di codice presenta modifiche strutturali al database. Dopo aver aggiornato il DB per Server 1, esploderà il server 2. Ora è possibile eseguire il backup / ripristino del database per i test sul server 1, ma poi si ha il problema di ordinare i dati che sono stati modificati nel database live mentre era in esecuzione la copia parallela.
EBarr,

1
@AndreiRinea - come pensi che funzionerebbe in un sistema OLTP ad alto volume? Il sistema non è sincronizzato e perdi i dati quando lo tagli, oppure devi mettere in pausa l'inserimento dei dati e scrivere uno script per identificare e migrare i dati transitori nella nuova struttura DB.
EBarr

9
@EBarr: e in ogni caso tecnicamente hai ancora zero tempi di inattività sull'app ASP.NET - la domanda non è "come distribuire in un server sql db senza tempi di inattività".
Sklivvz,

6
La loro chiave è quella di sviluppare in modo che i cambiamenti sql non siano distruttivi. Una volta che non viene più utilizzata, è spesso necessario apportare modifiche sql distruttive nella seguente versione. Non è difficile avere a che fare con la pratica.
Bealer,

60

Lo Strumento di distribuzione Web Microsoft supporta questa in una certa misura:

Abilita il supporto del file system transazionale di Windows (TxF). Quando il supporto TxF è abilitato, le operazioni sui file sono atomiche; cioè, hanno successo o falliscono completamente. Ciò garantisce l'integrità dei dati e impedisce che dati o file esistano in uno stato "a metà strada" o danneggiato. In MS Deploy, TxF è disabilitato per impostazione predefinita.

Sembra che la transazione sia per l'intera sincronizzazione. Inoltre, TxF è una funzionalità di Windows Server 2008, quindi questa funzionalità di transazione non funzionerà con le versioni precedenti.

Credo che sia possibile modificare lo script per 0-downtime usando le cartelle come versioni e la metabase IIS:

  • per un percorso / URL esistente:
  • Copia il nuovo sito Web (o modificato) sul server in
    • \ Web \ app \ v2.1 \
  • Modifica la metabase IIS per cambiare il percorso del sito Web
    • da \ web \ app \ 2.0 \
    • a \ web \ app \ v2.1 \

Questo metodo offre i seguenti vantaggi:

  • Nel caso in cui la nuova versione abbia un problema, è possibile eseguire facilmente il rollback alla v2.0
  • Per distribuire su più server fisici o virtuali, è possibile utilizzare lo script per la distribuzione di file. Una volta che tutti i server hanno la nuova versione, è possibile modificare contemporaneamente tutte le metabase dei server utilizzando lo strumento di distribuzione Web Microsoft.

5
Ho implementato questo approccio adattando i nostri script di distribuzione PowerShell. Puoi vedere la parte dello script che modifica la cartella del sito IIS qui: stackoverflow.com/questions/330608/… Grazie per il puntatore.
Karl Glennon,

17
Sfortunatamente, questo metodo non tiene conto delle modifiche strutturali al DB. Dopo aver aggiornato il DB per v2.1, esplode v.2.0.
EBarr,

8
L'uso di TxF è eccessivo qui, IMO. Non fa male avere contemporaneamente sia la v2.0 che la v2.1 nel filesystem. Il grande cambiamento si verifica quando la v2.1 è online e a quel punto è stata impegnata la transazione TxF. I tempi di inattività pari a zero si verificano davvero a causa del modo in cui IIS passa da un vecchio AppPool a uno nuovo, non a causa di TxF.
RickNZ,

5
Un altro problema con questo è se una grande quantità di dati utente viene archiviata nelle sottocartelle delle cartelle dell'app.
Kenny Evitt,

4
Non si tratta di una distribuzione di 0 secondi perché è necessario avviare la nuova app.
usr

12

È possibile ottenere una distribuzione zero dei tempi di inattività su un singolo server utilizzando il routing delle richieste dell'applicazione in IIS come bilanciamento del carico software tra due siti IIS locali su porte diverse. Questa è nota come strategia di distribuzione verde blu in cui solo uno dei due siti è disponibile nel bilanciamento del carico in un determinato momento. Distribuire sul sito "inattivo", riscaldarlo e portarlo nel bilanciamento del carico (in genere passando un controllo dello stato del routing delle richieste dell'applicazione), quindi portare il sito originale che era attivo, fuori dal "pool" (di nuovo facendo fallire il controllo dello stato).

Un tutorial completo può essere trovato qui.


7

Ci sono passato di recente e la soluzione che mi è venuta in mente è stata quella di creare due siti in IIS e passare da uno all'altro.

Per la mia configurazione, avevo una directory web per ogni sito A e B come questa: c: \ Intranet \ Live A \ Interface c: \ Intranet \ Live B \ Interface

In IIS, ho due siti identici (stesse porte, autenticazione ecc.) Ciascuno con il proprio pool di applicazioni. Uno dei siti è in esecuzione (A) e l'altro viene arrestato (B). quello live ha anche l'intestazione host live.

Quando si tratta di distribuire per vivere, pubblico semplicemente nella posizione del sito STOPPED. Poiché posso accedere al sito B utilizzando la sua porta, posso preriscaldare il sito in modo che il primo utente non causi l'avvio di un'applicazione. Quindi, utilizzando un file batch, copio l'intestazione host live su B, interrompo A e avvio B.


1
Questo aiuta con i tempi di inattività dovuti alla copia dei file, ma ha lo stesso problema di @Sklivvz - non appena il roll-code ha cambiamenti strutturali nel database, il sito diventa boom.
EBarr,

Questo mi è sembrato anche il modo intuitivo, ma perché non esiste un modo semplice e integrato per farlo?
Petrus Theron

3
@Ebarr quindi non implementare modifiche sql distruttive. Ad esempio, se devi rimuovere una colonna, fallo nella prossima versione quando non viene più utilizzata da A o B.
Bealer

@Bealer: d'accordo (con avvertimento). Esistono tutta una serie di domande su "tempi di inattività durante i ruoli di codice". Devo ancora trovarne uno che discuti davvero delle realtà dell'evoluzione di uno schema DB. Avvertenza: ci sono una serie di complicazioni che si presentano con modifiche a due fasi allo schema. Un esempio: molti degli ORM vengono visualizzati se la definizione della tabella differisce dalla definizione come la comprende (colonne nuove o mancanti).
EBarr

2
@Rob come puoi "preriscaldare" il sito se è stato arrestato?
Andrew Gee,

7

Utilizzando la classe ServerManager di Microsoft.Web.Administration è possibile sviluppare il proprio agente di distribuzione.

Il trucco è cambiare PhysicalPath di VirtualDirectory, che si traduce in un passaggio atomico online tra vecchie e nuove app Web.

Tieni presente che ciò può comportare l'esecuzione in parallelo di vecchi e nuovi AppDomain!

Il problema è come sincronizzare le modifiche ai database ecc.

Effettuando il polling per l'esistenza di AppDomain con PhysicalPaths vecchi o nuovi è possibile rilevare quando i vecchi AppDomain sono stati chiusi e se i nuovi AppDomain sono stati avviati.

Per forzare l'avvio di AppDomain è necessario effettuare una richiesta HTTP (IIS 7.5 supporta la funzione di avvio automatico)

Ora hai bisogno di un modo per bloccare le richieste per il nuovo AppDomain. Uso un mutex denominato, creato e di proprietà dell'agente di distribuzione, atteso da Application_Start della nuova app Web e quindi rilasciato dall'agente di distribuzione dopo aver effettuato gli aggiornamenti del database.

(Uso un file marker nell'app Web per abilitare il comportamento di attesa mutex) Una volta che la nuova app Web è in esecuzione, elimino il file marker.


6

OK, dato che tutti stanno sottovalutando la risposta che ho scritto nel lontano 2008 * ...

Ti dirò come lo facciamo ora nel 2014. Non utilizziamo più i siti Web perché stiamo utilizzando ASP.NET MVC ora.

Non abbiamo certamente bisogno di un bilanciamento del carico e di due server per farlo, va bene se hai 3 server per ogni sito web che gestisci, ma è eccessivo per la maggior parte dei siti web.

Inoltre, non facciamo affidamento sull'ultima procedura guidata di Microsoft: troppo lenta, troppa magia nascosta e troppo incline a cambiarne il nome.

Ecco come lo facciamo:

  1. Abbiamo un passaggio post build che copia le DLL generate in una cartella 'bin-pub'.

  2. Usiamo Beyond Compare (che è eccellente **) per verificare e sincronizzare i file modificati (su FTP perché ampiamente supportati) fino al server di produzione

  3. Sul sito Web abbiamo un URL sicuro contenente un pulsante che copia tutto in "bin-pub" su "bin" (eseguendo prima un backup per abilitare il rollback rapido). A questo punto l'app si riavvia da sola. Quindi il nostro ORM verifica se ci sono tabelle o colonne da aggiungere e le crea.

Sono solo millisecondi di downtime. Il riavvio dell'app può richiedere uno o due secondi, ma durante il riavvio le richieste vengono memorizzate nel buffer in modo da ridurre effettivamente i tempi di inattività.

L'intero processo di distribuzione richiede da 5 secondi a 30 minuti, a seconda del numero di file modificati e del numero di modifiche da rivedere.

In questo modo non è necessario copiare un intero sito Web in una directory diversa ma solo la cartella bin. Hai anche il controllo completo sul processo e sai esattamente cosa sta cambiando.

** Facciamo sempre una rapida occhiata alle modifiche che stiamo implementando - come doppio controllo dell'ultimo minuto, quindi sappiamo cosa testare e se qualcosa si rompe siamo pronti. Usiamo Beyond Compare perché ti consente di diffondere facilmente i file tramite FTP. Non lo farei mai senza BC, non hai idea di cosa stai sovrascrivendo.

* Scorri verso il basso per vederlo :( A proposito, non consiglierei più i siti Web perché sono più lenti da costruire e possono arrestarsi in modo anomalo con i file temporanei compilati a metà. Li abbiamo usati in passato perché consentivano più agili file per file distribuzione: molto veloce per risolvere un problema minore e puoi vedere esattamente cosa stai distribuendo (se usi Beyond Compare ovviamente - altrimenti dimenticalo).


Avrai comunque tempi di inattività perché il pool di app ricicla.
testpattern

No, nessun tempo di inattività perché le richieste vengono memorizzate automaticamente da IIS durante il riavvio dell'app
mike nelson

5

Gli unici metodi di downtime zero che riesco a pensare riguardano l'hosting su almeno 2 server.


1

Perfezionerei un po 'la risposta di George, come segue, per un singolo server:

  1. Utilizzare un progetto di distribuzione Web per precompilare il sito in una singola DLL
  2. Comprimi il nuovo sito e caricalo sul server
  3. Decomprimilo in una nuova cartella situata in una cartella con le autorizzazioni giuste per il sito, quindi i file decompressi ereditano correttamente le autorizzazioni (forse e: \ web, con le sottocartelle v20090901, v20090916, ecc.)
  4. Utilizzare Gestione IIS per modificare il nome della cartella contenente il sito
  5. Conservare la vecchia cartella per un po ', in modo da poterla ricorrere in caso di problemi

Il passaggio 4 causerà il riciclo del processo di lavoro IIS.

Questo è solo zero tempi di inattività se non si utilizzano le sessioni InProc; usa invece la modalità SQL se puoi (ancora meglio, evita del tutto lo stato della sessione).

Certo, è un po 'più coinvolto quando ci sono più server e / o modifiche al database ....


1
Stesso problema di @Sklivvz: questo metodo si interrompe non appena il rotolo di codice presenta modifiche strutturali al database.
EBarr,

3
Ecco perché ho detto che era più coinvolto in caso di modifiche al DB ... Il roll-out del codice con modifiche strutturali al DB non è solo un problema di distribuzione; deve esserci anche supporto nel codice e probabilmente anche nel DB.
RickNZ,

1

Per espandere la risposta di sklivvz, che si basava sull'avere un qualche tipo di bilanciamento del carico (o solo una copia in standby sullo stesso server)

  1. Indirizzare tutto il traffico verso il sito / server 2
  2. Facoltativamente, attendere un po 'per assicurarsi che il minor numero possibile di utenti abbia flussi di lavoro in sospeso sulla versione distribuita
  3. Distribuire sul sito / server 1 e riscaldarlo il più possibile
  4. Esegui migrazioni di database in modo transazionale (cerca di renderlo possibile)
  5. Direttamente immediatamente tutto il traffico verso il sito / server 1
  6. Distribuire su sito / server 2
  7. Traffico diretto verso entrambi i siti / server

È possibile introdurre un po 'di test del fumo, creando uno snapshot / copia del database, ma non è sempre possibile.

Se possibile e necessario, utilizzare "differenze di instradamento", come diversi URL degli inquilini: s (customerX.myapp.net) o utenti diversi, da distribuire prima a un gruppo inconsapevole di cavie. Se nulla fallisce, rilascialo a tutti.

Poiché sono coinvolte le migrazioni del database, è spesso impossibile eseguire il rollback a una versione precedente.

Esistono modi per rendere le applicazioni più piacevoli in questi scenari, come l'utilizzo di code di eventi e meccanismi di riproduzione, ma dal momento che stiamo parlando di implementare le modifiche a qualcosa che è in uso, non c'è davvero alcun modo infallibile.


1

Ecco come lo faccio:

Requisiti minimi di sistema assoluti:
1 server con

  • 1 bilanciamento del carico / proxy inverso (ad esempio nginx) in esecuzione sulla porta 80
  • 2 ASP.NET-Core / mono reverse-proxy / fastcgi chroot-jail o docker-container in ascolto su 2 diverse porte TCP
    (o anche solo due applicazioni di proxy inverso su 2 diverse porte TCP senza sandbox)

Flusso di lavoro:

avvia la transazione myupdate

try
    Web-Service: Tell all applications on all web-servers to go into primary read-only mode 
    Application switch to primary read-only mode, and responds 
    Web sockets begin notifying all clients 
    Wait for all applications to respond

    wait (custom short interval)

    Web-Service: Tell all applications on all web-servers to go into secondary read-only mode 
    Application switch to secondary read-only mode (data-entry fuse)
    Updatedb - secondary read-only mode (switches database to read-only)

    Web-Service: Create backup of database 
    Web-Service: Restore backup to new database
    Web-Service: Update new database with new schema 

    Deploy new application to apt-repository 
    (for windows, you will have to write your own custom deployment web-service)
    ssh into every machine in array_of_new_webapps
    run apt-get update
    then either 
    apt-get dist-upgrade
    OR
    apt-get install <packagename>
    OR 
    apt-get install --only-upgrade <packagename>
    depending on what you need
    -- This deploys the new application to all new chroots (or servers/VMs)

    Test: Test new application under test.domain.xxx
    -- everything that fails should throw an exception here
    commit myupdate;

    Web-Service: Tell all applications to send web-socket request to reload the pages to all clients at time x (+/- random number)
    @client: notify of reload and that this causes loss of unsafed data, with option to abort 

    @ time x:  Switch load balancer from array_of_old_webapps to array_of_new_webapps 
    Decomission/Recycle array_of_old_webapps, etc.

catch
        rollback myupdate 
        switch to read-write mode
        Web-Service: Tell all applications to send web-socket request to unblock read-only mode
end try 

-7

Suggerirei di conservare lì i vecchi file e semplicemente di sovrascriverli. In questo modo il tempo di inattività è limitato ai tempi di sovrascrittura a file singolo e manca sempre un solo file alla volta.

Non sono sicuro che questo aiuti in una "applicazione web" (penso che tu stia dicendo che è quello che stai usando), motivo per cui usiamo sempre i "siti web". Anche con la distribuzione di "siti web" la distribuzione non riavvia il sito e elimina tutte le sessioni utente.


Ciao Mike, potresti voler rimuovere questa risposta.
Sohail Ahmed,
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.