Konfigurera kontinuerlig integration och kontinuerlig distribution med Jenkins

En utvecklares vardag fylls med monotont och repetitiva uppgifter. Lyckligtvis lever vi i en konstnärlig intelligensålder, vilket innebär datorer är bra för att hantera tråkiga sysslor och de knappast någonsin klaga på det! Så låt oss inrätta en viss automation för att göra vår dagliga slipa lite mindre grindig.

Testa och distribuera är två integrerade delar av webbutveckling. Med viss automatisering blandad blir de lösningar som vanligtvis kallas "kontinuerlig integration" (CI) och "kontinuerlig distribution" (CD). Den "kontinuerliga" aspekten av dessa lösningar innebär att dina projekt kommer att testas automatiskt och distribueras, så att du kan fokusera mer på att skriva kod och mindre på att hämta den på servrar.

I den här handledningen sätter vi upp en populär kontinuerlig integrationsserver som heter Jenkins och synkroniserar den med GitHub så att den kör test varje gång ny kod trycks. Därefter skapar vi en lösning för att automatiskt trycka den koden på vår appserver, vilket eliminerar behovet av att vi ska distribuera manuellt.

Vi använder DigitalOcean för att snabbt och enkelt skapa molnbaserade virtuella privata servrar (VPS) för att vara värd för vår app och Jenkins.

Obs! Denna handledning förutsätter att du har en grundläggande kunskap om att arbeta på kommandoraden och att din maskin har både Git och Node.js installerad.

Vår Super Sample App

Innan vi kan testa eller distribuera någonting behöver vi något att testa och distribuera. Låt mig introducera dig till vår vänliga handledningstest, aptly kallad "hej-jenkins".

Vi skriver en enkel Node.js app som passar våra syften. Det kommer inte göra mycket mer än att visa en rad text i webbläsaren, men det är bara tillräckligt med funktionalitet för att säkerställa att vi har ordentligt satt upp kontinuerlig integration och kontinuerlig utplacering.

Git Up på GitHub

Eftersom vi lagrar vårt projekt på GitHub, låt oss börja där. Logga in på (eller skapa) ditt GitHub-konto och skapa ett nytt förråd. Namn det "hej-jenkins" och ge den följande beskrivning:

Mitt superprovapp för att testa Jenkins.

För enkelhet, låt oss behålla repo offentlig. Gå vidare och kolla Initiera detta förråd med ett README alternativet och välj Nod alternativ från Lägg till .gitignore listrutan.

Klicka på Skapa förråd knappen, och vår repo är klar.

Låt oss nu klona vårt nya förråd till vår lokala maskin och navigera in i det:

git klon [email protected]:/hello-jenkins.git cd hej-jenkins

Vårt Nodapp

Här är vad den slutliga strukturen i vår app kommer att vara:

├── .gitignore ├── app.js ├── package.json ├── README.md ─ - skript │ ├── deploy │ └── test └── test └── test.js

Låt oss ta itu med den här en och en. Det första steget att bygga någon Node.js app är att skapa en package.json fil. Här är vårt:

"namn": "hej-jenkins", "beskrivning": "hej jenkins test app", "version": "0.0.1", "privat": true, "beroenden": "express": "3.12. 0 "," devDependencies ": " mocka ":" 1.20.1 "," supertest ":" 0.13.0 "

Under beroenden vi har lagt till uttrycka, som vi ska använda för att hjälpa till att skapa vår Node.js-app. Under devDependencies vi har lagt till mocka och supertest, vilka båda hjälper oss att skriva våra test.

Nu när vår package.json definieras, installera våra appberoende genom att köra:

npm installera

Det är dags att skriva vår app-kod. Skapa en fil med namnet app.js och lägg till följande till det:

var express = kräver ("express"); var app = express (); app.get ('/', funktion (req, res) res.send ('hej world');); app.listen (process.env.PORT || 5000); module.exports = app;

Låt oss bryta ner vår enkla Node.js-app:

  • Först importerar vi uttrycka lib vi angav i vår package.json.
  • Vi använder uttrycka att skapa en ny app.
  • Vi berätta för oss app att svara på alla förfrågningar som slår till rot på vår webbplats (/) med texten "hej världen".
  • Därefter berättar vi för oss app på vilken hamn att lyssna på förfrågningar (process.env.PORT hänvisar till miljövariabeln som kallas "PORT", och om den inte existerar, är vi istället standard till port 5000).
  • Slutligen gör vi vårt app tillgänglig för andra Node.js-moduler genom module.exports (detta kommer att vara till nytta senare när vi lägger till test).

