Impostazione dell'integrazione continua e della distribuzione continua con Jenkins

La vita quotidiana di uno sviluppatore è piena di compiti monotoni e ripetitivi. Fortunatamente, viviamo in un'età dell'intelligenza pre-artificiale, il che significa che i computer sono bravi a gestire le faccende noiose e difficilmente mai lamentatevi! Quindi impostiamo un po 'di automazione per rendere la nostra routine quotidiana un po' meno grindy.

Test e implementazione sono due elementi fondamentali dello sviluppo web. Con un po 'di automazione integrata, diventano soluzioni comunemente chiamate "continuous integration" (CI) e "continuous deployment" (CD). L'aspetto "continuo" di queste soluzioni significa che i tuoi progetti verranno automaticamente testati e implementati, consentendoti di concentrarti maggiormente sulla scrittura di codice e meno sul herding sui server.

In questo tutorial, creeremo un popolare server di integrazione continua chiamato Jenkins e lo sincronizzeremo con GitHub in modo che esegua i test ogni volta che viene inviato un nuovo codice. Dopodiché, creeremo una soluzione per spingere automaticamente quel codice sul nostro server delle app, eliminando la necessità di una distribuzione manuale.

Useremo DigitalOcean per creare rapidamente e facilmente server virtuali virtuali (VPS) basati su cloud per ospitare la nostra app e Jenkins.

Nota: questo tutorial presume che tu abbia una conoscenza di base sul funzionamento della riga di comando e che sul tuo computer siano installati Git e Node.js.

La nostra app Super Sample

Prima di poter testare o implementare qualsiasi cosa, abbiamo bisogno qualcosa per testare e distribuire. Permettetemi di presentarvi la nostra amichevole app di tutorial test, giustamente chiamata "ciao-jenkins".

Scriveremo una semplice app Node.js per soddisfare i nostri scopi. Non farà molto di più che visualizzare una riga di testo nel browser, ma questa è solo una funzionalità sufficiente per garantire che abbiamo impostato correttamente l'integrazione continua e la distribuzione continua.

Git Up su GitHub

Dato che archiviamo il nostro progetto su GitHub, iniziamo da lì. Accedi a (o crea) il tuo account GitHub e crea un nuovo repository. Chiamalo "ciao-jenkins" e dagli la seguente descrizione:

La mia app super campione per testare Jenkins.

