Özgür Adem Işıklı

GitLab İle CI/CD

16 January 2019

Bu makaleyi iyi bir şekilde özümseyebilmek için temel düzeyde Linux ve Docker bilgisi gerekmektedir.

Continuous Integration ve Continuous Delivery kavramları sırasıyla; Sürekli Entegrasyon ve Sürekli Teslimat manasına gelmektedir. Söz konusu olan eğer yazılımlarsa, sürekli entegrasyon ve teslimat, kodlanan yazılımların durmaksızın derlenmesi, test edilmesi, entegre edilmesi, sürümlenmesi ve yayınlanmasını anlatmaktadır. Bu işlemlerin tümü el yordamıyla yapıldığında kaybedilecek vaktin azaltılması ve doğabilecek hataların en aza indirgenmesi amacıyla, CI/CD kavramları kelime dağarcığımıza yerleşmiş, modern yazılım geliştirme metodolojileri arasında kendilerine sağlam bir yer edinmiştir.

CI/CD kavramları tek başına var olmaktan ziyade, kendisinden çok daha büyük bir kavramın -DevOps- yalnızca bir alt başlığını oluşturmaktadır. Bu nedenle CI/CD uygulamalarına geçmeden evvel, DevOps kelimesini biraz açarak irdelememiz ve hakkında bilgi sahibi olmamız yerinde olacaktır.

Amazon Web Services dokümanları üzerinde yazılan tanıma göre DevOps; organizasyonların yüksek hızda yazılım geliştirme ve sunma kabiliyetlerini arttıran uygulamaların, araçların ve bu kültürün bir birleşimidir. [1] DevOps ile birlikte, geleneksel metotlar ile uygulama geliştiren süreçlere nazaran daha hızlı bir şekilde ürün geliştirilebilmektedir.

Google Trends üzerinde biraz araştırma yaptığımızda bu kavramın, yazılımın beşiği sayılabilecek ABD'de 2013 yılından sonra daha çok gündeme gelmeye başlamış olduğunu görüyoruz. Türkiye'de ise 2015 yılından itibaren gösterilen ilgi artmaya başlamış ve ardımızda bıraktığımız 2018 yılında, deyim yerindeyse altın çağını yaşamıştır. [2]

Resim 1 - Google arama trendleri ABD-Türkiye karşılaştırması

DevOps Kültürü

AWS tarafından yapılan tanımda da görülebileceği üzere, özellikle seçilmiş olan bir kelime vardır; kültür. DevOps ve alt başlıkları hakkında konuşmaya başladığımız ilk andan itibaren yoğun olarak çeşitli araçlardan bahsederiz. Buna karşın bu işin kurumsal bir kültür haline getirilmesi muhtelif zaruretler içermektedir.

Dünyaca ünlü Refactoring Improving the Design of Existing Code kitabının yazarı Martin Fowler'ın kişisel blogunda DevOpsCulture başlıklı bir makale yayınlayan Rouan Wilsenach, en iyi araçlar kullanılsa dahi, doğru bir kültüre sahip olmadığımızda DevOps kelimesinin sadece modalı bir terim olacağını savunmaktadır. [3] Wilsenach, DevOps'un ana karakteristiğinin geliştirme ve operasyon bölümleri arasında iş birliğinin arttırılması olduğunu belirtmekle birlikte, sektörde aksi yönde görüşlerin de olduğunun unutulmaması gerekir. [4] Bu makaledeki asıl amacımız bu olmadığından, biz bu tartışmaya dahil olmayacak, şimdilik, DevOps'un birkaç yazılım geliştirme aracından çok daha fazlası olduğunu aklımızda tutacak ve bu makalemizde sadece GitLab'ın CI/CD için sunduğu altyapıya odaklanacağız.

GitLab

GitLab, yazılım projelerini barındırabileceğiniz, versiyon kontrol sistemi tabanlı ve açık kaynak kodlu bir yazılım projesidir. Buna ilave olarak DevOps alanında da her geçen gün daha iddialı bir konum kazanmaktadır. Kendi tanıtım sayfalarında, doğrudan DevOps döngüsünü entegre eden ilk uygulama olduğunu ve sadece GitLab'ın eşzamanlı DevOps işlemlerine olanak sağladığını özellikle vurgulamaktadır. [5]

Eğer GitLab kullanmazsanız, bir çok farklı uygulamayı bir araya getirerek DevOps süreçlerinizi yönetmeniz de elbette mümkündür. Ancak GitLab planlama aşamasından, monitoring aşamasına kadar tüm adımları dahili olarak çözümlemektedir.

