Bu yazıda Jenkins CI/CD süreçlerinin tamamını bulabilirsiniz. Gelecek yazımda SonarQube entegrasyonuna da değineceğim. Ayrıca Kubernetes deployment stratejilerine de daha sonraki yazımda değineceğim.

Harbor, Jenkins, Gitlab ve Kubernetes – Rancher kurulumlarına bağlantılara tıklayarak ulaşabilirsiniz.

NOT: Bu işlemler için Jenkins yazımda olan alternatif kurulumu yapmanız gerekir.

Ek olarak ilgili toolların resmi web siteleri aşağıdaki gibidir.

Jenkins: https://www.jenkins.io/

Gitlab: https://about.gitlab.com/install/

Rancher: https://rancher.com/

Harbor: https://goharbor.io/

Gitlab Repository Yapılandırması

Jenkins CI/CD İçin Proje Dosyalarının Yüklenmesi

Gitlab üzerinde test ci/cd projemizi yapılandıralım. Root altında test adlı repository oluşturdum. Oluşturulan repository’nin Project Visibility ayarını Gitlab üzerinden “Internal” olarak ayarladım. Bu şekilde Jenkins, Gitlab’a username password ile giriş yapıp, projeyi görebilecek. Aşağıdaki registry ve gitlab adreslerini kendinize göre yapılandırın.

Dockerfile Dosyasının Yapılandırılması

Dockerfile adlı dosyamıza aşağıdaki komutları yazalım.

FROM httpd:2.4
COPY ./index.html /usr/local/apache2/htdocs/

Jenkinsfile Dosyasının Yapılandırılması

Jenkinsfile adlı dosyamıza aşağıdaki komutları yazalım.

pipeline {

  environment {
    registry = "harbor.sezer.test:443/library"
    dockerImage = ""
    projectName = "test-image" 
  }

  agent any

  stages {

        stage('Checkout Gitlab Source') {
            steps {
                git url:'https://gitlab.sezer.test/root/test.git', branch:'main', credentialsId:'deneme-test-123-deneme-2345'
            }
        }

        stage('Build Image') {
            steps{
                script {
                    docker.withServer('tcp://127.0.0.1:2376') {
                        dockerImage = docker.build registry + "/" + projectName + ":$BUILD_NUMBER"
                    }
                }
            }
        }

        stage('Version Image Push') {
            steps{
                script {
                    docker.withServer('tcp://127.0.0.1:2376') {
                        withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'harbor-registry', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
                            sh "echo ${PASSWORD} | docker -H tcp://127.0.0.1:2376 --config /bitnami/jenkins/home/.docker login harbor.sezer.test:443 -u ${USERNAME} --password-stdin"
                        }
                        sh "docker -H tcp://127.0.0.1:2376 --config /bitnami/jenkins/home/.docker push ${registry}/${projectName}:${BUILD_NUMBER}"
                    }
                }
            }
        }
    
        stage('Retag Image') {
            steps{
                script {
                    docker.withServer('tcp://127.0.0.1:2376') {
                        withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'harbor-registry', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
                            sh "echo ${PASSWORD} | docker -H tcp://127.0.0.1:2376 --config /bitnami/jenkins/home/.docker login harbor.sezer.test:443 -u ${USERNAME} --password-stdin"
                        }
                        sh "docker -H tcp://127.0.0.1:2376 --config /bitnami/jenkins/home/.docker tag ${registry}/${projectName}:${BUILD_NUMBER} ${registry}/${projectName}:latest"
                    }
                }
            }
        }

        stage('Latest Image Push') {
            steps{
                script {
                    docker.withServer('tcp://127.0.0.1:2376') {
                        withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'harbor-registry', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
                            sh "echo ${PASSWORD} | docker -H tcp://127.0.0.1:2376 --config /bitnami/jenkins/home/.docker login harbor.sezer.test:443 -u ${USERNAME} --password-stdin"
                        }
                        sh "docker -H tcp://127.0.0.1:2376 --config /bitnami/jenkins/home/.docker push ${registry}/${projectName}:latest"
                    }
                }
            }
        }

        stage('Remove All Image') {
            steps{
                script {
                    docker.withServer('tcp://127.0.0.1:2376') {
                        sh 'docker -H tcp://127.0.0.1:2376 rmi $(docker -H tcp://127.0.0.1:2376 images -a -q) -f'
                    }
                }
            }
        }

        stage('Deploy App on K8S') {
            steps {
                script {
                    withKubeConfig([credentialsId: 'rancherconfig']) {
                        sh 'sed -ie "s/THIS_STRING_IS_REPLACED_DURING_BUILD/$(date)/g" myweb.yaml'
                        sh 'kubectl apply -f myweb.yaml'
                    }
                }
            }
        }
    }
}

