CI/CD
Tags: ci, cd, gitlab, github
Published: 2021-06-28 07:04:56

# Continuous Integration

Basitçe, tüm geliştiriciler tarafından yapılan işleri günde birkaç kez paylaşılan bir ana hatta birleştirme uygulaması.

Kodu bir Git reposunda saklanan bir uygulama göz önüne alındığında, geliştiriciler, bir uygulama için kod değişikliklerini gün içerisinde birden çok kez pushlar.
Repoya yapılan her pushlama için, uygulamayı otomatik olarak oluşturmak ve test etmek için bir dizi komut dosyası oluşturulabilir. Bunlar
komut dosyaları, uygulamada hatalara neden olma olasılığını azaltmaya yardımcı olur.

Bir dalda ana hatta birleşmeden geliştirme ne kadar uzun sürerse, çoklu
geliştirici dalı sonunda tekrar birleştirildiğinde entegrasyon çakışmaları ve başarısızlıklar oluşması riski o kadar artar. Geliştiriciler kod gönderdiğinde
kopyalarını aldıkları andan itibaren repodaki değişiklikleri yansıtmak için önce kodlarını güncellemeleri gerekir.
Repo ne kadar çok değişiklik içeriyorsa, geliştiricilerin kendi değişikliklerini göndermeden önce o kadar çok iş yapması gerekir. 

![ci-pipeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1mhm8cfevoeahd4h7ub8.png)

Bir uygulamaya gönderilen her değişiklik, otomatik ve sürekli olarak oluşturulur ve test edilir. Bahsedilen testler değişikliklerin tüm testlerden geçtiğini, kod içerisindeki yönergelerin ve kod uyumluluk standartlarının geçerliliğini kontrol eder.

# Continuous Delivery & Deployment

Sürekli Teslimat, her seferinde yalnızca uygulamanın oluşturulduğu ve test edildiği Sürekli Entegrasyonun ötesinde bir adımdır.
kod değişikliği kod tabanına aktarılır, ancak uygulama da sürekli olarak dağıtılır. Sürekli Teslimat
kodu otomatik olarak kontrol eder, ancak değişikliğin dağıtımı manuel olarak gerçekleşir ve stratejik olarak tetiklemek için insan müdahalesi gerektirir.

![sequence](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4x9g0f507egfypgztotq.png)

Sürekli Dağıtım, Sürekli Teslimata benzer bir başka adımdır. Aradaki fark,
uygulama manuel olarak dağıtımının yerine, otomatik olarak dağıtılacak şekilde ayarlanmıştır, bu nedenle insan müdahalesi gerekmez. 

![cd-pipeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2ocml4pv743ulk1xh98.png)

Deployment pipeline, yazılım kalitesini iyileştirmek, kararlılığı arttırıp kodun maliyetini düşürmek adına yapılandırma yönetimini, sürekli entegrasyonu ve dağıtım otomasyonunu bir arada birbirine bağlar.  

Deploymentlarda dikkat edilmesi gerekenler;

- **Paketleri yalnızca bir kez oluşturmak.** Dağıttığımız projenin deployment pipeline boyunca baştan beri test ettiğimiz projeyle aynı olduğundan emin olmak istiyoruz, böylece bir dağıtım başarısız olursa, paketleri hatanın kaynağı olarak ortadan kaldırabiliriz.
- Geliştirme dahil **her ortama aynı şekilde dağıtmak**. Bu şekilde, dağıtım sürecini birçok kez test ediyoruz.
  Herhangi bir sorun kaynağının ortadan kaldırılabilmesi için üretime geçme aşamasından önce gerçekleştirilebiliyor.
- **Dağıtımlarınızı duman testi ile test etmek** Uygulamanın tüm bağımlılıklarını doğrulayan bir komut dosyasına sahip olun.
  uygulamanın yapılandırıldığı konum, uygulamanın çalıştığından ve uygulamanın dağıtım sürecenin bir parçası olarak kullanılabilir olduğundan emin olun.
- **Ortamları benzer tutmak** Donanım yapılandırmasında farklılık gösterseler de aynı
  işletim sistemi ve ara yazılım paketlerinin sürümü ve aynı şekilde yapılandırılmalıdır. Günümüzde modern sanallaştırma ve kapsayıcı teknolojisi ile bunu başarmak çok daha kolay hale geldi.  


# GitLab'da CI/CD

