Bu yazıda Kubernetes CI/CD süreçlerinin nasıl işlediğine dair temel bilgileri bulabileceksiniz. Kubernetes üzerinde Gitlab ve Gitlab Runner kurulumunu yapacağız. Ardından SonarQube kurulumu ve son olarak Harbor Registry kurulumunu yapacağız. Tüm bu araçlar Kubernetes üzerinde çalışacak. Gitlab Runner üzerinden build, code analysis ve deployment süreçlerini yazacağız.

Bu süreçlerin geliştirilmesi ve değiştirilmesi tamamen projenize bağlıdır. Test aşaması ekleyebilirsiniz ya da bir çok open-source tool araya koyabilirsiniz. Bu yazıda sadece temel seviyede ilgili CI/CD kavramını ve nasıl sistemin kurulduğuna değiniyorum. Kıvılcım benden ateşi körüklemek sizden :]

image 1

CI/CD Nedir?

CI Continuous Integration (Sürekli Entegrasyon), CD ise Continuous Delivery (Sürekli Teslimat) ya da (Continuous Deployment) Sürekli Dağıtım kısaltmasını ifade etmektedir.

Continuous Integration (Sürekli Entegrasyon)

Sürekli entegrasyon mimarisi yapılan değişiklikler/geliştirmeler sonucu oluşan yeni sürümün, bir boru hattı (pipeline) vasıtasıyla, mevcut sürüm ile sürekli şekilde otomatik olarak birleştirilmesini sağlar. Burada önemli olan nokta sürecin bir boru hattı (pipeline) üzerinden yürütülüyor olmasıdır.

Boru hattı (pipeline) dediğimiz şey aslında süreçleri uç uca eklemek ve ihtiyaçlarımız doğrultusunda akışlara yön vererek otomatize edilmiş bir düzen oluşturmaktır.

CI pipeline içerisinde kabul kriterlerimiz için boru hatları oluşturur ve bunları bir araya getirerek yeni paketin ancak bir dizi otomatik kontrol sonrasında hedef sürüm ile birleştirilmesini sağlayabiliriz.

Continuous Delivery (Sürekli Teslimat)

Sürekli teslimat ise istenilen bir sürümün istenilen bir ortama elle tetiklemek suretiyle deploy edilebilmesidir. Yani biz istemedikçe deployment sağlanmaz, deployment istediğimizde süreci manuel olarak başlatırız.

Normalde ilgili bir sürümü deploy etmek için manuel gerçekleştirdiğimiz tüm adımları komut dosyaları vs. aracılığıyla otomatize ederiz.

Yani görselleştirmek gerekirse artık akışımız aşağıdaki şekilde olacaktır

kubernetes-ci/cd

Continuous Deployment (Sürekli Dağıtım)

Sürekli dağıtım ise CI sonrasında ilgili ortamlara deploymentin otomatik olarak yapılmasıdır. CI pipeline’i CD pipeline’a bağlarız ve bu sayede güncel sürümün yayınlanmasını istediğimiz ortamlara otomatik deploy ederiz.

Örnek üzerinden ilerleyecek olursak;

dev branch’te bir güncelleme olduğunda internal ya da staging ortamlarına deploy olmasını, master branch’te bir güncelleme olduğunda pre-prod ya da prod ortamlarına deploy olmasını sağlayabiliriz.

CD pipeline içerisinde migrationların çalışması, trafiğin yeni sürüme iletilmesi için swap/routing işlemleri, cdn önbellek temizliği, log rotation vs. hedef ortamın ve mimarinizin dinamiklerine göre dilediğiniz işlevleri dahil edebilirsiniz.

Harbor Registry Kurulumu

Daha önce Harbor registry’nin nasıl kurulduğuna dair bir yazı hazırlamıştım. Aşağıdaki bağlantıdan ulaşabilirsiniz.

SonarQube Kurulumu

Aşağıdaki yazımda SonarQube kurulumunu ve Jenkins’e entegrasyonunu anlatmıştım. İlgili yazımdaki SonarQube kurulumunu sadece yapmanız yeterli olacaktır.

Gitlab/Gitlab Runner Kurulumu

