{"id":69351,"date":"2020-02-18T14:44:06","date_gmt":"2020-02-18T11:44:06","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker"},"modified":"2020-03-03T16:15:00","modified_gmt":"2020-03-03T13:15:00","slug":"sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","title":{"rendered":"Creazione di una pipeline CI\/CD e automazione del lavoro con Docker","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Ho creato i miei primi siti alla fine degli anni '90. Allora era molto semplice metterli in funzione. C'era un server Apache su qualche hosting condiviso, potevi accedere a questo server via FTP, inserendo nella barra del browser qualcosa come <code>ftp:\/\/ftp.example.com<\/code>. Poi bisognava inserire nome e password e caricare i file sul server. Altri tempi, era tutto pi\u00f9 semplice allora rispetto a oggi.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/488668\/\"><img decoding=\"async\" alt=\"Creazione di una pipeline CI\/CD e automazione del lavoro con Docker\" src=\"\/wp-content\/uploads\/2020\/02\/0861cdd82770ef4695ffac7a09777c52.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><\/p>\n<p>Negli ultimi due decenni, \u00e8 cambiato molto. I siti sono diventati pi\u00f9 complessi e, prima di essere messi in produzione, devono essere costruiti. Un singolo server \u00e8 diventato un insieme di server che operano dietro bilanciatori di carico, l'uso dei sistemi di controllo versione \u00e8 diventato normale.<\/p>\n<p>Per il mio progetto personale avevo una configurazione speciale. E sapevo che avevo bisogno di poter implementare il sito in produzione con un'unica azione: pubblicare il codice in un ramo. <code>master<\/code> su GitHub. Inoltre, sapevo che per far funzionare la mia piccola applicazione web non volevo gestire un enorme cluster Kubernetes, n\u00e9 usare la tecnologia Docker Swarm, n\u00e9 mantenere un parco server con pod, agenti e tutte le altre complicazioni. Per raggiungere l'obiettivo di semplificare al massimo il lavoro, ho dovuto familiarizzare con CI\/CD.<\/p>\n<p>Se hai un piccolo progetto (nel nostro caso si tratta di un progetto Node.js) e desideri scoprire come automatizzare il deployment di questo progetto, assicurandoti che ci\u00f2 che \u00e8 archiviato nel repository corrisponda esattamente a ci\u00f2 che funziona in produzione, penso che questo articolo possa interessarti.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Requisiti preliminari<\/h2>\n<p>\nSi prevede che il lettore di questo articolo abbia una conoscenza di base dell'uso della riga di comando e della scrittura di script Bash. Inoltre, saranno necessarie delle account. <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">Travis CI<\/a><\/noindex> e <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a><\/noindex>.<\/p>\n<h2>Obiettivi<\/h2>\n<p>\nNon dir\u00f2 che questo articolo possa essere definito senza riserve come un 'manuale'. \u00c8 piuttosto un documento in cui condivido ci\u00f2 che ho imparato e descrivo il processo di test e deployment del codice in produzione che mi soddisfa, eseguito in un'unica passata automatizzata.<\/p>\n<p>Ecco come \u00e8 risultato il mio flusso di lavoro.<\/p>\n<p>Per il codice inviato in qualsiasi ramo del repository, eccetto <code>master<\/code>, vengono effettuate le seguenti azioni:<\/p>\n<ul>\n<li>Viene avviata la build del progetto su Travis CI.<\/li>\n<li>Vengono eseguiti tutti i test unitari, di integrazione e end-to-end.<\/li>\n<\/ul>\n<p>\nSolo per il codice che arriva in <code>master<\/code>, viene eseguito il seguente processo:<\/p>\n<ul>\n<li>Tutto ci\u00f2 che \u00e8 stato detto sopra, pi\u00f9\u2026<\/li>\n<li>Creazione dell'immagine Docker basata sul codice attuale, sulle impostazioni e sull'ambiente.<\/li>\n<li>Pubblicazione dell'immagine su Docker Hub.<\/li>\n<li>Connessione al server di produzione.<\/li>\n<li>Download dell'immagine da Docker Hub sul server.<\/li>\n<li>Arresto del contenitore attuale e avvio di uno nuovo basato sulla nuova immagine.<\/li>\n<\/ul>\n<p>\nSe non sai assolutamente nulla su Docker, immagini e contenitori, non preoccuparti. Ti spiegher\u00f2 tutto.<\/p>\n<h2>Che cos'\u00e8 il CI\/CD?<\/h2>\n<p>\nL'abbreviazione CI\/CD sta per \u00abintegrazione continua \/ distribuzione continua\u00bb.<\/p>\n<h3>\u258dIntegrazione continua<\/h3>\n<p>\nL'integrazione continua \u00e8 un processo in cui gli sviluppatori effettuano commit nel repository principale del codice sorgente del progetto (di solito nel branch <code>master<\/code>). La qualit\u00e0 del codice \u00e8 garantita attraverso test automatizzati.<\/p>\n<h3>\u258dDistribuzione continua<\/h3>\n<p>\nLa distribuzione continua \u00e8 un'implementazione automatizzata frequente del codice in produzione. La seconda parte dell'abbreviazione CI\/CD viene talvolta interpretata come \u00abconsegna continua\u00bb. Questo \u00e8, in generale, lo stesso della \u00abdistribuzione continua\u00bb, ma la \u00abconsegna continua\u00bb implica la necessit\u00e0 di una conferma manuale delle modifiche prima di avviare il processo di distribuzione del progetto.<\/p>\n<h2>Inizio<\/h2>\n<p>\nL'applicazione su cui ho imparato tutto questo si chiama <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/taniarascia\/takenote\">TakeNote<\/a><\/noindex>. Questo \u00e8 un progetto web su cui sto lavorando, destinato a prendere appunti. Inizialmente ho cercato di realizzare <noindex><a rel=\"nofollow\" href=\"https:\/\/jamstack.org\/\">JAMStack<\/a><\/noindex>- progetto, o solo un'applicazione front-end senza server, per sfruttare le funzionalit\u00e0 standard di hosting e deploy dei progetti offerte da <noindex><a rel=\"nofollow\" href=\"https:\/\/www.netlify.com\/\">Netlify<\/a><\/noindex>. Man mano che la complessit\u00e0 dell'applicazione aumentava, ho dovuto creare anche la sua parte server, il che significava che dovevo sviluppare una mia strategia per l'integrazione automatizzata e il deploy automatizzato del progetto.<\/p>\n<p>Nel mio caso, l'applicazione \u00e8 un server Express che funziona in un ambiente Node.js, che serve un'applicazione React a pagina singola e supporta un'API server protetta. Questa architettura segue una strategia che si pu\u00f2 trovare in <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/full-stack-cookies-localstorage-react-express\/\">questa<\/a><\/noindex> guida all'autenticazione full-stack.<\/p>\n<p>Ho consultato un <noindex><a rel=\"nofollow\" href=\"http:\/\/qualitytesting.tech\/\">amico<\/a><\/noindex>, che \u00e8 esperto in automazione, e gli ho chiesto cosa devo fare affinch\u00e9 tutto funzioni come desidero. Mi ha suggerito un'idea su come dovrebbe apparire il flusso di lavoro automatizzato, descritto nella sezione 'Obiettivi' di questo articolo. Il fatto di aver stabilito tali obiettivi significava che dovevo capire come utilizzare Docker.<\/p>\n<h2>Docker<\/h2>\n<p>\nDocker \u00e8 uno strumento che, grazie alla tecnologia della containerizzazione, consente di distribuire facilmente le applicazioni e di eseguirle nello stesso ambiente, anche se la piattaforma Docker opera in ambienti diversi. Per iniziare, avevo bisogno di avere a disposizione gli strumenti da riga di comando (CLI) di Docker. <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/install\/\">Le istruzioni<\/a><\/noindex> per l'installazione di Docker non sono molto chiare e comprensibili, ma si pu\u00f2 apprendere che per fare il primo passo nell'installazione \u00e8 necessario scaricare Docker Desktop (per Mac o Windows).<\/p>\n<p>Docker Hub \u00e8 pi\u00f9 o meno equivalente a <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/\">GitHub<\/a><\/noindex> per i repository git, o il registro <noindex><a rel=\"nofollow\" href=\"https:\/\/www.npmjs.com\/\">npm<\/a><\/noindex> per i pacchetti JavaScript. \u00c8 un repository online per le immagini Docker. \u00c8 a questo a cui si collega Docker Desktop.<\/p>\n<p>Per iniziare a lavorare con Docker, bisogna fare due cose:<\/p>\n<ul>\n<li>Installa <noindex><a rel=\"nofollow\" href=\"https:\/\/www.docker.com\/get-started\">Docker Desktop<\/a><\/noindex>.<\/li>\n<li>Registrati su <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>\nDopo di che, puoi verificare se Docker CLI funziona eseguendo il seguente comando per controllare la versione di Docker:<\/p>\n<pre><code class=\"plaintext\">docker -v<\/code><\/pre>\n<p>\nSuccessivamente, accedi a Docker Hub inserendo il tuo nome utente e password quando richiesto:<\/p>\n<pre><code class=\"plaintext\">docker login<\/code><\/pre>\n<p>\nPer utilizzare Docker, devi comprendere i concetti di immagini e contenitori.<\/p>\n<h3>\u258dImmagini<\/h3>\n<p>\nUn'immagine \u00e8 come un piano che contiene istruzioni per costruire un contenitore. \u00c8 un'istantanea immutabile del file system e delle impostazioni dell'applicazione. Gli sviluppatori possono facilmente condividere le immagini.<\/p>\n<pre><code class=\"plaintext\"># \u0412\u044b\u0432\u043e\u0434 \u0441\u0432\u0435\u0434\u0435\u043d\u0438\u0439 \u043e\u0431\u043e \u0432\u0441\u0435\u0445 \u043e\u0431\u0440\u0430\u0437\u0430\u0445\ndocker images<\/code><\/pre>\n<p>\nQuesto comando restituir\u00e0 una tabella con il seguente titolo:<\/p>\n<pre><code class=\"plaintext\">REPOSITORY \u00a0 \u00a0 TAG     IMAGE ID \u00a0 \u00a0 CREATED     SIZE\n---<\/code><\/pre>\n<p>\nIn seguito, esamineremo alcuni esempi di comandi in questo stesso formato \u2014 prima il comando con un commento, poi un esempio di ci\u00f2 che potrebbe restituire.<\/p>\n<h3>\u258dContenitori<\/h3>\n<p>\nUn container \u00e8 un pacchetto eseguibile che contiene tutto il necessario per eseguire un'applicazione. Con questo approccio, l'applicazione funzioner\u00e0 sempre allo stesso modo, indipendentemente dall'infrastruttura: in un ambiente isolato e nella stessa configurazione. Si tratta di eseguire istanze della stessa immagine in ambienti diversi.<\/p>\n<pre><code class=\"plaintext\"># \u041f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432\ndocker ps -a\nCONTAINER ID \u00a0 \u00a0 IMAGE     COMMAND \u00a0 \u00a0 CREATED     STATUS     PORTS \u00a0 \u00a0 NAMES\n---<\/code><\/pre>\n<p><\/p>\n<h3>\u258dTag<\/h3>\n<p>\nUn tag \u00e8 un'indicazione di una versione specifica dell'immagine.<\/p>\n<h3>\u258dPanoramica dei comandi Docker<\/h3>\n<p>\nEcco una panoramica di alcuni dei comandi Docker pi\u00f9 utilizzati.<\/p>\n<p>Team<\/p>\n<p>Contesto<\/p>\n<p>Azione<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">docker build<\/a><\/noindex><\/p>\n<p>Immagine<\/p>\n<p>Costruzione di un'immagine da Dockerfile<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/tag\/\">docker tag<\/a><\/noindex><\/p>\n<p>Immagine<\/p>\n<p>Tagging dell'immagine<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/images\/\">docker images<\/a><\/noindex><\/p>\n<p>Immagine<\/p>\n<p>Visualizzazione dell'elenco delle immagini<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/run\/\">docker run<\/a><\/noindex><\/p>\n<p>Container<\/p>\n<p>Avvio di un container basato su un'immagine<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/push\/\">docker push<\/a><\/noindex><\/p>\n<p>Immagine<\/p>\n<p>Invio dell'immagine nel registry<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/about:blank\">docker pull<\/a><\/noindex><\/p>\n<p>Immagine<\/p>\n<p>Download dell'immagine dal registry<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/ps\/\">docker ps<\/a><\/noindex><\/p>\n<p>Container<\/p>\n<p>Visualizzazione dell'elenco dei container<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/system_prune\/\">docker system prune<\/a><\/noindex><\/p>\n<p>Immagine\/Container<\/p>\n<p>Rimozione di container e immagini non utilizzati<\/p>\n<h3>\u258dFile Dockerfile<\/h3>\n<p>\nSo come eseguire un'applicazione in locale per la produzione. Ho una configurazione Webpack progettata per costruire un'applicazione React pronta. Successivamente, ho un comando che avvia un server basato su Node.js sulla porta <code>5000<\/code>. Ecco come si presenta:<\/p>\n<pre><code class=\"plaintext\">npm i \u00a0 \u00a0 \u00a0 \u00a0 # installazione delle dipendenze\nnpm run build # creazione dell'applicazione React\nnpm run start # avvio del server Node<\/code><\/pre>\n<p>\nVa notato che non ho un'applicazione di esempio per questo materiale. Ma qui, per esperimenti, andr\u00e0 bene qualsiasi semplice applicazione Node.<\/p>\n<p>Per utilizzare il contenitore, dovrai fornire istruzioni a Docker. Questo si fa tramite un file chiamato <code>Dockerfile<\/code>, che si trova nella directory principale del progetto. Questo file, all'inizio, pu\u00f2 sembrare piuttosto complicato.<\/p>\n<p>Ma ci\u00f2 che contiene \u00e8 solo una descrizione, con comandi speciali, di qualcosa di simile alla configurazione dell'ambiente di lavoro. Ecco alcuni di questi comandi:<\/p>\n<ul>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#from\">DA<\/a><\/noindex> \u2014 Questo comando avvia il file. Specifica l'immagine di base su cui viene costruito il contenitore.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#copy\">COPY<\/a><\/noindex> \u2014 Copiando file da una fonte locale al contenitore.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#workdir\">WORKDIR<\/a><\/noindex> \u2014 Imposta la directory di lavoro per i comandi successivi.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#run\">RUN<\/a><\/noindex> \u2014 Esecuzione dei comandi.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#expose\">EXPOSE<\/a><\/noindex> \u2014 Configurazione della porta.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#entrypoint\">ENTRYPOINT<\/a><\/noindex> \u2014 Specifica il comando da eseguire.<\/li>\n<\/ul>\n<p>\n<code>Dockerfile<\/code> pu\u00f2 apparire circa cos\u00ec:<\/p>\n<pre><code class=\"plaintext\"># \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043e\u0431\u0440\u0430\u0437\nFROM node:12-alpine\n\n# \u0421\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u0438\u0437 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e app\/\nCOPY . app\/\n\n# \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c app\/ \u0432 \u0440\u043e\u043b\u0438 \u0440\u0430\u0431\u043e\u0447\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438\nWORKDIR app\/\n\n# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 (\u043a\u043e\u043c\u0430\u043d\u0434\u0430 npm ci \u043f\u043e\u0445\u043e\u0436\u0430 npm i, \u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0441\u0431\u043e\u0440\u043e\u043a)\nRUN npm ci --only-production\n\n# \u0421\u043e\u0431\u0440\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0435 React-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d\u0430\nRUN npm run build\n\n# \u041f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442\nEXPOSE 5000\n\n# \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c Node-\u0441\u0435\u0440\u0432\u0435\u0440\nENTRYPOINT npm run start<\/code><\/pre>\n<p>\nA seconda dell'immagine di base scelta, potrebbe essere necessario installare dipendenze aggiuntive. Alcune immagini di base (come Node Alpine Linux) sono state create per essere il pi\u00f9 compatte possibile. Di conseguenza, potrebbero mancare alcuni programmi su cui conti.<\/p>\n<h3>\u258dCostruzione, tagging e avvio del container<\/h3>\n<p>\nLa costruzione e l'avvio locale del container sono, dopo che abbiamo <code>Dockerfile<\/code>, compiti piuttosto semplici. Prima di inviare l'immagine su Docker Hub, deve essere testata localmente.<\/p>\n<h3>\u258dCostruzione<\/h3>\n<p>\nPrima di tutto, devi costruire <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">immagine<\/a><\/noindex>, specificando un nome e, se necessario, un tag (se non viene specificato un tag, il sistema assegner\u00e0 automaticamente un tag all'immagine <code>latest<\/code>).<\/p>\n<pre><code class=\"plaintext\"># \u0421\u0431\u043e\u0440\u043a\u0430 \u043e\u0431\u0440\u0430\u0437\u0430\ndocker build -t &lt;image&gt;:&lt;tag&gt; .<\/code><\/pre>\n<p>\nDopo aver eseguito questo comando, puoi vedere come Docker sta eseguendo la costruzione dell'immagine.<\/p>\n<pre><code class=\"plaintext\">Invio del contesto di costruzione al demone Docker \u00a0 2.88MB\nPasso 1\/9 : FROM node:12-alpine\n\u00a0---&gt; ...esecuzione delle fasi di costruzione...\nCostruzione riuscita 123456789123\nTaggato con successo &lt;image&gt;:&lt;tag&gt;<\/code><\/pre>\n<p>\nLa costruzione pu\u00f2 richiedere un paio di minuti \u2014 tutto dipende da quante dipendenze hai. Dopo il completamento della costruzione, puoi eseguire il comando <code>docker images<\/code> e guardare la descrizione della tua nuova immagine.<\/p>\n<pre><code class=\"plaintext\">REPOSITORY          TAG               IMAGE ID            CREATED              SIZE\n             latest            123456789123        About a minute ago   x.xxGB<\/code><\/pre>\n<p><\/p>\n<h3>\u258dAvvio<\/h3>\n<p>\nL'immagine \u00e8 stata creata. Questo significa che si pu\u00f2 avviare un container basato su di essa. Poich\u00e9 voglio avere la possibilit\u00e0 di accedere all'applicazione che gira nel container all'indirizzo <code>localhost:5000<\/code>, ho impostato nella parte sinistra della coppia <code>5000:5000<\/code> nel comando seguente. <code>5000<\/code>. Nella parte destra si trova la porta del container.<\/p>\n<pre><code class=\"plaintext\"># \u0417\u0430\u043f\u0443\u0441\u043a \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u0440\u0442\u0430 5000 \u0438 \u043f\u043e\u0440\u0442\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 5000\ndocker run -p 5000:5000 &lt;image&gt;:&lt;tag&gt;<\/code><\/pre>\n<p>\nOra che il container \u00e8 stato creato e avviato, posso utilizzare il comando <code>docker ps<\/code> per dare un'occhiata alle informazioni su questo container (oppure posso utilizzare il comando <code>docker ps -a<\/code>, che restituisce informazioni su tutti i container, non solo su quelli in esecuzione).<\/p>\n<pre><code class=\"plaintext\">CONTAINER ID\u00a0 \u00a0 \u00a0 \u00a0 IMAGE               COMMAND\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 CREATED              STATUS                  \u00a0 \u00a0 PORTS                    NAMES\n987654321234\u00a0 \u00a0 \u00a0 \u00a0 &lt;image&gt;             &quot;\/bin\/sh -c 'npm run\u2026&quot; \u00a0 6 seconds ago        Up 6 seconds            \u00a0 \u00a0 0.0.0.0:5000-&gt;5000\/tcp   stoic_darwin<\/code><\/pre>\n<p>\nSe ora accedi all'indirizzo <code>localhost:5000<\/code> puoi vedere la pagina dell'applicazione in esecuzione, che appare esattamente come la pagina dell'applicazione in esecuzione nell'ambiente di produzione.<\/p>\n<h3>\u258dAssegnazione del tag e pubblicazione<\/h3>\n<p>\nPer utilizzare una delle immagini create su un server di produzione, \u00e8 necessario avere la possibilit\u00e0 di caricare quest'immagine da Docker Hub. Questo significa che prima bisogna creare un repository su Docker Hub per il progetto. Dopo di che avremo a disposizione uno spazio in cui inviare l'immagine. L'immagine deve essere rinominata in modo che il suo nome inizi con il nostro nome utente su Docker Hub. Dopo di questo deve seguire il nome del repository. Alla fine del nome pu\u00f2 esserci qualsiasi tag. Di seguito \u00e8 mostrato un esempio di denominazione delle immagini secondo questo schema.<\/p>\n<p>Ora possiamo costruire l'immagine assegnandole un nuovo nome e eseguire il comando <code>docker push<\/code> per inviarla nel repository Docker Hub.<\/p>\n<pre><code class=\"plaintext\">docker build -t \/: .\ndocker tag \/: \/:latest\ndocker push \/:\n\n# In pratica, questo pu\u00f2 apparire, ad esempio, cos\u00ec:\ndocker build -t user\/app:v1.0.0 .\ndocker tag user\/app:v1.0.0 user\/app:latest\ndocker push user\/app:v1.0.0<\/code><\/pre>\n<p>\nSe tutto va come previsto, l'immagine sar\u00e0 disponibile su Docker Hub e potr\u00e0 essere facilmente caricata sul server o condivisa con altri sviluppatori.<\/p>\n<h2>Prossimi passi<\/h2>\n<p>\nFino ad ora, abbiamo verificato che l'applicazione, in forma di contenitore Docker, funziona localmente. Abbiamo caricato il contenitore su Docker Hub. Questo significa che abbiamo gi\u00e0 fatto significativi progressi verso l'obiettivo. Ora dobbiamo affrontare altre due questioni:<\/p>\n<ul>\n<li>Configurazione dello strumento CI per testare e distribuire il codice.<\/li>\n<li>Impostazione del server di produzione in modo che possa caricare e avviare il nostro codice.<\/li>\n<\/ul>\n<p>\nNel nostro caso, come soluzione CI\/CD \u00e8 utilizzato <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">Travis CI<\/a><\/noindex>. Come server \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/\">DigitalOcean<\/a><\/noindex>.<\/p>\n<p>Va notato che qui \u00e8 possibile utilizzare anche un'altra combinazione di servizi. Ad esempio, invece di Travis CI, si possono utilizzare CircleCI o GitHub Actions. E invece di DigitalOcean \u2014 AWS o Linode.<\/p>\n<p>Abbiamo deciso di lavorare con Travis CI, e in questo servizio ho gi\u00e0 alcune impostazioni. Quindi ora descriver\u00f2 brevemente come prepararlo per l'uso.<\/p>\n<h2>Travis CI<\/h2>\n<p>\nTravis CI \u00e8 uno strumento per il testing e il deployment del codice. Non voglio entrare nei dettagli della configurazione di Travis CI, poich\u00e9 ogni progetto \u00e8 unico e ci\u00f2 non sarebbe particolarmente utile. Tuttavia, posso spiegare le basi per aiutarvi a iniziare nel caso decidiate di utilizzare Travis CI. Qualunque cosa scegliate \u2014 Travis CI, CircleCI, Jenkins o altro \u2014 metodi di configurazione simili verranno applicati ovunque.<\/p>\n<p>Per iniziare a lavorare con Travis CI, visitate <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">il sito del progetto<\/a><\/noindex> e create un account. Poi integrate Travis CI con il vostro account GitHub. Durante la configurazione dovrete specificare il repository con cui desiderate automatizzare il lavoro e fornire accesso a esso. (Utilizzo GitHub, ma sono certa che Travis CI possa integrarsi anche con BitBucket, GitLab e altri servizi simili).<\/p>\n<p>Ogni volta che Travis CI inizia a lavorare, un server viene avviato ed esegue i comandi specificati nel file di configurazione, compreso il deployment delle relative branche del repository.<\/p>\n<h3>\u258dCiclo di vita del compito<\/h3>\n<p>\nIl file di configurazione di Travis CI, chiamato <code>.travis.yml<\/code> e memorizzato nella directory principale del progetto, supporta il concetto di eventi <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.travis-ci.com\/user\/job-lifecycle\/\">del ciclo di vita<\/a><\/noindex> di un\u2019attivit\u00e0. Questi sono gli eventi, presentati nell'ordine in cui si verificano:<\/p>\n<ul>\n<li><code>apt addons<\/code><\/li>\n<li><code>cache components<\/code><\/li>\n<li><code>before_install<\/code><\/li>\n<li><code>install<\/code><\/li>\n<li><code>before_script<\/code><\/li>\n<li><code>script<\/code><\/li>\n<li><code>before_cache<\/code><\/li>\n<li><code>after_success o after_failure<\/code><\/li>\n<li><code>before_deploy<\/code><\/li>\n<li><code>deploy<\/code><\/li>\n<li><code>after_deploy<\/code><\/li>\n<li><code>after_script<\/code><\/li>\n<\/ul>\n<p><\/p>\n<h3>\u258dTesting<\/h3>\n<p>\nNel file di configurazione, andr\u00f2 a impostare un server locale Travis CI. Come linguaggio ho scelto Node versione 12 e ho indicato al sistema di installare le dipendenze necessarie per utilizzare Docker.<\/p>\n<p>Tutto ci\u00f2 che \u00e8 elencato in <code>.travis.yml<\/code>, verr\u00e0 eseguito per tutte le pull request su tutti i rami del repository, a meno che non venga specificato diversamente. Questa \u00e8 una funzionalit\u00e0 utile, poich\u00e9 significa che possiamo testare tutto il codice che viene nel repository. Questo ci permette di sapere se il codice \u00e8 pronto per essere scritto nel ramo <code>master<\/code>, e non romper\u00e0 il processo di build del progetto. In questa configurazione globale, installo tutto localmente, avvio il server di sviluppo Webpack in background (questa \u00e8 una caratteristica del mio flusso di lavoro) e faccio eseguire i test.<\/p>\n<p>Se desideri che nel tuo repository vengano visualizzati badge con informazioni sulla copertura del codice nei test, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/display-build-status-and-test-coverage\/\">qui<\/a><\/noindex> puoi trovare una breve guida all'uso di Jest, Travis CI e Coveralls per raccogliere e visualizzare queste informazioni.<\/p>\n<p>Quindi, ecco il contenuto del file <code>.travis.yml<\/code>:<\/p>\n<pre><code class=\"plaintext\"># \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u044f\u0437\u044b\u043a\nlanguage: node_js\n\n# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u044e Node.js\nnode_js:\n\u00a0\u00a0- '12'\n\nservices:\n\u00a0\u00a0# \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 Docker\n\u00a0\u00a0- docker\n\ninstall:\n\u00a0\u00a0# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432\n\u00a0\u00a0- npm ci\n\nbefore_script:\n\u00a0\u00a0# \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432\n\u00a0\u00a0- npm run dev &amp;\n\nscript:\n\u00a0\u00a0# \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u044b\n\u00a0\u00a0- npm run test<\/code><\/pre>\n<p>\nQui terminano le azioni eseguite per tutti i rami del repository e per le pull request.<\/p>\n<h3>\u258dDistribuzione<\/h3>\n<p>\nPresumendo che tutti i test automatici siano stati eseguiti con successo, possiamo distribuire il codice sul server di produzione, il che non \u00e8 obbligatorio. Poich\u00e9 vogliamo farlo solo per il codice proveniente dal ramo <code>master<\/code>, diamo al sistema le istruzioni appropriate nelle impostazioni di distribuzione. Prima di provare a utilizzare nel tuo progetto il codice che esamineremo di seguito, vorrei avvisarti che \u00e8 necessario avere uno script reale che venga chiamato per la distribuzione.<\/p>\n<pre><code class=\"plaintext\">deploy:\n\u00a0\u00a0# Compila il contenitore Docker e invialo a Docker Hub\n\u00a0\u00a0provider: script\n\u00a0\u00a0script: bash deploy.sh\n\u00a0\u00a0on:\n\u00a0\u00a0\u00a0\u00a0branch: master<\/code><\/pre>\n<p>\nLo script di distribuzione svolge due compiti:<\/p>\n<ul>\n<li>Compilazione, tagging e invio dell'immagine a Docker Hub tramite lo strumento CI (in questo caso, Travis CI).<\/li>\n<li>Caricamento dell'immagine sul server, arresto del vecchio contenitore e avvio di quello nuovo (in questo caso, il server \u00e8 ospitato su DigitalOcean).<\/li>\n<\/ul>\n<p>\nPer prima cosa, \u00e8 necessario configurare un processo automatico per la creazione, il tagging e l'invio dell'immagine a Docker Hub. Tutto questo \u00e8 molto simile a ci\u00f2 che abbiamo gi\u00e0 fatto manualmente, tranne per il fatto che abbiamo bisogno di una strategia per assegnare ai tag immagini unici e automatizzare l'accesso. Ho avuto alcune difficolt\u00e0 con i dettagli dello script di distribuzione, come la strategia di tagging, l'accesso, la codifica delle chiavi SSH e l'instaurazione di una connessione SSH. Ma, per fortuna, il mio ragazzo \u00e8 molto bravo con bash, cos\u00ec come con molte altre cose. Mi ha aiutato a scrivere questo script.<\/p>\n<p>Quindi, la prima parte dello script \u00e8 l'invio dell'immagine a Docker Hub. \u00c8 abbastanza semplice. Lo schema di tagging che ho utilizzato prevede la combinazione dell'hash git e del tag git, se disponibile. Questo assicura la creazione di un tag unico e semplifica l'identificazione della build sulla quale si basa. <code>DOCKER_USERNAME<\/code> e <code>DOCKER_PASSWORD<\/code> \u2014 queste sono variabili d'ambiente personalizzate, che possono essere impostate tramite l'interfaccia di Travis CI. Travis CI gestir\u00e0 automaticamente i dati sensibili in modo che non finiscano nelle mani sbagliate.<\/p>\n<p>Ecco la prima parte dello script <code>deploy.sh<\/code>.<\/p>\n<pre><code class=\"plaintext\">#!\/bin\/sh\nset -e # \u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u043a\u0440\u0438\u043f\u0442 \u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u043e\u0448\u0438\u0431\u043e\u043a\n\nIMAGE=&quot;&lt;username&gt;\/&lt;repository&gt;&quot; \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 # \u041e\u0431\u0440\u0430\u0437 Docker\nGIT_VERSION=$(git describe --always --abbrev --tags --long) # Git-\u0445\u044d\u0448 \u0438 \u0442\u0435\u0433\u0438\n\n# \u0421\u0431\u043e\u0440\u043a\u0430 \u0438 \u0442\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0437\u0430\ndocker build -t ${IMAGE}:${GIT_VERSION} .\ndocker tag ${IMAGE}:${GIT_VERSION} ${IMAGE}:latest\n\n# \u0412\u0445\u043e\u0434 \u0432 Docker Hub \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u043a\u0430 \u043e\u0431\u0440\u0430\u0437\u0430\necho &quot;${DOCKER_PASSWORD}&quot; | docker login -u &quot;${DOCKER_USERNAME}&quot; --password-stdin\ndocker push ${IMAGE}:${GIT_VERSION}<\/code><\/pre>\n<p>\nLa configurazione della seconda parte dello script dipende completamente dal provider di hosting che utilizzi e da come \u00e8 organizzata la connessione a esso. Nel mio caso, dato che utilizzo Digital Ocean, per connettermi al server utilizzo il comando <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/digitalocean\/doctl\">doctl<\/a><\/noindex>. Quando si lavora con AWS, si utilizzer\u00e0 l'utility <code>aws<\/code>, e cos\u00ec via.<\/p>\n<p>Impostare il funzionamento del server non \u00e8 stato particolarmente difficile. Ho configurato un droplet basato su un'immagine di base. Va notato che il sistema da me scelto richiede un'installazione manuale singola di Docker e un avvio manuale singolo di Docker. Per installare Docker, ho utilizzato Ubuntu 18.04, quindi se utilizzate anche Ubuntu, potete semplicemente seguire <noindex><a rel=\"nofollow\" href=\"https:\/\/phoenixnap.com\/kb\/how-to-install-docker-on-ubuntu-18-04\">questa<\/a><\/noindex> semplice guida.<\/p>\n<p>Non menziono qui i comandi specifici per il servizio, poich\u00e9 questo aspetto pu\u00f2 variare notevolmente in base ai casi. Fornir\u00f2 solo un piano generale di azione da seguire dopo essersi connessi tramite SSH al server su cui verr\u00e0 distribuito il progetto:<\/p>\n<ul>\n<li>\u00c8 necessario trovare il contenitore attualmente in esecuzione e fermarlo.<\/li>\n<li>Successivamente, \u00e8 necessario lanciare un nuovo contenitore in background.<\/li>\n<li>Dovrai impostare la porta locale del server su <code>80<\/code> \u2014 questo ti permetter\u00e0 di accedere al sito all'indirizzo del tipo <code>example.com<\/code>, senza specificare la porta, invece di usare un indirizzo come <code>example.com:5000<\/code>.<\/li>\n<li>E infine, \u00e8 necessario rimuovere tutti i vecchi container e immagini.<\/li>\n<\/ul>\n<p>\nEcco la continuazione dello script.<\/p>\n<pre><code class=\"plaintext\"># \u041d\u0430\u0439\u0442\u0438 ID \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0435\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430\nCONTAINER_ID=$(docker ps | grep takenote | cut -d&quot; &quot; -f1)\n\n# \u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0442\u0430\u0440\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439, \u043e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u0443\ndocker stop ${CONTAINER_ID}\ndocker run --restart unless-stopped -d -p 80:5000 ${IMAGE}:${GIT_VERSION}\ndocker system prune -a -f<\/code><\/pre>\n<p><\/p>\n<h2>Alcune cose a cui prestare attenzione<\/h2>\n<p>\n\u00c8 possibile che, collegandoti al server via SSH da Travis CI, tu possa vedere un avviso che non permette di continuare l'installazione, poich\u00e9 il sistema attender\u00e0 una risposta dall'utente.<\/p>\n<pre><code class=\"plaintext\">L'autenticit\u00e0 dell'host '&lt;hostname&gt; (&lt;IP address&gt;)' non pu\u00f2 essere stabilita.\nLa chiave RSA fingerprint \u00e8 &lt;key fingerprint&gt;.\nVuoi davvero continuare a collegarti (s\u00ec\/no)?<\/code><\/pre>\n<p>\nHo scoperto che la chiave in formato stringa pu\u00f2 essere codificata in base64 per essere conservata in una forma comoda e sicura. Durante l'installazione, puoi decodificare la chiave pubblica e scriverla in un file <code>known_hosts<\/code> per eliminare l'errore descritto sopra.<\/p>\n<pre><code class=\"plaintext\">echo &lt;public key&gt; | base64 # restituisce &lt;la chiave pubblica codificata in base64&gt;<\/code><\/pre>\n<p>\nIn pratica, questo comando pu\u00f2 apparire cos\u00ec:<\/p>\n<pre><code class=\"plaintext\">echo &quot;123.45.67.89 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom\/BWDSU\nGPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3\nPbv7kOdJ\/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK\/7XA\nt3FaoJoAsncM1Q9x5+3V0Ww68\/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw\/Pb0rwert\/En\nmZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z\/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx\nNrRFi9wrf+M7Q== you@example.com&quot; | base64<\/code><\/pre>\n<p>\nEcco come appare ci\u00f2 che restituisce \u2014 una stringa in codifica base64:<\/p>\n<pre><code class=\"plaintext\">MTIzLjQ1LjY3Ljg5IHNzaC1yc2EgQUFBQUIzTnphQzF5YzJFQUFBQUJJd0FBQVFFQWtsT1Vwa0RIcmZIWTE3U2JybVRJcE5MVEdLOVRqb20vQldEU1UKR1BsK25hZnpsSERUWVc3aGRJNHlaNWV3MThKSDRKVzlqYmhVRnJ2aVF6TTd4bEVMRVZmNGg5bEZYNVFWa2JQcHBTd2cwY2RhMwpQYnY3a09kSi9NVHlCbFdYRkNSK0hBbzNGWFJpdEJxeGlYMW5LaFhwSEFac01jaUxxOFY2UmpzTkFRd2RzZE1GdlNsVksvN1hBCnQzRmFvSm9Bc25jTTFROXg1KzNWMFd3NjgvZUlGbWIxenVVRmxqUUpLcHJyWDg4WHlwTkR2allOYnk2dncvUGIwcndlcnQvRW4KbVorQVc0T1pQblRQSTg5WlBtVk1MdWF5ckQyY0U4NlovaWw4YitndzNyMysxbkthdG1Ja2puMnNvMWQwMVFyYVRsTXFWU3NieApOclJGaTl3cmYrTTdRPT0geW91QGV4YW1wbGUuY29tCg==<\/code><\/pre>\n<p>\nEcco il comando di cui si parlava sopra<\/p>\n<pre><code class=\"plaintext\">install:\n  - echo  | base64 -d &gt;&gt; $HOME\/.ssh\/known_hosts<\/code><\/pre>\n<p>\nLo stesso approccio pu\u00f2 essere utilizzato con la chiave privata durante la connessione, poich\u00e9 potrebbe essere necessario avere una chiave privata per accedere al server. Quando si lavora con la chiave, \u00e8 importante assicurarsi che venga conservata in modo sicuro nella variabile d'ambiente Travis CI e che non venga mai visualizzata.<\/p>\n<p>Un'altra cosa da considerare \u00e8 che potresti dover eseguire l'intero script di distribuzione come una sola riga, ad esempio utilizzando <code>doctl<\/code>. Questo potrebbe richiedere alcuni sforzi aggiuntivi.<\/p>\n<pre><code class=\"plaintext\">doctl compute ssh &lt;droplet&gt; --ssh-command &quot;\u0432\u0441\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0431\u0443\u0434\u0443\u0442 \u0437\u0434\u0435\u0441\u044c &amp;&amp; \u0437\u0434\u0435\u0441\u044c&quot;<\/code><\/pre>\n<p><\/p>\n<h2>TLS\/SSL e bilanciamento del carico<\/h2>\n<p>\nDopo aver fatto tutto ci\u00f2 di cui si \u00e8 parlato sopra, l'ultimo problema che mi sono trovata di fronte \u00e8 stato che il server non aveva SSL. Poich\u00e9 utilizzo un server Node.js, per far funzionare <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/how-to-secure-a-containerized-node-js-application-with-nginx-let-s-encrypt-and-docker-compose\">il<\/a><\/noindex> proxy inverso Nginx e Let\u2019s Encrypt, \u00e8 necessario impegnarsi bastante.<\/p>\n<p>Non avevo davvero voglia di configurare tutto manualmente per SSL, cos\u00ec ho semplicemente creato un bilanciatore di carico e registrato i suoi dettagli nel DNS. Nel caso di DigitalOcean, ad esempio, creare un certificato autofirmato autoaggiornante sul bilanciatore di carico \u00e8 una procedura semplice, gratuita e veloce. Questo approccio ha anche un vantaggio aggiuntivo: consente di configurare facilmente SSL su molti server che operano dietro al bilanciatore di carico. In questo modo, i server stessi non devono preoccuparsi di SSL, ma possono utilizzare normalmente la porta <code>80<\/code>. Quindi, configurare SSL sul bilanciatore di carico \u00e8 molto pi\u00f9 semplice e comodo rispetto ad altri metodi di configurazione SSL.<\/p>\n<p>Ora puoi chiudere sul server tutte le porte che accettano connessioni in ingresso, tranne la porta <code>80<\/code>, utilizzata per la comunicazione con il bilanciatore di carico, e la porta <code>22<\/code> per SSH. Di conseguenza, ogni tentativo di accesso diretto al server su qualsiasi porta, tranne queste due, fallir\u00e0.<\/p>\n<h2>Risultati<\/h2>\n<p>\nDopo aver fatto tutto ci\u00f2 di cui ho parlato in questo materiale, non avevo pi\u00f9 paura n\u00e9 della piattaforma Docker n\u00e9 dei concetti delle catene CI\/CD automatizzate. Sono riuscita a configurare una catena di integrazione continua, durante la quale viene testato il codice prima che arrivi in produzione e viene effettuato il deploy automatico del codice sul server. Tutto ci\u00f2 \u00e8 ancora relativamente nuovo per me e sono sicura che ci siano modi per migliorare il mio flusso di lavoro automatizzato e renderlo pi\u00f9 efficiente. Quindi, se avete idee al riguardo, fatemelo sapere. <noindex><a rel=\"nofollow\" href=\"mailto:hello@taniarascia.com\">me<\/a><\/noindex> saperlo. Spero che questo articolo vi sia stato utile. Spero che, leggendolo, abbiate appreso tanto quanto ho appreso io mentre mi occupavo di tutto ci\u00f2 di cui ho parlato.<\/p>\n<p><b>P.S.<\/b> Nel nostro <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace#order\">marketplace<\/a><\/noindex> \u00e8 disponibile un'immagine <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace\/docker#advantages\">Docker<\/a><\/noindex>, che pu\u00f2 essere installata con un clic. Potete controllare il funzionamento dei container su <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/\">VPS<\/a><\/noindex>. A tutti i nuovi clienti sono offerti 3 giorni di prova gratuita.<\/p>\n<p><b>Gentili lettori!<\/b> Utilizzate tecnologie CI\/CD nei vostri progetti?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/#order\"><img decoding=\"async\" alt=\"Creazione di una pipeline CI\/CD e automazione del lavoro con Docker\" src=\"\/wp-content\/uploads\/2020\/02\/3fbfa90ea56a3d9b6b89ba945eb94570.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/488668\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u0411\u044b\u043b Apache-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u043a\u0430\u043a\u043e\u043c-\u043d\u0438\u0431\u0443\u0434\u044c \u043e\u0431\u0449\u0435\u043c \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435, \u043d\u0430 \u044d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432\u043e\u0439\u0442\u0438 \u043f\u043e FTP, \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u043d\u0435\u0447\u0442\u043e \u0432\u0440\u043e\u0434\u0435 ftp:\/\/ftp.example.com. \u041f\u043e\u0442\u043e\u043c \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u0438\u043c\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u0414\u0440\u0443\u0433\u0438\u0435 \u0431\u044b\u043b\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0430, \u0432\u0441\u0451 \u0442\u043e\u0433\u0434\u0430 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":69352,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-69351","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u0411\u044b\u043b Apache-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u043a\u0430\u043a\u043e\u043c-\u043d\u0438\u0431\u0443\u0434\u044c \u043e\u0431\u0449\u0435\u043c \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435, \u043d\u0430 \u044d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432\u043e\u0439\u0442\u0438 \u043f\u043e FTP, \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u043d\u0435\u0447\u0442\u043e \u0432\u0440\u043e\u0434\u0435 ftp:\/\/ftp.example.com. \u041f\u043e\u0442\u043e\u043c \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u0438\u043c\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u0414\u0440\u0443\u0433\u0438\u0435 \u0431\u044b\u043b\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0430, \u0432\u0441\u0451 \u0442\u043e\u0433\u0434\u0430\" \/>\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\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\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\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 CI\/CD-\u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Docker | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u0411\u044b\u043b Apache-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u043a\u0430\u043a\u043e\u043c-\u043d\u0438\u0431\u0443\u0434\u044c \u043e\u0431\u0449\u0435\u043c \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435, \u043d\u0430 \u044d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432\u043e\u0439\u0442\u0438 \u043f\u043e FTP, \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u043d\u0435\u0447\u0442\u043e \u0432\u0440\u043e\u0434\u0435 ftp:\/\/ftp.example.com. \u041f\u043e\u0442\u043e\u043c \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u0438\u043c\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u0414\u0440\u0443\u0433\u0438\u0435 \u0431\u044b\u043b\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0430, \u0432\u0441\u0451 \u0442\u043e\u0433\u0434\u0430\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker\" \/>\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=\"2020-02-18T11:44:06+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-03-03T13:15:00+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\udd47 Creazione di una catena CI\/CD e automazione del lavoro con Docker | ProHoster","description":"Ho creato i miei primi siti alla fine degli anni '90. Allora era molto semplice metterli in funzione. C'era un server Apache su qualche hosting condiviso, e si poteva accedere a questo server tramite FTP, digitando nella barra del browser qualcosa come ftp:\/\/ftp.example.com. Poi dovevi inserire il nome utente e la password e caricare i file sul server. Erano altri tempi, tutto era diverso allora.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","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\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 CI\/CD-\u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Docker | ProHoster","og:description":"\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0430 \u043c\u043e\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u0432 \u043a\u043e\u043d\u0446\u0435 90-\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u0411\u044b\u043b Apache-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u043a\u0430\u043a\u043e\u043c-\u043d\u0438\u0431\u0443\u0434\u044c \u043e\u0431\u0449\u0435\u043c \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0435, \u043d\u0430 \u044d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432\u043e\u0439\u0442\u0438 \u043f\u043e FTP, \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u043d\u0435\u0447\u0442\u043e \u0432\u0440\u043e\u0434\u0435 ftp:\/\/ftp.example.com. \u041f\u043e\u0442\u043e\u043c \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u0438\u043c\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u0414\u0440\u0443\u0433\u0438\u0435 \u0431\u044b\u043b\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0430, \u0432\u0441\u0451 \u0442\u043e\u0433\u0434\u0430","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/sozdanie-ci-cd-czepochki-i-avtomatizacziya-raboty-s-docker","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":"2020-02-18T11:44:06+00:00","article:modified_time":"2020-03-03T13:15:00+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"69351","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":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 19:24:45","updated":"2022-09-28 11:40:45"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/69351","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=69351"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/69351\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/69352"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=69351"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=69351"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=69351"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}