{"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 catena CI\/CD e automazione del lavoro con Docker","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Ho scritto i miei primi siti alla fine degli anni '90. Allora era molto semplice renderli operativi. C'era un server Apache su un hosting condiviso, a cui si poteva accedere via FTP, scrivendo nella barra degli indirizzi del browser qualcosa come <code>ftp:\/\/ftp.example.com<\/code>. Poi era necessario inserire nome utente e password e caricare i file sul server. Erano altri tempi, tutto era 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 catena 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, tutto \u00e8 cambiato notevolmente. I siti sono diventati pi\u00f9 complessi e, prima del rilascio in produzione, devono essere assemblati. Un unico server \u00e8 diventato un insieme di server che lavorano dietro bilanciatori di carico, l'uso di sistemi di controllo versione \u00e8 diventato normale.<\/p>\n<p>Per il mio progetto personale avevo una configurazione speciale. E sapevo che avevo bisogno della possibilit\u00e0 di implementare il sito in produzione eseguendo un'unica azione: scrivere il codice nel branch <code>master<\/code> su GitHub. Inoltre, sapevo che, per garantire il funzionamento della mia piccola applicazione web, non volevo gestire un enorme cluster Kubernetes, o utilizzare la tecnologia Docker Swarm, o sostenere un parco server con pod, agenti e altre complessit\u00e0. Per raggiungere l'obiettivo di semplificare al massimo il lavoro, avevo bisogno di familiarizzare con CI\/CD.<\/p>\n<p>Se hai un piccolo progetto (nel nostro caso stiamo parlando di un progetto Node.js) e ti piacerebbe sapere come automatizzare il rilascio di questo progetto, facendo in modo che ci\u00f2 che \u00e8 contenuto nel repository corrisponda esattamente a ci\u00f2 che funziona in produzione, allora credo che questo articolo possa interessarti.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Requisiti preliminari<\/h2>\n<p>\nCi si aspetta che il lettore di questo articolo abbia una conoscenza di base del funzionamento della riga di comando e della scrittura di script Bash. Inoltre, avr\u00e0 bisogno di 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 considerato senza riserve una \"guida pratica\". \u00c8 piuttosto un documento in cui racconto ci\u00f2 che ho imparato e descrivo il processo di test e distribuzione del codice in produzione che mi soddisfa, eseguito con un solo passaggio automatizzato.<\/p>\n<p>Ecco come \u00e8 diventato il mio flusso di lavoro.<\/p>\n<p>Per il codice inviato in qualsiasi branch del repository, tranne <code>master<\/code>, vengono eseguite 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 si inserisce 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 corrente, 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>Caricamento dell'immagine da Docker Hub sul server.<\/li>\n<li>Interruzione del contenitore corrente e avvio di uno nuovo, basato sulla nuova immagine.<\/li>\n<\/ul>\n<p>\nSe non sai assolutamente nulla di Docker, delle immagini e dei contenitori, non preoccuparti. Ti parler\u00f2 di tutto questo.<\/p>\n<h2>Cos'\u00e8 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 ramo <code>master<\/code>). Durante questo processo, la qualit\u00e0 del codice \u00e8 garantita attraverso test automatizzati.<\/p>\n<h3>\u258dDistribuzione continua<\/h3>\n<p>\nLa distribuzione continua \u00e8 un rilascio automatizzato e 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 del lavoro<\/h2>\n<p>\nL'applicazione su cui ho appreso tutto questo si chiama <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/taniarascia\/takenote\">TakeNote<\/a><\/noindex>. \u00c8 un progetto web su cui sto lavorando, destinato a prendere appunti. Inizialmente ho cercato di creare un <noindex><a rel=\"nofollow\" href=\"https:\/\/jamstack.org\/\">progetto JAMStack<\/a><\/noindex>, o un'applicazione front-end senza server, per approfittare delle possibilit\u00e0 standard di hosting e distribuzione di progetti offerte da <noindex><a rel=\"nofollow\" href=\"https:\/\/www.netlify.com\/\">Netlify<\/a><\/noindex>. Con l'aumentare della complessit\u00e0 dell'applicazione, ho dovuto creare anche la sua parte server, il che significava che dovevo sviluppare una mia strategia per l'integrazione automatizzata e la distribuzione automatizzata del progetto.<\/p>\n<p>Nel mio caso, l'applicazione \u00e8 un server Express che opera in un ambiente Node.js, gestendo un'applicazione React a pagina singola e supportando un'API server sicura. Questa architettura segue una strategia che puoi 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 <noindex><a rel=\"nofollow\" href=\"http:\/\/qualitytesting.tech\/\">un'altra<\/a><\/noindex>, che \u00e8 un esperto di automazione, e gli ho chiesto cosa devo fare affinch\u00e9 tutto funzioni come voglio io. Mi ha suggerito un'idea su come dovrebbe essere il processo di lavoro automatizzato, descritto nella sezione \u201cObiettivi\u201d di questo articolo. Il fatto che avessi fissato obiettivi del genere significava che dovevo capire come utilizzare Docker.<\/p>\n<h2>Docker<\/h2>\n<p>\nDocker \u00e8 uno strumento che, grazie alla tecnologia di containerizzazione, consente di distribuire facilmente le applicazioni e di eseguirne il deployment e l'avvio nello stesso ambiente, anche se la piattaforma Docker stessa funziona in ambienti diversi. Per iniziare, avevo bisogno di avere a disposizione gli strumenti della 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 possono essere definite molto chiare e comprensibili, ma da esse si pu\u00f2 capire 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 ci\u00f2 che <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/\">GitHub<\/a><\/noindex> \u00e8 per i repository git, o un registro <noindex><a rel=\"nofollow\" href=\"https:\/\/www.npmjs.com\/\">npm<\/a><\/noindex> per i pacchetti JavaScript. Si tratta di un repository online per le immagini di Docker. \u00c8 a esso che si collega Docker Desktop.<\/p>\n<p>Quindi, per iniziare a lavorare con Docker, \u00e8 necessario fare due cose:<\/p>\n<ul>\n<li>Installare <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 ci\u00f2, puoi controllare se Docker CLI funziona correttamente eseguendo il seguente comando per verificare la versione di Docker:<\/p>\n<pre><code class=\"plaintext\">docker -v<\/code><\/pre>\n<p>\nSuccessivamente, accedi a Docker Hub inserendo, quando richiesto, il tuo nome utente e password:<\/p>\n<pre><code class=\"plaintext\">docker login<\/code><\/pre>\n<p>\nPer utilizzare Docker, \u00e8 necessario comprendere i concetti di immagini e contenitori.<\/p>\n<h3>\u258dImmagini<\/h3>\n<p>\nUn'immagine \u00e8 un po' come un piano che contiene istruzioni per costruire un contenitore. \u00c8 uno snapshot immutabile del file system e delle impostazioni dell'applicazione. Gli sviluppatori possono scambiare immagini con facilit\u00e0.<\/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 intestazione:<\/p>\n<pre><code class=\"plaintext\">REPOSITORY \u00a0 \u00a0 TAG     IMAGE ID \u00a0 \u00a0 CREATED     SIZE\n---<\/code><\/pre>\n<p>\nSuccessivamente, esamineremo alcuni esempi di comandi in questo formato \u2013 prima c'\u00e8 il comando con un commento, poi un esempio di ci\u00f2 che pu\u00f2 restituire.<\/p>\n<h3>\u258dContenitori<\/h3>\n<p>\nIl contenitore \u00e8 un pacchetto eseguibile che contiene tutto il necessario per l'esecuzione dell'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 del fatto che in ambienti diversi vengono avviati esemplari della stessa immagine.<\/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>\nIl tag \u00e8 un'indicazione di una versione specifica dell'immagine.<\/p>\n<h3>\u258dBreve guida ai comandi Docker<\/h3>\n<p>\nEcco una panoramica di alcuni comandi Docker comunemente usati.<\/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 dell'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>Contenitore<\/p>\n<p>Avvio del contenitore 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 al registro<\/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 registro<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/ps\/\">docker ps<\/a><\/noindex><\/p>\n<p>Contenitore<\/p>\n<p>Visualizzazione dell'elenco dei contenitori<\/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\/Contenitore<\/p>\n<p>Rimozione di contenitori e immagini non utilizzati<\/p>\n<h3>\u258dFile Dockerfile<\/h3>\n<p>\nSo come avviare localmente un'applicazione per la produzione. Ho una configurazione di Webpack destinata a costruire un'applicazione React pronta. Quindi, ho un comando che avvia un server basato su Node.js sulla porta <code>5000<\/code>. Risulta cos\u00ec:<\/p>\n<pre><code class=\"plaintext\">npm i \u00a0 \u00a0 \u00a0 \u00a0 # installazione delle dipendenze\nnpm run build # costruzione dell'applicazione React\nnpm run start # avvio del server Node<\/code><\/pre>\n<p>\n\u00c8 importante notare che non ho un'applicazione di esempio per questo materiale. Ma qui, qualsiasi semplice applicazione Node andr\u00e0 bene per esperimenti.<\/p>\n<p>Per utilizzare il contenitore, sar\u00e0 necessario dare istruzioni a Docker. Questo avviene tramite un file chiamato <code>Dockerfile<\/code>, che si trova nella directory principale del progetto. Questo file, inizialmente, sembra piuttosto incomprensibile.<\/p>\n<p>Ma ci\u00f2 che contiene \u00e8 solo una descrizione, tramite comandi speciali, di qualcosa di simile alla configurazione di un 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. Indica l'immagine di base su cui si costruisce il contenitore.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#copy\">COPY<\/a><\/noindex> \u2014 Copia dei file da una fonte locale nel contenitore.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#workdir\">WORKDIR<\/a><\/noindex> \u2014 Impostazione della directory di lavoro per i comandi successivi.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#run\">comandi 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 Indicazione del comando da eseguire.<\/li>\n<\/ul>\n<p>\n<code>Dockerfile<\/code> pu\u00f2 apparire pi\u00f9 o meno 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, potresti dover installare dipendenze aggiuntive. Il fatto \u00e8 che alcune immagini di base (come Node Alpine Linux) sono create per essere il pi\u00f9 compatte possibile. Di conseguenza, potrebbero mancare alcuni programmi di cui ti aspetti.<\/p>\n<h3>\u258dCostruzione, etichettatura e avvio del container<\/h3>\n<p>\nLa costruzione e l'avvio locali del container sono, dopo che abbiamo, <code>Dockerfile<\/code>le attivit\u00e0 abbastanza 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'etichetta (se non viene specificata un'etichetta, il sistema ne assegner\u00e0 automaticamente una 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 osservare come Docker esegue la costruzione dell'immagine.<\/p>\n<pre><code class=\"plaintext\">Invio del contesto di build al demone Docker \u00a0 2.88MB\nStep 1\/9 : FROM node:12-alpine\n\u00a0---&gt; ...esecuzione delle fasi di costruzione...\nCostruito con successo 123456789123\nEtichettato con successo &lt;image&gt;:&lt;tag&gt;<\/code><\/pre>\n<p>\nLa costruzione pu\u00f2 richiedere alcuni minuti \u2014 tutto dipende da quante dipendenze hai. Dopo che la costruzione \u00e8 completata, puoi eseguire il comando <code>docker images<\/code> e dare un'occhiata alla descrizione della tua nuova immagine.<\/p>\n<pre><code class=\"plaintext\">REPOSITORY\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 TAG               IMAGE ID\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 CREATED              SIZE\n&lt;image&gt; \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 latest            123456789123\u00a0 \u00a0 \u00a0 \u00a0 Circa un minuto fa   x.xxGB<\/code><\/pre>\n<p><\/p>\n<h3>\u258dAvvio<\/h3>\n<p>\nL'immagine \u00e8 stata creata. Questo significa che puoi avviare un container a partire da essa. Poich\u00e9 voglio avere la possibilit\u00e0 di accedere all'applicazione che sta funzionando nel container all'indirizzo <code>localhost:5000<\/code>, ho impostato nella parte sinistra della coppia <code>5000:5000<\/code> nella seguente riga di comando. <code>5000<\/code>A 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, quando il container \u00e8 stato creato e avviato, puoi usare il comando <code>docker ps<\/code> per visualizzare le informazioni su questo container (oppure puoi utilizzare il comando <code>docker ps -a<\/code>, che mostra le informazioni su tutti i container, non solo su quelli in esecuzione).<\/p>\n<pre><code class=\"plaintext\">ID CONTAINER\u00a0 \u00a0 \u00a0 \u00a0 IMMAGINE               COMANDO\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 CREATO              STATO                  \u00a0 \u00a0 PORTE                    NOME\n987654321234\u00a0 \u00a0 \u00a0 \u00a0              \"\/bin\/sh -c 'npm run\u2026\" \u00a0 6 secondi fa        Attivo 6 secondi            \u00a0 \u00a0 0.0.0.0:5000-&gt;5000\/tcp   stoic_darwin<\/code><\/pre>\n<p>\nSe vai ora all'indirizzo <code>localhost:5000<\/code> puoi vedere la pagina dell'applicazione in esecuzione, che appare esattamente come la pagina dell'applicazione in esecuzione in ambiente di produzione.<\/p>\n<h3>\u258dAssegnazione dell'etichetta e pubblicazione<\/h3>\n<p>\nPer utilizzare uno dei modelli creati sul server di produzione, \u00e8 necessario avere la possibilit\u00e0 di caricare questo modello 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 dove poter inviare il modello. \u00c8 necessario rinominare il modello in modo che il suo nome inizi con il nostro nome utente su Docker Hub. Dopo di ci\u00f2, deve seguire il nome del repository. Alla fine del nome pu\u00f2 esserci qualsiasi tag. Di seguito \u00e8 mostrato un esempio di denominazione dei modelli secondo questo schema.<\/p>\n<p>Ora possiamo costruire il modello assegnandogli un nuovo nome ed eseguire il comando <code>docker push<\/code> per inviarlo al repository di Docker Hub.<\/p>\n<pre><code class=\"plaintext\">docker build -t \\\/: .\ndocker tag \\\/: \\\/:latest\ndocker push \\\/:\n\n# In pratica, 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, il modello sar\u00e0 disponibile su Docker Hub e sar\u00e0 facile caricarlo sul server o trasferirlo ad altri sviluppatori.<\/p>\n<h2>Passaggi successivi<\/h2>\n<p>\nFino a questo momento siamo certi che l'applicazione, sotto forma di contenitore Docker, funziona localmente. Abbiamo caricato il contenitore su Docker Hub. Tutto ci\u00f2 significa che abbiamo gi\u00e0 fatto dei buoni progressi verso l'obiettivo. Ora \u00e8 necessario risolvere altri due problemi:<\/p>\n<ul>\n<li>Configurazione dello strumento CI per il testing e il deployment del codice.<\/li>\n<li>Configurazione 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 viene 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 pu\u00f2 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 configurato alcune cose. Quindi ora descriver\u00f2 brevemente come prepararlo per il lavoro.<\/p>\n<h2>Travis CI<\/h2>\n<p>\nTravis CI \u00e8 uno strumento per il testing e il deployment del codice. Non vorrei entrare nei dettagli della configurazione di Travis CI, poich\u00e9 ogni progetto \u00e8 unico e non sarebbe particolarmente utile. Tuttavia, parler\u00f2 delle basi che ti permetteranno di iniziare a lavorare nel caso tu decida di utilizzare Travis CI. Qualunque cosa tu scelga \u2014 Travis CI, CircleCI, Jenkins, o altro, verranno utilizzati metodi di configurazione simili.<\/p>\n<p>Per iniziare a lavorare con Travis CI, vai su <noindex><a rel=\"nofollow\" href=\"https:\/\/travis-ci.org\/\">sito del progetto<\/a><\/noindex> e crea un account. Poi integra Travis CI con il tuo account GitHub. Durante la configurazione del sistema, ti verr\u00e0 chiesto di specificare il repository con cui desideri automatizzare il lavoro e di concedere l'accesso. (Io utilizzo GitHub, ma sono sicura che Travis CI possa integrarsi anche con BitBucket, GitLab e altri servizi simili).<\/p>\n<p>Ogni volta che Travis CI inizia a lavorare, viene avviato un server che esegue i comandi specificati nel file di configurazione, incluso il deploy delle appropriate branch 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 situato nella directory principale del progetto, supporta il concetto di eventi <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.travis-ci.com\/user\/job-lifecycle\/\">durata della vita<\/a><\/noindex> del compito. Ecco questi eventi, elencati 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 intendo impostare un server locale per 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>, sar\u00e0 eseguito per tutti i pull request su tutte le branch del repository, a meno che non sia specificato diversamente. Questa \u00e8 una funzionalit\u00e0 utile, poich\u00e9 significa che possiamo testare tutto il codice che arriva nel repository. Questo permette di sapere se il codice \u00e8 pronto per essere registrato nella branch <code>master<\/code>, e se non interromper\u00e0 il processo di build del progetto. In questa configurazione globale, installo tutto localmente, avvio il server di sviluppo Webpack in background (questo \u00e8 un aspetto del mio flusso di lavoro) ed eseguo i test.<\/p>\n<p>Se desideri che nel tuo repository vengano visualizzati i badge con le informazioni sulla copertura del codice dai test, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.taniarascia.com\/display-build-status-and-test-coverage\/\">qui<\/a><\/noindex> puoi trovare una breve guida su come utilizzare 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 tutte le branch del repository e per i pull request.<\/p>\n<h3>\u258dDeploy<\/h3>\n<p>\nBasandoci sull'ipotesi che tutti i test automatizzati siano stati completati con successo, possiamo, se lo desideriamo, effettuare il deploy del codice sul server di produzione. Poich\u00e9 vogliamo farlo solo per il codice proveniente dalla branch <code>master<\/code>, diamo al sistema le indicazioni appropriate nelle impostazioni di distribuzione. Prima di tentare di utilizzare nel tuo progetto il codice che esamineremo in seguito, vorrei avvisarti che devi avere uno script reale chiamato per la distribuzione.<\/p>\n<pre><code class=\"plaintext\">deploy:\n  # Costruire il container Docker e inviarlo su Docker Hub\n  provider: script\n  script: bash deploy.sh\n  on:\n    branch: master<\/code><\/pre>\n<p>\nLo script di distribuzione ha due compiti:<\/p>\n<ul>\n<li>Costruzione, tagging e invio dell'immagine su Docker Hub tramite uno strumento CI (nel nostro caso Travis CI).<\/li>\n<li>Caricamento dell'immagine sul server, fermando il vecchio container e avviando il nuovo (nel nostro caso il server funziona sulla piattaforma DigitalOcean).<\/li>\n<\/ul>\n<p>\nPer prima cosa, \u00e8 necessario impostare il processo automatico di costruzione, tagging e invio dell'immagine su Docker Hub. Tutto questo \u00e8 molto simile a ci\u00f2 che abbiamo gi\u00e0 fatto manualmente, a parte il fatto che qui abbiamo bisogno di una strategia per assegnare tag unici alle immagini e automatizzare l'accesso. Ho avuto difficolt\u00e0 con alcuni dettagli dello script di distribuzione, come la strategia di tagging, l'accesso, la codifica delle chiavi SSH e l'instaurazione della connessione SSH. Ma, fortunatamente, il mio ragazzo si destreggia molto bene 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 su Docker Hub. Farlo \u00e8 abbastanza semplice. Lo schema di tagging che ho utilizzato prevede la combinazione di un hash git e un tag git, se esiste. Questo consente di garantire la creazione di un tag unico e semplifica l'identificazione della build da cui \u00e8 basato. <code>DOCKER_USERNAME<\/code> e <code>DOCKER_PASSWORD<\/code> sono variabili ambientali 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 seconda parte dello script dipende completamente dall'host che stai utilizzando e da come \u00e8 organizzata la connessione. Nel mio caso, poich\u00e9 utilizzo Digital Ocean, per collegarmi al server vengono utilizzati i comandi <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/digitalocean\/doctl\">doctl<\/a><\/noindex>. Quando lavoro con AWS, verr\u00e0 utilizzato lo strumento <code>aws<\/code>, e cos\u00ec via.<\/p>\n<p>Configurare il lavoro del server non \u00e8 stato particolarmente complicato. Ho configurato un droplet basato su un'immagine di base. Va notato che il sistema da me scelto richiede l'installazione manuale una tantum di Docker e l'avvio manuale di Docker una sola volta. Io, per installare Docker, ho usato Ubuntu 18.04, quindi anche voi, se utilizzate Ubuntu, per fare lo stesso, potete semplicemente seguire <noindex><a rel=\"nofollow\" href=\"https:\/\/phoenixnap.com\/kb\/how-to-install-docker-on-ubuntu-18-04\">questo<\/a><\/noindex> una guida semplice.<\/p>\n<p>Non parlo qui di comandi specifici per il servizio, poich\u00e9 questo aspetto pu\u00f2 variare notevolmente in diversi casi. Fornir\u00f2 solo un piano generale di azione che si esegue dopo aver effettuato la connessione SSH al server dove sar\u00e0 distribuito il progetto:<\/p>\n<ul>\n<li>Dovete trovare il contenitore che attualmente \u00e8 in esecuzione e fermarlo.<\/li>\n<li>Poi dovete avviare un nuovo contenitore in background.<\/li>\n<li>Dovrete impostare la porta locale del server sul valore <code>80<\/code> \u2014 questo permetter\u00e0 di accedere al sito all'indirizzo del tipo <code>example.com<\/code>, senza specificare la porta, invece di utilizzare un indirizzo come <code>example.com:5000<\/code>.<\/li>\n<li>E, infine, dovete eliminare tutti i vecchi contenitori e le 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>\nProbabilmente, quando vi connettete al server tramite SSH da Travis CI, vedrete un avviso che impedir\u00e0 di continuare l'installazione, poich\u00e9 il sistema attender\u00e0 una reazione da parte dell'utente.<\/p>\n<pre><code class=\"plaintext\">L'autenticit\u00e0 dell'host ' ()' non pu\u00f2 essere stabilita.\nL'impronta della chiave RSA \u00e8 .\nSiete sicuri di voler continuare a connettervi (s\u00ec\/no)?<\/code><\/pre>\n<p>\nHo scoperto che \u00e8 possibile codificare la chiave di stringa in base64 per conservarla in un formato con cui \u00e8 possibile lavorare comodamente e in modo sicuro. Durante la fase di installazione, \u00e8 possibile decodificare la chiave pubblica e scriverla nel file <code>known_hosts<\/code> per eliminarne l'errore descritto in precedenza.<\/p>\n<pre><code class=\"plaintext\">echo  | base64 # restituisce<\/code><\/pre>\n<p>\nNella pratica, questo comando potrebbe apparire cos\u00ec:<\/p>\n<pre><code class=\"plaintext\">echo \"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\" | base64<\/code><\/pre>\n<p>\nEcco come appare ci\u00f2 che restituisce: 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 l'instaurazione della connessione, poich\u00e9 per accedere al server potrebbe essere necessaria una chiave privata. Quando si utilizza la chiave, \u00e8 sufficiente assicurarsi che venga memorizzata in modo sicuro nella variabile d'ambiente Travis CI e che non venga stampata da nessuna parte.<\/p>\n<p>Un'altra cosa da tenere a mente \u00e8 che potrebbe essere necessario eseguire l'intero script di distribuzione presentato come una sola riga, ad esempio - tramite <code>doctl<\/code>. Questo potrebbe richiedere alcuni sforzi aggiuntivi.<\/p>\n<pre><code class=\"plaintext\">doctl compute ssh  --ssh-command \"tutte le comandi saranno qui &amp;&amp; qui\"<\/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 ad affrontare \u00e8 stato che il server non aveva SSL. Poich\u00e9 utilizzo un server Node.js, per costringere <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\">funzionare<\/a><\/noindex> un reverse proxy Nginx e Let's Encrypt, \u00e8 necessario lavorarci un po'.<\/p>\n<p>Non volevamo affatto eseguire manualmente tutte queste configurazioni SSL, quindi ho semplicemente creato un bilanciatore di carico e registrato le informazioni in DNS. Nel caso di DigitalOcean, ad esempio, la creazione di un certificato autofirmato autoaggiornante sul bilanciatore di carico \u00e8 una procedura semplice, gratuita e veloce. Questo approccio ha anche un ulteriore vantaggio, poich\u00e9 consente di configurare SSL molto facilmente su diversi server che lavorano dietro il bilanciatore di carico. Ci\u00f2 consente ai server stessi di non <code>80<\/code>dover preoccuparsi<\/p>\n<p>di SSL, ma di utilizzare, come al solito, la porta <code>80<\/code>, utilizzata per comunicare con il bilanciatore di carico, e la porta <code>22<\/code> per SSH. Di conseguenza, il tentativo di accesso diretto al server su qualsiasi porta, ad eccezione di queste due, fallir\u00e0.<\/p>\n<h2>Conclusioni<\/h2>\n<p>\nDopo aver fatto tutto ci\u00f2 di cui ho parlato in questo articolo, 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 effettuato il testing del codice prima che arrivi in produzione e il rilascio automatico del codice sul server. Tutto questo \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 <noindex><a rel=\"nofollow\" href=\"mailto:hello@taniarascia.com\">a me<\/a><\/noindex> sapere. Spero che questo articolo vi sia stato utile. Spero che leggendo, abbiate appreso tanto quanto ho appreso io mentre facevo chiarezza su 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> si ha un'immagine <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/marketplace\/docker#advantages\">Docker<\/a><\/noindex>, che si installa con un clic. Puoi verificare il funzionamento dei container su <noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/\">VPS<\/a><\/noindex>. A tutti i nuovi clienti vengono forniti gratuitamente 3 giorni per la sperimentazione.<\/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 catena 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 5.0.1.1 - 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.\" \/>\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) 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\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.\" \/>\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\udd47Creazione di una catena CI\/CD e automazione del lavoro con Docker | ProHoster","description":"Ho scritto i miei primi siti alla fine degli anni '90. All'epoca, metterli in funzione era molto semplice.","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.","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":{"focus":[],"additional":[]},"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":"2026-08-11 12:50: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\/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}]}}