Zero downtime deployment con Magento 2
Lettura 11 minutiSotto la categoria zero downtime deployment ricadono quei processi che consentono di eliminare i tempi di disservizio durante il rilascio di una nuova versione di codice.
Uno dei processi più conosciuti è noto con il nome di blue/green deployment.
Una introduzione all’argomento si può trovare in un popolare articolo di Martin Fowler mentre per quel che riguarda Magento consigliamo la lettura di un recente articolo di Fabrizio Branca.
Nel nostro articolo descriviamo un processo di zero downtime deployment con Magento 2. Per la verità non sempre si riesce ad eliminare il tempo di downtime, ci sono situazioni in cui è necessario attivare il maintenance mode e vedremo quali sono.
Prima di iniziare la lettura, consigliamo di fare riferimento al nostro precedente articolo relativo agli application mode perché di seguito li citeremo dandone per scontato la conoscenza.
I passaggi operativi
Il deploy di un progetto Magento 2 è decisamente più complesso rispetto a Magento 1.
Il motivo è dato dal fatto che, per ottimizzare le prestazioni in production mode, Magento 2 ha introdotto una serie di passaggi operativi da eseguire nel giusto ordine.
A complicare ulteriormente le cose si aggiunge qualche pecca di gioventù che siamo sicuri sarà risolta in futuro.
I passaggi operativi riassunti nell’elenco seguente sono suddivisi in due fasi principali:
Fase di build (creazione di un artefact)
- Download del codice
- Download delle dipendenze
- Installazione di Magento 2 (opzionale)
- Impostazione del production mode
- Deploy dei contenuti statici (opzionale)
- Compilazione (opzionale)
- Dump dell’autoloader
- Passaggi conclusivi
Fase di release
- Caricamento dell’artefact su server di produzione
- Impostazione permessi su file e directory
- Attivazione modalità di manutenzione (opzionale)
- Abilitazione moduli (opzionale)
- Avvio script di aggiornamento (opzionale)
- Flush della cache
- Passaggi conclusivi
Separare la fase di build da quella di deploy non è sicuramente l’unico approccio possibile ma, considerando quanto può durare la fase di preparazione dei contenuti statici e della compilazione di codice, è quello che preferiamo utilizzare e che ci sentiamo di raccomandare.
Di seguito analizziamo con maggior dettaglio i singoli passaggi.
Fase di build
Facciamo alcune assunzioni di base:
- Il codice risiede su un sistema di controllo di versione che consente di scaricare una specifica versione sotto forma di archivio compresso.
- Il progetto utilizza Composer per la gestione delle dipendenze; anche i nostri file di progetto (tema, moduli, personalizzazioni varie) sono package esterni aggiunti come dipendenza al progetto principale.
- L’ambiente sul quale avviene questa fase ha accesso in download al sistema di controllo di versione e in *upload*all’ambiente di produzione.
Ci riferiamo alla cartella radice in cui è installato Magento con <magedir>
; è la cartella dove si trovano il file composer.json
e la sottocartella bin/
.
Se non è specificato diversamente, tutti i comandi di shell si intendono lanciati da questa cartella.
DOWNLOAD DEL CODICE
Questo passaggio prevede di scaricare dal sistema di versioning parte del codice che pubblicheremo in produzione.
Parliamo di “parte del codice” perché, come vedremo, alcune classi saranno generate successivamente e non sono sotto controllo di versione.
Scarichiamo dunque il pacchetto e lo scompattiamo in <magedir>
.
D’ora in poi chiamiamo la versione del codice scaricata nuova release, per differenziarla dalla release corrente che invece rappresenta la versione del codice attualmente in produzione.
La versione da pubblicare può corrispondere al master branch del repository, a un altro branch, a un tag o a una specifica commit.
DOWNLOAD DELLE DIPENDENZE
Una volta scaricata la versione del codice che dobbiamo pubblicare in <magedir>
, ci posizioniamo nella cartella e scarichiamo le dipendenza con Composer:
$ composer install
Ci sono un paio di parametri che si possono passare al composer install su cui vale la pena soffermarsi:
--optimize-autoloader
- solitamente è consigliato in ambiente di produzione dove il codice dovrebbe essere immutabile; genera un mapping statico di classi sotto forma di array; l’array è utilizzato dall’autoloader al posto della ricerca dinamica su file system. In un progetto Magento 2 che prevede l’uso del production mode, la generazione dell’autoloader deve necessariamente essere eseguita dopo la fase di compilazione.
Occorre però prestare attenzione al fatto che l’elevato numero di package di cui si compone Magento 2 può generare array con molti elementi. Sappiamo quanto la gestione degli array in PHP sia esosa di risorse pertanto, non avendo fatto un benchmark, non possiamo affermare con certezza che ci sia un reale beneficio nell’utilizzarlo. Utilizzando invece Composer su Magento 1, l’array dovrebbe essere più limitato. In questo caso i benefici dovrebbero essere garantiti.--no-dev
- anche questo è un parametro consigliato in ambiente di produzione e infatti se il progetto è su Magento 1 consigliamo di utilizzarlo; su Magento 2 c’è da fare attenzione: il packagesjparkinson/static-review
, richiesto dal comando di compilazione, è inserito tra le dipendenze di sviluppo fino alla versione 2.0; quindi se si utilizza Magento 2.0 occorre ricordarsi di spostare quella dipendenza darequire-dev
arequire
per evitare un errore in fase di compilazione.
INSTALLAZIONE DI MAGENTO 2
Questa fase si rende necessaria solo se la fase di build avviene in un ambiente che, per scelta o necessità, non accede al database di produzione.
In questo caso si parla di build in condizioni di isolamento.
Per installare Magento 2, di cui al precedente passaggio abbiamo già scaricato il codice sorgente, occorre aver creato un database dedicato e lanciare il comando seguente:
$ bin/magento setup:install --base-url=http://$SITE_URL/ --db-host=$DB_HOSTNAME
--db-name=$DB_NAME --db-user=$DB_USERNAME --db-password=$DB_PASSWORD
--admin-firstname=Admin --admin-lastname=Admin --admin-email=devel@bitbull.it
--admin-user=admin --admin-password=admin123 --language=it_IT --currency=EUR
--timezone=Europe/Rome --use-rewrites=1 --session-save=db
Le variabili che iniziano con il simbolo $
devono essere impostate da un processo esterno come variabili d’ambiente o passate come parametro al nostro eventuale script.
PRODUCTION MODE
Arrivati a questo punto è possibile impostare Magento in production mode, utilizzando il comando seguente:
$ bin/magento deploy:mode:set production
Nel caso la fase di build venga eseguita in isolamento, quindi con un database vuoto non allineato a quello di produzione, occorre utilizzare il parametro --skip-compilation
che evita che Magento lanci automaticamente il deploy dei contenuti statici e la compilazione.
Un database vuoto infatti non avrà le eventuali localizzazioni aggiuntive presenti in quello di produzione e il deploy dei contenuti statici si limiterebbe a generare i contenuti per il locale di default. In questa circostanza, dunque, occorre lanciare il deploy dei contenuti statici (e la compilazione) a parte.
DEPLOY DEI CONTENUTI STATICI
Se si esegue la fase di build in isolamento e si è impostato il production mode utilizzando il parametro --skip-compilation
, occorrerà avviare manualmente il deploy dei contenuti statici.
Il deploy dei contenuti statici si effettua con il comando seguente, passando ogni localizzazione necessaria; supponiamo it_IT
e en_US
:
`
$ bin/magento setup:static-content:deploy “it_IT” “en_US”
Questa fase può durare anche decine di minuti, che rappresentano un potenziale downtime, ed è uno dei motivi per cui si sceglie di effettuare la fase di build in un ambiente che non sia quello di produzione.
COMPILAZIONE
Se si esegue la fase di build in isolamento e si è impostato il production mode utilizzando il parametro --skip-compilation
, occorrerà avviare manualmente la compilazione.
La compilazione in Magento 2 è quel processo di ottimizzazione che genera le classi dinamiche che in developer mode sono invece generate a runtime; per un approfondimento rimandiamo sia alla documentazione ufficiale di Magento 2.0 sia alla documentazione ufficiale di Magento 2.1 che presentano alcune differenze.
La compilazione può essere avviata con il comando seguente se il progetto è basato su Magento 2.0:
$ bin/magento setup:di:compile-multi-tenant
La compilazione multi-tenant è riservata a quelle situazioni in cui Magento è utilizzato con più di un website e più di uno store ma a causa di un bug (documentato qui) è necessario utilizzarla anche nel caso single-tenant, cioè se si ha un solo website e un solo store.
In caso di progetti Magento 2.1 invece la compilazione deve essere lanciata con il comando seguente:
$ bin/magento setup:di:compile
In questa versione infatti la compilazione multi-tenant non è ancora supportata.
DUMP DELL’AUTOLOADER
Come abbiamo accennato nel paragrafo “Download delle dipendenze”, è solo dopo aver generato tutte le classi necessarie all’applicazione che possiamo preoccuparci di effettuare il dump dell’autoloader.
Il comando da eseguire è il seguente:
$ composer dump-autoload --optimize
Il parametro --optimize
consente di generare il mapping statico delle classi; in questo caso dunque valgono le considerazioni sulle performance fatte nel paragrafo “Download delle dipendenze”.
PASSAGGI CONCLUSIVI
A causa di un bug (documentato qui), al termine del processo di compilazione è necessario rimuovere il file seguente:
var/di/relations.ser
Al termine creiamo un archivio compresso che sarà quello di cui faremo upload sul server di produzione nella successiva fase di deploy.
L’insieme dei file che compongono l’archivio prende il nome di artefact.
Fase di rilascio
Facciamo alcune assunzioni di base:
- La cartella della release corrente è un link simbolico che viene modificato per operare un cambio versione, come vedremo più avanti. Di seguito ci riferiamo a tale cartella con
current
. - Allo stesso livello di
current
avremo la cartellarelease
che contiene le sotto-cartelle con gli archivi corrispondenti alle diverse versioni. Se la versione corrente è ad esempio inrelease/20160810-1634
il link simbolicocurrent
punterà a quella cartella. - La documet root dello store punta alla sotto-cartella
current/pub
. - Le cartelle con contenuti invarianti rispetto al cambio release sono esterne alla cartella della release corrente e vengono puntate attraverso link simbolici.
Cartelle di questo tipo sono ad esempio lavar/log
e lapub/media
.
Ipotizziamo di avere allo stesso livello dicurrent
erelease
la cartellastorage
: sotto di essa la cartellamedia
sarà puntata dacurrent/pub/media
e la cartellalog
sarà puntata dacurrent/var/log
.
CARICAMENTO SU SERVER
Il caricamento dell’archivio compresso sul server può avvenire secondo la modalità che si preferisce, caricandolo o facendo sì che il server lo scarichi da una sorgente sicura.
Una volta caricato l’archivio, scompattiamolo in una sotto-cartella di release/
che tipicamente ha un nome legato al timestamp di riferimento; l’esempio seguente è nella forma YYYYMMDD-hhmm
:
release/20160810-1634
IMPOSTAZIONE PERMESSI SU FILE E DIRECTORY
Una volta scompattato l’archivio dobbiamo impostare i permessi corretti sui relativi file e sotto-cartelle.
Questa procedura dipende da come è configurato il server perciò senza entrare troppo nei dettagli rimandiamo alla documentazione ufficiale che tratta le diverse casistiche:
ATTIVAZIONE MODALITÀ DI MANUTENZIONE
Questa fase non è sempre obbligatoria. Lo è se il codice che rilasciamo prevede l’esecuzione di script di upgrade.
Si può verificare la necessità di eseguire script di upgrade con il comando seguente:
bin/magento setup:db:status
Purtroppo l’output è testuale e l’exit code di questo comando non cambia a seconda del risultato, quindi per automatizzare la decisione occorre valutare l’output.
Se la risposta è del tipo “The module code base doesn’t match the DB schema and data” dovremo abilitare la modalità di manutenzione e avviare gli script di aggiornamento.
Altrimenti la risposta sarà del tipo “All modules are up to date”.
Ecco un esempio di bash script che converte l’output in un exit code: 0 se non c’è nessuna azione da intraprendere, 1 se occorre eseguire gli script di upgrade:
#!/bin/bash
# use --no-ansi to avoid color characters
message=$(bin/magento setup:db:status --no-ansi)
if [[ ${message:0:3} == "All" ]];
then
exit 0 # 0 not required being default exit code; used for clarity
else
exit 1
fi
La modalità di manutenzione va sempre attivata sulla release corrente prima che questa punti alla nuova release; il comando da utilizzare è:
$ bin/magento maintenance:enable
ABILITAZIONE MODULI
Se non si lavora in isolamento e non si mantiene il file app/etc/config.php
sotto controllo di versione (perché è lì che viene memorizzato lo stato di abilitazione dei moduli) occorre eseguire il comando che abilita eventuali moduli aggiuntivi:
$ bin/magento module:enable --all
Il parametro --all
abilita tutti i moduli aggiuntivi; se occorre abilitarli selettivamente allora è necessario far seguire a module:enable
i nomi dei moduli da abilitare separati da uno spazio; ad esempio:
$ bin/magento module:enable Vendor_Module1 Vendor_Module2
SCRIPT DI AGGIORNAMENTO
Se, come visto in precedenza, è necessario eseguire gli script di aggiornamento, il comando da utilizzare è:
$ bin/magento setup:upgrade --keep-generated
Il parametro --keep-generated
è necessario per evitare che i file dinamici generati nei precedenti passaggi vengano invalidati.
FLUSH DELLA CACHE
Il comando per eliminare dati di cache è il seguente:
$ bin/magento cache:flush
Usare questo comando anziché rimuovere manualmente i contenuti dal file system ci garantisce che, nel caso in cui la cache non sia memorizzata su file system ma su sistemi esterni come Redis, il flush avvenga correttamente.
PASSAGGI CONCLUSIVI
L’ultimo passaggio consiste nel far diventare la nuova release quella corrente.
Lo si fa aggiornando il link simbolico current/
facendolo puntare alla cartella della nuova release, nel nostro esempio release/20160810-1634
.
A questo punto dovremmo disattivare il maintenance mode; poiché questa modalità è di fatto determinata dalla presenza del file .maintenance.flag
nella cartella var/
, se questa non è tra quelle che abbiamo eventualmente portato fuori dalla cartella di release e messo sotto storage/
, il maintenance mode sarà disattivato automaticamente a seguito dell’aggiornamento del link simbolico.
Altrimenti il comando per disattivarlo è:
$ bin/magento maintenance:disable
Automatizzare il processo
Pensare di effettuare manualmente tutti i passaggi operativi che abbiamo visto in precedenza viola la seconda regola del Test di Joel ma soprattutto va contro ogni possibilità di non commettere errori, soprattutto se l’ambiente di produzione è multi-server.
È dunque auspicabile l’utilizzo di tecnologie che ci consentano di “premere un pulsante” o, come va più di moda oggigiorno, “chiedere a un bot” di rilasciare una nuova versione in produzione.
In un prossimo articolo vi racconteremo come stiamo affrontando la questione e con quali strumenti.
Conclusioni
In questo articolo abbiamo visto le complessità del processo di deployment introdotte da Magento 2. La piattaforma è in continua evoluzione e nell’articolo abbiamo dovuto differenziare alcuni passaggi a seconda della versione utilizzata (2.0 o 2.1).
Speriamo di avere dato un quadro esaustivo e di potervi dare presto qualche impressione sull’utilizzo di strumenti che consentono di mettere tutto quello che avete letto in pratica in maniera automatizzata.
Se avete esperienza diretta con il deploy di un progetto Magento 2 fatecelo sapere e condividete qui, se vi va, le vostre impressioni.
Crediti: la foto di copertina è di U.S. Pacific Command, rilasciata in accordo ad una licenza Creative Commons.
Articolo scritto da
Alessandro Ronchi☝ Ti piace quello che facciamo? Unisciti a noi!