3 Nisan 2014 Perşembe

Sürekli Entegrasyon


CI sistemlerde işleyiş mantığı,
Tıkır tıkır çalışmak
Orta ve büyük yazılım projelerinin kaçınılmaz ögelerinden biridir sürekli entegrasyon. Ayrıca agile süreçler içerisinde de olmazsa olmaz bir kavram olarak göze çarpmaktadır. Bu yazı dizisinde Sürekli entegrasyon detaylarına, teorisine çok derinlemesine girmeyeceğim daha çok neden gerekli olduğunu ortaya koyup pratik olarak nasıl hayata geçirebiliriz gibi konuları ele almayı düşünüyorum. Şimdiye kadar nette bulduğum bilgileri sizinle paylaşayım ki vakit kazanalım: Oğuz Dağ kendi bloğunda iki yazı dizisi halinde (Bir ve iki) konuyu ele almış. Özcan Acar Bey zamanında sitesinde yeterince anlatmış öyle ki kitap bile hazırlamış bunun için biraz eskimiş ama olsun temeller aynı. Kodcu.com'dan Rahman Usta'nın da bu videosu diğer güzel bir kaynak. Güncel bir kaynak olarak Berat Doğan'ın bloğundan bu yazı da enterasan. Böyle online bir hizmeti yeni görüyorum. Bu da Cem Güler'in görüşlerini içeren bir yazı. İşin iyice temellerine ineceğim derseniz Martin Fowler ile sizi baş başa bırakabilirim.

Temel kavramları hemen bu sayfada anlayabilmek adına acm-software den bir alıntı yapalım, güzel özetlemişler:
"Continuous Integration (Sürekli Entegrasyon), bir yazılım takımının ürettiği kodun sürekli ve sorunsuz olarak sisteme entegre edilmesini amaçlayan bir sistemdir. Bu entegrasyon süreci, çeşitli araçlar ile otomatik hale getirilmekte ve belirlenen sürelerde çeşitli scriptler ve işlemler çalıştırılarak kodun sürüme uygunluğu sürekli olarak test edilmektedir. Böylece entegrasyon hataları mümkün olan en kısa sürede bulanabilmektedir. Ayrıca, repository de bulunan kodların belirli aralıklarla test senaryolarının koşturulması ile sistem tutarlılığı sağlanmakta, geliştirilen deployment otomasyonu ile deployment süreçlerinde, insan faktöründen kaynaklanabilecek hatalar en aza indirgenmektedir.
Sürekli Entegrasyonun bazı yararları şu şekilde de sıralanabilir: 
  • Hatalar hızlı bir şekilde bulunur ve çözüm süreci başlar, 
  • Yazılım ürününün durumunun metriklerle ölçülmesi kolaylaşır, 
  • Varsayımlar üzerinden değil, somut yazılım üzerinden test yapılması sağlanır,
  • Kişilerce tekrarlanmak zorunda olan süreçleri kısaltır ve otomatik hale getirir, 
  • Her an deploy edilebilen yazılım çıkarılabilinmesine olanak verir, 
  • Projelerde saydamlık artar, 
  • Yazılımcılara ve ürüne güven aşılanmış olur."