Daha önce aynı şekilde bu kurulumu anlatan bir yazı yazmıştım. Aşağıdaki yazıyı baz alarak kurulumunuzu tamamlayabilirsiniz. Kurulumu yaparken gözden kaçırmamanız gereken en önemli nokta yazımda bulunan aşağıdaki helm komutunu

--set gitlab-runner.install=false \ 

aşağıdaki gibi değiştirmelisiniz.

--set gitlab-runner.install=true \

Böylelikle Kubernetes üzerinde Gitlab’ı kurarken beraberinde Gitlab Runner’ı da kurmuş olacağız.

CI/CD Süreçleri

Aşağıdaki süreçler test için oluşturduğum Dockerfile, .gitlab-ci.yml, index.html, myweb.yaml dosyalarını kapsamaktadır. Bu dosyaları Gitlab reponuzun içerisine yüklediğinizi ve/veya oluşturduğunuzu varsayıyorum. Anlatım bu kıstasa göre olacaktır.

Bu repo içerisindeki dosyaların temel işlevleri aşağıdaki gibidir.

  • Apache ile birlikte index.html dosyamızın da içerisinde bulunduğu bir image oluşturmak
  • Bu image’ı versiyonlayıp Harbor Registry’e yüklemek.
  • Oluşan her değişiklikte myweb.yaml dosyamızı kubectl aracılığı ile Kubernetes üzerinde deploy etmek.
  • .gitlab-ci.yml ile birlikte SonarQube aracını CI/CD sürecine dahil etmek.

NOT: Bu süreçte “Argo-CD” gibi araçlarda kullanılabilir. Başta belirttiğim gibi size göre kusursuz bir sistemi yaratmak kendi elinizde. Bu anlatım sadece temel seviyede yol gösterici niteliğindedir.

Gitlab Reposunun Oluşturulması

Gitlab / Gitlab Runner kurulumunuzu yaptıktan sonra arayüze web üzerinden erişin. Daha sonra projeniz için reponuzu oluşturun. Bunu oluşturduğunuzu varsayarak görsel anlatım yapmıyorum. Klasik Gitlab arayüzü ve aşamaları.

Web Dosyasının Oluşturulması

Oluşturacağımız Dockerfile dosyasında Apache üzerinden yayınlayacağımız index.html dosyası aşağıdaki gibidir.

<center>
    <h1>Sezer.in</h1>
    <h1>v1.0</h1>
    <br><hr>
    <p>CI/CD Süreçleri - Kubernetes - Gitlab/Gitlab Runner - SonarQube - Harbor Registry</p>
</center>

Dockerfile Dosyasının Oluşturulması

Repomuzun içerisinde oluşturacağımız Dockerfile dosyamızın içeriği aşağıdaki gibi olacaktır.

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

Kubernetes Üzerinde ImagePullSecret Oluşturulması

Şifre korumalı bir Registry üzerinde çalışıyorsanız ve Deployment’larınızda bu Registry üzerinden Image çekiyorsanız mutlaka Deployment dosyanızda bu Image’ın hangi bilgileri kullanarak Registry’e login olacağını belirtmeniz gerekir. Bu şekilde Kubernetes podları oluştururken Private Registry adresinizden sorunsuz Image’ları çekebilecek.

Bununla ilgili yazımı bu bağlantıda bulabilirsiniz.

Kubernetes Deployment Dosyasının Oluşturulması

Repomuzun içerisinde oluşturacağımız myweb.yaml dosyamızın içeriği aşağıdaki gibi olacaktır.

Yaml kodlarımız arasında bulunan Registry adresini kendi adresiniz ile değiştirin. Ayrıca imagePullSecrets kısmındaki veriyi de üstte anlattığım daha önce oluşturduğunuz ImagePullSecret adınızla değiştirmeyi unutmayın. Yine de .gitlab-ci.yml dosyasının içerisinde ImagePullSecret oluşturma aşamasını da ekledim. Manuel yapmanıza gerek kalmadan bu kısmı da Pipeline üzerinden yönetebilirsiniz.

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'
      imagePullSecrets:
        - name: sezertestsecret
---
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

Gitlab İçin Cluster Yetkisi Tanımlanması

