Sürekli entegrasyon için Docker'da mikro hizmetlerin otomatik testi

Mikro hizmet mimarisinin geliştirilmesiyle ilgili projelerde CI/CD, hoş bir fırsat kategorisinden acil ihtiyaç kategorisine geçiyor. Otomatik test, sürekli entegrasyonun ayrılmaz bir parçası olup, ekibe aile ve arkadaşlarla keyifli akşamlar yaşatabilecek yetkin bir yaklaşımdır. Aksi halde projenin asla tamamlanamaması riski vardır.

Mikro hizmet kodunun tamamını sahte nesneler içeren birim testleriyle kaplamak mümkündür, ancak bu, sorunu yalnızca kısmen çözer ve özellikle verilerle çalışmayı test ederken birçok soru ve zorluk bırakır. Her zaman olduğu gibi, en acil olanlar ilişkisel bir veritabanındaki veri tutarlılığını test etmek, bulut hizmetleriyle çalışmayı test etmek ve sahte nesneler yazarken yanlış varsayımlarda bulunmaktır.

Tüm bunlar ve biraz daha fazlası, mikro hizmetin tamamının bir Docker konteynerinde test edilmesiyle çözülebilir. Testlerin geçerliliğini sağlamanın şüphesiz bir avantajı, üretime giren aynı Docker görüntülerinin test edilmesidir.

Bu yaklaşımın otomasyonu, çözümü aşağıda açıklanacak olan bir dizi sorunu beraberinde getirir:

  • aynı liman işçisi ana bilgisayarındaki paralel görevlerin çakışmaları;
  • test yinelemeleri sırasında veritabanındaki tanımlayıcı çakışmaları;
  • mikro hizmetlerin hazır olmasını beklemek;
  • günlüklerin harici sistemlere birleştirilmesi ve çıktısının alınması;
  • giden HTTP isteklerini test etmek;
  • web soketi testi (SignalR kullanarak);
  • OAuth kimlik doğrulaması ve yetkilendirmesi test ediliyor.

Bu makaleye dayanmaktadır konuşmam SECR 2019'da. Okumak için fazla tembel olanlar için, işte konuşmanın kaydı.

Sürekli entegrasyon için Docker'da mikro hizmetlerin otomatik testi

Bu yazıda size test altındaki hizmeti, Docker'da bir veritabanını ve Amazon AWS hizmetlerini çalıştırmak için bir komut dosyasının nasıl kullanılacağını, ardından Postman'da testler yapmayı ve bunlar tamamlandıktan sonra oluşturulan konteynerleri durdurup silmeyi anlatacağım. Kod her değiştiğinde testler gerçekleştirilir. Bu şekilde her sürümün AWS veritabanı ve hizmetleriyle doğru şekilde çalıştığından emin oluyoruz.

Aynı komut dosyası hem geliştiricilerin kendileri tarafından Windows masaüstlerinde hem de Linux altındaki Gitlab CI sunucusu tarafından çalıştırılır.

Haklı olmak gerekirse, yeni testlerin tanıtılması, geliştiricinin bilgisayarına veya testlerin commit ile çalıştırıldığı sunucuya ek araçların kurulumunu gerektirmemelidir.Docker bu sorunu çözmektedir.

Aşağıdaki nedenlerden dolayı testin yerel bir sunucuda çalıştırılması gerekir:

  • Ağ asla tamamen güvenilir değildir. Binlerce istekten biri başarısız olabilir;
    Bu durumda otomatik test çalışmaz, iş durur ve bunun nedenini günlüklerde aramanız gerekir;
  • Bazı üçüncü taraf hizmetler çok sık yapılan isteklere izin vermez.

Ayrıca standın kullanılması istenmez çünkü:

  • Bir stand, yalnızca üzerinde çalışan hatalı kod nedeniyle değil, aynı zamanda doğru kodun işleyemediği veriler nedeniyle de bozulabilir;
  • Test sırasında testin yaptığı tüm değişiklikleri geri döndürmeye ne kadar çalışırsak çalışalım, bir şeyler ters gidebilir (aksi takdirde neden test edelim ki?).

Proje ve süreç organizasyonu hakkında

