{"id":32526,"date":"2019-10-31T21:47:32","date_gmt":"2019-10-31T18:47:32","guid":{"rendered":"https:\/\/prohoster.info\/blog\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci\/"},"modified":"2019-10-31T21:47:32","modified_gmt":"2019-10-31T18:47:32","slug":"protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","title":{"rendered":"Processo di sviluppo e test con Docker e Gitlab CI","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>Ti invitiamo a prendere visione della trascrizione della relazione di Alexander Sigachev di Inventos \"Il processo di sviluppo e testing con Docker + Gitlab CI\"<\/strong><\/p>\n<p><\/p>\n<p>Coloro che iniziano a implementare il processo di sviluppo e testing basato su Docker + Gitlab CI spesso pongono domande fondamentali. Da dove iniziare? Come organizzare? Come testare?<\/p>\n<p><\/p>\n<p>Questa relazione \u00e8 interessante perch\u00e9 descrive in modo strutturato il processo di sviluppo e testing utilizzando Docker e Gitlab CI. La relazione risale al 2017. Penso che si possano trarre da essa le basi, la metodologia, l'idea e le esperienze di utilizzo. <\/p>\n<p>\n<center><div class=\"youtube-placeholder\" data-id=\"lJsqRwULRVA\" onclick=\"loadVideo(this)\">\r\n        <img decoding=\"async\" src=\"https:\/\/img.youtube.com\/vi\/lJsqRwULRVA\/hqdefault.jpg\" alt=\"Guarda il video\" loading=\"lazy\" width=\"480\" height=\"360\" style=\"width:100%;height:auto;\">\r\n        <div class=\"play-button\"><\/div>\r\n    <\/div><\/center><\/p>\n<p>Chi \u00e8 interessato, prego di proseguire. <noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p><\/p>\n<p>Mi chiamo Aleksandr Sigacev. Lavoro per l'azienda Inventos. Condivider\u00f2 la mia esperienza nell'uso di Docker e come gradualmente lo stiamo implementando nei progetti dell'azienda.<\/p>\n<p><\/p>\n<p>Tema della relazione: Il processo di sviluppo utilizzando Docker e Gitlab CI. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/e30c4757e4bda1bcc3e20b16e8e2709e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Questa \u00e8 la mia seconda relazione su Docker. Al momento della prima relazione, usavamo Docker solo in sviluppo sulle macchine degli sviluppatori. Il numero di dipendenti che utilizzavano Docker era di circa 2-3 persone. Gradualmente abbiamo accumulato esperienza e siamo progrediti. Ecco il link alla nostra <noindex><a rel=\"nofollow\" href=\"https:\/\/www.slideshare.net\/gled\/docker-development-70411088\">prima relazione<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Cosa ci sar\u00e0 in questa relazione? Condivideremo le esperienze su quali ostacoli abbiamo incontrato e come abbiamo risolto i problemi. Non sempre \u00e8 stato semplice, ma ci ha permesso di andare avanti.<\/p>\n<p><\/p>\n<p>Il nostro motto: dockerizza tutto ci\u00f2 che riusciamo a toccare.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/25ae4f97f1c502384abfe697bac502b1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Quali problemi risolviamo?<\/p>\n<p><\/p>\n<p>Quando in un'azienda ci sono pi\u00f9 team, il programmatore diventa una risorsa condivisa. Ci sono fasi in cui il programmatore viene estratto da un progetto e assegnato per un certo periodo a un altro progetto.<\/p>\n<p><\/p>\n<p>Per consentire al programmatore di orientarsi rapidamente, \u00e8 necessario che scarichi il codice sorgente del progetto e avvii il pi\u00f9 velocemente possibile un ambiente che gli permetta di avanzare risolvendo le attivit\u00e0 di quel progetto.<\/p>\n<p><\/p>\n<p>Di solito, se si inizia da zero, la documentazione nel progetto \u00e8 scarsa. Le informazioni su come configurare sono disponibili solo per i veterani. I dipendenti configurano autonomamente il proprio posto di lavoro in uno o due giorni. Per velocizzare questo processo, abbiamo utilizzato Docker.<\/p>\n<p><\/p>\n<p>Un altro motivo \u00e8 la standardizzazione delle configurazioni in sviluppo. Dalla mia esperienza, gli sviluppatori sono sempre proattivi. In ogni quinto caso viene introdotto un dominio personalizzato, ad esempio vasya.dev. Vicino c'\u00e8 l'amico Petya, che ha il dominio petya.dev. Sviluppano un sito o un componente del sistema utilizzando questo nome di dominio.<\/p>\n<p><\/p>\n<p>Quando il sistema cresce e questi nomi di dominio iniziano a comparire nelle configurazioni, sorge un conflitto negli ambienti di sviluppo e il percorso del sito viene riscritto.<\/p>\n<p><\/p>\n<p>Lo stesso accade con le impostazioni del database. Alcuni non si preoccupano della sicurezza e lavorano con una password root vuota. Qualcuno, durante l'installazione, ha richiesto una password MySQL e si \u00e8 rivelata 123. Spesso accade che la configurazione del database cambi continuamente a seconda del commit dello sviluppatore. Qualcuno l'ha corretta, qualcun altro no. Ci sono stati artifici quando abbiamo estratto una configurazione di test in <code>.gitignore<\/code> e ogni sviluppatore doveva installare il database. Questo complicava il processo di partenza. Oltre a tutto il resto, \u00e8 necessario tenere a mente il database. Il database deve essere inizializzato, \u00e8 necessario specificare la password, \u00e8 necessario specificare l'utente, creare una tabella e cos\u00ec via.<\/p>\n<p><\/p>\n<p>Un altro problema \u00e8 rappresentato dalle diverse versioni delle librerie. Spesso succede che uno sviluppatore lavori su diversi progetti. C'\u00e8 un progetto Legacy, iniziato cinque anni fa (dal 2017 \u2013 nota dell'editore). All'inizio si part\u00ec con MySQL 5.5. Ci sono anche progetti moderni in cui tentiamo di implementare versioni MySQL pi\u00f9 recenti, come 5.7 o superiori (nel 2017 \u2013 nota dell'editore).<\/p>\n<p><\/p>\n<p>Chi lavora con MySQL sa che queste librerie portano con s\u00e9 delle dipendenze. \u00c8 abbastanza problematico far funzionare insieme 2 database. Almeno, \u00e8 problematico collegare vecchi client a un nuovo database. Questo genera a sua volta diversi problemi.<\/p>\n<p><\/p>\n<p>Il problema successivo \u00e8 quando lo sviluppatore lavora su una macchina locale, utilizza risorse locali, file locali, RAM locale. Tutta l'interazione durante lo sviluppo delle soluzioni avviene nell'ambito del funzionamento su una sola macchina. Un esempio pu\u00f2 essere quando abbiamo in produzione 3 server backend, mentre lo sviluppatore salva i file nella cartella radice e nginx prende i file da l\u00ec per rispondere alle richieste. Quando questo codice arriva in produzione, si scopre che il file \u00e8 presente su uno dei 3 server.<\/p>\n<p><\/p>\n<p>Attualmente si sta sviluppando la direzione dei microservizi. Quando dividiamo le nostre grandi applicazioni in piccole componenti che interagiscono tra loro. Questo consente di scegliere le tecnologie specifiche per un determinato stack di compiti. Inoltre, consente di suddividere il lavoro e l'area di responsabilit\u00e0 tra gli sviluppatori.<\/p>\n<p><\/p>\n<p>Lo sviluppatore Frontend, lavorando con JS, non influisce praticamente sul Backend. Lo sviluppatore Backend, a sua volta, sviluppa, nel nostro caso, Ruby on Rails e non interferisce con il Frontend. L'interazione avviene tramite API.<\/p>\n<p><\/p>\n<p>Come bonus, grazie a Docker siamo riusciti a ottimizzare le risorse su Staging. Ogni progetto, a causa della sua specificit\u00e0, richiedeva impostazioni particolari. Era necessario dedicare un server virtuale oppure dividere un ambiente variabile, e i progetti potevano influenzarsi a vicenda a seconda della versione delle librerie.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/fa5c99f90faef84a356d4791fd915830.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Strumenti. Cosa usiamo? <\/p>\n<p><\/p>\n<ul>\n<li>Direttamente Docker stesso. Nel Dockerfile vengono descritte le dipendenze di una applicazione. <\/li>\n<li>Docker-compose \u00e8 il legame che unisce le nostre diverse applicazioni Docker.<\/li>\n<li>Utilizziamo GitLab per la conservazione del codice sorgente.<\/li>\n<li>Utilizziamo GitLab-CI per l'integrazione sistemica.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/b6041827a6ba928e0d33add6b79c581c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>La presentazione \u00e8 composta da due parti.<\/p>\n<p><\/p>\n<p>La prima parte parler\u00e0 di come abbiamo eseguito Docker sulle macchine degli sviluppatori.<\/p>\n<p><\/p>\n<p>La seconda parte parler\u00e0 di come interagire con GitLab, di come eseguiamo i test e di come distribuiamo su Staging.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/45746d3323d35e0d18f066c8bfcb2214.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Docker \u00e8 una tecnologia che permette (utilizzando un approccio dichiarativo) di descrivere i componenti necessari. Questo \u00e8 un esempio di Dockerfile. Qui dichiariamo che ereditiamo dall'immagine Docker ufficiale Ruby:2.3.0. Contiene Ruby versione 2.3 installato. Installiamo le librerie di compilazione necessarie e NodeJS. Descriviamo che stiamo creando una directory <code>\/app<\/code>. Assegniamo la directory app come directory di lavoro. In questa directory posizioniamo il minimo necessario di Gemfile e Gemfile.lock. Successivamente eseguiamo la compilazione dei progetti che installano le dipendenze di questa immagine. Indichiamo che il contenitore sar\u00e0 pronto ad ascoltare sulla porta esterna 3000. L'ultima comando \u00e8 quello che avvia effettivamente la nostra applicazione. Se eseguiamo il comando di avvio del progetto, l'applicazione cercher\u00e0 di avviarsi e di eseguire il comando specificato.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/ba503c305ae90f49b39f4ae2cbda1668.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Questo \u00e8 un esempio minimale di un file docker-compose. In questo caso mostriamo come avviene la connessione tra due contenitori. Si tratta direttamente del servizio del database e del servizio web. Le nostre applicazioni web in maggior parte dei casi richiedono un database per la memorizzazione dei dati come backend. Poich\u00e9 utilizziamo MySQL, l'esempio \u00e8 con MySQL, ma nulla vieta di usare un altro database (PostgreSQL, Redis).<\/p>\n<p><\/p>\n<p>Prendiamo dal sorgente ufficiale su Docker hub l'immagine di MySQL 5.7.14 senza modifiche. L'immagine che gestisce la nostra applicazione web viene costruita dalla directory corrente. Durante il primo avvio ci costruisce l'immagine. Dopodich\u00e9 esegue il comando che stiamo qui eseguendo. Se torniamo indietro, vedremo che \u00e8 stato definito un comando di avvio tramite Puma. Puma \u00e8 un servizio scritto in Ruby. Nel secondo caso si pu\u00f2 sovrascrivere. Questo comando pu\u00f2 essere arbitrario a seconda delle nostre necessit\u00e0 o obiettivi.<\/p>\n<p><\/p>\n<p>Inoltre, descriviamo che \u00e8 necessario inoltrare la porta sulla macchina host dello sviluppatore dalla porta 3000 alla porta 3000 del contenitore. Questo viene eseguito automaticamente tramite iptables e il suo meccanismo, che \u00e8 direttamente integrato in Docker. <\/p>\n<p><\/p>\n<p>Lo sviluppatore pu\u00f2 anche, come in precedenza, connettersi a qualsiasi IP disponibile, ad esempio, 127.0.0.1 per l'indirizzo IP locale o l'indirizzo IP esterno della macchina.<\/p>\n<p><\/p>\n<p>L'ultima riga indica che il contenitore web dipende dal contenitore db. Quando avviamo il contenitore web, prima docker-compose avvier\u00e0 il nostro database. Solo una volta avviato il database (in realt\u00e0 \u2014 dopo l'avvio del contenitore! La prontezza del DB non \u00e8 garantita) avvier\u00e0 la nostra applicazione, il nostro backend.<\/p>\n<p><\/p>\n<p>Questo permette di evitare errori quando il database non \u00e8 attivo e consente di risparmiare risorse quando fermiamo il contenitore del database, liberando cos\u00ec risorse per altri progetti. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4acbcf5de89b98db055e8abc419202de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Cosa ci offre l'uso della containerizzazione del database nel progetto. Fissiamo la versione di MySQL per tutti gli sviluppatori. Questo permette di evitare alcuni errori che possono insorgere a causa di discrepanze nelle versioni, quando cambia la sintassi, la configurazione, le impostazioni predefinit. Questo consente di specificare hostname comuni per il database, login, password. Ci allontaniamo dalla giungla di nomi e conflitti nei file di configurazione che esisteva in precedenza. <\/p>\n<p><\/p>\n<p>Abbiamo la possibilit\u00e0 di utilizzare un config pi\u00f9 ottimale per l'ambiente di sviluppo, che differir\u00e0 da quello predefinito. MySQL \u00e8 configurato per impostazione predefinita su macchine poco potenti e la sua performance out-of-the-box \u00e8 molto bassa.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/417d77a06b18e2df9b71440c0f2a1653.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Docker consente di utilizzare l'interprete Python, Ruby, NodeJS, PHP nella versione richiesta. Ci liberiamo dalla necessit\u00e0 di utilizzare un gestore di versioni. In precedenza, per Ruby usavamo un pacchetto rpm, che consentiva di cambiare versione a seconda del progetto. Inoltre, grazie al container Docker, possiamo migrare il codice in modo fluido e versionarlo insieme alle dipendenze. Non abbiamo problemi a comprendere la versione sia per l'interprete sia per il codice. Per aggiornare la versione \u00e8 necessario abbattere il vecchio container e alzare un nuovo container. Se qualcosa va storto, possiamo abbattere il nuovo container e ripristinare il vecchio container.<\/p>\n<p><\/p>\n<p>Dopo la creazione dell'immagine, i container sia in sviluppo che in produzione saranno identici. Questo \u00e8 particolarmente rilevante per grandi installazioni.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/185a1bfe257f7e3e0eecdc6e0e0d330f.jpg\" style=\"display:block;margin: 0 auto;\" \/> Nel frontend utilizziamo JavaScript e NodeJS.<\/p>\n<p><\/p>\n<p>Attualmente, l'ultimo progetto \u00e8 su ReactJS. Lo sviluppatore avviava tutti i container e sviluppava utilizzando il hot-reload.<\/p>\n<p><\/p>\n<p>Successivamente, viene avviato il processo di compilazione di JavaScript e il codice, compilato in statico, viene servito tramite nginx per risparmiare risorse.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4c2e28319d56d7fc81845b4133a1bfe6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Qui ho presentato lo schema del nostro ultimo progetto.<\/p>\n<p><\/p>\n<p>Quali problemi abbiamo risolto? Abbiamo avuto la necessit\u00e0 di costruire un sistema con cui interagiscono i dispositivi mobili. Essi ricevono dati. Una delle possibilit\u00e0 \u00e8 inviare notifiche push a questo dispositivo. <\/p>\n<p><\/p>\n<p>Cosa abbiamo fatto per questo?<\/p>\n<p><\/p>\n<p>Abbiamo suddiviso l'applicazione in componenti come: parte admin in JS, backend che opera tramite interfaccia REST sotto Ruby on Rails. Il backend interagisce con il database. Il risultato generato viene restituito al cliente. L'amministrazione interagisce con il backend e il database tramite interfaccia REST.<\/p>\n<p><\/p>\n<p>Abbiamo anche avuto la necessit\u00e0 di inviare notifiche Push. Prima avevamo un progetto in cui era implementato un meccanismo che gestisce la consegna delle notifiche alle piattaforme mobili. <\/p>\n<p><\/p>\n<p>Abbiamo sviluppato il seguente schema: l'operatore dal browser interagisce con l'interfaccia di amministrazione, l'interfaccia di amministrazione interagisce con il backend e si imposta il compito di inviare notifiche Push.<\/p>\n<p><\/p>\n<p>Le notifiche push interagiscono con un altro componente, realizzato su NodeJS.<\/p>\n<p><\/p>\n<p>Si stanno formando delle code e continua il meccanismo di invio delle notifiche.<\/p>\n<p><\/p>\n<p>Qui sono disegnati due database. Attualmente utilizziamo due database indipendenti tramite Docker, che non sono in alcun modo collegati tra loro. A parte il fatto che condividono una rete virtuale, i dati fisici sono memorizzati in directory diverse sulla macchina dello sviluppatore.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/dfde59db7e6886d2d851dcf884049318.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lo stesso ma in cifre. Qui \u00e8 importante il riutilizzo del codice.<\/p>\n<p><\/p>\n<p>Se in precedenza parlavamo di riutilizzo del codice sotto forma di librerie, in questo esempio il nostro servizio che gestisce le notifiche Push viene riutilizzato come server completo. Fornisce un'API. E con essa interagisce il nostro nuovo sviluppo.<\/p>\n<p><\/p>\n<p>A quel tempo utilizzavamo la versione 4 di NodeJS. Attualmente (nel 2017 \u2014 nota dell'editore) nei nuovi sviluppi utilizziamo la versione 7 di NodeJS. Non ci sono problemi a utilizzare nuove versioni di librerie nei nuovi componenti. <\/p>\n<p><\/p>\n<p>Se necessario, possiamo effettuare il refactoring e aggiornare la versione di NodeJS per il servizio di notifiche Push. <\/p>\n<p><\/p>\n<p>E se riusciremo a mantenere la compatibilit\u00e0 con l'API, potremo sostituirlo in altri progetti utilizzati in precedenza.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/5c58c65be078a5a1b9fc17ee2e7b4ad4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Cosa serve per aggiungere Docker? Aggiungiamo al nostro repository un Dockerfile, che descrive le dipendenze necessarie. In questo esempio, i componenti sono suddivisi per logica. Questo \u00e8 il set minimo per un sviluppatore backend.<\/p>\n<p><\/p>\n<p>Quando creiamo un nuovo progetto, creiamo un Dockerfile, descriviamo l'ecosistema necessario (Python, Ruby, NodeJS). In docker-compose, descriviamo la dipendenza necessaria: il database. Indichiamo che abbiamo bisogno di un database di una certa versione, per memorizzare i dati l\u00ec.<\/p>\n<p><\/p>\n<p>Utilizziamo un terzo container separato con nginx per servire statiche. \u00c8 prevista la possibilit\u00e0 di caricare immagini. Il backend le memorizza in un volume predefinito, che \u00e8 anche montato nel container con nginx, che serve i contenuti statici.<\/p>\n<p><\/p>\n<p>Per memorizzare la configurazione di nginx e mysql, abbiamo aggiunto una cartella Docker, in cui conserviamo le configurazioni necessarie. Quando uno sviluppatore esegue git clone del repository sulla propria macchina, ottiene gi\u00e0 un progetto pronto per lo sviluppo locale. Non ci sono domande su quale porta o quali impostazioni applicare.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/e7a6b308a75cacf8ce60687a9e6081dd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Successivamente, abbiamo diversi componenti: admin, inform-API, notifiche push.<\/p>\n<p><\/p>\n<p>Per far partire tutto questo, abbiamo creato un ulteriore repository, che abbiamo chiamato dockerized-app. Al momento, utilizziamo diversi repository per ogni componente. Si differenziano solo logicamente: in GitLab appare come una cartella, mentre sulla macchina dello sviluppatore \u00e8 una cartella per il progetto specifico. A un livello inferiore si trovano i componenti che saranno uniti.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/f4e2c45c8ec20fca0bac74b3a3892543.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Questo \u00e8 un esempio del contenuto di dockerized-app. Qui portiamo anche una directory Docker, in cui prepariamo le configurazioni richieste per le interazioni di tutti i componenti. C'\u00e8 un README.md, in cui \u00e8 descritto brevemente come avviare il progetto.<\/p>\n<p><\/p>\n<p>Qui abbiamo utilizzato due file docker-compose. Questo \u00e8 stato fatto per avere la possibilit\u00e0 di avviare in modo graduale. Quando uno sviluppatore lavora con il core, non ha bisogno di notifiche push, quindi avvia semplicemente il file docker-compose e di conseguenza si risparmiano risorse.<\/p>\n<p><\/p>\n<p>Se \u00e8 necessario integrare le notifiche push, si avviano docker-compose.yaml e docker-compose-push.yaml.<\/p>\n<p><\/p>\n<p>Poich\u00e9 docker-compose.yaml e docker-compose-push.yaml si trovano nella cartella, viene automaticamente creata una rete virtuale unica.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/535019c82c72c1602176fbaf4aa5640f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Descrizione dei componenti. Questo \u00e8 un file pi\u00f9 dettagliato, che \u00e8 responsabile della raccolta dei componenti. Cosa c'\u00e8 di interessante qui? Introduciamo il componente bilanciatore.<\/p>\n<p><\/p>\n<p>Questo \u00e8 un'immagine Docker pronta, in cui viene eseguito nginx e un'applicazione che ascolta il socket Docker. In modo dinamico, all'accensione e allo spegnimento dei container, rigenera la configurazione di nginx. La comunicazione con i componenti \u00e8 distribuita su nomi di dominio di terzo livello.<\/p>\n<p><\/p>\n<p>Per l'ambiente di sviluppo utilizziamo il dominio .dev \u2014 api.informer.dev. Le applicazioni con dominio .dev sono accessibili sulla macchina locale dello sviluppatore.<\/p>\n<p><\/p>\n<p>Poi si trasferiscono le configurazioni per ogni progetto e vengono avviati tutti i progetti insieme contemporaneamente.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/bc1e11e6f1fd1c837d2c8087d04145c4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Se lo rappresentiamo graficamente, il cliente \u00e8 il nostro browser o qualche strumento con cui facciamo richieste al bilanciatore.<\/p>\n<p><\/p>\n<p>Il bilanciatore, tramite il nome di dominio, determina a quale container deve indirizzarsi.<\/p>\n<p><\/p>\n<p>Questo pu\u00f2 essere nginx, che fornisce l'admin panel JS. Pu\u00f2 essere nginx, che fornisce l'API o file statici, che vengono forniti da nginx come immagini da scaricare.<\/p>\n<p><\/p>\n<p>Nello schema si vede che i container sono uniti in una rete virtuale e nascosti dietro un proxy.<\/p>\n<p><\/p>\n<p>Con l'auto dello sviluppatore \u00e8 possibile accedere al contenitore conoscendo l'IP, ma in linea di massima non lo usiamo. Non si presenta praticamente mai la necessit\u00e0 di un accesso diretto.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/2908c31fd9d3c79f6189bdbeeb6d028f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Quale esempio guardare per dockerizzare la propria applicazione? A mio parere, un buon esempio \u00e8 l'immagine ufficiale di Docker per MySQL.<\/p>\n<p><\/p>\n<p>\u00c8 piuttosto complesso. Ci sono molte versioni. Ma la sua funzionalit\u00e0 consente di coprire molte esigenze che possono sorgere durante lo sviluppo successivo. Se dedichi del tempo a capire come interagisce tutto questo, penso che non avrai problemi nell'implementazione autonoma.<\/p>\n<p><\/p>\n<p>Su hub.docker.com ci sono di solito collegamenti a github.com, dove sono forniti i dati grezzi da cui puoi creare autonomamente l'immagine.<\/p>\n<p><\/p>\n<p>In questo repository c'\u00e8 anche uno script docker-endpoint.sh, che si occupa dell'inizializzazione iniziale e della gestione successiva dell'avvio dell'applicazione.<\/p>\n<p><\/p>\n<p>In questo esempio c'\u00e8 anche la possibilit\u00e0 di configurare tramite variabili d'ambiente. Definendo una variabile d'ambiente al momento dell'avvio di un singolo contenitore o tramite docker-compose, puoi dire che dobbiamo impostare una password vuota per Docker per l'utente root in MySQL o una qualsiasi password che preferiamo.<\/p>\n<p><\/p>\n<p>C'\u00e8 la possibilit\u00e0 di creare una password casuale. Diciamo che abbiamo bisogno di un utente, dobbiamo impostare una password per l'utente e dobbiamo creare un database.<\/p>\n<p><\/p>\n<p>Nei nostri progetti abbiamo un po' uniformato il Dockerfile, che si occupa dell'inizializzazione. L'abbiamo modificato secondo le nostre esigenze per estendere semplicemente i diritti dell'utente utilizzato dall'applicazione. Questo ha permesso poi di creare facilmente un database dalla console dell'applicazione. Nelle applicazioni Ruby ci sono comandi per creare, modificare e cancellare database.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4a9b56e21819d28dd39753134d23fee4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Questo \u00e8 un esempio di come appare una versione specifica di MySQL su github.com. Puoi aprire il Dockerfile e vedere come avviene l'installazione.<\/p>\n<p><\/p>\n<p>Lo script docker-endpoint.sh \u00e8 responsabile del punto d'ingresso. Durante l'inizializzazione iniziale sono necessarie alcune azioni di preparazione e tutte queste azioni sono state trasferite nello script di inizializzazione.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4153fa46526f3d802931dd1db48b08cd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Passiamo alla seconda parte.<\/p>\n<p><\/p>\n<p>Per l'archiviazione dei codici sorgente siamo passati a GitLab. \u00c8 un sistema piuttosto potente che ha un'interfaccia visiva.<\/p>\n<p><\/p>\n<p>Uno dei componenti di GitLab \u00e8 GitLab CI. Consente di descrivere una sequenza di comandi che saranno successivamente utilizzati per organizzare il sistema di distribuzione del codice o l'esecuzione di test automatici.<\/p>\n<p><\/p>\n<p>Relazione su GitLab CI 2 <noindex><a rel=\"nofollow\" href=\"https:\/\/goo.gl\/uohKjI\">https:\/\/goo.gl\/uohKjI<\/a><\/noindex> \u2014 relazione del Ruby Russia club \u2014 \u00e8 abbastanza dettagliata e potrebbe interessarti.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/5d26d20500f34eeb9768e6c3d91bbefa.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ora esamineremo cosa \u00e8 necessario per attivare GitLab CI. Per avviare GitLab CI, \u00e8 sufficiente posizionare un file .gitlab-ci.yml nella radice del progetto.<\/p>\n<p><\/p>\n<p>Qui descriviamo cosa vogliamo eseguire in una sequenza di stati come test e deployment.<\/p>\n<p><\/p>\n<p>Eseguiamo script che invocano direttamente la build della nostra applicazione utilizzando docker-compose. Questo \u00e8 un esempio di backend.<\/p>\n<p><\/p>\n<p>Poi indichiamo che \u00e8 necessario eseguire le migrazioni per le modifiche del database e eseguire i test.<\/p>\n<p><\/p>\n<p>Se gli script vengono eseguiti correttamente e non restituiscono codici di errore, il sistema passa alla seconda fase del deployment.<\/p>\n<p><\/p>\n<p>Attualmente, la fase di deployment \u00e8 implementata per lo staging. Non abbiamo organizzato un riavvio senza interruzioni.<\/p>\n<p><\/p>\n<p>Costringiamo a arrestare tutti i container e poi riavviamo tutti i container che sono stati creati nel primo passo durante il test.<\/p>\n<p><\/p>\n<p>Eseguiamo le migrazioni del database per l'attuale ambiente variabile che sono state scritte dagli sviluppatori.<\/p>\n<p><\/p>\n<p>C'\u00e8 un'avvertenza che indica di applicarlo solo per il ramo master.<\/p>\n<p><\/p>\n<p>Non viene eseguito con altre modifiche di ramo.<\/p>\n<p><\/p>\n<p>C'\u00e8 la possibilit\u00e0 di organizzare i rilasci per ramo.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/8a5b880bd9a0d4bab0b276568e2ba413.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Per procedere in tal senso, dobbiamo installare GitLab Runner.<\/p>\n<p><\/p>\n<p>Questo strumento \u00e8 scritto in Golang. \u00c8 un singolo file, come di consueto nel mondo di Golang, che non richiede dipendenze.<\/p>\n<p><\/p>\n<p>All'avvio registriamo GitLab Runner.<\/p>\n<p><\/p>\n<p>Riceviamo la chiave nell'interfaccia web di GitLab.<\/p>\n<p><\/p>\n<p>Poi invochiamo il comando di inizializzazione nella riga di comando.<\/p>\n<p><\/p>\n<p>Configuriamo GitLab Runner in modalit\u00e0 dialogo (Shell, Docker, VirtualBox, SSH)<\/p>\n<p><\/p>\n<p>Il codice su GitLab Runner verr\u00e0 eseguito ad ogni commit in base alla configurazione di .gitlab-ci.yml.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/37c8ac7499f0cfba2eb07a84083e44e1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ecco come appare visivamente in GitLab nell'interfaccia web. Dopo aver collegato GitLab CI, appare una bandiera che indica lo stato attuale della build.<\/p>\n<p><\/p>\n<p>Vediamo che 4 minuti fa \u00e8 stato effettuato un commit che ha superato tutti i test senza problemi.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/33ac79c45f0651a5f8bde75092c4ac44.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Possiamo esaminare pi\u00f9 dettagliatamente i build. Qui vediamo che sono gi\u00e0 trascorsi due stati: lo stato di testing e lo stato di deploy su staging.<\/p>\n<p><\/p>\n<p>Se clicchiamo su un build specifico, vedremo l'output della console dei comandi che sono stati eseguiti nel processo secondo il file .gitlab-ci.yml.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/d26dd28708c7fc8a409af4be4d48f502.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ecco come appare la storia del nostro prodotto. Possiamo vedere che ci sono stati tentativi riusciti. Quando i test falliscono, non si passa al passaggio successivo e il codice su staging non viene aggiornato.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/ee7bd81dc283c7af0c194779d4230780.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Quali problemi abbiamo risolto su staging quando abbiamo implementato Docker? La nostra sistema \u00e8 composta da diversi componenti e abbiamo dovuto riavviare solo alcune parti dei componenti che erano stati aggiornati nel repository, e non l'intero sistema.<\/p>\n<p><\/p>\n<p>Per questo abbiamo dovuto separare tutto in cartelle distinte.<\/p>\n<p><\/p>\n<p>Dopo aver fatto ci\u00f2, abbiamo riscontrato un problema: Docker-compose crea per ogni cartella un proprio spazio di rete e non riesce a vedere i componenti vicini.<\/p>\n<p><\/p>\n<p>Per aggirare questo, abbiamo creato manualmente una rete in Docker. Nel Docker-compose abbiamo specificato di utilizzare questa rete per il progetto.<\/p>\n<p><\/p>\n<p>In questo modo, ogni componente che avvia con questa rete vede i componenti in altre parti del sistema.<\/p>\n<p><\/p>\n<p>Il problema successivo \u00e8 stata la separazione dello staging tra diversi progetti.<\/p>\n<p><\/p>\n<p>Poich\u00e9 per far sembrare tutto elegante e il pi\u00f9 vicino possibile alla produzione, \u00e8 bene utilizzare le porte 80 o 443, che sono comuni nel WEB.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/f863cf1833271bf132455a70ad7d2706.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Come abbiamo risolto questo? Abbiamo assegnato un solo Gitlab Runner a tutti i grandi progetti.<\/p>\n<p><\/p>\n<p>Gitlab consente di avviare pi\u00f9 Gitlab Runner distribuiti, che prenderanno semplicemente tutti i lavori in modo casuale e li eseguiranno.<\/p>\n<p><\/p>\n<p>Per evitare il caos, abbiamo limitato il gruppo dei nostri progetti a un solo Gitlab Runner, che riesce a gestire i nostri carichi senza problemi.<\/p>\n<p><\/p>\n<p>Abbiamo esternalizzato nginx-proxy in uno script di avvio separato e abbiamo descritto le reti di tutti i progetti in esso. <\/p>\n<p><\/p>\n<p>Il nostro progetto ha una rete, mentre il bilanciatore ha pi\u00f9 reti denominate con i nomi dei progetti. Pu\u00f2 fare proxy ulteriori per i nomi di dominio.<\/p>\n<p><\/p>\n<p>Le richieste arrivano per dominio sulla porta 80 e vengono gestite in un gruppo di contenitori che servono questo dominio.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/25c769f029b272a17fd06bbe60cf0ea0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Quali ulteriori problemi ci sono stati? Il fatto che per impostazione predefinita tutti i contenitori vengono eseguiti come utente root. Questo root \u00e8 diverso dal root dell'host del sistema.<\/p>\n<p><\/p>\n<p>Tuttavia, se si entra nel contenitore, questo diventer\u00e0 root e il file che creiamo in questo contenitore avr\u00e0 i diritti di root.<\/p>\n<p><\/p>\n<p>Se uno sviluppatore \u00e8 entrato nel contenitore e ha eseguito alcuni comandi che generano file, poi \u00e8 uscito dal contenitore, nella sua directory di lavoro avr\u00e0 un file a cui non ha accesso.<\/p>\n<p><\/p>\n<p>Come possiamo risolvere questo problema? Possiamo aggiungere utenti che saranno nel contenitore.<\/p>\n<p><\/p>\n<p>Quali problemi sono emersi quando abbiamo aggiunto un utente?<\/p>\n<p><\/p>\n<p>Creando un utente, spesso non coincidono l'ID del gruppo (UID) e l'ID dell'utente (GID).<\/p>\n<p><\/p>\n<p>Per risolvere questo problema nel contenitore utilizziamo utenti con ID 1000.<\/p>\n<p><\/p>\n<p>Nel nostro caso, questo coincide con il fatto che praticamente tutti gli sviluppatori utilizzano il sistema operativo Ubuntu. E su Ubuntu, il primo utente ha ID 1000.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/d4911374e4953fdcd807c387efeafec3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Quali sono i nostri piani?<\/p>\n<p><\/p>\n<p>Rileggere la documentazione su Docker. Il progetto si sta sviluppando attivamente, la documentazione cambia. I dati ottenuti due-tre mesi fa stanno gi\u00e0 cominciando a diventare obsoleti. <\/p>\n<p><\/p>\n<p>Alcuni dei problemi che abbiamo risolto sono gi\u00e0 stati probabilmente risolti con strumenti standard.<\/p>\n<p><\/p>\n<p>Ho molta voglia di andare avanti e passare direttamente all'orchestrazione.<\/p>\n<p><\/p>\n<p>Uno dei esempi \u00e8 il meccanismo integrato in Docker chiamato Docker Swarm, che \u00e8 fornito di default. Vorrei avviare qualcosa in produzione basato sulla tecnologia Docker Swarm.<\/p>\n<p><\/p>\n<p>La generazione di contenitori rende scomodo lavorare con i log. Attualmente, i log sono isolati. Sono sparsi tra i contenitori. Una delle sfide \u00e8 rendere l'accesso ai log semplice tramite un'interfaccia web.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Processo di sviluppo e test con Docker e Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/cff8c72dd8af7de1fa0227601bd1d69c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/449742\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0421\u0438\u0433\u0430\u0447\u0435\u0432\u0430 \u0438\u0437 Inventos &#171;\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker + Gitlab CI&#187; \u0422\u0435, \u043a\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 \u0431\u0430\u0437\u0435 Docker + Gitlab CI \u0447\u0430\u0441\u0442\u043e \u0441\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u044e\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b. \u0421 \u0447\u0435\u0433\u043e \u043d\u0430\u0447\u0430\u0442\u044c? \u041a\u0430\u043a \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c? \u041a\u0430\u043a \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c? \u042d\u0442\u043e\u0442 \u0434\u043e\u043a\u043b\u0430\u0434 \u0445\u043e\u0440\u043e\u0448 \u0442\u0435\u043c, \u0447\u0442\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":24326,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-32526","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0421\u0438\u0433\u0430\u0447\u0435\u0432\u0430 \u0438\u0437 Inventos &quot;\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker + Gitlab CI&quot; \u0422\u0435, \u043a\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker \u0438 Gitlab CI | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0421\u0438\u0433\u0430\u0447\u0435\u0432\u0430 \u0438\u0437 Inventos &quot;\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker + Gitlab CI&quot; \u0422\u0435, \u043a\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T18:47:32+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T18:47:32+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Processo di sviluppo e test con Docker e Gitlab CI | ProHoster","description":"Propongo di dare un'occhiata alla trascrizione della relazione di Aleksandr Sigacev di Inventos \"Processo di sviluppo e test con Docker + Gitlab CI\". Coloro che stanno appena iniziando a implementare il processo di sviluppo.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker \u0438 Gitlab CI | ProHoster","og:description":"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0421\u0438\u0433\u0430\u0447\u0435\u0432\u0430 \u0438\u0437 Inventos &quot;\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker + Gitlab CI&quot; \u0422\u0435, \u043a\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438.","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T18:47:32+00:00","article:modified_time":"2019-10-31T18:47:32+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"32526","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-21 11:18:24","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 02:57:22","updated":"2026-01-21 11:18:24","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/32526","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=32526"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/32526\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/24326"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=32526"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=32526"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=32526"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}