Gitlab Runner’ın Kubernetes üzerinde deployment yapabilmesi için Cluster yetkisinin olması gerekiyor. Aşağıdaki komutla Gitlab’a yetki tanımlayabilirsiniz.

kubectl create clusterrolebinding gitlab-cluster-admin --clusterrole=cluster-admin --group=system:serviceaccounts --namespace=gitlab

Pipeline İçin Variable Tanımlamaları

Pipeline üzerinde kullanacağımız bazı variable’lar var. Bunları tanımlamak için Gitlab Web arayüzüne girdikten sonra reponuzun içerisindeki Settings > CI/CD menüsüne gidin. İlgili sayfada Variables sekmesini göreceksiniz. Tanımlamalarınız aşağıdaki gibi olmalı.

  • DOCKER_REGISTRY_HOST => Image’ı çektiğiniz Registry adresi
  • DOCKER_REGISTRY_PASSWORD => Registry şifresi
  • DOCKER_REGISTRY_USER => Registry kullanıcı adı
  • KUBE_CONFIG => Kubernetes Cluster’ınıza bağlanmak için kullandığınız .kube/config dosyanız
  • SONAR_HOST_URL => SonarQube kurulumu yaptığınız URL
  • SONAR_TOKEN => SonarQube üzerinden projenize oluşturduğunuz Token

Tüm tanımları aşağıya görsel olarak ekledim.

image 6

Gitlab Runner Pipeline Dosyasının Oluşturulması

Repomuzun içerisinde oluşturacağımız .gitlab-ci.yml dosyası bizim için tüm işleri yapacak Pipeline kodlarını içeren dosyadır.

stages:
  - build
  - sonarqubecheck
  - deploy
before_script:
  - |
    for i in $(seq 1 30)
    do
        docker info && break
        echo "Waiting for docker to start"
        sleep 1s
    done
docker_build_version_push:
  stage: build
  image: docker:latest
  services:
  - docker:dind
  variables:
    DOCKER_HOST: tcp://docker:2376
    DOCKER_TLS_CERTDIR: "/certs"
    DOCKER_TLS_VERIFY: 1
    DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
  script: 
    - docker build -t harbor.sezer.test:443/library/test-image:latest -t harbor.sezer.test:443/library/test-image:$CI_PIPELINE_IID .
    - docker login $DOCKER_REGISTRY_HOST -u $DOCKER_REGISTRY_USER -p $DOCKER_REGISTRY_PASSWORD
    - docker push harbor.sezer.test:443/library/test-image:latest && docker push harbor.sezer.test:443/library/test-image:$CI_PIPELINE_IID
    - docker rmi $(docker images -a -q) -f
sonarqube_check:
  stage: sonarqubecheck
  image: 
    name: sonarsource/sonar-scanner-cli:latest
    entrypoint: [""]
  variables:
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
    GIT_DEPTH: "0"
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .sonar/cache
  script: 
    - sonar-scanner
  allow_failure: true
  only:
    - main
deploy_to_k8s:
  stage: deploy
  image: alpine:latest
  script:
    - apk update  && apk add --no-cache curl
    - curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
    - chmod +x ./kubectl && mv ./kubectl /usr/local/bin/kubectl
    - mkdir -p $HOME/.kube
    - echo -n $KUBE_CONFIG | base64 -d > $HOME/.kube/config
    - sed -ie "s/THIS_STRING_IS_REPLACED_DURING_BUILD/$(date)/g" myweb.yaml
    - kubectl apply -f myweb.yaml
    - kubectl create secret docker-registry sezertestsecret -n myweb --docker-server=$DOCKER_REGISTRY_HOST --docker-username=$DOCKER_REGISTRY_USER --docker-password=$DOCKER_REGISTRY_PASSWORD --dry-run=client -o yaml | kubectl apply -f -

Sonuç

Artık repomuza herhangi bir push yaptığımızda otomatik olarak Pipeline devreye girecek. Böylelikle projemizi derlemiş, versiyonlamış, analiz etmiş ve Cluster üzerinde dağıtmış olduk.

Gitlab Pipeline

Genel Bakış

image 7

Stages

image 8

Harbor Registry

image 9

SonarQube

image 10

Kubernetes

image 11

Website

image 12