GitLab, kendisine ait olan Sürekli Entegrasyon ve Sürekli Dağıtım sistemiyle oluşturulmuştur. Bir GitLab projesinde, projenin yapılandırılması için **_gitlab-ci.yml_** adlı bir configuration dosyası reponun içine yerleştirilir. Dosya, projede gerçekleşen her bir push komutuyla uygulamanın inşasının ve testinin tapılacağı GitLab Runner tarafından yürütülür.

GitLab Runner projenin işlerini çalıştırmak ve sonuçları GitLab'a geri göndermek için kullanılan açık kaynaklı bir projedir. Bu servis GitLab'a dahil olup CI ve CD'den ardışık düzen komut dosyasını yürütmek için onunla birlikte kullanılır.

## GitLab'da CI/CD Örneği

Bu örnek proje bir küme yönetim sistemi olan Google Cloud Container Engine'e deploy edilecektir.

Gereksinimler; Dockerfile, Kubernetes deployment.

### Spring Boot uygulamasını Docker Container olarak paketleme 

```dockerfile
FROM adoptopenjdk/openjdk11:alpine-jre
VOLUME /tmp
ADD /target/cdexample-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
```

### Kubernetes deployment'ı tanımlama

```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: cdexample
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: cdexample
    spec:
      containers:
        - name: cdexample
          image: registry.gitlab.com/enesgerem/cdexample
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
      imagePullSecrets:
        - name: registry.gitlab.com
```

### Bir GitLab CI Pipeline oluşturma

Yukarıdaki dosyaları commitleyip pushladıktan sonra GitLab CI Pipeline'ı tanımlamaya hazırız.

```yaml
image: docker:latest
services:
  - docker:dind

variables:
  DOCKER_DRIVER: overlay2
  SPRING_PROFILES_ACTIVE: gitlab-ci

stages:
  - build
  - package
  - deploy

maven-build:
  image: maven:3.8.1-jdk-11
  stage: build
  script: "mvn package -B"
  artifacts:
    paths:
      - target/*.jar

docker-build:
  stage: package
  script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker build -t registry.gitlab.com/enesgerem/cdexample .
    - docker push registry.gitlab.com/enesgerem/cdexample

k8s-deploy:
  image: google/cloud-sdk
  stage: deploy
  script:
    - echo "$GOOGLE_KEY" > key.json
    - gcloud auth activate-service-account --key-file key.json
    - gcloud config set compute/zone europe-west1-c
    - gcloud config set project cdexample
    - gcloud config set container/use_client_certificate True
    - gcloud container clusters get-credentials cdexample
    - kubectl delete secret registry.gitlab.com
    - kubectl create secret docker-registry registry.gitlab.com --docker-server=https://registry.gitlab.com --docker-username=enesgerem --docker-password=$REGISTRY_PASSWD --docker-email=enesgerem@gmail.com
    - kubectl apply -f deployment.yml
```

- **image** objesi kullanmak istediğimiz Docker image'ın ismini tanımlar.
- **services** objesi projeye linklenmiş kullanmak istediğimiz ek Docker image'larını tanımlar.
- **variables** objesi oluşturduğumuz environment içindeki değişkenleri tanımlar.
  - **_DOCKER_DRIVER_** hangi depolama sürücülerini kullandığımızı Docker Engine'a bildirir.
  - **_SPRING_PROFILES_ACTIVE_** uygulamamızın bölümlerini ayıran ve yalnızca belirli ortamlarda kullanılabilir hale getirmenin bir tolunu sağlayan Spring Profiles'ı etkinleştirir.
- **stages** objesi build'ımızın yaşam döngüsünü tanımlar.
- **maven-build, docker-build** ve **k8s-deploy** iş tanımlamalarıdır.
  - **_$GOOGLE_KEY_** Google Cloud serviste kullandığımız hesap anahtarımızdır, API Manager Section'daki Google Cloud Platform'dan edinilir.
  - **_kubectl create secret docker-registery ..._** kodu _deployment.yml_ içinde tanımladığımız _imagePullSecret_ objesini yaratır.

Son olarak, her şeyin yolunda olduğunu kontrol etmek adına küme aşağıdaki gibi kontrol edilebilir.

```shell
$ kubectl get deployments
NAME        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE    AGE
cdexample   2         2         2            2            2m

$ kubectl get pods
NAME                         READY     STATUS    RESTARTS   AGE
cdexample-3641958612-3e5xy   1/1       Running   0          2m
cdexample-5542343546-fr4gh   1/1       Running   0          2m
```