Det är allt! Vår app är klar - låt oss köra den:

nod app.js

Öppna din favoritwebbläsare och navigera till http: // localhost: 5000, och du borde se Hej världen sitter i all sin härliga plainness.

Det är inte det mest spännande testet, men det fungerar! Fortsätt och stäng av vår Node.js app med Ctrl-C, och låt oss gå vidare.

Vissa test är i ordning

Det är dags att skriva ett test för vår app - trots allt, om vi inte har något att testa, då kommer Jenkins inte ha något att göra!

Skapa en mapp som heter testa, och skapa en fil med namnet test.js. Lägg till följande kod till test / test.js:

var request = kräver ("supertest"); var app = kräver ('... /app.js'); beskriv ('GET /', funktion () it 'svara med hej världen', funktion (gjort) request (app) .get ('/'). );

Hur fungerar vårt test? För det första importerar vi båda supertest lib och vår app. Sedan lägger vi till ett enda test som beskriver vad som ska hända när a SKAFFA SIG förfrågan träffar rotten på vår webbplats. Vi berättar för vårt test att förvänta sig att svaret är "hej världen" och om det är, passerar testet.

För att köra testet använder vi Mocha-biblioteket. Vi installerade Mocha som en del av vår devDependencies, så vi kommer helt enkelt att köra ett kommando som skickar vår testfil till Mocka och Mocka kommer att köra våra tester:

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

När du är klar bör du se en grön punkt tillsammans med information som säger att ett test har passerat. Det betyder att vårt test var framgångsrikt! Men att skriva det kommandot om och om igen kommer snart att producera fingerkramper och ögonkrämningar, så låt oss göra ett hjälpskript för att göra det för oss (kom ihåg, datorer blir inte uttråkad!).

Skapa en ny katalog som heter manus, och skapa en fil med namnet testa (observera att det inte finns någon förlängning). Lägg till följande till script / prov:

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

Där - nu har vi ett skalskript för att utföra den gnarly linjen för oss. Men innan vi kan använda det måste vi ge det körbara behörigheter:

chmod + x-skript / test

Låt oss testa det! Springa:

./ Script / prov

... och du borde se samma provning som tidigare.

Tid att trycka

Okej, vi har en fungerande app och ett arbetstest, så låt oss trycka vår nya kod till GitHub:

git lägg till. git commit -m 'Lägg till nod app' git push origin master

Och det är det - vår app är klar och på GitHub!

Vår App Går Serverad

Vi har en enthralling och fängslande app ("hej värld" har en slags poesi till det, håller du inte med?), Men ingen kan se det! Låt oss ändra det och få vår app att köra på en server.

För våra värdbehov vänder vi oss till DigitalOcean. DigitalOcean ger ett snabbt och enkelt sätt att snurra upp VPS-molninstanser, vilket gör den till en perfekt värd för vår CI / CD-lekplats.

Den första droppen

Logga in på (eller registrera dig för) DigitalOcean och klicka på Skapa droppe knapp. För värdnamnet, kalla det "hej-jenkins". Den lägsta storleken förekomst (512MB / 1 / 20GB) kommer att passa våra behov och välj det geografiska området som ligger närmast dig. Därefter måste vi välja den bild som används för att skapa droppen. DigitalOcean erbjuder ett brett urval operativsystem att välja mellan, men det som är riktigt fint är att de också ger bilder skräddarsydda specifikt för vissa applikationstyper.

Klicka på tillämpningar fliken och välj nod-v0.10.29 på Ubuntu 14.04 alternativ - det här kommer att skapa en server som är snyggstartad för vår Node.js-app.

Klicka nu Skapa droppe, och DigitalOcean kommer att komma igång med att initiera vår server.

Konfigurera servern

Inom en minut borde vår nya server vara redo, och du borde ha fått ett mail med din serverns rotuppgifter. Låt oss använda den informationen för att logga in:

ssh [email protected]

Du kommer att bli uppmanad till lösenordet i e-postmeddelandet och omedelbart tvungen att skapa ett nytt lösenord (gör det något väldigt starkt och lagra det på ett säkert ställe, som en KeePass-databas).

Just nu är vi inloggade som rot, vilket är den allmäktige demigoden av Linux-land. Men tung är huvudet som bär kronan och fungerar som rot är generellt en dålig idé. Så det första vi vill göra är att skapa en ny användare - låt oss kalla det "app":

adduser app

Du måste ange ett lösenord (a annorlundastarkt lösenord, lagras säkert), och sedan kommer det att fråga en rad valfria frågor.

