Magento 2

Zero downtime deployment con Magento 2

Lettura 11 minuti

Sotto 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)

    1. Download del codice
    2. Download delle dipendenze
    3. Installazione di Magento 2 (opzionale)
    4. Impostazione del production mode
    5. Deploy dei contenuti statici (opzionale)
    6. Compilazione (opzionale)
    7. Dump dell’autoloader
    8. Passaggi conclusivi
  • Fase di release

    1. Caricamento dell’artefact su server di produzione
    2. Impostazione permessi su file e directory
    3. Attivazione modalità di manutenzione (opzionale)
    4. Abilitazione moduli (opzionale)
    5. Avvio script di aggiornamento (opzionale)
    6. Flush della cache
    7. 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 package sjparkinson/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 da require-dev a require 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 cartella release che contiene le sotto-cartelle con gli archivi corrispondenti alle diverse versioni. Se la versione corrente è ad esempio in release/20160810-1634 il link simbolico current 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 la var/log e la pub/media.
    Ipotizziamo di avere allo stesso livello di current e release la cartella storage: sotto di essa la cartella media sarà puntata da current/pub/media e la cartella log sarà puntata da current/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!