GitLab CI/CD Mimarisi

GitLab CI/CD yapısını incelediğimizde, GitLab, web arayüzleri ve API kullanımı ile CI/CD süreçlerinin veri tabanı üzerinde saklamakta, buna ek olarak süreci yöneten ilave bir araç bulunmaktadır. Süreci yönetme işinde kullanılan bu araca GitLab Runner adı verilmektedir. Bu araç, yukarıda bahsettiğimiz bütünleşik yapının içerisinde düşünülmelidir. GitLab'ın bütünleşik çözümlerinden kast, tüm DevOps kültürü için araçlar geliştiriyor olmalarıdır. Ancak GitLab'ın geliştirdiği her araç ayrı bir uygulama olabilmektedir. Bu şekilde bir tasarıma gidilmesinin amacı, harici başka alternatiflerin de GitLab'a entegre edilebilmesine olanak sağlamaktır.

GitLab Runner, GitLab'tan tamamen izole bir şekilde, Go dili kullanılarak MIT Lisansı ile geliştirilmiş ve GNU/Linux, macOS, FreeBSD ve Windows üzerine kurulabilmektedir. [6]

GitLab Runner'ı herhangi bir makineye kurabilirsiniz. Söz konusu makine DigitalOcean üzerinde kiralayacağınız basit bir sunucu da olabilir, kendi bilgisayarınız da. GitLab sunucusunun, GitLab Runner ile aynı makine üzerinde bulunmak gibi bir zorunluluğu yoktur.

İlave olarak, GitLab doğrudan ücretsiz hesapların da kullanabileceği Shared Runner'lar sunmaktadır. Dilerseniz bu paylaşımlı Runner'lardan herhangi birini de kullanmanız mümkündür.

Ayrıca önemli bir diğer avantajı da, kendi GitLab hesabınıza birden fazla GitLab Runner bağlayabilmenizdir. Böylece birden fazla GitLab Runner entegrasyonuyla CI/CD süreçlerinizi yönetebilirsiniz.

Resim 2 - GitLab Server, Runner ve geliştirici ilişkisi

GitLab Runner Nasıl Çalışır?

Yukarıda da belirttiğimiz gibi GitLab Runner, GitLab'dan tamamen ayrı bir cihaz üzerine kurulabilir. Daha sonrasında ise GitLab Runner, GitLab ile GitLab API üzerinden haberleşerek bilgi alış verişinde bulunur.

Resim 3 - GitLab Server ve Runner arasındaki iletişim

Bu esnek yapı sayesinde, örneğin yeni bir commit geldiğinde, hangi Runner üzerinde, hangi görevlerin çalıştırılabileceğine karar verebilir, commit geldiğinde GitLab sizin direktiflerinize göre GitLab Runner'ı çalıştırır, GitLab Runner da elde ettiği sonuçları GitLab ile paylaşarak sonucu sizin web arayüzünde ya da API cevaplarında görebilmenizi sağlar.

Ek olarak, GitLab Runner görevleri çalıştırırken kurulduğu makinenin komut satırını kullanabildiği gibi Docker desteği de sağlamaktadır.

Docker burada müthiş bir kolaylık sağlamaktadır. Misalen; JavaScript uygulamanızı derleyeceğiniz, içerisinde nodejs bulunan Docker container'ı, hemen ardından da .Net Core ile geliştirilen bir uygulamayı derlemek için, içerisinde dotnet bulunan bir Docker container'ı kullanabilirsiniz. Runner'ın kurulu olduğu makinede nodejs ya da dotnet bulunması gerekmez. Sadece Docker olması yeterlidir.

GitLab Runner Kurulumu

Yukarıda GitLab Runner'ı çeşitli platformlar üzerine kurabileceğimizden bahsetmiştik. Makalemiz içerisinde biz tercihimizi Ubuntu'dan yana kullandık.

Diğer kurulumlar için Kurulum Dokümanını inceleyebilirsiniz.

Ubuntu kurulumu için aşağıdaki satırlar yeterlidir;

curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
sudo apt-get install gitlab-runner

İlk kurulum tamamlandıktan sonra yapılacak güncellemeler için aşağıdaki komutlar işimizi görecektir;

sudo apt-get update
sudo apt-get install gitlab-runner

GitLab Runner Entegrasyonu