Web Dosyasının Yapılandırılması

index.html dosyamıza aşağıdaki komutları yazalım.

<center>
    <h1>Sezer.test</h1>
    <h1>v1.0</h1>
    <br><hr>
    <p>Harbor Registry - Gitlab - Jenkins CI/CD - Kubernetes Deployment</p>
</center>

Deployment Dosyasının Yapılandırılması

myweb.yaml dosyamıza aşağıdaki kubernetes komutlarını yazalım

apiVersion: v1
kind: Namespace
metadata:
  name: myweb
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: myweb
  name: myweb
  namespace: myweb
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myweb
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    metadata:
      labels:
        app: myweb
    spec:
      containers:
      - image: harbor.sezer.test:443/library/test-image:latest
        imagePullPolicy: Always
        name: myweb
        env:
        - name: BUILD_DATE
          value: 'THIS_STRING_IS_REPLACED_DURING_BUILD'
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: myweb
  name: myweb
  namespace: myweb
spec:
  ports:
  - nodePort: 32223
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: myweb
  type: NodePort
--- 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: 
  name: myweb
  namespace: myweb
spec: 
  ingressClassName: nginx
  rules: 
    - 
      host: myweb.test
      http: 
        paths: 
          - 
            backend: 
              service: 
                name: myweb
                port: 
                  number: 80
            path: /
            pathType: Prefix

Jenkins Ayarları

Jenkis admin arayüzüne giriş yapalım. Ardından Jenkins’i Yönet menüsüne girelim.

jenkins ci/cd

Kullanılabilir menüsü altında arama yapıp daha sonra aşağıdaki 5 eklentiyi tek tek yüklüyoruz.

1- Kubernetes CLI Plugin
2- Kubernetes
3- Docker Pipeline
4- GitLab Plugin
5- Generic Webhook Trigger Plugin

NOT: Yükleme yaparken without restart butonundan yapın. Tüm eklentileri yükledikten sonra Jenkins’i yeniden başlat kutusunu seçin.

Yükleme yaptıktan sonra Jenkins’i yeniden başlatın butonuna tıklayarak arayüzden yeniden başlatma işlemini gerçekleştirin.

image 104

Tekrar Jenkins admin arayüzüne giriş yaptıktan sonra “Jenkins’i Yönet > Sistem Konfigürasyonunu Değiştir” adımlarını takip edelim.

image 105

Sayfanın en altına inip Cloud menüsündeki “a separate configuration page.” bağlantısına tıklıyoruz.

image 106

Kubernetes’i seçiyoruz.

image 107

Kubernetes Cloud details butonuna tıklıyoruz

image 108

Credentials kısmında Add’e tıklayıp “Jenkins” seçeneğine tıklıyoruz

image 109

Açılan pencerede Kind kısmından Secretfile seçeneğini seçiyoruz

image 110

Rancher arayüzünden Cluster config‘i indiriyorum.

image 111

İlgili kısımları Jenkins üzerinden dolduruyorum ardından “Add” butonuna basıyorum. Buradaki ID ana repodaki Jenkinsfile dosyasına bağlı.

withKubeConfig([credentialsId: 'rancherconfig'])
image 112

Credentials kısmından eklediğim Credential’ı seçip “Test Connection” butonuna basıyorum.

image 113

Test sonucu başarılı ise size aşağıdaki gibi sonuç göstermeli.

Connected to Kubernetes v1.21.8