Firmamız Amazon AWS bulutunda Docker üzerinde çalışan microservice web uygulaması geliştirdi. Projede birim testleri zaten kullanılıyordu ancak birim testlerin tespit edemediği hatalar sıklıkla meydana geliyordu. Veritabanı ve Amazon hizmetleriyle birlikte bir mikro hizmetin tamamını test etmek gerekiyordu.

Proje, mikro hizmetin her taahhütte test edilmesini içeren standart bir sürekli entegrasyon süreci kullanıyor. Bir görev atadıktan sonra geliştirici, mikro hizmette değişiklikler yapar, bunu manuel olarak test eder ve mevcut tüm otomatik testleri çalıştırır. Gerekirse geliştirici testleri değiştirir. Herhangi bir sorun görülmediği takdirde bu konu ile ilgili şubeye taahhütte bulunulur. Her taahhütten sonra testler sunucuda otomatik olarak çalıştırılır. Başarılı bir incelemenin ardından ortak bir dalda birleşme ve otomatik testlerin başlatılması gerçekleşir. Paylaşılan şubedeki testler başarılı olursa hizmet, Amazon Elastic Container Service (bench) üzerindeki test ortamında otomatik olarak güncellenir. Stand tüm geliştiriciler ve testçiler için gereklidir ve kırılması tavsiye edilmez. Bu ortamdaki test uzmanları, manuel testler gerçekleştirerek bir düzeltmeyi veya yeni bir özelliği kontrol eder.

Proje mimarisi

Sürekli entegrasyon için Docker'da mikro hizmetlerin otomatik testi

Uygulama ondan fazla hizmetten oluşmaktadır. Bazıları .NET Core'da, bazıları ise NodeJ'lerde yazılmıştır. Her hizmet, Amazon Elastic Container Service'teki bir Docker kapsayıcısında çalışır. Her birinin kendi Postgres veritabanı vardır ve bazılarının da Redis'i vardır. Ortak veritabanları yoktur. Birden fazla hizmetin aynı verilere ihtiyacı varsa, bu veriler değiştiğinde SNS (Basit Bildirim Hizmeti) ve SQS (Amazon Basit Kuyruk Hizmeti) aracılığıyla bu hizmetlerin her birine iletilir ve hizmetler bunu kendi ayrı veritabanlarına kaydeder.

SQS ve SNS

SQS, HTTPS protokolünü kullanarak mesajları bir sıraya koymanıza ve kuyruktan mesajları okumanıza olanak tanır.

Birden fazla hizmet bir kuyruğu okuyorsa, her mesaj bunlardan yalnızca birine ulaşır. Bu, yükü aralarında dağıtmak için aynı hizmetin birkaç örneğini çalıştırırken kullanışlıdır.

Her mesajın birden fazla hizmete teslim edilmesini istiyorsanız, her alıcının kendi kuyruğu olması gerekir ve mesajları birden fazla kuyruğa kopyalamak için SNS'ye ihtiyaç vardır.

SNS'de bir konu oluşturursunuz ve buna (örneğin bir SQS kuyruğu) abone olursunuz. Konuya mesaj gönderebilirsiniz. Bu durumda mesaj, bu konuya abone olan her kuyruğa gönderilir. SNS'in mesajları okumak için bir yöntemi yoktur. Hata ayıklama veya test sırasında SNS'ye neyin gönderildiğini bulmanız gerekiyorsa, bir SQS kuyruğu oluşturabilir, istediğiniz konuya abone olabilir ve kuyruğu okuyabilirsiniz.

Sürekli entegrasyon için Docker'da mikro hizmetlerin otomatik testi

API ağ geçidi

Çoğu hizmete İnternet'ten doğrudan erişilemez. Erişim, erişim haklarını kontrol eden API Ağ Geçidi aracılığıyla gerçekleştirilir. Bu aynı zamanda bizim hizmetimizdir ve bunun için de testler vardır.

Gerçek zamanlı bildirimler