GitLab Runner kurulduktan sonra, GitLab Runner ile GitLab'ı entegre etmemiz gerekmektedir. Bu işlemle birlikte GitLab Runner, GitLab sunucusunun hangi adresten hizmet verdiğini bilecek ve GitLab da hangi özelliklere sahip Runner'ın nerede bulunduğunu anlamış olacaktır.

GitLab açık kaynak kodlu bir proje olduğundan, kendi GitLab sunucunuzu da kendi belirlediğiniz bir cihaz üzerine kurabilirsiniz.

Kaydetme işlemine başlamadan önce, kaydetme işleminde kullanacağımız GitLab CI Token değerini, GitLab sunucusun üzerinde bulunan bir proje ya da group üzerinde Settings -> CI/CD bölümü altındaki Runners sekmesinde içerisinden alabilirsiniz.

Resim 4 - GitLab CI token

Elimizde artık bir GitLab CI Token varken, Runner'ın kurulu olduğu makinada aşağıdaki komut çalıştırıldıktan sonra, Runner size sırayla sunucunun adresini, token bilgisini, Runner tanımını, Runner etiketini ve Runner'ın hangi yöntemle çalışacağını soracaktır.

Biz Docker ile birlikte çalışacağımız için, 5. adımda docker seçimi yapacağız.

sudo gitlab-runner register

Bilgileri aşağıdaki örnekteki gibi girebilirsiniz;

Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
https://gitlab.com

Please enter the gitlab-ci token for this runner
xxx

Please enter the gitlab-ci description for this runner
[hostame] my-runner

Please enter the gitlab-ci tags for this runner (comma separated):
docker,another-tag

Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell:
docker

Please enter the Docker image (eg. ruby:2.1):
alpine:latest

Yukarıdaki tanımlamalardan sonra, token aldığımız bölümde bulunan Specific Runners bölümünde, henüz tanımladığımız Runner'ı görebiliyor olmanız gerekmektedir.

Resim 5 - GitLab Runner tanımı görüntüsü

Böylece artık GitLab sunucumuzun, ilgili proje ya da grubumuz içerisinde kullanabileceği ve iletişimde olduğu bir Runner bulunmaktadır. Bundan sonraki aşamada, GitLab'a yüklediğimiz çeşitli tanımlamalarla birlikte, bu Runner'a ne zaman, nasıl ve neler yapması gerektiğini söyleyeceğiz.

CI/CD Direktifleri

Şu ana kadar yaptığımız çalışmalarla birlikte, GitLab ile Runner arasındaki ilişkinin nasıl olacağını tanımladık. Bundan sonra yapacaklarımız, herhangi bir projemiz (repository) içerisinde, herhangi bir olay gerçekleştiğinde (commit, push, tag vb.) hangi işlemlerin nasıl yapılacağını GitLab'a bildirmekten ibarettir. Bu işlemleri GitLab'a bildirmek için kullandığımız ortak lisan, Yaml formatında yazacağımız .gitlab-ci.yml dosyasıdır.

Bunun için projenizin ana dizininde .gitlab-ci.yml isimli bir dosya oluşturarak, nasıl bir yapılandırma istediğimizi yazarak işe başlıyoruz.


stages:
  - test
  - deploy

jstest:
  stage: test
  image: node:latest
  tags:
    - 'docker'
  script:
    - npm install
    - npm run test

Yukarıdaki dosyayı incelediğimizde, iki ana bölüm dikkat çekmektedir; stages ve test.

stages bölümünde projemiz içerisinde hangi stage adımlarının bulunduğunu GitLab'a söylüyoruz. Buraya dilediğimiz adımları yazabilmekle birlikte, genel olarak test, build, publish ya da deploy gibi kalıplaşmış stage isimlendirmeleri kullanabiliriz. Her stage içerisinde birden fazla görev tanımı yapabiliriz. Örneğin test stage'i içerisinde, JS testleri için ayrı bir task, Ruby testleri için ayrı bir task yazabiliriz. Ya da build aşaması içerisinde farklı platformlar için ayrı ayrı görevler ile build işlemi gerçekleştirebiliriz. Stage tanımlarının sırasına göre, çalışma anında GitLab bize aşağıdaki gibi bir görselleştirme yapacaktır;

Resim 6 - GitLab CE Pipeline Detayı

Üstteki resmi incelediğimiz zaman Build, Prepare, Test gibi bir stage sıralaması yapıldığını ve örneğin Prepare stage'i altında compile-assets, setup-test-env gibi görevler bulunduğu görebiliriz. Bu tanımlar tamamen projenin ihtiyaçlarına göre bizim tarafımızdan belirlenmektedir.