Vi vill byta till vårt app användaren, men innan vi loggar ut måste vi bevilja vår nya användare sudo privilegier så det kommer att kunna hantera administrativa åtgärder:

usermod -a -G sudo app

Stäng nu anslutningen med utgång, och anslut sedan som app:

ssh [email protected]

Du kommer att bli uppmanad till app användarens lösenord, och då ska du vara inloggad och bra att gå.

Installera vår app

Låt oss få vår app på maskinen. Tack vare DigitalOceans applikationsbilder kommer vår maskin med Node.js och npm förinstallerad, men vi behöver fortfarande installera Git:

sudo apt-get install git

Du kommer att bli uppmanad till ditt lösenord (eftersom du använder sudo), och du måste bekräfta installationen med Y. När Git har installerats kan vi använda den för att få vår app från GitHub.

Kopiera HTTPS-klonadressen från projektets GitHub-sida och klonera sedan repo till din hemmapp på servern:

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

Nu är vår app på vår server, i en mapp som heter "hej-jenkins". Låt oss navigera in i det:

cd hej-jenkins

Det första vi behöver göra är att installera appberoende:

npm installera - produktion

När det är klart kan vi köra vår app! Snurra upp med:

nod app.js

... och navigera till din servers IP-adress i din webbläsare.

Men vänta, det fungerar inte! Vad är grejen?

Tja, låt oss återkalla den här koden i vår app.js:

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