Uygulamanın kullandığı SinyalRkullanıcıya gerçek zamanlı bildirimler göstermek için. Bu, bildirim hizmetinde uygulanır. Doğrudan İnternet'ten erişilebilir ve kendisi OAuth ile çalışır, çünkü OAuth ve bildirim hizmetinin entegre edilmesiyle karşılaştırıldığında Ağ Geçidinde Web yuvaları için destek oluşturmanın pratik olmadığı ortaya çıktı.

Tanınmış Test Yaklaşımı

Birim testleri, veritabanı gibi şeyleri sahte nesnelerle değiştirir. Örneğin bir mikro hizmet, yabancı anahtara sahip bir tabloda kayıt oluşturmaya çalışırsa ve bu anahtarın referans verdiği kayıt mevcut değilse, istek yürütülemez. Birim testleri bunu tespit edemez.

В Microsoft'tan makale Bellek içi bir veritabanının kullanılması ve sahte nesnelerin uygulanması önerilmektedir.

Bellek içi veritabanı, Entity Framework tarafından desteklenen DBMS'lerden biridir. Özellikle test etmek için oluşturuldu. Böyle bir veritabanındaki veriler yalnızca onu kullanan işlem sona erene kadar saklanır. Tablo oluşturmayı gerektirmez ve veri bütünlüğünü kontrol etmez.

Sahte nesneler, değiştirdikleri sınıfı yalnızca test geliştiricisinin nasıl çalıştığını anladığı ölçüde modeller.

Bir test çalıştırdığınızda Postgres'in otomatik olarak geçişleri başlatmasını ve gerçekleştirmesini nasıl sağlayacağınız Microsoft makalesinde belirtilmemiştir. Benim çözümüm bunu yapıyor ve ayrıca mikro hizmetin kendisine testler için özel olarak herhangi bir kod eklemiyor.

Çözüme geçelim

Geliştirme sürecinde birim testlerin tüm sorunları zamanında bulmak için yeterli olmadığı anlaşıldı ve bu konuya farklı bir açıdan yaklaşmaya karar verildi.

Test ortamı oluşturma

İlk görev bir test ortamı dağıtmaktır. Bir mikro hizmeti çalıştırmak için gereken adımlar:

  • Yerel ortam için test edilen hizmeti yapılandırın, ortam değişkenlerinde veritabanına ve AWS'ye bağlanmaya ilişkin ayrıntıları belirtin;
  • Postgres'i başlatın ve Liquibase'i çalıştırarak taşıma işlemini gerçekleştirin.
    İlişkisel DBMS'lerde veritabanına veri yazmadan önce bir veri şeması yani tablolar oluşturmanız gerekir. Bir uygulamayı güncellerken tabloların yeni versiyonun kullandığı forma ve tercihen veri kaybı olmadan getirilmesi gerekmektedir. Buna göç denir. Başlangıçta boş bir veritabanında tablo oluşturmak, geçişin özel bir durumudur. Geçiş, uygulamanın içine yerleştirilebilir. Hem .NET hem de NodeJS geçiş çerçevelerine sahiptir. Bizim durumumuzda güvenlik nedeniyle mikro servislerin veri şemasını değiştirme hakkından mahrum bırakılmış ve geçiş Liquibase kullanılarak gerçekleştirilmektedir.
  • Amazon LocalStack'i başlatın. Bu, AWS hizmetlerinin evde çalıştırılacak bir uygulamasıdır. Docker Hub üzerinde LocalStack için hazır bir image bulunmaktadır.
  • LocalStack'te gerekli varlıkları oluşturmak için betiği çalıştırın. Kabuk komut dosyaları AWS CLI'yi kullanır.

Projede test etmek için kullanılır Postacı. Daha önce de mevcuttu ancak manuel olarak başlatıldı ve halihazırda stantta konuşlandırılmış bir uygulamayı test etti. Bu araç, rastgele HTTP(S) istekleri yapmanıza ve yanıtların beklentilerle eşleşip eşleşmediğini kontrol etmenize olanak tanır. Sorgular bir koleksiyonda birleştirilir ve koleksiyonun tamamı çalıştırılabilir.

Sürekli entegrasyon için Docker'da mikro hizmetlerin otomatik testi

Otomatik test nasıl çalışır?