Ardından Jenkins ayarlarını kendi Jenkins sunucunuza göre düzenleyin.

image 152

Pod templates butonuna tıklayarak devam edin

image 115

Ardından “Add Pod Template” butonuna tıklayın.

image 116

Name kısmına “kube” yazın ve ardından “Pod template details” butonuna basın

image 117

Labels kısmına “kubepod” yazın. Ardından Containers başlığı altında “Add Container” butonuna basarak “Container Template‘i” seçin.

image 118

Ayarları aşağıdaki gibi düzenleyin ve kaydedin

Name => inbound-agent
Docker image => jenkins/inbound-agent:latest
Working directory => /bitnami/jenkins/home  (Bitnami reposu ile kurduysanız)
Command to run => /bin/sh -c
Arguments to pass to the command => cat
Allocate pseudo-TTY => 
image 153

Jenkins üzerinde yeni bir iş oluşturuyoruz

image 125

Projeye bir isim verip, Pipeline seçip Ok butonuna basın

image 126

Advanced Project Options sekmesine tıklayıp, Pipeline > Defination kısmını “Pipeline script from SCM” olarak seçin.

image 127

SCM > Git – Repository URL: Gitlab URL adresi olarak ayarlayıp Credential kısmındaki add butonuna basıp “Jenkins‘i” seçin.

image 129

Açılan kısımda bilgileri kendi Gitlab bilgileriniz ile doldurun. ID kısmını da Jenkinsfile‘a eklediğimiz CridentialsID ile değiştirin. Bizimki test için aşağıdaki gibiydi

steps {
        git url:'https://gitlab.sezer.test/root/test.git', branch:'test-deploy-stage', credentialsId:'deneme-test-123-deneme-2345'
      }

Buradaki “deneme-test-123-deneme-2345” değeri tırnaklar olmadan ID kısmına yazın.

image 130

Ardından Cretandial seçin, Branch Specifier kısmına da branch adınızı yazıp kaydedin.”main” olabilir veya benimki gibi farklı olabilir. “test-deploy-stage

image 133

Ana sayfadan Jenkins’i Yönet > Manage Nodes and Clouds kısmına tıklayalım.

image 138

Configure seçeneğine tıklayalım

image 139

Etiketler kısmına “kubepod” ekleyelim. Bu pipeline dosyamızda belirttiğimiz alan.

agent { label 'kubepod' }
image 140

Oluşturduğumuz test-pipeline’a girelim ve “Şimdi yapılandır” butonuna basarak yapılandırmayı bir test edelim.

image 132

Build işlemi başladı. Tıklayıp canlı izleyebilirsiniz.

image 134

Sonuç aşağıdaki gibi

image
kubectl get pods -n myweb
NAME                     READY   STATUS    RESTARTS   AGE
myweb-6b849784f5-j9xks   1/1     Running   0          57s

Gitlab Webhook Yapılandırması

Şimdi Github push veya commit yapıldığında auto build işlemini sağlayacağız.

Oluşturduğumuz test-pipeline projemize Jenkins üzerinden girelim

image 142

Konfigürasyonu düzenle kısmına tıklayalım

image 143

Build trigger sekmesine tıklayıp ayarları aşağıdaki gibi yapalım ardından Gelişmiş butonuna basalım

image 144

Sayfanın aşağı kısmında Generate Token butonuna basalım çıkan token’i kopyalayalım ve ardından projeyi tekrardan kaydedelim.

image 145

Gitlab projemize girip Settings > Webhooks menüsüne tıklayalım

image 146

Ayarları aşağıdaki gibi kendinize göre düzenleyip Add Webhook butonuna basarak kaydedin.

image 147

Test > Push Event ‘e basarak konfigürasyonun doğru çalışıp çalışmadığını kontrol edebilirsiniz.

image 148

Başarılı ise 200 kodu dönecektir

image 149

Şimdi gitlab kısmında index.html’i değiştirelim ve bakalım push yapacak mı?

image 150

Bingooo. Artık oto build yapıyor

image 151

Buraya kadar okuyup uyguladıysanız bir alkışı hak ettiniz CLAP CLAP