Per semplicità, continuiamo il repository Pubblico. Vai avanti e controlla il Inizializza questo repository con un README opzione e selezionare il Nodo opzione dal Aggiungi .gitignore menu `A tendina.

Clicca il Crea repository pulsante, e il nostro pronti contro termine sarà pronto.

Ora cloniamo il nostro nuovo repository sul nostro computer locale e naviga in esso:

git clone [email protected]:/hello-jenkins.git cd ciao-jenkins

La nostra app di nodo

Ecco quale sarà la struttura finale della nostra app:

├── .gitignore ├── app.js ├── package.json ├── README.md ├── script │ ├── distribuisci │ └── test └── test └── test.js

Affrontiamo questo uno per uno. Il primo passo per creare qualsiasi app Node.js è creare un package.json file. Ecco la nostra:

"name": "hello-jenkins", "description": "hello jenkins test app", "version": "0.0.1", "private": true, "dependencies": "express": "3.12. 0 "," devDependencies ": " mocha ":" 1.20.1 "," supertest ":" 0.13.0 "

Sotto dipendenze abbiamo aggiunto esprimere, che utilizzeremo per aiutare a creare la nostra app Node.js. Sotto devDependencies abbiamo aggiunto moca e Supertest, entrambi i quali ci aiuteranno a scrivere i nostri test.

Ora che il nostro package.json è definito, installa le nostre dipendenze dell'app eseguendo:

installazione di npm

È tempo di scrivere il nostro codice app. Crea un file chiamato app.js e aggiungi il seguente:

var express = require ('express'); var app = express (); app.get ('/', function (req, res) res.send ('hello world');); app.listen (process.env.PORT || 5000); module.exports = app;

Analizziamo la nostra semplice app Node.js:

  • Innanzitutto, importiamo il esprimere lib abbiamo specificato nel nostro package.json.
  • Noi usiamo esprimere per creare un nuovo App.
  • Noi diciamo al nostro App rispondere a tutte le richieste che colpiscono alla radice del nostro sito (/) con il testo "ciao mondo".
  • Quindi, diciamo al nostro App su quale porta ascoltare le richieste (process.env.PORT si riferisce alla variabile d'ambiente chiamata "PORT", e se non esiste, preferiamo invece la porta 5000).
  • Alla fine, facciamo il nostro App disponibile per altri moduli Node.js tramite module.exports (questo ti tornerà utile più tardi quando aggiungeremo dei test).

Questo è tutto! La nostra app è pronta - corriamola:

nodo app.js

Apri il tuo browser preferito e naviga verso http: // localhost: 5000, e dovresti vedere Ciao mondo seduto in tutta la sua gloriosa semplicità.

Non è l'app di test più eccitante, ma funziona! Vai avanti e chiudi la nostra app Node.js con Ctrl-C, e andiamo avanti.

Alcuni test sono in ordine

È tempo di scrivere un test per la nostra app - dopotutto, se non abbiamo nulla da testare, allora Jenkins non avrà nulla da fare!

Crea una cartella chiamata test, e in esso crea un file chiamato test.js. Aggiungere il seguente codice a test / test.js:

var request = require ('supertest'); var app = require ('... /app.js'); define ('GET /', function () it ('rispondi con Hello world', function (done) request (app) .get ('/'). expect ('hello world', done);); );

Come funziona il nostro test? Innanzitutto, importiamo entrambi Supertest lib e nostro App. Quindi aggiungiamo un singolo test, descrivendo cosa dovrebbe accadere quando a OTTENERE richiesta colpisce la radice del nostro sito. Diciamo al nostro test che aspettiamo che la risposta sia "ciao mondo", e se lo è, passa il test.

Per eseguire il test, useremo la libreria Mocha. Abbiamo installato Mocha come parte del nostro devDependencies, quindi eseguiremo semplicemente un comando che passa il nostro file di test a Mocha e Mocha eseguirà i nostri test:

./node_modules/.bin/mocha ./test/test.js

Al termine, dovresti vedere un punto verde insieme a informazioni che dicono che è passato un test. Ciò significa che il nostro test ha avuto successo! Ma digitando quel comando più e più volte produrrà presto crampi alle dita e contrazioni agli occhi, quindi facciamo uno script di aiuto per farlo per noi (ricorda, i computer non si annoiano!).

Crea una nuova directory chiamata copione, e in esso crea un file chiamato test (notare che non c'è estensione). Aggiungi il seguente a script / Test:

#! / bin / sh ./node_modules/.bin/mocha ./test/test.js

Lì - ora abbiamo uno script di shell per eseguire quella linea gnarly per noi. Ma prima che possiamo usarlo, dobbiamo concedergli autorizzazioni eseguibili:

chmod + x script / test

Proviamoci! Correre:

./ Script / test

... e dovresti vedere lo stesso test di passaggio di prima.

Tempo di spingere

Bene, abbiamo un'app funzionante e un test di lavoro, quindi spingiamo il nostro nuovo codice su GitHub:

aggiungi git. git commit -m 'Aggiungi app nodo' git push origine master

E questo è tutto: la nostra app è fatta e su GitHub!

La nostra app viene servita

Abbiamo un'app avvincente e accattivante ("ciao mondo" ha una sorta di poesia, non sei d'accordo?), Ma nessuno può vederlo! Cambiamo e facciamo funzionare la nostra app su un server.

Per le nostre esigenze di hosting, ci rivolgeremo a DigitalOcean. DigitalOcean fornisce un modo semplice e veloce per creare istanze cloud VPS, rendendolo l'ospite perfetto per il nostro parco giochi CI / CD.

La prima goccia

Accedi a (o registrati) DigitalOcean e fai clic su Crea Droplet pulsante. Per il nome host, chiamalo "ciao-jenkins". L'istanza di dimensione più bassa (512/1 / 20GB) soddisferà le nostre esigenze e selezionerà la regione geografica più vicina a te. Successivamente, dobbiamo scegliere l'immagine utilizzata per creare il droplet. DigitalOcean offre una vasta gamma di sistemi operativi tra cui scegliere, ma ciò che è veramente bello è che forniscono anche immagini su misura per determinati tipi di applicazioni.

Clicca il applicazioni scheda e selezionare il node-v0.10.29 su Ubuntu 14.04 opzione - questo creerà un server che è ben avviato per la nostra app Node.js.

Ora fai clic Crea Droplet, e DigitalOcean inizierà a inizializzare il nostro server.

Configura il server

Entro un minuto il nostro nuovo server dovrebbe essere pronto e dovresti aver ricevuto un'e-mail con le credenziali di root del tuo server. Utilizziamo queste informazioni per accedere:

ssh [email protected]

Ti verrà richiesta la password fornita nell'e-mail, quindi dovrai immediatamente creare una nuova password (renderla qualcosa di molto forte e memorizzarla in un luogo sicuro, come un database KeePass).

In questo momento siamo registrati come radice, che è l'onnipotente semidio di Linux-land. Ma pesante è la testa che indossa la corona e opera come radice è generalmente una cattiva idea. Quindi la prima cosa che vorremmo fare è creare un nuovo utente - chiamiamolo "app":

app adduser

Dovrai fornire una password (a diversopassword sicura, archiviata in modo sicuro), quindi richiederà una serie di domande opzionali.

Vogliamo passare al nostro App utente, ma prima di uscire, dobbiamo concedere il nostro nuovo utente sudo privilegi in modo che abbia la capacità di eseguire azioni amministrative:

usermod -a -G app sudo

Ora chiudi la connessione con Uscita, e quindi connetti come App:

ssh [email protected]

Ti verrà richiesto il App la password dell'utente, quindi è necessario effettuare l'accesso e andare.

Installa la nostra app

Portiamo la nostra app sulla macchina. Grazie alle immagini dell'applicazione di DigitalOcean, la nostra macchina viene fornita con Node.js e npm preinstallati, ma dobbiamo ancora installare Git:

sudo apt-get install git

Ti verrà richiesta la tua password (dal momento che stai usando sudo), e dovrai confermare l'installazione con Y. Una volta installato Git, possiamo utilizzarlo per ottenere la nostra app da GitHub.

Copia l'URL clone HTTPS dalla pagina GitHub del progetto, quindi clona il repository nella tua cartella Inizio sul server:

cd git clone https://github.com//hello-jenkins.git

Ora la nostra app è sul nostro server, in una cartella chiamata "ciao-jenkins". Passiamo a questo:

cd ciao-jenkins

La prima cosa che dobbiamo fare è installare le dipendenze dell'app:

installazione di npm - produzione

Una volta fatto, possiamo eseguire la nostra app! Spin it up con:

nodo app.js

... e accedi all'indirizzo IP del tuo server nel browser.

Ma aspetta, non funziona! Qual è l'accordo?

Bene, ricordiamo questa linea di codice nella nostra app.js:

app.listen (process.env.PORT || 5000);

In questo momento, non abbiamo un PORTA variabile di ambiente impostata, quindi la nostra app è quella predefinita per la porta 5000 ed è necessario aggiungere la porta all'indirizzo IP nel browser (http: //YOUR.SERVER.IP.ADDRESS: 5000).

Quindi, come possiamo far funzionare la nostra app come previsto, senza dover specificare la porta? Bene, quando un browser fa una richiesta HTTP, ha come valore predefinito la porta 80. Quindi dobbiamo solo impostare la nostra PORTA variabile di ambiente a 80.

Imposteremo le nostre variabili di ambiente nel / Etc / environment file sul server - questo file viene caricato all'accesso e le variabili impostate saranno disponibili globalmente su tutte le applicazioni. Apri il file:

sudo nano / etc / environment

Lo vedrai proprio ora SENTIERO è impostato in questo file. Aggiungi la seguente riga dopo:

PORT = 80

Quindi digita Ctrl-X, Y, e accedere per salvare ed uscire. Disconnettersi al server (Uscita) e SSH di nuovo (questo caricherà la nuova variabile di ambiente).

Un ultimo piccolo problema: l'esecuzione di un'app sulla porta 80 richiede i privilegi di root, ma l'esecuzione sudo node app.js non conserverà le variabili di ambiente che abbiamo impostato. Per ovviare a questo, abiliteremo nodo avere la possibilità di funzionare sulla porta 80 sans sudo:

sudo setcap cap_net_bind_service = + ep / usr / local / bin / node

Questo dovrebbe farlo. Ora esegui:

nodo app.js

Navigare verso http: //YOUR.SERVER.IP.ADDRESS, e vedrai Ciao mondo!

Keep It Running

Al momento la nostra app funziona solo mentre stiamo eseguendo il processo - se lo chiudiamo, il nostro sito non è più disponibile. Ciò di cui abbiamo bisogno è un modo per mantenere la nostra app Node.js in esecuzione in background. Per quello, useremo per sempre. Il primo passo è installarlo globalmente:

sudo npm install -g per sempre

Ora, invece di avviare la nostra app con nodo app.js, useremo:

inizia per sempre app.js

Si noti che al posto del processo che pende dall'esecuzione, si esce immediatamente e si ottiene il controllo posteriore. Questo perché il server Node.js è in esecuzione in background. Ora non dobbiamo preoccuparci che il nostro server si spenga quando effettuiamo il logout del server. per sempre riavvierà automaticamente la nostra app anche in caso di crash!

Per interrompere la nostra app, possiamo eseguire:

per sempre

Per ora, continuiamo a correre e passiamo a Jenkins.

Un tempo per testare

Ospiteremo il nostro server Jenkins su un droplet DigitalOcean separato. Facciamo girare questo ora.

La seconda goccia

Crea un nuovo droplet con il nome host "jenkins-box". Scegliere 512/1 / 20GB di nuovo, insieme alla stessa posizione e allo stesso tipo di applicazione (node-v0.10.29 su Ubuntu 14.04) come con la gocciolina precedente.

Clic Crea Droplet e una volta terminato, usa le credenziali inviate via email per accedere tramite SSH (dovrai impostare una nuova password, proprio come prima).

Come prima, dovremmo creare un nuovo utente prima di fare qualsiasi altra cosa. Questa volta chiamiamolo Admin:

adduser admin usermod -a -G sudo admin

Disconnettersi come radice e accedere come appena creato Admin.

Poiché lo scopo di Jenkins è recuperare il nostro progetto ed eseguire i suoi test, la nostra macchina deve avere tutte le dipendenze del progetto installate. Abbiamo trasformato questa istanza con l'applicazione Node.js di DigitalOcean, quindi Node.js e npm sono già installati. Ma dobbiamo ancora installare Git:

sudo apt-get install git

Assumi il maggiordomo

Il prossimo è Jenkins. Installare Jenkins è abbastanza semplice, lo faremo apt-get fai tutto il sollevamento pesante. L'unico problema è che dobbiamo aggiungere un nuovo adatto repository prima di avviare l'installazione:

sudo wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add - sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary /> /etc/apt/sources.list.d/jenkins.list' sudo apt-get update

Ora possiamo installare Jenkins:

sudo apt-get install jenkins

Una volta completato, Jenkins sarà in esecuzione e disponibile sulla porta 8080. Navigare nel browser in Jenkins-box Indirizzo IP sulla porta 8080 e vedrai la pagina di destinazione di Jenkins.

Clicca il Gestisci Jenkins link, e quindi il Gestisci i plugin collegamento. Passare al A disposizione scheda e cerca il GitHub Plugin. Clicca il Installare checkbox, e poi il Scarica ora e installa dopo il riavvio pulsante.

Questo avvierà la sequenza di installazione. Il plugin GitHub ha diverse dipendenze, quindi verranno installati più plug-in. In fondo alla pagina, controlla il Riavvia Jenkins quando l'installazione è completa e nessun lavoro è in esecuzione - questo richiederà a Jenkins di riavviarsi una volta completate le installazioni.

Una volta che Jenkins è stato riavviato, è tempo di aggiungere il nostro progetto. Clicca il Nuovo oggetto pulsante. Usa "ciao-jenkins" per il nome dell'oggetto, seleziona Costruisci un progetto software gratuito, e fare clic sul pulsante etichettato ok.

Una volta impostato il progetto, ti troverai nella pagina delle impostazioni del progetto. Aggiungi l'URL GitHub del nostro progetto al Progetto GitHub scatola:

https://github.com// Hello-Jenkins

Quindi, selezionare il Idiota opzione sotto Gestione del codice sorgente. Nei campi appena apparsi, aggiungi l'URL al nostro repository di progetto GitHub al URL del repository campo:

https://github.com//hello-jenkins.git

Scorri un po 'più in basso e fai clic sulla casella per abilitare Costruisci quando una modifica viene inviata a GitHub. Con questa opzione selezionata, il nostro progetto si costruirà ogni volta che effettueremo il push del nostro repo GitHub. Certo, abbiamo bisogno che Jenkins sappia cosa fare quando esegue una build. Clicca il Aggiungi step di costruzione menu a discesa e selezionare Esegui shell. Questo farà un Comando dialogo disponibile, e ciò che inseriremo in questo dialogo verrà eseguito quando una costruzione inizia. Aggiungi il seguente:

npm install ./script/test

La nostra build consiste in due passaggi. Innanzitutto, installa le nostre dipendenze dell'app. Quindi esegue ./ Script / test per eseguire i nostri test.

Clicca "Salvare".

Per completare la configurazione dell'integrazione, andare al repository GitHub e fare clic su impostazioni. Clicca il Webhooks e servizi scheda, quindi il Aggiungi servizio cadere in picchiata. Seleziona il Jenkins (plugin GitHub) servizio.

Aggiungi il seguente come il URL di Jenkins:

http: //JENKINS.SERVER.IP.ADDRESS: 8080 / github-webhook /

Clic Aggiungi servizio. Il nostro progetto è ora pronto per il suo primo test di integrazione continua!

Diamo qualcosa da testare. Aprire app.js localmente e cambia questa linea:

res.send ('ciao mondo');

… a questo:

res.send ('ciao jenkins');

Salva la modifica e confermala:

aggiungi git. git commit -m 'Passa a ciao jenkins'

Ora tieni d'occhio Jenkins mentre trasmetti le tue modifiche a GitHub:

git push origin master

Dopo un secondo o due, dovresti vedere che è stato avviato un nuovo lavoro per il nostro Hello-Jenkins progetto in Jenkins - la nostra integrazione continua funziona!

Il flusso di integrazione continua

Ma ... il lavoro fallisce! Perché?

Bene, ricorda che il nostro test si aspetta che la chiamata di root restituisca "Ciao mondo", ma l'abbiamo modificata in "ciao jenkins". Quindi cambiamo le aspettative del nostro test. Scambia questa riga:

richiesta (app) .get ('/'). expect ('ciao mondo', fatto);

... con questa linea:

richiesta (app) .get ('/'). expect ('ciao jenkins', fatto);

Salva, conferma e invia nuovamente:

aggiungi git. git commit -m 'Passa test al master di origine git push di hello jenkins

Guarda Jenkins: ancora una volta vedrai che una build viene avviata automaticamente e questa volta ha successo!

Questo è il flusso di integrazione continua. Il server di test sta continuamente testando qualsiasi nuovo codice che si preme per essere rapidamente informato di eventuali test non riusciti.

Get It Deployed

Bene, quindi stiamo testando automaticamente le nostre modifiche, ma per quanto riguarda la distribuzione di tali modifiche? Nessun problema!

Se hai osservato da vicino, hai sicuramente notato che manca qualcosa dal nostro progetto fino ad ora. Nella struttura del progetto all'inizio del tutorial, esiste a script / deploy file, ma non abbiamo ancora creato alcun file di questo tipo. Bene, ora lo faremo!

La chiave dell'autenticazione

Ma prima, discutiamo di come funzionerà la distribuzione. Il nostro script (gestito dal passo di costruzione di Jenkin) accederà al server dell'app tramite SSH, naviga nella nostra cartella app, aggiorna l'app e quindi riavvia il server. Prima di scrivere il nostro script di distribuzione, dobbiamo gestire il modo in cui il nostro server Jenkins eseguirà SSH nel nostro server delle applicazioni.

Finora, abbiamo accesso ai nostri server inserendo manualmente le password, ma questo approccio non funziona per gli script automatici. Invece, creeremo una chiave SSH che il server Jenkins utilizzerà per autenticarsi con il server dell'app.

Quando installa Jenkins, crea un nuovo utente chiamato Jenkins. Jenkins esegue tutti i comandi con questo utente, quindi abbiamo bisogno di generare la nostra chiave con il Jenkins utente in modo che abbia l'accesso appropriato ad esso.

Mentre sei loggato come Admin sul Jenkins-box, eseguire quanto segue:

sudo su

Fornisci il tuo Admin password, e ti passerà al radice utente. Quindi eseguire:

su jenkins

Ora ti stai comportando come il Jenkins utente. Genera una chiave SSH:

ssh-keygen -t rsa

Salva il file nel percorso predefinito (/var/lib/jenkins/.ssh/id_rsa), e assicurati di non usare una passphrase (altrimenti l'accesso SSH richiederà una password e non funzionerà automaticamente).

Successivamente, dobbiamo copiare la chiave pubblica che è stata creata. Esegui questo:

cat ~ / .ssh / id_rsa.pub

... e copia l'output. Dovrebbe essere una lunga stringa che inizia con "ssh-rsa" e termina con "jenkins @ jenkins-box".

Esci Jenkins-box e accedere nuovamente al nostro server delle applicazioni (Hello-Jenkins) come il App utente. Dobbiamo creare un file chiamato authorized_keys nel nostro App l'utente.SSH cartella:

mkdir ~ / .ssh nano ~ / .ssh / authorized_keys

Incolla la chiave pubblica che hai copiato e poi Ctrl-X/Y/accedere per salvare ed uscire. Affinché questo file funzioni correttamente, è necessario disporre di autorizzazioni rigorose su di esso:

chmod 700 ~ / .ssh chmod 600 ~ / .ssh / *

Tornate al Jenkins casella, passare al Jenkins utente e verificare che sia possibile accedere al nostro server app senza immettere una password:

ssh [email protected]

È necessario accedere correttamente al server dell'app senza inserire la password. Con quello stabilito, possiamo ora passare alla distribuzione.

Spedirlo automaticamente

Crea un file nel copione cartella denominata schierare (notare che non c'è estensione). Aggiungi il seguente a script / deploy:

#! / bin / sh ssh [email protected] <

Passiamo attraverso questo:

  • Innanzitutto, accediamo al server dell'app come App utente.
  • Quindi navighiamo nella nostra cartella dell'app e aggiorniamo l'ultima versione da GitHub.
  • Successivamente, installiamo le nostre dipendenze.
  • Infine, una volta aggiornato il codice dell'app, riavviamo il nostro server con per sempre riavvio.

Rendi eseguibile il nostro nuovo file script:

chmod + x script / deploy

Aggiungi questo nuovo file e confermalo:

aggiungi git. git commit -m 'Aggiungi script di distribuzione'

Ma non spingiamo ancora abbastanza. Per prima cosa, torna alla configurazione del nostro progetto in Jenkins e scorri fino al comando di compilazione. Aggiungi questa nuova linea alla fine di esso:

./ Script / deploy

Salva il progetto Jenkins.

Ora vai avanti e spingi su GitHub, e osserva come Jenkins costruisce automaticamente. Una volta che la compilazione è terminata (dovrebbe avere successo), naviga il tuo browser sull'IP del nostro server delle applicazioni. Presto! Il nostro entusiasmante "ciao mondo" è stato sostituito da un esilarante "ciao jenkins"!

La nostra app viene ora distribuita continuamente!

Va bene che si automatizza bene

Uff. E 'stata abbastanza la corsa!

Alla fine, abbiamo impostato con successo sia l'integrazione continua e implementazione continua, che fornisce un livello molto elevato di automazione nella nostra vita quotidiana degli sviluppatori. Ricorda, i computer non si annoiano, quindi mentre gestiscono i test e la distribuzione, sei libero di fare cose importanti, come prepararti un panino. Quindi vai a fare quel sandwich e mangiarlo come un campione d'automazione!