Test sırasında Docker'da her şey çalışıyor: test edilen hizmet, Postgres, geçiş aracı ve Postman, daha doğrusu konsol sürümü - Newman.

Docker bir dizi sorunu çözer:

  • Ana bilgisayar yapılandırmasından bağımsızlık;
  • Bağımlılıkları yükleme: Docker, görüntüleri Docker Hub'dan indirir;
  • Sistemi orijinal durumuna döndürmek: kapları çıkarmak yeterlidir.

liman işçisi-oluştur kapları, kapların birbirini alan adlarına göre bulduğu, İnternetten yalıtılmış bir sanal ağda birleştirir.

Test bir kabuk betiği tarafından kontrol edilir. Testi Windows'ta çalıştırmak için git-bash kullanıyoruz. Böylece hem Windows hem de Linux için tek bir komut dosyası yeterlidir. Git ve Docker projedeki tüm geliştiriciler tarafından kurulur. Git'i Windows'a yüklerken git-bash yüklenir, dolayısıyla herkeste de bulunur.

Komut dosyası aşağıdaki adımları gerçekleştirir:

  • Liman işçisi görselleri oluşturma
    docker-compose build
  • Veritabanını ve LocalStack'i başlatma
    docker-compose up -d <контейнер>
  • Veritabanı geçişi ve LocalStack'in hazırlanması
    docker-compose run <контейнер>
  • Test altındaki hizmeti başlatma
    docker-compose up -d <сервис>
  • Testi çalıştırma (Newman)
  • Tüm konteynerlerin durdurulması
    docker-compose down
  • Sonuçlar Slack'te yayınlanıyor
    Yeşil onay işareti veya kırmızı çarpı işareti ve günlüğe bağlantı içeren mesajların gittiği bir sohbetimiz var.

Bu adımlarda aşağıdaki Docker görüntüleri yer almaktadır:

  • Test edilen hizmet, üretimle aynı görüntüdür. Testin yapılandırması ortam değişkenleri aracılığıyla yapılır.
  • Postgres, Redis ve LocalStack için Docker Hub'dan hazır görseller kullanılıyor. Liquibase ve Newman için de hazır görseller mevcut. Dosyalarımızı oraya ekleyerek kendimizi onların iskeleti üzerine inşa ediyoruz.
  • LocalStack'i hazırlamak için hazır bir AWS CLI görüntüsü kullanırsınız ve buna dayalı bir komut dosyası içeren bir görüntü oluşturursunuz.

Kullanma hacimleriyalnızca kapsayıcıya dosya eklemek için Docker görüntüsü oluşturmanıza gerek yoktur. Ancak Gitlab CI görevlerinin kendisi konteynerlerde çalıştığından hacimler ortamımız için uygun değildir. Docker'ı böyle bir kapsayıcıdan kontrol edebilirsiniz, ancak birimler yalnızca ana sistemdeki klasörleri bağlar, başka bir kapsayıcıdan bağlamaz.

Karşılaşabileceğiniz sorunlar

hazır olmak için bekliyorum

Hizmet içeren bir konteynerin çalışıyor olması, onun bağlantıları kabul etmeye hazır olduğu anlamına gelmez. Bağlantının devam etmesini beklemelisiniz.

Bu sorun bazen bir komut dosyası kullanılarak çözülür bekle-bunu.shTCP bağlantısı kurma fırsatını bekleyen. Ancak LocalStack 502 Bad Gateway hatası verebilir. Ayrıca birçok hizmetten oluşuyor ve bunlardan birinin hazır olması diğerleri hakkında hiçbir şey söylemiyor.

karar: Hem SQS hem de SNS'den 200 yanıt bekleyen LocalStack sağlama komut dosyaları.

Paralel Görev Çatışmaları

Aynı Docker ana bilgisayarında birden fazla test aynı anda çalıştırılabilir, bu nedenle kapsayıcı ve ağ adlarının benzersiz olması gerekir. Üstelik aynı servisin farklı şubelerindeki testler de aynı anda çalışabildiği için her compose dosyasına isimlerini yazmak yeterli olmuyor.

karar: Komut dosyası, COMPOSE_PROJECT_NAME değişkenini benzersiz bir değere ayarlar.

