Sådan konfigureres CI / CD-arbejdsgang for Node.js-apps med Jenkins og Kubernetes

af Konstantin Feofantov

Introduktion

Kontinuerlig integration og kontinuerlig levering er to af de praksis, der former DevOps-filosofi i dag. Grundlæggende inkluderer det implementering af udviklings- og integrationsarbejdsgange, hvor udviklere ofte forpligter deres ændringer til det centrale arkiv, hvilket sikrer, at deres forpligtelser er funktionelle og klar til at blive implementeret til produktion.

Disse arbejdsgange bruger automatiseringsmotorer tilpasset opgaverne. I denne tutorial vil jeg forklare, hvordan man opretter en CI / CD-arbejdsgang for et Node.js-program, der er vært på Kubernetes. Til automatiseringsdelen vil jeg bruge Jenkins, hvilket nok er et af de mest populære CI / CD-værktøjer i dag.

Jeg vil antage, at du allerede har en Kubernetes-klynge med en installeret ror / rorkult-server. Jeg vil også antage, at du er bekendt med Git, og at du har et lager. I denne artikel bruger vi Bitbucket, men du kan f.eks. Bruge enhver anden Git-platform, f.eks. Github eller Gitlab.

Arbejdsgang og arkitektur

Vi bruger følgende arbejdsgang:

Bemærk, at vi vil bruge Kubernetes navneområder som implementeringsmiljøer. For at forenkle denne tutorial vil jeg bruge en 2-lags implementeringsarkitektur med integrations- og produktionsmiljøer, men du kan nemt udvide systemet til en 3-lags eller 4-lags arkitektur.

For at oprette disse navneområder skal du køre kommandoerne i kubectl:

kubectl oprette navneområde myapp-integration
kubectl oprette navneområde myapp-produktion

Kontroller, at navneområdet er oprettet:

kubectl få navneområder

Opsætning af arbejdsgangen

Den applikation, vi skal lancere, vil bestå af to dele:

  • Nginx omvendt proxy
  • NodeJS-app

Applikationskoden er vært i et Git-lager med følgende biblioteksstruktur:

/
   -src /
     index.js
   -tests /
     integration-tests.sh
     production-tests.sh
   -indsætte/
     Nginx-reverseproxy.yaml
     nodejs.yaml
   Jenkinsfile

Lad os se på disse filer i detaljer.

Src-biblioteket indeholder et grundlæggende Node.js-program, der opretter en http-server på port 8080 og returnerer en meddelelse afhængigt af den besøgte sti:

  • “Dette er hjemmesiden”, når du besøger “/”
  • “Velkommen til dir1, hvordan kan jeg hjælpe dig?” Når du besøger “/ dir1”
  • “Oplysningerne om person med id 1 er X”, når man besøger “/ dir2 / person / 1”
// index.js
var http = kræver ('http');
var url = kræver ('url');
var server = http.createServer (funktion (req, res) {
 var side = url.parse (req.url) .pathname;
 console.log (side);
 res.writeHead (200, {"Content-Type": "text / plain"});
 
 if (side == '/') {
   res.write ('Dette er hjemmesiden');
 }
 ellers hvis (side == '/ dir1') {
  res.write ('Velkommen til dir1, hvordan kan jeg hjælpe dig?');
 }
 ellers hvis (side == '/ dir2 / person / 1') {
  res.write ('Oplysningerne om person med id 1 er X');
 }
 res.end ();
});
server.listen (8080);

Tests-bibliotek indeholder to scripts med test, der skal udføres for at sikre, at applikationen kører. Til denne tutorial og for enkelhedens skyld udføres de samme test i integrations- og produktionsmiljøerne, men du kan (og bør) have forskellige test afhængigt af miljøet.

I dette tilfælde vil vi bruge krøller til at teste de tre stier i Node.js-appen. Hvis serveren returnerer det rigtige svar, lykkes testen. Ellers afslutter scriptet med en fejl.

Integration tests.sh:

#! / Bin / bash
# integration-tests.sh
ekko "Start af integrationstest ..."
ekko "Test af rodsti ..."
res1 = $ (krøllet -s http: // $ 1 /)
if ["$ res1"! = "Dette er hjemmesiden"]; derefter
 ekko "Sti / test mislykkedes. Aborterer ..."
 Afslut 1
fi
ekko "Testing path / dir1 ..."
res2 = $ (krøllet -s http: // $ 1 / dir1)
hvis ["$ res2"! = "Velkommen til dir1, hvordan kan jeg hjælpe dig?" ]; derefter
 ekko "Sti / dir1-test mislykkedes. Aborterer ..."
 Afslut 1
fi
ekko "Test af rodsti / dir2 / person / 1 ..."
res3 = $ (krøllet -s http: // $ 1 / dir2 / person / 1)
if ["$ res3"! = "Oplysningerne om person med id 1 er X"]; derefter
 ekko "Sti / dir1-test mislykkedes. Aborterer ..."
 Afslut 1
fi
ekko "Integrationstests lykkedes."

Produktionstest.sh:

#! / Bin / bash
# produktion-tests.sh
ekko "Start produktionstest ..."
ekko "Test af rodsti ..."
res1 = $ (krøllet -s http: // $ 1 /)
if ["$ res1"! = "Dette er hjemmesiden"]; derefter
 ekko "Sti / test mislykkedes. Aborterer ..."
 Afslut 1
fi
ekko "Testing path / dir1 ..."
res2 = $ (krøllet -s http: // $ 1 / dir1)
hvis ["$ res2"! = "Velkommen til dir1, hvordan kan jeg hjælpe dig?" ]; derefter
 ekko "Sti / dir1-test mislykkedes. Aborterer ..."
 Afslut 1
fi
ekko "Test af rodsti / dir2 / person / 1 ..."
res3 = $ (krøllet -s http: // $ 1 / dir2 / person / 1)
if ["$ res3"! = "Oplysningerne om person med id 1 er X"]; derefter
 ekko "Sti / dir1-test mislykkedes. Aborterer ..."
 Afslut 1
fi
ekko "Produktionstests lykkedes."

Distributionsmappe indeholder alle de yaml-filer, der er nødvendige for at implementere appen på Kubernetes. Yamlen nedenfor indeholder Kubernetes-ressourcer til at implementere Nginx-omvendt proxy.

# Nginx-reverseproxy.yaml
apiVersion: v1
kind: Service
metadata:
  navn: nginx-reverseproxy-service
spec:
 vælgeren:
    app: nginx-reverseproxy
 type: LoadBalancer #LB for at afsløre tjenesten og få en ekstern IP-adresse
 havne:
  - navn: http
    havn: 80
    protokol: TCP
---
apiVersion: extensions / v1beta1
art: Implementering
metadata:
  etiketter:
    app: nginx-reverseproxy
  navn: nginx-reverseproxy-distribution
spec:
  kopier: 1
  skabelon:
    metadata:
      etiketter:
        app: nginx-reverseproxy
    spec:
      beholdere:
      - billede: nginx: 1.13
        navn: kubecont-nginx
        havne:
        - containerPort: 80
        volumeMounts:
        - navn: config-volume
          mountPath: /etc/nginx/conf.d
      mængder:
        - navn: config-volume
          configMap:
            navn: nginx-reverseproxy-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  navn: nginx-reverseproxy-config
data:
  default.conf: | -
    server {
     servernavn dit hostnavn.com;
     lyt 80;
     #deny adgang til .htaccess-filer, hvis Apache's dokument root
     #forfølger med nginx's
     #
     placering ~ /\.ht {
         benægter alt;
     }
Beliggenhed / {
         proxy_pass http: // nodejs-service: 8080; # dette er den service, der er beskrevet i nodejs.yaml
     }
    }

Og endelig, her er den yaml til lancering af Node.js på Kubernetes. Bemærk, at CongigMap, der refereres til af nodejs-distribution, oprettes dynamisk under udførelsen af ​​rørledningen, som jeg vil udvide nedenfor.

# nodejs.yaml
apiVersion: v1
kind: Service
metadata:
  navn: nodejs-service
spec:
 vælgeren:
    app: nodejs
 havne:
  - navn: http
    port: 8080
    protokol: TCP
---
apiVersion: extensions / v1beta1
art: Implementering
metadata:
  etiketter:
    app: nodejs
  navn: nodejs-distribution
spec:
  kopier: 1
  skabelon:
    metadata:
      etiketter:
        app: nodejs
    spec:
      beholdere:
      - billede: knude: 9.11
        navn: kubecont-nodejs
        kommando: ["node", "/usr/src/app/index.js"]
        havne:
        - containerPort: 8080
        volumeMounts:
        - navn: app-volumen
          mountPath: / usr / src / app
      mængder:
        - navn: app-volumen
          configMap:
            navn: nodejs-app

Og endelig har vi en Jenkinsfile, der beskriver CI / CD-arbejdsgangen i Jenkins. Arbejdsgangen består af tre faser:

  • Forberedelsestrinn: kubectl er installeret, og appens lager klones
  • Integrationsfase: der oprettes en ConfigMap fra Node.js-appen, og Kubernetes-ressourcer oprettes. Derefter testes applikationen, og til sidst renses miljøet
  • Produktionstrin: De samme trin som i integrationsfasen udføres, bortset fra rengøring, da Kubernetes-ressourcerne skal holdes i produktion.

Så dette er Jenkinsfile:

// Jenkinsfile
node {
fase ('Forberedelse') {
      // Installation af kubectl i Jenkins-agent
      sh 'curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux / amd64 / kubectl'
  sh 'chmod + x ./kubectl && mv kubectl / usr / local / sbin'
// Klon-git-lager
  git url: 'https: //bitbucket.org/advatys/jenkins-pipeline.git'
   }
scene ('integration') {
 
      withKubeConfig ([legitimationsoplysninger: 'jenkins-deployer-legitimationsoplysninger', serverUrl: 'https://104.155.31.202']) {
      
         sh 'kubectl create cm nodejs-app --from-file = src / --namespace = myapp-integration -o = yaml - dryry run> deploy / cm.yaml'
         sh 'kubectl Apply -f deploy / --namespace = myapp-integration'
         prøve{
          // Gathering Node.js app's eksterne IP-adresse
          def ip = ''
          def tæller = 0
          def countLimit = 10
       
          // Venteløjde for IP-adressetildeling
          println ("Venter på IP-adresse")
          while (ip == '' && count 
}
   }
 scene ('Produktion') {
      withKubeConfig ([legitimationsoplysninger: 'jenkins-deployer-legitimationsoplysninger', serverUrl: 'https://104.155.31.202']) {
      
       sh 'kubectl oprette cm nodejs-app - fra-file = src / --namespace = myapp-produktion -o = yaml - tørrkør> implementere / cm.yaml'
sh 'kubectl Apply -f deploy / --namespace = myapp-produktion'
      
  
      // Gathering Node.js app's eksterne IP-adresse
         def ip = ''
         def tæller = 0
         def countLimit = 10
        
         // Venteløjde for IP-adressetildeling
         println ("Venter på IP-adresse")
         while (ip == '' && count 

Installer Jenkins

For at installere Jenkins vil vi bruge Helm-diagrammet, der er tilgængeligt på det officielle stabile depot. Brug følgende kommando til at distribuere Jenkins i Kubernetes med de nødvendige plugins:

helm install - navn my-jenkins-distribution stabilt / jenkins --version 0.16.1 - værdier jenkins-params.yaml

Hvor jenkins-params.yaml er som følger:

# Jenkins-params.yaml
Mestre:
  Billede: jenkins / jenkins
  ImageTag: 2.121
  ServiceType: LoadBalancer
  ServicePort: 80
  AdminPassword: admin_313
  InstallPlugins:
    - kubernetes: 1.5.2
    - workflow-aggregator: 2.5
    - workflow-job: 2.21
    - legitimationsforpligtende: 1.16
    - git: 3,9,0
    - kubernetes-cli: 1.0.0
    - brugerdefineret værktøj-plugin: 0.5
    - bitbucket: 1.1.7
RBAC:
  install: sandt
  apiVersion: v1
Agent:
  Billede: jenkins / jnlp-slave
  ImageTag: 3.19-1
  mængder:
  - type: EmptyDir
    mountPath: / usr / local / sbin

Når du kører kommandoen, får du instruktionerne for at få adgangskoden. I vores tilfælde var det at udføre følgende kommando:

printf $ (kubectl get secret --namespace default my-jenkins-distribution -o jsonpath = "{. data.jenkins-admin-password}" | base64 - decode); echo

Hvis alt er i orden, skal du se, at roret frigøres implementeret:

roret ls

Efter et stykke tid (ca. 30 sekunder til 1 minut) skal du køre kommandoen herunder for at få en ekstern IP-adresse, hvor du kan få adgang til din Jenkins-forekomst.

kubectl få tjenester

I dette tilfælde er det 35.189.215.166. Naviger til denne IP-adresse i din browser og log ind som administrator med det adgangskode, du tidligere har (glem ikke at ændre disse legitimationsoplysninger :)).

Konfiguration af kubectl i Jenkins til kontinuerlig implementering

Lad os nu konfigurere Kubernetes-legitimationsoplysninger, så Jenkins kan indsætte til vores Kubernetes-klynge.

Vi er nødt til at oprette et ServiceAccount i Kubernetes, der vil blive brugt af Jenkins til implementering.

kubectl skabe en jenkins-deployer
kubectl skabe clusterrolbinding jenkins-deployer-rolle - clusterrole = cluster-admin - serviceaccount = jenkins-deployer

Kør derefter kommandoen

kubectl får hemmeligheder

Du skal vælge hemmeligheden, der starter med “jenkins-deployer” og få de legitimationsoplysninger, der er knyttet til den:

kubectl beskriver hemmelige jenkins-deployer-token-jvdmf

Gå til legitimationsoplysninger i venstre menu på hovedsiden, vælg derefter System og Tilføj domæne. Du kan f.eks. Tilføje navnet på din virksomhed. Klik derefter på Tilføj legitimationsoplysninger i menuen til venstre.

Udfyld formularen som følger:

  • Venlig: Hemmelig tekst
  • Omfang: Globalt
  • Hemmelighed: token kopieret fra jenkins-deployer-token-jvdmf (lang streng)
  • ID: jenkins-deployer-legitimationsoplysninger (det samme som angivet i funktionen medKubeConfig i Jenkinsfile)

Oprettelse af Jenkins Job

Gå til hovedsiden af ​​Jenkins, klik på New Item i venstre menu. Angiv derefter et jobnavn og vælg Rørledning som jobtype.

I det næste skærmbillede skal du markere "Opbyg, når en ændring skubbes til Bitbucket". Dette vil blive brugt til at automatisere udløb af rørledninger som forklaret senere (selvom denne funktion er valgfri).

Endelig skal du gå til afsnittet Rørledning og konfigurere det som følger:

  • Definition: Rørledningsscript fra SCM
  • SCM: Git
  • Opbevaringssteder: Opbevarings-URL: dit opbevarings-URL

Og det er det, bare gem dine indstillinger.

Start af arbejdsgangen

For at starte arbejdsgangen skal du vælge den nyligt oprettede pipeline og klikke på “Build now” i venstre menu. Rørledningen starter om få sekunder.

Det er også muligt at udløse arbejdsgangen automatisk, når en bruger foretager en ændring i git-arkivet. Dette er en anbefalet praksis i henhold til CI / CD-principper.

For at gøre det, skal du opsætte en webhook i dit git-lager. For Bitbucket skal du følge instruktionerne beskrevet her. Bemærk, at den URL, du skal angive i Bitbucket, er JENKINS_URL / bitbucket-hook /

I vores tilfælde er det:

http://35.189.215.166/bitbucket-hook/

Konklusion

Jeg har demonstreret en simpel CI / CD-arbejdsgang med Jenkins og Kubernetes. Den største fordel ved denne stak er fleksibilitet, da den giver dig mulighed for at implementere praktisk talt alle typer arbejdsgange. Denne arbejdsgang kan udvides eller kompliceres afhængigt af dine udviklingsbehov. Under alle omstændigheder vil processen være langt mere effektiv end den traditionelle måde.

Jeg håber, du finder dette indlæg nyttigt. Kommenter og still spørgsmål - Jeg vil meget gerne få din feedback. Glem ikke at følge os på Twitter og deltage i vores Telegram-chat for at holde dig tunet!

Du vil måske også tjekke vores Containerum-projekt på GitHub. Vi har brug for dig feedback for at gøre det stærkere - du kan indsende et problem eller bare støtte projektet ved at give det et . Din support betyder virkelig noget for os!

Containerum er din kilde til viden om Kubernetes og Docker.