Just nu har vi inte en HAMN miljövariabeluppsättningen, så vår app är defaulting till port 5000 och du måste lägga till porten till IP-adressen i webbläsaren (http: //YOUR.SERVER.IP.ADDRESS: 5000).

Så hur får vi vår app att fungera som förväntat, men behöver inte ange hamnen? Tja, när en webbläsare gör en HTTP-begäran, är den som standard port 80. Så vi behöver bara ställa in vår HAMN miljövariabel till 80.

Vi ställer in våra miljövariabler i / Etc / miljö filen på servern - den här filen laddas in vid inloggning, och de inställda variablerna kommer att vara tillgängliga globalt för alla applikationer. Öppna filen:

sudo nano / etc / environment

Du får se det just nu VÄG ställs in i den här filen. Lägg till följande rad efter det:

PORT = 80

Skriv sedan in Ctrl-X, Y, och Stiga på att spara och avsluta Utloggning från servern (utgång) och SSH tillbaka in (det här laddar ny miljövariabel).

En sista liten chore - kör en app på port 80 kräver root privilegier, men kör sudo nod app.js kommer inte behålla de miljövariabler vi har satt upp. För att komma runt så aktiverar vi nod att ha möjlighet att köra på port 80 sans sudo:

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

Det borde göra det. Kör nu:

nod app.js

Navigera till http: //YOUR.SERVER.IP.ADDRESS, och du kommer att se Hej världen!

Håll den igång

Just nu körs vår app bara när vi kör processen - om vi stänger den är vår webbplats inte längre tillgänglig. Vad vi behöver är ett sätt att hålla vår Node.js-app igång i bakgrunden. För det brukar vi använda evigt. Det första steget är att installera det globalt:

sudo npm installera -g för alltid

Nu, istället för att starta vår app med nod app.js, vi ska använda:

för alltid börja app.js

Observera att i stället för processen som hänger på körning, avslutas den omedelbart och ger dig tillbaka kontroll. Detta beror på att Node.js-servern körs i bakgrunden. Nu behöver vi inte oroa oss för att vår server stänger av när vi loggar ut från servern. evigt kommer även automatiskt att starta om vår app om det händer att krascha!

För att stoppa vår app kan vi köra:

för alltid stopall

För nu, låt oss fortsätta springa och gå vidare till Jenkins.

En tid att testa

Vi kommer att vara värd för vår Jenkins-server på en separat DigitalOcean-droppe. Låt oss snurra upp det nu.

Den andra droppen

Skapa en ny droppe med värdnamnet "jenkins-box". Välja 512MB / 1 / 20GB igen, tillsammans med samma plats och samma applikationstyp (nod-v0.10.29 på Ubuntu 14.04) som med den tidigare droppen.

Klick Skapa droppe och när det är klart, använd de behörighetsuppgifter som skickats till dig för att logga in via SSH (du måste ange ett nytt lösenord, precis som tidigare).

Som tidigare borde vi skapa en ny användare innan vi gör någonting annat. Den här gången låt oss ringa det administration:

adduser admin usermod -a -G sudo admin

Logga ut som rot och logga in som den nyskapade administration.

Eftersom Jenkins syfte är att hämta vårt projekt och köra sina tester, måste vår maskin ha alla projektets beroenden installerade. Vi snurrade upp denna instans med DigitalOcean's Node.js-applikation, så Node.js och npm är redan installerade. Men vi behöver fortfarande installera Git:

sudo apt-get install git

Hyra Butler

Nästa upp är Jenkins. Installera Jenkins är ganska enkelt - vi får ha apt-get gör all den tunga lyftningen. Den enda fångsten är att vi måste lägga till en ny benägen förvar innan installationen startas:

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 binär /> /etc/apt/sources.list.d/jenkins.list" sudo apt-get update

Nu kan vi installera Jenkins:

sudo apt-get install jenkins

När du är klar kommer Jenkins att köras och vara tillgänglig på port 8080. Navigera din webbläsare till jenkins-box IP-adress i port 8080 och du kommer att se Jenkins målsida.

Klicka på Hantera Jenkins länk, och sedan Hantera plugins länk. Byt till Tillgängliga fliken och söka efter GitHub Plugin. Klicka på Installera kryssrutan och sedan på Ladda ner nu och installera efter omstart knapp.

Detta kommer att initiera installationssekvensen. GitHub-plugin har flera beroenden, så flera plugins installeras. Längst ner på sidan, kolla på Starta om Jenkins när installationen är klar och inga jobb körs - Detta kommer att leda till att Jenkins startar om när installationerna är färdiga.

När Jenkins har startat om, är det dags att lägga till vårt projekt. Klicka på Nytt föremål knapp. Använd "hej-jenkins" för objektnamnet, välj Bygg ett kostnadsfritt mjukvaruprojekt, och klicka på knappen märkt ok.

När projektet är inställt, hittar du dig själv på projektets inställningssida. Lägg till vårt projekts GitHub-URL till GitHub-projektet låda:

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

Välj sedan Git alternativet under Källkodshantering. I de nyligen dynade fälten lägger du till webbadressen till vår GitHub-projektrepo till Repository URL fält:

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

Bläddra lite längre ner och klicka på rutan för att aktivera Bygg när en förändring trycks till GitHub. Med det här alternativet markerat, kommer vårt projekt att byggas varje gång vi trycker på vår GitHub repo. Självklart behöver vi Jenkins veta vad man ska göra när det går att bygga. Klicka på Lägg till byggsteg rulla ned och välj Utför skal. Detta kommer att göra en Kommando dialog tillgänglig och vad vi lägger i denna dialog kommer att köras när en byggning initieras. Lägg till följande för det:

npm installera ./script/test

Vår byggnad består av två steg. Först installerar vi våra appberoende. Sedan exekverar den ./ Script / prov att köra våra test.

Klick "Spara".

För att slutföra inställningen av integrationen, gå över till GitHub repo och klicka på inställningar. Klicka på Webhooks & Services fliken och sedan på Lägg till service falla ner. Välj Jenkins (GitHub plugin) service.

Lägg till följande som Jenkins hook url:

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

Klick Lägg till service. Vårt projekt är nu klart för sitt första kontinuerliga integrationsprov!

Låt oss ge det något att testa. Öppna app.js lokalt och ändra den här raden:

res.send ("hej värld");

… till detta:

res.send ("hej jenkins");

Spara ändringen och begå den:

git lägg till. git commit -m 'Växla till hej jenkins'

Håll ögonen på Jenkins när du trycker på dina ändringar i GitHub:

git push origin master

Efter en sekund eller två ska du se att ett nytt jobb har initierats för vårt hello-jenkins projekt i Jenkins - våra kontinuerliga integrationsarbeten!

Den kontinuerliga integrationsflödet

Men ... jobbet misslyckas! Varför?

Tja, kom ihåg att vårt test förväntar oss att röstsamtalet ska returnera "hej värld", men vi har ändrat det till "hej jenkins". Så låt oss ändra förväntningarna på vårt test. Byt denna rad:

förfrågan (app) .get ('/'). förvänta ("hej värld", gjort);

... med den här raden:

förfrågan (app) .get ('/'). förvänta ("hej jenkins", gjort);

Spara, begå och tryck igen:

git lägg till. git commit -m 'Byt test till hej jenkins' git push origin master

Titta på Jenkins - återigen ser du att en byggnad startas automatiskt, och den här gången lyckas den!

Detta är flödet av kontinuerlig integration. Testservern testar kontinuerligt vilken ny kod du trycker så du blir snabbt informerad om eventuella felaktiga test.

Få det distribuerat

Okej, så vi testar automatiskt våra ändringar, men hur är det med att använda dessa förändringar? Inga problem!

Om du har tittat noggrant har du utan tvekan märkt att något saknas från vårt projekt hittills. I projektstrukturen i början av handledningen finns det en script / deploy filen, men vi har ännu inte göra någon sådan fil. Nå, det gör vi nu!

Nyckeln till autentisering

Men först, låt oss diskutera hur utbyggnaden kommer att fungera. Vårt skript (kört av Jenkins byggsteg) loggar in på appservern via SSH, navigerar till vår appmapp, uppdaterar appen och startar om servern. Innan vi skriver vårt deployeringsskript måste vi hantera hur vår Jenkins-server ska SSH till vår appserver.

Hittills har vi nått våra servrar genom att manuellt ange lösenord, men det här sättet fungerar inte för automatiserade skript. I stället skapar vi en SSH-nyckel som Jenkins-servern ska använda för att autentisera sig med appservern.

När Jenkins installeras skapas en ny användare som heter jenkins. Jenkins exekverar alla kommandon med den här användaren, så vi behöver generera vår nyckel med jenkins användaren så att den har rätt åtkomst till den.

Medan inloggad som administration på jenkins-box, exekvera följande:

sudo su

Ge din administration lösenord, och det kommer att byta dig till rot användare. Kör sedan:

su jenkins

Nu verkar du som jenkins användare. Skapa en SSH-nyckel:

ssh-keygen -t rsa

Spara filen i standardplatsen (/var/lib/jenkins/.ssh/id_rsa), och se till att du inte använder en lösenordsfras (annars kommer SSH-åtkomst att kräva ett lösenord och fungerar inte när det automatiseras).

Därefter måste vi kopiera den offentliga nyckeln som skapades. Kör detta:

katt ~ / .ssh / id_rsa.pub

... och kopiera utmatningen. Det ska vara en lång sträng som börjar med "ssh-rsa" och slutar med "jenkins @ jenkins-box".

Logga ut från jenkins-box och logga in på vår appserver (hello-jenkins) som den app användare. Vi måste skapa en fil med namnet authorized_keys i vår app användarens.ssh mapp:

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

Klistra in den offentliga nyckeln du kopierade och sedan Ctrl-X/Y/Stiga på att spara och avsluta För att den här filen ska fungera korrekt måste den ha strikta behörigheter på den:

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

Gå tillbaka till jenkins box, byt till jenkins användare och verifiera att du kan logga in på vår appserver utan att skriva in ett lösenord:

ssh [email protected]

Du borde logga in på appservern utan att behöva ange lösenordet. Med det uppbyggda kan vi nu vända sig till utplacering.

Skicka det automatiskt

Skapa en fil i manus mapp heter distribuera (observera att det inte finns någon förlängning). Lägg till följande till script / deploy:

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

Låt oss gå igenom det här:

  • Först loggar vi in ​​på appservern som app användare.
  • Sedan navigerar vi in ​​i vår appmapp och uppdaterar till den senaste versionen från GitHub.
  • Därefter installerar vi våra beroenden.
  • Slutligen, när vår app-kod är uppdaterad, startar vi om vår server med för evigt restartall.

Gör vår nya skriptfil körbar:

chmod + x script / deploy

Lägg till den här nya filen och begå den:

git lägg till. git commit -m 'Lägg till implementera script'

Men låt oss inte trycka ganska ändå. Först, gå tillbaka till vår projektkonfiguration i Jenkins, och rulla ner till byggkommandot. Lägg till den här nya raden i slutet av den:

./ Script / distribuera

Spara Jenkins-projektet.

Nu fortsätt och tryck till GitHub, och titta på som Jenkins bygger automatiskt. När byggnaden är klar (det ska lyckas), navigera i din webbläsare till vår appservers IP. Presto! Vår spännande "hej värld" har ersatts med en spännande "hej jenkins"!

Vår app utökas kontinuerligt!

Allt är det som automatiserar väl

Phew. Det var ganska turen!

Till sist har vi lyckats skapa både kontinuerlig integration och kontinuerlig implementering, vilket ger en mycket fin nivå av automation i våra dagliga utvecklar liv. Kom ihåg att datorer inte blir uttråkad, så medan de hanterar testning och distribuering, är du fri att göra viktiga saker, som gör dig själv en smörgås. Så gör gör den smörgås och äta den som en automationskamp!