Pencere özellikleri

Docker'ı Windows'ta kullanırken size belirtmek istediğim birkaç şey var çünkü bu deneyimler hataların neden oluştuğunu anlamak açısından önemlidir.

  1. Bir kapsayıcıdaki kabuk komut dosyalarının Linux satır sonları olması gerekir.
    Kabuk CR sembolü bir sözdizimi hatasıdır. Hata mesajından durumun böyle olduğunu söylemek zor. Windows'ta bu tür komut dosyalarını düzenlerken uygun bir metin düzenleyiciye ihtiyacınız vardır. Ayrıca versiyon kontrol sisteminin de doğru yapılandırılması gerekmektedir.

Git şu şekilde yapılandırıldı:

git config core.autocrlf input

  1. Git-bash standart Linux klasörlerini taklit eder ve bir exe dosyasını (docker.exe dahil) çağırırken mutlak Linux yollarını Windows yollarıyla değiştirir. Ancak bu, yerel makinede olmayan yollar (veya kapsayıcıdaki yollar) için anlamlı değildir. Bu davranış devre dışı bırakılamaz.

karar: yolun başına ek bir eğik çizgi ekleyin: /bin yerine //bin. Linux bu tür yolları anlıyor; onun için birkaç eğik çizgi birle aynıdır. Ancak git-bash bu tür yolları tanımıyor ve bunları dönüştürmeye çalışmıyor.

Günlük çıkışı

Testleri çalıştırırken hem Newman'dan hem de test edilen hizmetten gelen günlükleri görmek istiyorum. Bu günlüklerin olayları birbirine bağlı olduğundan, bunları tek bir konsolda birleştirmek, iki ayrı dosyadan çok daha kullanışlıdır. Newman aracılığıyla başlıyor liman işçisi oluşturma çalıştırmasıve böylece çıktısı konsolda biter. Geriye kalan tek şey, hizmetin çıktısının da oraya gittiğinden emin olmaktır.

Orijinal çözüm yapmaktı docker-oluşturmak bayrak yok -d, ancak kabuk yeteneklerini kullanarak bu işlemi arka plana gönderin:

docker-compose up <service> &

Bu, günlüklerin Docker'dan üçüncü taraf bir hizmete gönderilmesi gerekli olana kadar işe yaradı. docker-oluşturmak günlüklerin konsola çıktısını almayı durdurdu. Ancak ekip çalıştı docker ekle.

karar:

docker attach --no-stdin ${COMPOSE_PROJECT_NAME}_<сервис>_1 &

Test yinelemeleri sırasında tanımlayıcı çakışması

Testler birkaç yinelemede gerçekleştirilir. Veritabanı temizlenmedi. Veritabanındaki kayıtların benzersiz kimlikleri vardır. İsteklerde belirli kimlikleri yazarsak ikinci yinelemede çakışmayla karşılaşırız.

Bunu önlemek için kimliklerin benzersiz olması veya test tarafından oluşturulan tüm nesnelerin silinmesi gerekir. Bazı nesneler gereksinimler nedeniyle silinememektedir.

karar: Postacı komut dosyalarını kullanarak GUID'ler oluşturun.

var uuid = require('uuid');
var myid = uuid.v4();
pm.environment.set('myUUID', myid);

Daha sonra sorgudaki sembolü kullanın {{myUUID}}değişkenin değeriyle değiştirilecektir.

LocalStack aracılığıyla işbirliği

Test edilen hizmet bir SQS kuyruğunu okuyor veya yazıyorsa, bunu doğrulamak için testin kendisinin de bu kuyrukla çalışması gerekir.

karar: Postacıdan LocalStack'e istekler.

AWS hizmetleri API'si belgelenmiştir ve sorguların SDK olmadan yapılmasına olanak tanır.

Bir hizmet bir kuyruğa yazıyorsa, onu okuruz ve mesajın içeriğini kontrol ederiz.

Servis SNS'e mesaj gönderirse, hazırlık aşamasında LocalStack da bir kuyruk oluşturur ve bu SNS konusuna abone olur. Sonra her şey yukarıda anlatılanlara gelir.

