Ho un progetto di medie dimensioni ora che si sta avvicinando alla fine della fase "sciatti prototipi alimentati con caffeina per le dimostrazioni dei clienti" e il passaggio alla fase "pensa al futuro". Il progetto consiste in dispositivi basati su Linux con software e firmware e un server Web amministrativo centrale. Attualmente esistono 10 prototipi, la produzione dovrebbe essere dell'ordine dei 1000 bassi.
Non essendo esperto nell'arte degli aggiornamenti automatici ed essendo a corto di tempo, avevo rapidamente sviluppato la mia strategia di distribuzione / aggiornamento automatico del software e, francamente, fa schifo. Attualmente è composto da:
- Un repository git ospitato (GitLab) con un ramo di rilascio della produzione (si noti che anche l'origine del server Web si trova in questo stesso repository, oltre a poche altre cose).
- Un pulsante "distribuisci aggiornamento" sull'interfaccia Web che:
- Estrae l'ultima versione dal ramo di rilascio della produzione in un'area repository locale e la copia anche in un'area temporanea di preparazione temporanea del pacchetto.
- Esegue uno script di sanificazione (memorizzato nel repository) nell'area di gestione temporanea per rimuovere i file di origine non correlati (ad es. Origine del server, origine del firmware, ecc.) E file .git.
- Scrive l'hash git corrente su un file nel pacchetto di aggiornamento (lo scopo verrà chiarito di seguito).
- Se tutto è andato bene, lo decomprime e lo rende pronto per essere utilizzato sovrascrivendo il precedente pacchetto gzipped con un file con lo stesso nome, quindi elimina l'area di gestione temporanea.
- Si noti che ora ci sono due copie del software del dispositivo corrente sul server, che dovrebbero essere sincronizzate: un repository git locale completo sull'ultimo ramo di produzione e un pacchetto gzip pronto all'uso che si presume ora rappresenti che stessa versione.
- Il software sul dispositivo è autonomo in una directory denominata
/opt/example/current
, che è un collegamento simbolico alla versione corrente del software. - Una funzione di aggiornamento automatico sul dispositivo che, all'avvio:
- Verifica la presenza di un
do_not_update
file e non esegue ulteriori azioni se esiste (per i dispositivi di sviluppo, vedi sotto). - Legge l'attuale hash di commit dal file di testo sopra menzionato.
- Invia una richiesta HTTP al server con quell'hash come parametro di query. Il server risponderà con un 304 (l'hash è la versione corrente) o servirà il pacchetto di aggiornamento gzipped.
- Installa il pacchetto di aggiornamento, se ricevuto, in
/opt/example
:- Estrarre le informazioni aggiornate sul software in una cartella denominata
stage
. - Esecuzione di uno script post-installazione dal pacchetto di aggiornamento che fa cose come apportare le modifiche locali necessarie per quell'aggiornamento, ecc.
- Copia della cartella principale del software corrente in
previous
(eliminaprevious
prima esistente , se presente). - Copia della
stage
cartella inlatest
(eliminalatest
prima la cartella esistente , se presente). - Garantire il
current
collegamento simbolico a cui puntarelatest
. - Riavvio del dispositivo (gli aggiornamenti del firmware, se presenti, vengono applicati al riavvio).
- Estrarre le informazioni aggiornate sul software in una cartella denominata
- Verifica la presenza di un
Esiste anche il problema della distribuzione iniziale su dispositivi di nuova costruzione. I dispositivi sono attualmente basati su scheda SD (ha una propria serie di problemi, fuori portata qui) quindi questo processo consiste in:
- Esiste un'immagine SD che contiene una versione precedente stabile del software.
- Da questa immagine viene creata una scheda SD.
- Al primo avvio, viene eseguita una prima inizializzazione specifica per dispositivo (basata sul numero seriale) per la prima volta, quindi l'aggiornamento automatico acquisisce e installa l'ultima versione di produzione del software come al solito.
Inoltre, avevo bisogno di supporto per i dispositivi di sviluppo. Per i dispositivi di sviluppo:
- Sul dispositivo è gestito un repository git locale completo.
- Il
current
collegamento simbolico punta alla directory di sviluppo. do_not_update
Esiste un file locale che impedisce all'aggiornamento automatico di eliminare il codice di sviluppo con un aggiornamento di produzione.
Ora, il processo di distribuzione era teoricamente destinato a essere:
- Una volta che il codice è pronto per la distribuzione, spingerlo nel ramo di rilascio.
- Premi il pulsante "distribuisci aggiornamento" sul server.
- L'aggiornamento è ora attivo e i dispositivi si aggiorneranno automaticamente al successivo controllo.
Tuttavia, ci sono molti problemi nella pratica:
- Il codice del server Web si trova nello stesso repository del codice dispositivo e il server ha un repository git locale da cui eseguo. Il codice del server Web più recente non si trova sullo stesso ramo del codice dispositivo più recente. La struttura delle directory è problematica. Quando il pulsante "distribuisci aggiornamento" estrae l'ultima versione dal ramo di produzione, la inserisce in una sottodirectory del codice del server. Ciò significa che quando eseguo la distribuzione da zero su un server, devo "seedare" manualmente questa sottodirectory afferrando il ramo di produzione del dispositivo in esso, perché, probabilmente a causa di un errore utente git da parte mia, se non eseguo i tentativi di distribuzione estrarre il codice del dispositivo dal ramo del server Web della directory principale . Penso che questo sia risolvibile rendendo l'area di gestione temporanea non una sottodirectory del repository git locale del server.
- Il web server attualmente non mantiene persistentemente l'hash git del software del dispositivo. All'avvio del server, esegue un
git rev-parse HEAD
repository software nel dispositivo locale per recuperare l'hash corrente. Per motivi che non posso avvolgere la mia testa, questo sta causando anche una tonnellata di errori logici che non descriverò qui, basti dire che a volte il riavvio del server rovina le cose, specialmente se il server è nuovo di zecca e nessuna produzione il repo di filiale è stato ancora tirato. Condividerei volentieri la fonte per quella logica se richiesto, ma questo post sta diventando lungo. - Se lo script di sanitizzazione (lato server) non riesce per qualche motivo, il server viene lasciato con un repository aggiornato ma un pacchetto di aggiornamento non sincronizzato / mancante, quindi
git rev-parse HEAD
restituirà un hash che non corrisponde a ciò che è effettivamente servito ai dispositivi e qui i problemi devono essere corretti manualmente dalla riga di comando del server. Cioè il server non sa che il pacchetto di aggiornamento non è corretto, semplicemente lo assume sempre per pura fede. Questo combinato con i punti precedenti rende il server estremamente fragile in pratica. - Uno dei maggiori problemi è : al momento non esiste un demone di aggiornamento separato in esecuzione sul dispositivo. A causa di complicazioni in attesa dell'accesso a Internet Wi-Fi e di alcuni hacker dell'ultimo minuto, è lo stesso software di controllo del dispositivo stesso che controlla e aggiorna il dispositivo. Ciò significa che se in qualche modo una versione scarsamente testata viene messa in produzione e il software di controllo non può essere avviato, tutti i dispositivi esistenti sono sostanzialmente in muratura, poiché non possono più aggiornarsi. Questo sarebbe un incubo assoluto nella produzione. Lo stesso affare per un singolo dispositivo se perde potenza in un momento sfortunato.
- L'altro problema principale è : non esiste supporto per gli aggiornamenti incrementali. Se un dispositivo, per esempio, non viene acceso per un po ', quindi al successivo aggiornamento salta un sacco di versioni di rilascio, deve essere in grado di eseguire un aggiornamento diretto per saltare la versione. La conseguenza di ciò è che la distribuzione aggiornata è un incubo nell'assicurarsi che ogni dato aggiornamento possa essere applicato su una data versione precedente. Inoltre, poiché gli hash git vengono utilizzati per identificare le versioni anziché i numeri di versione, al momento non è possibile il confronto lessicografico delle versioni per facilitare gli aggiornamenti incrementali.
- Un nuovo requisito che al momento non supporta è che esistano alcune opzioni di configurazione per dispositivo (coppie chiave / valore) che devono essere configurate sul lato del server amministrativo. Non mi dispiacerebbe in alcun modo servire queste opzioni per dispositivo sul dispositivo nella stessa richiesta HTTP dell'aggiornamento software (forse potrei incapsularlo in intestazioni / cookie HTTP) anche se non sono troppo preoccupato per questo, come posso rendilo sempre una richiesta HTTP separata.
- Vi è una leggera complicazione dovuta al fatto che esistono due (e più in futuro) versioni dell'hardware. La versione corrente dell'hardware viene effettivamente archiviata come variabile d'ambiente nella sua immagine SD iniziale (non è in grado di identificarsi automaticamente) e tutto il software è progettato per essere compatibile con tutte le versioni dei dispositivi. Gli aggiornamenti del firmware vengono scelti in base a questa variabile di ambiente e il pacchetto di aggiornamento contiene il firmware per tutte le versioni dell'hardware. Posso conviverci anche se è un po 'goffo.
- Al momento non esiste alcun modo per caricare manualmente un aggiornamento sul dispositivo (per farla breve questi dispositivi hanno due adattatori wifi, uno per connettersi a Internet e uno in modalità AP che l'utente utilizzerà per configurare il dispositivo; in futuro Intendo aggiungere una funzione di "aggiornamento software" all'interfaccia web locale del dispositivo). Questo non è un grosso problema, ma ha un certo impatto sul metodo di installazione dell'aggiornamento.
- Un sacco di altre frustrazioni e insicurezza generale.
Quindi ... è stato lungo. Ma la mia domanda si riduce a questo:
Come posso farlo in modo corretto e sicuro? Ci sono piccoli aggiustamenti che posso apportare al mio processo esistente? Esiste una strategia collaudata nel tempo / un sistema esistente che posso sfruttare in modo da non dover implementare il mio sistema di aggiornamento scadente ? O se devo fare il mio, quali sono le cose che devono essere vere affinché un processo di distribuzione / aggiornamento sia sicuro e di successo? Devo anche essere in grado di includere dispositivi di sviluppo nel mix.
Spero che la domanda sia chiara. Mi rendo conto che è un po 'confuso, ma sono sicuro al 100% che questo è un problema che è stato affrontato in precedenza e risolto con successo, semplicemente non so quali siano le strategie attualmente accettate.