Configurarea integrării continue și desfășurarea continuă cu Jenkins

Viața de zi cu zi a unui dezvoltator este plină de sarcini monotone și repetitive. Din fericire, trăim într-o vârstă de inteligență pre-artificială, ceea ce înseamnă că computerele sunt minunate în a manipula treburile plictisitoare și ele greu vreodată plângi despre asta! Deci, să punem niște automatizări pentru ca zgârierea zilnică să fie puțin mai slabă.

Testarea și implementarea sunt două elemente integrale ale dezvoltării web. Cu unele automatizări amestecate, ele devin soluții denumite în mod obișnuit "integrare continuă" (CI) și "desfășurare continuă" (CD). Aspectul "continuu" al acestor soluții înseamnă că proiectele dvs. vor fi testate și desfășurate automat, permițându-vă să vă concentrați mai mult pe scrierea codului și mai puțin pe planificarea pe servere.

În acest tutorial, vom crea un server popular de integrare continuă, numit Jenkins, și îl vom sincroniza cu GitHub, astfel încât acesta va executa teste de fiecare dată când este împins un nou cod. După aceea, vom crea o soluție pentru a împinge automat acest cod pe serverul de aplicații, eliminând necesitatea de a implementa manual.

Vom folosi DigitalOcean pentru a crea rapid și ușor servere private virtuale bazate pe cloud (VPS) pentru a găzdui aplicația noastră și Jenkins.

Notă: Acest tutorial presupune că aveți cunoștințe de bază despre lucrul la linia de comandă și că mașina dvs. are instalate atât Git cât și Node.js.

Aplicația noastră Super Sample

Înainte de a putea testa sau implementa ceva, avem nevoie ceva pentru a testa și a implementa. Permiteți-mi să vă prezint aplicația noastră de testare prietenoasă, numită în mod corespunzător "hello-jenkins".

Vom scrie o aplicație simplă Node.js care să corespundă scopurilor noastre. Nu va face decât să afișeze o linie de text în browser, dar aceasta este doar o funcție suficientă pentru a ne asigura că am stabilit corect integrarea continuă și implementarea continuă.

Git Up pe GitHub

De când ne vom păstra proiectul pe GitHub, să începem acolo. Conectați-vă la (sau creați) contul dvs. GitHub și creați un depozit nou. Denumiți-l "hello-jenkins" și dați-i următoarea descriere:

Aplicația mea super-probă pentru a testa Jenkins.

Pentru simplitate, hai să păstrăm repo Public. Continuați și verificați Inițializați acest depozit cu un README și selectați Nodul opțiune de la Adăugați .gitignore lista verticală.

Apasă pe Creați un depozit buton, iar repo-ul nostru va fi gata.

Acum, să ne clonăm noul depozit până la mașina noastră locală și să navigăm în el:

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

Nodul nostru App

Iată care va fi structura finală a aplicației noastre:

├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ───

Să abordăm acest lucru unul câte unul. Primul pas pentru construirea oricărei aplicații Node.js este crearea unui package.json fişier. Iată-ne:

"nume": "hello-jenkins", "descriere": "hello jenkins test app", "versiune": "0.0.1", "privat": true; 0 "," devDependencies ": " mocha ":" 1.20.1 "," supertest ":" 0.13.0 "

Sub dependențe am adăugat expres, pe care o vom folosi pentru a ajuta la crearea aplicației noastre Node.js. Sub devDependencies am adăugat cafea și supertest, ambele care ne vor ajuta să ne scriem testele.

Acum asta package.json este definită, instalați dependențele de aplicații executând:

npm install

E timpul să scriem codul nostru de aplicație. Creați un fișier numit app.js și adăugați următoarele:

var express = necesită ("expres"); var app = expres (); app.get ('/', funcția (req, res) res.send ('hello world');); app.listen (process.env.PORT || 5000); module.exports = app;