Bir diğer alıntıyı da prime teknolojinin bu sayfasından yapalım:
"Sürekli entegrasyon ekip üyelerinin birbirlerinin ayaklarına basmamaları için kullanılır. Projelerde fark edilmesi ve düzeltilmesi en zahmetli problemler entegrasyon problemleridir. Sürekli entegrasyon sunucusu ekibin yaptığı kod değişikliklerini versiyon kontrol sunucusu (örn Subversion) vasıtasıyla izler. Herhangi bir değişikliği gördüğünde entegrasyon kurulumunu başlatır. Bu kurulum projenin özelliklerine göre şekillenir fakat basitçe tüm kodlar derlenir, testler çalıştırılır ve sonuçlar yayınlanır. Eğer bir hata varsa kurulum kırılır. Bu durumda ekibin önceliği entegrasyon problemini çözmektir. Kurulumun kırılması üretim hattında problemin ortaya çıkması gibidir, şalter indirilir ve bozukluk tamir edilir ve üretim hattı devam eder. Kurulum kırılması ekibin verimliliğini negatif etkiler. Geliştiriciler bunu önlemek için yaptıkları değişikleri versiyon kontrole eklemeden önce son değişikleri alırlar, yerel bir kurulum yapıp sonuç başarılı ise kendi değişikliklerini versiyon kontrole eklerler. Kurulumlar tamamen otomatiktir ve kurulum süresinin 10 dk nın altında olmasına özen gösterilir. Büyük projelerde kurulumlar sürekli, gecelik gibi aşamalara bölünür. Sürekli entegrasyon kodların hatasız, her an kullanıma hazır tutulmasını mümkün kılar. Bir noktalı virgül dahi değiştiğinde kapsamlı testler değişikliğin etkilerini inceler ve problemler çıkar çıkmaz ekip tarafından düzeltilir. Sürekli entegrasyon ‘Benim makinemde çalışıyordu burada niye çalışmıyor’ problemini çözer."
Son bir alıntıyı da ekşi sözlükte gördüğüm whisky adlı kullanıcıdan yapalım:
"Yazılım geliştirirken geliştirmenin her aşamasında projeyi çalışabilir halde tutmak için geliştirilmiş disiplindir. örneğin her commit sonrası unit test, 4 saatte bir integration test, gecelik olarak functional testler bir ci server üzerinde çalıştırılır. Bu build lerden herhangi birisini bozan işini gücünü bırakır ilgili sorunu fixler, fixlemezse product owner developerı bi güzel fixler. Bu şekilde saldım çayıra mevlam kayıra metodolojisinden uzak durulur."
Kendi cümlelerimle özetlemek gerekirse, aşağıdakilerin olmasını istiyorsanız CI sistem kurmanız gerekmekte:
  • Entegrasyon problemlerini minimize etmek, 
  • Sürekli olarak build edilebilir, güncellenen, sürümlendirmesi yapılan kodlarınızı tutmak,
  • Geliştirdiğiniz yazılım ürününü sürekli çalışabilir halde tutmak,
  • Düzenli olarak birim testleri, kod gözden geçirmeleri, kodlama standartlarını, bug tespitini yapmak,
  • Projenizin gidişatını görsel olarak raporlamak, istatistik almak,
  • Tüm bu işlemler sırasında geliştiriciler arasında projenin gidişatı ile ilgili otomatik haberleşmeyi (sms, mail) sağlamak, ("Gönderdiğin kod güncellemeleri projenin şurasını bozdu, bir an önce şu şu yerleri kontrol edip düzeltilmesini sağlar mısın?" tarzında mesajlar)
  • Ekibe projenin her an ilerlediğini ve entegrasyon hatası olursa sistemin kendisini otomatik uyaracağını, kendisine işin doğrusunu yapmakta katkısı olduğunu ve tüm ekibin bu şekilde çalıştığını hissettirmek, böylece ona güven vermek,
  • Çıkaracağınız ürünü yazılım mühendisliği açısından kaliteli kılmak, dolayısı ile ürünü kaliteli kılmak,
  • ... ve belki de şu an aklıma gelmeyen bir çok fayda...
Aslında bu saydıklarım, herhangi bir entegrasyon sürecinde olması gereken adımlar. Sürekli entegrasyon deyince bu adımların tamamının bir kere ayarlanması ondan sonra sadece küçük müdahaleler ile, saat gibi tıkır tıkır her şeyin vaktinde otomatik olarak araçlar ile çalıştırılması ve seyredilmesi akla gelmelidir. Sürecin neredeyse tamamını araçlar işletir, CI yöneticisine sadece sistemdeki bir hata veya yeni bir özellik eklenmesi durumunda ihtiyaç duyulur. (Burada da aslında harcanacak vakit çok azımsanacak bir vakit değil ama ortaya çıkan iş açısından feda olsun deneceğinden eminim)
Eski yaklaşımlar yukarıdaki adımların tamamının belirli dönemlerle entegre edilmesi fikrine dayanmaktadır. Yeni yaklaşımımız ise bu sürecin her saat veya her gün sürekli tekrar edilmesi şeklindedir ki artık orta boy ve üzeri yazılım projelerinde bazı riskleri minimize etme adına bu vazgeçilemez bir hal almıştır.
Sürekli Entegrasyon Süreci
Olayın anlaşılması için bir de şekil üzerinden bakalım duruma. Yukarıda bir java web projesi tecrübesinden hareketle sürekli entegrasyon sürecini anlatan bir şekil çizmeye çalıştım. Diğer tipteki projeler içinde örnek kabul edilebilir. Görüldüğü üzere süreç geliştiriciler ile başlamakta. Geliştiricinin yapmış olduğu bir commit çelik kasa gibi görünen repoya gelmekte (Svn). Sürekli entegrasyon sunucumuz (kısaca SES diyelim, yani Jenkin aracımız) ise svn deki güncel kodları alıp, derleyip, birim testlerini yapıp, kod kalitesinden geçirip, tag leyip vs (bir çok işlem yaptırılabilir), oluşan derlenmiş projemizi web portallerine gönderir (Deploy eder). Burada bir kaç konuyu açalım. SES her commit geldiğinde de bu işi yapabilir, her gün belli saatlerde de yapabilir, bir başka projeyi çalıştırıp bitirdikten sonra otomatik tetiklenerek de yapabilir. Bu gibi daha bir çok konfigürasyon seçeneğine sahiptir. Bu takvimleme sıklığı, SES yöneticisinin tercihlerine bağlı bir durumdur. Tabii SES yöneticisi de keyfine göre değil, proje ihtiyaçlarına uygun bir konfigürasyon belirlemeye çalışacaktır. Projelerde genel kabul en azından iki ayrı sürüm çıkarmaktır.
  • Bir geliştirici sürümü çıkarılır ki bu her commit de veya 2 saatte bir gibi sıklıkla çalışan bir Jenkins job u demektir. Bu sürüm daha çok geliştirici hatasını bir an önce görsün diye çıkarılır. 
  • Bir de test sürümü çıkartılır ki bu 3 günlük veya haftalık olabilir. Bu proje ekibinin geliştirme hızına, test ekibinin büyüklüğüne vs bağlıdır.