Yukarıda kendi yazdığımız yapılandırma dosyasına döndüğümüzde, jstest isimli bir görev tanımını, bu görev tanımının test isimli stage altına yer alacağını, Docker image'ı olarak node isimli image'ın son sürümünün kullanılacağını ve bu görevin docker etiketine sahip herhangi bir Runner tarafından çalıştırılacağını tanımladığımızı görebilirsiniz.

Bu dosya repository içerisinde GitLab'a ulaştığı zaman, GitLab bu dosyayı doğrudan yorumlayarak gerekli işlemleri yapacaktır.

Bu makale için oluşturduğum örnek repository kodlarını buradan inceleyebilirsiniz.

GitLab üzerinde, repository içerisinde CI/CD menüsü altında çalıştırılan Pipeline'larını ve pipeline içeriklerini görebilirsiniz. Hemen aşağıda yazdığımı jstask isimli görev çalıştığında console üzerinde neler yapılmış görebilmekteyiz.

Resim 7 - GitLab CI/CD Pipeline Job

Logları incelediğimizde, Runner çalışmaya başladıktan sonra öncelikle bizim belirttiğimiz node:latest image'ının Docker Hub üzerinden alındığını, daha sonra repository içeriğinin git clone ile ana dizine çıkartıldığını görebiliriz. Sonrasında $ ile başlayan her bölüm, bizim script altına yazdığımız komutlardan ibarettir. Özetle; gerekli ortamı hazırlayıp, koşturulması gereken komutları sırasıyla .gitlab-ci.yml dosyasına yazarak, Runner'ın sırayla neler yapması gerektiğini adım adım belirtmiş oluyoruz. Repository üzerinde herhangi bir hareket olduğunda GitLab bu dosyayı okuyarak ilgili Runner ile iletişime geçer, ve çalıştılması gereken komutları Runner'a bildirdikten sonra arkasına yaslanarak Runner'dan gelen cevapları bekler. Runner yaptığı işlemlerin loglarını tekrar GitLab'a gönderir ve biz de web arayüzünden görevlerin sonuçlarını görebiliriz.

Son Sözler

GitLab CI/CD entegrasyonu için gerçekleştirdiğimiz işlemlerin, sadece birkaç yazılım aracını kullanmaktan ibaret olmadığına yazının ilk bölümünde bir miktar değinmiştim. CI/CD, DevOps sürecinin yalnızca bir parçasını oluşturmaktadır ve CI/CD entegrasyonuna ek olarak DevOps içerisinde yapılabilecek bir çok farklı eylem bulunmaktadır. Konu hakkında daha fazla araştırma yaparak bu konudaki bilgi dağarcığınızı büyütebilirsiniz.

Ek olarak, bu makalede bu kadar detay olmasının bir diğer sebebi de; kaputun altında neler olduğunun yeterince anlaşılmadığı durumlarda, ezbere yapılan hareketlerin, yazılım geliştirme süreçlerine yarardan çok zarar sağladığını düşünmemdir. Yazılım geliştirme süreçleri nasıl dilleri kullanmaktan ibaret değilse, DevOps da benzer şekilde, sadece araçları kullanmaktan ibaret değildir. DevOps kültürü, doğrudan doğruya geliştirme hızınıza katkıda bulunmasının yanı sıra, dolaylı olarak da, geliştiricilerin yaptıkları ve kısa süre sonra nasıl yaptıklarını unutacakları pek çok işin bir sistematiğe oturtulması demektir.

Tam da bu nedenle geliştirme süreçlerinize günbegün yedirmeniz gereken bir kültür olduğundan, bir çok farklı kaynakta bahsedilmektedir. Aynı zamanda DevOps; Yazılım Geliştirme süreci içerisinde bulunan bütün ekiplerin, yaptıkları işi basit kodlara ve şemalara dökerek birbirlerine anlatma sürecidir de. Oluşturulacek her yeni proje, yapılacak her iş bu kültür içerisinde düşünülmeli, kurgulanmalı ve uygulanmalıdır.

Bir bilgisayarın yapabileceği bir işi, asla bir insan kendisi yapmamalıdır.

Referanslar

[1] What is DevOps?

[2] Google Trends Results

[3] DevOpsCulture, Rouan Wilsenach

[4] DevOps is a culture, but here's why it's actually not

[5] GitLab

[6] GitLab Runner