Servisin kuyruktan bir mesaj okuması gerekiyorsa, önceki test adımında bu mesajı kuyruğa yazıyoruz.

Test edilen mikro hizmetten kaynaklanan HTTP isteklerini test etme

Bazı hizmetler HTTP üzerinden AWS dışında bir şeyle çalışır ve bazı AWS özellikleri LocalStack'te uygulanmaz.

karar: bu durumlarda yardımcı olabilir Sahte Sunucuiçinde hazır bir görüntü bulunan Docker Hub'ı. Beklenen istekler ve bunlara verilen yanıtlar bir HTTP isteği tarafından yapılandırılır. API belgelenmiştir, dolayısıyla Postacı'dan talepte bulunuruz.

OAuth Kimlik Doğrulamasını ve Yetkilendirmeyi Test Etme

OAuth kullanıyoruz ve JSON Web Belirteçleri (JWT). Test, yerel olarak çalıştırabileceğimiz bir OAuth sağlayıcısına ihtiyaç duyuyor.

Hizmet ile OAuth sağlayıcısı arasındaki tüm etkileşim iki isteğe bağlıdır: ilk olarak yapılandırma istenir /.iyi bilinen/openid-yapılandırmasıve ardından yapılandırmadaki adreste ortak anahtar (JWKS) istenir. Bütün bunlar statik içeriktir.

karar: Test OAuth sağlayıcımız statik bir içerik sunucusudur ve üzerinde iki dosya bulunur. Belirteç bir kez oluşturulur ve Git'e aktarılır.

SignalR testinin özellikleri

Postacı websocket'lerle çalışmaz. SignalR'ı test etmek için özel bir araç oluşturuldu.

SignalR istemcisi bir tarayıcıdan daha fazlası olabilir. .NET Core altında bunun için bir istemci kütüphanesi bulunmaktadır. .NET Core'da yazılan istemci bir bağlantı kurar, kimliği doğrulanır ve belirli bir mesaj dizisini bekler. Beklenmeyen bir mesaj alınırsa veya bağlantı kesilirse istemci 1 koduyla çıkar. Beklenen son mesaj alınırsa istemci 0 koduyla çıkar.

Newman müşteriyle eş zamanlı olarak çalışıyor. İletilerin ihtiyacı olan herkese iletildiğini kontrol etmek için birkaç istemci başlatılır.

Sürekli entegrasyon için Docker'da mikro hizmetlerin otomatik testi

Birden fazla istemciyi çalıştırmak için bu seçeneği kullanın --ölçek docker-compose komut satırında.

Postman betiği çalıştırmadan önce tüm istemcilerin bağlantı kurmasını bekler.
Bağlantı bekleme sorunuyla zaten karşılaştık. Ancak sunucular vardı ve işte müşteri. Farklı bir yaklaşıma ihtiyaç var.

karar: kapsayıcıdaki istemci mekanizmayı kullanır Sağlık kontrolüAna bilgisayardaki komut dosyasını durumu hakkında bilgilendirmek için. İstemci, bağlantı kurulur kurulmaz, örneğin /healthcheck gibi belirli bir yolda bir dosya oluşturur. Docker dosyasındaki HealthCheck betiği şuna benzer:

HEALTHCHECK --interval=3s CMD if [ ! -e /healthcheck ]; then false; fi

Ekip liman işçisi teftiş Konteynerin normal durumunu, sağlık durumunu ve çıkış kodunu gösterir.

Newman tamamlandıktan sonra komut dosyası, istemcideki tüm kapsayıcıların sonlandırıldığını 0 koduyla kontrol eder.

Mutluluk var

Yukarıda açıklanan zorlukların üstesinden geldikten sonra bir dizi kararlı çalışma testi yaptık. Testlerde her hizmet, veritabanı ve Amazon LocalStack ile etkileşim kurarak tek bir birim olarak çalışır.

Bu testler, 30'dan fazla geliştiriciden oluşan bir ekibi, sık dağıtımlarla 10'dan fazla mikro hizmetin karmaşık etkileşimi içeren bir uygulamadaki hatalardan korur.

Kaynak: habr.com

Yorum ekle