Eğer sistemde veri tabanı kullanılıyorsa test ve geliştirici sürümleri farklı veri tabanlarına bağlı, değiştirilebilir ortak bir dosya havuzu kullanılıyorsa yine test ve geliştirici sürümleri farklı havuzları kullanmalıdır. İki sürümün ortak olabileceği örnek bir unsur, mesela Ldap otantikasyonu gibi sürümlerin birbirlerine müdahale etmeyeceği bağımsız testlerinin yapılabileceği unsurlar olmalıdır. Aksi takdirde mesela veri tabanı ortak olsa, tutulan bir değerin geliştirici sürüm tarafında silinmesi, değiştirilmesi, test sürümünü test eden test ekibini çalışamaz hale getirecektir.
Sistemde hata olduğu an SES tarafından ilgili geliştiriciye (Geçerli build'deki commit sahipleri) anında mail atılır ve patlamayla (yukarıdaki alıntılarda daha formal olsun diye kırılma olarak yazılmış) ilgili bilgi verilebilir. Neyin patlama sebebi olacağı da yine SES yöneticisine kalmıştır. Build hatası zaten varsayılan patlama sebebi sayılacaktır. Ama mesela kodların yedeğinin bir başka bilgisayara atılması bir alt iş olabilir (Scriptler ile basitçe yapılabilir) ve attığınız makineye erişim sıkıntısı veya diskin dolu olması gibi bir hatayı dahi patlama sebebi sayıp bu durumda sürümün kırıldığını kabul edebilirsiniz. Tabii bu durumda gelecek mail geliştiriciden ziyade SES yöneticisini ilgilendirecektir.

Evet CI anlamak ve biraz olsun sindirmek için bu kadar ön bilgi yeterli, gelelim temel amacımıza.
Bu yazı dizisinde, gerekli araştırmayı yapmış, "iyi ama nereden başlayacağız bu işe" diyenler için pratik bilgilerle birlikte bir yol haritası sunmaya çalışacağım, faydalı olmasını diliyorum...

İlk iş olarak, kullanacağımız araçlardan biraz bahsedelim.
1. Apache Tomcat (Jenkins aracımızı deploy etme amacıyla)
2. Jenkins (Eski adı ile Hudson ki hala farklı adlarla (Eclipse Hudson) devam ettirilmeye çalışılmakta)
3. Ant (Jenkins'te koşacak build scriptlerimiz için)
4. Sonar (Kod gözden geçirme, kod kalitesi ve çeşitli programlama metriklerin hesaplanması için)

"Her şey güzel de bu kadar yazı okuduk, araçları da gördük ama projemizde kullandığımız programlama diline dair bir şey görmedik henüz" der gibisiniz  muhtemelen :) Kullanacağımız tüm araçlar neredeyse dilden bağımsız (adını duymadığım bazı diller var, sonar aracının onlara herhangi bir plug-in i olmayabilir, onun için neredeyse diyorum) bir şekilde size sürekli entegrasyon ortamını sağlayacaklar emin olabilirsiniz. Ben Java ile sürekli entegrasyon süreci atlatmış biri olarak Java dan başlayacağım örneklerime, yeni projemde aynı işlemleri c++ için de oturtmam gerekecek, vakit olur da sıra gelirse c++ için de bu yazı dizisine devam etmeyi düşünüyorum. Baştan söyleyeyim bazı ufak tefek değişiklikler haricinde dil olayı çok fazla bir uğraş verdirmeyecektir diye düşünüyorum. Yalnız şöyle bir sonuç kaçınılmaz olacak, yukarıdaki araçlar Java-merkezli olduğundan Java projeleri için daha fazla plug-in e sahipler bunu baştan söylemek lazım. Dolayısı ile Java projelerinizde c++ projelerine göre daha çok plug-in, daha fazla esneklik, daha fazla görsellik vs. demek olacak bunu kabul etmek lazım.

Bir sonraki yazımızda Jenkins ve Hudson arasında kalanlar için "Jenkins mi Hudson mı ?" adlı bir yazı kaleme aldım, okuyabilirsiniz.

Hiç yorum yok :

Yorum Gönder

Bu sayfalarda iseniz sizden de tecrübelerinizi paylaşmanızı bekliyorum... Görüş, öneri veya istek fark etmez.