Să descoperim aplicația noastră simplă Node.js:

  • În primul rând, importăm expres am specificat în nostru package.json.
  • Folosim expres pentru a crea un nou aplicaţia.
  • Îi spunem aplicaţia pentru a răspunde la toate solicitările care duc la rădăcina site-ului nostru (/) cu textul "salut lume".
  • Apoi, noi le spunem aplicaţia pe ce port să ascultați cereri (process.env.PORT se referă la variabila de mediu numită "PORT", iar dacă nu există, în loc de implicit avem portul 5000).
  • În cele din urmă, ne facem aplicaţia disponibilă altor module Node.js prin module.exports (aceasta va fi utilă mai târziu când vom adăuga teste).

Asta e! Aplicația noastră este gata - să o executăm:

nod app.js

Deschideți browserul dvs. preferat și navigați la http: // localhost: 5000, și ar trebui să vezi Salut Lume stând în toată simplitatea ei glorioasă.

Nu este cea mai interesantă aplicație de testare, dar funcționează! Continuați și închideți aplicația noastră Node.js cu Ctrl-C, și să mergem mai departe.

Unele teste sunt în ordine

Este timpul să scriem un test pentru aplicația noastră - la urma urmei, dacă nu avem nimic de testat, atunci Jenkins nu va avea nimic de făcut!

Creați un dosar numit Test, și în el creează un fișier numit test.js. Adăugați următorul cod la testare / test.js:

var request = solicită ('supertest'); var app = solicită ('... /app.js'); descrie ('GET /', funcția () it ('răspuns cu hello world', funcția (done) request (app) .get ('/'). );

Cum funcționează testul nostru? Mai întâi, importăm atât supertest lib și noi aplicaţia. Apoi adăugăm un singur test, care descrie ce ar trebui să se întâmple când a OBȚINE solicitată apare rădăcina site-ului nostru. Spunem testul nostru să ne așteptăm ca răspunsul să fie "salut lume", iar dacă este așa, testul trece.

Pentru a rula testul, vom folosi biblioteca Mocha. Am instalat Mocha ca parte a noastră devDependencies, așa că vom rula pur și simplu o comandă care trece fișierul nostru de testare la Mocha și Mocha va rula testele noastre:

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

Când ați terminat, ar trebui să vedeți un punct verde, împreună cu informații care spun că a trecut un test. Asta înseamnă că testul nostru a avut succes! Dar tastând acea comandă de peste și peste va produce în curând crampe deget și șocuri de ochi, așa că hai să facem un script de ajutor pentru a face pentru noi (amintiți-vă, computerele nu se plictisesc!).

Creați un nou director numit scenariu, și în el creează un fișier numit Test (observați că nu există nicio extensie). Adăugați următoarele la script / test:

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

Acolo - acum avem un script de shell pentru a executa acea linie gnarly pentru noi. Dar înainte de ao putea folosi, trebuie să îi acordăm permisiuni executabile:

chmod + x script / test

Să-l testați! Alerga:

./ Script / test

... și ar trebui să vedeți același test de trecere ca și înainte.

Timpul de împingere

Bine, avem o aplicație de lucru și un test de lucru, așa că să ne împingem noul cod la GitHub:

adăugați git. git commit -m "Adăugați aplicația de nod" git push master de origine

Și asta este - aplicația noastră este făcută și pe GitHub!

Aplicația noastră primește serviciul

Avem o aplicație entuziastă și captivantă ("lumea salută" are un fel de poezie, nu sunteți de acord?), Dar nimeni nu o poate vedea! Să schimbăm acest lucru și să lansăm aplicația pe un server.

Pentru nevoile noastre de gazduire, vom intoarce la DigitalOcean. DigitalOcean oferă o modalitate rapidă și simplă de a transforma instanțele norilor VPS, făcându-l gazdă perfectă pentru spațiul nostru de joacă CI / CD.

Prima cădere

Conectați-vă la (sau înregistrați-vă la) DigitalOcean și faceți clic pe Creați picături buton. Pentru numele de gazdă, numiți-l "hello-jenkins". Instanța cu cea mai mică dimensiune (512/1 / 20GB) vă vom satisface nevoile și vom selecta regiunea geografică cea mai apropiată de dvs. Apoi, trebuie să alegeți imaginea utilizată pentru a crea picăturile. DigitalOcean oferă o gamă largă de sisteme de operare pentru a alege, dar ceea ce este cu adevărat plăcut este că acestea oferă, de asemenea, imagini adaptate special pentru anumite tipuri de aplicații.

Apasă pe Aplicații și selectați node-v0.10.29 pe Ubuntu 14.04 opțiune - aceasta va crea un server care este frumos bootstrapped pentru aplicația noastră Node.js.

Acum faceți clic pe Creați picături, iar DigitalOcean va începe să ne inițieze serverul.

Configurați serverul

În decurs de un minut, noul server ar trebui să fie gata și ar fi trebuit să primiți un e-mail cu datele de conectare ale serverului dvs. Să folosim aceste informații pentru a vă conecta:

ssh [email protected]

Veți fi solicitat parola furnizată în e-mail și apoi ați forțat imediat să creați o nouă parolă (faceți-o foarte puternică și păstrați-o într-o locație sigură, cum ar fi o bază de date KeePass).

Acum suntem conectați ca rădăcină, care este demostrul atotputernic al landului Linux. Dar grea este capul care poartă coroana și funcționează ca rădăcină este, în general, o idee rea. Deci, primul lucru pe care vom dori să-l facem este să creați un utilizator nou - să îl numim "aplicație":

adduser app

Va trebui să furnizați o parolă (a diferitparola puternică, stocată în siguranță) și apoi va pune o serie de întrebări opționale.

Vrem să trecem la noi aplicaţia utilizator, dar înainte de a ne deconecta, trebuie să acordăm noului nostru utilizator sudo astfel încât va avea capacitatea de a efectua acțiuni administrative:

usermod -a-G sudo app

Acum închideți conexiunea Ieșire, și apoi conectați ca aplicaţia:

ssh [email protected]

Veți fi invitat (ă) aplicaţia parola utilizatorului, iar apoi ar trebui să fiți logat și bun pentru a merge.

Instalați aplicația noastră

Să punem aplicația noastră pe mașină. Datorită imaginilor de aplicație ale DigitalOcean, aparatul nostru vine cu Node.js și npm preinstalat, dar trebuie încă să instalăm Git:

sudo apt-get instalați git

Vi se va solicita parola (din moment ce utilizați sudo), și va trebui să confirmați instalarea cu Y. Odată ce Git este instalat, îl putem folosi pentru a obține aplicația noastră de la GitHub.

Copiați URL-ul clonei HTTPS din pagina GitHub a proiectului și apoi clonați repo-ul în folderul de acasă de pe server:

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

Aplicația noastră este pe serverul nostru, într-un dosar numit "hello-jenkins". Să navigăm în:

cd hello-jenkins

Primul lucru pe care trebuie să-l facem este să instalați dependențele de aplicații:

npm install --production

Odată ce sa terminat, putem rula aplicația noastră! Spin-o cu:

nod app.js

... și navigați la adresa IP a serverului dvs. în browserul dvs..

Dar așteptați, nu funcționează! Care-i treaba?

Ei bine, să ne amintim această linie de cod în nostru app.js:

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

Chiar acum, nu avem PORT set de variabile de mediu, deci aplicația noastră nu funcționează la portul 5000 și trebuie să adăugați portul la adresa IP din browser (http: //YOUR.SERVER.IP.ADDRESS: 5000).

Deci, cum ajungem ca aplicația noastră să funcționeze așa cum era de așteptat, fără a trebui să precizeze portul? Ei bine, atunci când un browser face o solicitare HTTP, este implicit la portul 80. Deci, trebuie doar să ne stabilim PORT mediu variabil la 80.

Vom stabili variabilele noastre de mediu în / Etc / mediu fișierul de pe server - acest fișier se încarcă la conectare, iar variabilele setate vor fi disponibile la nivel global pentru toate aplicațiile. Deschideți fișierul:

sudo nano / etc / mediu

Veți vedea asta chiar acum CALE este setat în acest fișier. Adăugați următoarea linie după aceasta:

PORT = 80

Apoi tastați Ctrl-X, Y, și introduce pentru a salva și a ieși. Deconectarea serverului (Ieșire) și SSH înapoi (aceasta va încărca noua variabilă de mediu).

O ultimă sarcină mică - rularea unei aplicații pe portul 80 necesită privilegii de root, dar execută sudo node app.js nu vom păstra variabilele de mediu pe care le-am înființat. Pentru a face acest lucru, vom permite nodul pentru a avea capacitatea de a rula pe portul 80 sans sudo:

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

Ar trebui să o facă. Acum rulați:

nod app.js

Navigheaza catre http: //YOUR.SERVER.IP.ADDRESS, și veți vedea Salut Lume!

Păstrați-l

În prezent, aplicația noastră rulează numai în timp ce executăm procesul - dacă îl închidem, site-ul nostru nu mai este disponibil. De ce avem nevoie este o modalitate de a păstra aplicația noastră Node.js care rulează în fundal. Pentru asta, vom folosi pentru totdeauna. Primul pas este să îl instalați la nivel global:

sudo npm install -g pentru totdeauna

Acum, în loc să începem aplicația noastră nod app.js, vom folosi:

pentru a începe întotdeauna app.js

Observați că în locul procesului care se execută, acesta iese imediat și vă oferă controlul înapoi. Acest lucru se datorează faptului că serverul Node.js rulează în fundal. Acum nu trebuie sa ne facem griji ca serverul nostru sa se inchida atunci cand am deconectat serverul. pentru totdeauna va reporni automat aplicația noastră dacă se întâmplă să se prăbușească!

Pentru a opri aplicația noastră, putem rula:

pentru totdeauna

Deocamdată, hai să o ducem în mișcare și să mergem la Jenkins.

Un timp pentru a testa

Vom găzdui serverul Jenkins pe o picătură separată DigitalOcean. Hai să ne întoarcem acum.

A doua cădere

Creați o nouă picătură cu numele de gazdă "jenkins-box". Alege 512/1 / 20GB din nou, împreună cu aceeași locație și același tip de aplicație (node-v0.10.29 pe Ubuntu 14.04) ca și cu picăturile anterioare.

Clic Creați picături și după ce este terminat, utilizați acreditările trimise prin e-mail pentru a vă conecta prin SSH (va trebui să setați o parolă nouă, la fel ca înainte).

Ca și înainte, ar trebui să creăm un utilizator nou înainte de a face orice altceva. De data aceasta să-l sunăm admin:

adduser admin usermod -a-G sudo admin

Deconectați-vă ca rădăcină și autentificați ca noul creat admin.

Deoarece scopul Jenkins este preluarea proiectului nostru și executarea testelor sale, mașina noastră trebuie să aibă toate dependentele proiectului instalate. Am inversat această instanță cu aplicația Node.js a DigitalOcean, astfel încât Node.js și npm sunt deja instalate. Dar trebuie încă să instalăm Git:

sudo apt-get instalați git

Închiriați Butlerul

Următorul este Jenkins. Instalarea lui Jenkins este destul de simplă - o vom avea apt-get faceți toate ridicările grele. Singura captura este că trebuie să adăugăm un nou potrivit depozit înainte de a începe instalarea:

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 binar /> /etc/apt/sources.list.d/jenkins.list' sudo apt-get update

Acum putem instala Jenkins:

sudo apt-get instala jenkins

Odată finalizată, Jenkins va fi rulat și disponibil pe portul 8080. Navigați browserul dvs. la -box Jenkins Adresa IP la portul 8080 și veți vedea pagina de destinație Jenkins.

Apasă pe Gestionați Jenkins și apoi Gestionați pluginurile legătură. Treceți la Disponibil filă și căutați GitHub Plugin. Apasă pe Instalare apoi caseta de validare Descărcați acum și instalați după repornire buton.

Aceasta va iniția secvența de instalare. Pluginul GitHub are mai multe dependențe, astfel încât vor fi instalate mai multe pluginuri. În partea de jos a paginii, verificați Reporniți Jenkins când instalarea este finalizată și nu se execută nicio lucrare - acest lucru îl va face pe Jenkins să se repornească odată ce instalațiile sunt finalizate.

Odată ce Jenkins a repornit, este timpul să adăugăm proiectul nostru. Apasă pe Articol nou buton. Utilizați "hello-jenkins" pentru numele articolului, selectați Creați un proiect software în stil liber, și faceți clic pe butonul etichetat O.K.

Odată ce proiectul este setat, veți găsi pe pagina de setări a proiectului. Adăugați URL-ul GitHub al proiectului nostru la Proiectul GitHub cutie:

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

Apoi, selectați git opțiune sub Gestionarea codului sursă. În câmpurile nou apărute, adăugați adresa URL la proiectul nostru repo GitHub la Adresa URL a repozitorului camp:

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

Derulați puțin mai jos și faceți clic pe caseta pentru a activa Construiți când o schimbare este împinsă către GitHub. Cu această opțiune verificată, proiectul nostru se va construi de fiecare dată când împingeți la repo GitHub. Desigur, avem nevoie de Jenkins să știe ce să facă atunci când rulează o construcție. Apasă pe Adăugați pasul de construire drop-down și selectați Executați coajă. Acest lucru va face a Comanda dialogul disponibil și ceea ce punem în acest dialog se va desfășura atunci când se inițiază o construcție. Adăugați următoarele:

npm install ./script/test

Construcția noastră constă în două etape. În primul rând, instalează dependențele de aplicații. Apoi se execută ./ Script / test pentru a rula testele noastre.

Faceți clic pe "Salvați".

Pentru a finaliza configurarea integrării, mergeți la GitHub repo și faceți clic pe Setări. Apasă pe Webhooks & Services și apoi Adăugați un serviciu scapă jos. Selectează Jenkins (plugin GitHub) serviciu.

Adăugați următoarele ca Jenkins cârlig url:

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

Clic Adăugați un serviciu. Proiectul nostru este acum pregătit pentru primul său test de integrare continuă!

Să-i dăm ceva de testat. Deschide app.js la nivel local și pentru a schimba această linie:

res.send ("salut lume");

… la acest:

res.send ('hello jenkins');

Salvați schimbarea și comiteți-o:

adăugați git. git commit -m 'Comutați la hello jenkins'

Acum ține-ți ochii pe Jenkins în timp ce îți împingi schimbările la GitHub:

git push master de origine

După o secundă sau două, ar trebui să vedeți că a fost inițiat un nou loc de muncă pentru noi salut-Jenkins proiect în Jenkins - lucrările noastre de integrare continuă!

Fluxul de integrare continuă

Dar ... slujba nu reușește! De ce?

Amintiți-vă că testul nostru așteaptă apelul rădăcină pentru a reveni la "salut lume", dar l-am schimbat în "hello jenkins". Asa ca sa schimbam asteptarile testului nostru. Schimbați această linie:

solicitați (app) .get ('/') așteptați ('hello world', done);

... cu această linie:

cere (app) .get ('/') așteptați ('hello jenkins', gata);

Salvați, comiteți și împingeți din nou:

adăugați git. git commit -m 'Schimbați testul pentru a salva masterul de origine a lui jenkins' git push

Urmăriți Jenkins - din nou, veți vedea că o construcție este inițiată automat și de această dată reușește!

Acesta este fluxul integrării continue. Serverul de testare testează continuu orice cod nou pe care îl împingeți, astfel încât să fiți informat rapid despre orice teste care nu reușesc.

Găsește-l

Bine, deci ne testez automat modificările, dar ce să facem cu implementarea acestor modificări? Nici o problema!

Dacă ați urmărit îndeaproape, ați observat fără îndoială că din proiectul nostru lipsește ceva până acum. În structura proiectului de la începutul tutorialului, există a script / Deploy fișier, dar încă nu am făcut niciun astfel de dosar. Ei bine, acum o vom face!

Cheia pentru autentificare

Dar, mai întâi, să discutăm cum va funcționa implementarea. Scriptul nostru (realizat de pasul de construcție Jenkin) se va conecta la serverul de aplicații prin SSH, va naviga la dosarul nostru de aplicații, va actualiza aplicația și apoi va reporni serverul. Înainte de a scrie scriptul nostru de implementare, trebuie să ne ocupăm de modul în care serverul nostru Jenkins va fi SSH în serverul nostru de aplicații.

Până acum, am accesat serverele noastre introducând manual parole, însă această abordare nu va funcționa pentru scripturile automate. În schimb, vom crea o cheie SSH pe care serverul Jenkins îl va utiliza pentru a se autentifica cu serverul de aplicații.

Când Jenkins se instalează, creează un nou utilizator numit Jenkins. Jenkins execută toate comenzile cu acest utilizator, așa că trebuie să generăm cheia noastră cu Jenkins utilizator astfel încât să aibă accesul corespunzător la acesta.

În timp ce sunteți conectat (ă) ca admin pe -box Jenkins, executați următoarele:

sudo su

Asigurați-vă admin parola și vă va comuta la rădăcină utilizator. Apoi executați:

su jenkins

Acum acționați ca Jenkins utilizator. Generați o cheie SSH:

ssh-keygen -t rsa

Salvați fișierul în locația implicită (/var/lib/jenkins/.ssh/id_rsa), și asigurați-vă că nu utilizați o expresie de acces (în caz contrar accesul SSH va necesita o parolă și nu va funcționa dacă este automatizat).

Apoi, trebuie să copiem cheia publică creată. Rulați acest lucru:

cat ~ / .ssh / id_rsa.pub

... și copiați rezultatul. Ar trebui să fie un șir lung, începând cu "ssh-rsa" și terminând cu "jenkins @ jenkins-box".

Ieșiți din -box Jenkins și reveniți în serverul de aplicații (salut-Jenkins) dupa cum aplicaţia utilizator. Trebuie să creați un fișier numit authorized_keys în a noastră aplicaţia utilizatorului.ssh pliant:

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

Lipiți cheia publică pe care ați copiat-o și apoi Ctrl-X/Y/introduce pentru a salva și a ieși. Pentru ca acest fișier să funcționeze corespunzător, trebuie să aibă setări stricte asupra acestuia:

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

Întoarceți-vă la Jenkins caseta, treceți la Jenkins utilizator și verificați dacă vă puteți conecta la serverul de aplicații fără a introduce o parolă:

ssh [email protected]

Trebuie să vă conectați cu succes la serverul de aplicații fără a trebui să introduceți parola. Cu ceea ce am stabilit, putem trece acum la implementare.

Transportă automat

Creați un fișier în scenariu dosarul numit lansa (observați că nu există nicio extensie). Adăugați următoarele la script / Deploy:

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

Să trecem prin aceasta:

  • În primul rând, vă conectăm la serverul de aplicații ca și aplicaţia utilizator.
  • Apoi navigăm în dosarul nostru de aplicații și actualizăm la ultima versiune de la GitHub.
  • După aceea, ne instalează dependențele.
  • În sfârșit, odată ce codul nostru de aplicație este actualizat, vom reîncepe serverul nostru pentru tot restartall.

Faceți noul nostru fișier de script executabil:

chmod + x script / implementare

Adăugați acest nou fișier și îl obligați:

adăugați git. git commit -m "Adăugați scriptul de implementare"

Dar să nu împingem încă. Mai întâi, du-te înapoi la configurația noastră de proiect în Jenkins și derulați în jos până la comanda de construire. Adăugați această nouă linie la sfârșitul acesteia:

./ Script / disloca

Salvați proiectul Jenkins.

Acum mergeți mai departe și împingeți la GitHub și urmăriți cum Jenkins construiește automat. Odată ce ați terminat construirea (ar trebui să reușească), navigați-vă în browser-ul dvs. la IP-ul serverului de aplicații. Presto! Lumea noastră de salut a fost înlocuită cu un "hello jenkins"!

Aplicația noastră este acum desfășurată în mod continuu!

Totul este bine Automat bine

Pfiu. A fost destul de plimbare!

În final, am stabilit cu succes integrarea continuă și o desfășurare continuă, care oferă un nivel foarte plăcut de automatizare în viața noastră zilnică a dezvoltatorilor. Amintiți-vă că calculatoarele nu vă plictisiți, așa că, în timp ce se ocupă de testare și desfășurare, sunteți liberi să faceți lucruri importante, cum ar fi să vă faceți un sandviș. Deci, du-te face sandwich-ul și mâncați-l ca un campion de automatizare!

Cod