paint-brush
Test Konteyner Tabanlı Yük Test Cihazı Hakkında Bilmeniz Gereken Her Şeyile@avvero
190 okumalar

Test Konteyner Tabanlı Yük Test Cihazı Hakkında Bilmeniz Gereken Her Şey

ile Anton Belyaev15m2024/06/07
Read on Terminal Reader

Çok uzun; Okumak

Bu makalenin amacı, normal entegrasyon testlerinin yazıldığı şekilde, yük testi için bir kurulum oluşturmaya yönelik bir yaklaşımı göstermektir: Gradle proje ortamında Testcontainers kullanılarak Spock testleri şeklinde. Gatling, WRK ve Yandex.Tank gibi yük testi yardımcı programları kullanılır.
featured image - Test Konteyner Tabanlı Yük Test Cihazı Hakkında Bilmeniz Gereken Her Şey
Anton Belyaev HackerNoon profile picture

Testcontainers'ı kullanmak, test senaryolarıyla çalışma sürecini kökten geliştirdi. Bu araç sayesinde entegrasyon testleri için ortamlar oluşturmak daha kolay hale geldi ( Kafka ile Test Etmede Yalıtım makalesine bakın). Artık veritabanlarının, mesaj aracılarının ve diğer hizmetlerin farklı sürümlerine sahip kapları kolayca başlatabiliyoruz. Entegrasyon testleri için Testcontainers'ın vazgeçilmez olduğu kanıtlanmıştır.


Yük testi, fonksiyonel teste göre daha az yaygın olsa da çok daha keyifli olabilir. Grafikleri incelemek ve belirli bir hizmetin performansını analiz etmek gerçek bir zevk getirebilir. Bu tür görevler nadirdir ancak benim için özellikle heyecan vericidirler.


Bu makalenin amacı, normal entegrasyon testlerinin yazıldığı şekilde, yük testi için bir kurulum oluşturmaya yönelik bir yaklaşımı göstermektir: Gradle proje ortamında Testcontainer'ları kullanan Spock testleri şeklinde. Gatling, WRK ve Yandex.Tank gibi yük testi araçları kullanılır.

Yük Testi Ortamı Oluşturma

Araç seti: Gradle + Spock Framework + Testcontainers. Uygulama değişkeni ayrı bir Gradle modülüdür. Kullanılan yük testi araçları Gatling, WRK ve Yandex.Tank'tır.


Test nesnesiyle çalışmanın iki yaklaşımı vardır:

  • Yayınlanan görsellerin test edilmesi;
  • Projenin kaynak kodundan görseller oluşturmak ve test etmek.


İlk durumda, projenin versiyonundan ve değişikliklerinden bağımsız bir dizi yük testimiz var. Bu yaklaşımın gelecekte sürdürülmesi daha kolaydır ancak yalnızca yayınlanan görüntülerin test edilmesiyle sınırlıdır. Elbette bu görüntüleri yerel olarak manuel olarak oluşturabiliriz, ancak bu daha az otomatiktir ve tekrarlanabilirliği azaltır. Gerekli görüntüler olmadan CI/CD'de çalıştırıldığında testler başarısız olur.


İkinci durumda testler hizmetin en son sürümü üzerinde gerçekleştirilir. Bu, yük testlerinin CI'ya entegre edilmesine ve hizmet sürümleri arasında performans verileri değişikliklerinin elde edilmesine olanak tanır. Ancak yük testleri genellikle birim testlerden daha uzun sürer. Bu tür testleri kalite kapısının bir parçası olarak CI'ya dahil etme kararı size bağlıdır.


Bu makale ilk yaklaşımı ele almaktadır. Spock sayesinde karşılaştırmalı analiz için hizmetin birden fazla sürümü üzerinde testler gerçekleştirebiliyoruz:

 where: image | _ 'avvero/sandbox:1.0.0' | _ 'avvero/sandbox:1.1.0' | _

Bu makalenin amacının tam ölçekli yük testi değil, test alanının organizasyonunu göstermek olduğunu unutmamak önemlidir.

Hedef Hizmet

Test nesnesi için, bir uç nokta yayınlayan ve istekleri işlemek için harici bir kaynaktan gelen verileri kullanan Sandbox adlı basit bir HTTP hizmetini ele alalım. Hizmetin bir veritabanı var.

Dockerfile dahil olmak üzere hizmetin kaynak kodu, spring-sandbox proje deposunda mevcuttur.

Modül Yapısına Genel Bakış

Makalenin ilerleyen kısımlarında ayrıntılara girerken, kompozisyonunun anlaşılmasını sağlamak için load-tests Gradle modülünün yapısına kısa bir genel bakışla başlamak istiyorum:

 load-tests/ |-- src/ | |-- gatling/ | | |-- scala/ | | | |-- MainSimulation.scala # Main Gatling simulation file | | |-- resources/ | | | |-- gatling.conf # Gatling configuration file | | | |-- logback-test.xml # Logback configuration for testing | |-- test/ | | |-- groovy/ | | | |-- pw.avvero.spring.sandbox/ | | | | |-- GatlingTests.groovy # Gatling load test file | | | | |-- WrkTests.groovy # Wrk load test file | | | | |-- YandexTankTests.groovy # Yandex.Tank load test file | | |-- java/ | | | |-- pw.avvero.spring.sandbox/ | | | | |-- FileHeadLogConsumer.java # Helper class for logging to a file | | |-- resources/ | | | |-- wiremock/ | | | | |-- mappings/ # WireMock setup for mocking external services | | | | | |-- health.json | | | | | |-- forecast.json | | | |-- yandex-tank/ # Yandex.Tank load testing configuration | | | | |-- ammo.txt | | | | |-- load.yaml | | | | |-- make_ammo.py | | | |-- wrk/ # LuaJIT scripts for Wrk | | | | |-- scripts/ | | | | | |-- getForecast.lua |-- build.gradle

Proje deposu - https://github.com/avvero/testing-bench .

Çevre

Yukarıdaki açıklamadan hizmetin iki bağımlılığı olduğunu görüyoruz: https://external-weather-api.com hizmeti ve bir veritabanı. Açıklamaları aşağıda verilecektir, ancak şemanın tüm bileşenlerinin Docker ortamında iletişim kurmasını sağlayarak başlayalım - ağı açıklayacağız:

 def network = Network.newNetwork()

ve her bileşen için ağ takma adları sağlayın. Bu son derece kullanışlıdır ve entegrasyon parametrelerini statik olarak tanımlamamıza olanak tanır.

WireMock ve yük testi yardımcı programları gibi bağımlılıkların çalışması için yapılandırma gerekir. Bunlar, konteynere aktarılabilecek parametreler veya konteynerlere bağlanması gereken tüm dosya ve dizinler olabilir.


Ayrıca yaptıkları işin sonuçlarını da konteynerlerden almamız gerekiyor. Bu görevleri çözmek için iki dizi dizin sağlamamız gerekir:


  • workingDirectory — modülün kaynak dizini, doğrudan load-tests/ konumunda.


  • reportDirectory — ölçümler ve günlükler de dahil olmak üzere çalışmanın sonuçlarının dizini. Bununla ilgili daha fazla bilgiyi raporlar bölümünde bulacaksınız.

Veri tabanı

Sandbox hizmeti, veritabanı olarak Postgres'i kullanır. Bu bağımlılığı şu şekilde tanımlayalım:

 def postgres = new PostgreSQLContainer<>("postgres:15-alpine") .withNetwork(network) .withNetworkAliases("postgres") .withUsername("sandbox") .withPassword("sandbox") .withDatabaseName("sandbox")


Bildirim, Sandbox hizmetinin veritabanına bağlanmak için kullanacağı postgres ağ takma adını belirtir. Veritabanıyla entegrasyon açıklamasını tamamlamak için hizmetin aşağıdaki parametrelerle sağlanması gerekir:

 'spring.datasource.url' : 'jdbc:postgresql://postgres:5432/sandbox', 'spring.datasource.username' : 'sandbox', 'spring.datasource.password' : 'sandbox', 'spring.jpa.properties.hibernate.default_schema': 'sandbox'


Veritabanı yapısı, Flyway kullanılarak uygulamanın kendisi tarafından yönetildiğinden, testte herhangi bir ek veritabanı manipülasyonuna gerek yoktur.

https://external-weather-api.com'a Yönelik Alaycı İstekler

Gerçek bileşeni bir kapta çalıştırma olanağımız, gerekliliğimiz veya isteğimiz yoksa, API'si için bir örnek sağlayabiliriz. https://external-weather-api.com hizmeti için WireMock kullanılır.


WireMock konteynerinin beyanı şu şekilde görünecektir:

 def wiremock = new GenericContainer<>("wiremock/wiremock:3.5.4") .withNetwork(network) .withNetworkAliases("wiremock") .withFileSystemBind("${workingDirectory}/src/test/resources/wiremock/mappings", "/home/wiremock/mappings", READ_WRITE) .withCommand("--no-request-journal") .waitingFor(new LogMessageWaitStrategy().withRegEx(".*https://wiremock.io/cloud.*")) wiremock.start()


WireMock sahte yapılandırma gerektirir. withFileSystemBind talimatı, yerel dosya yolu ile Docker kapsayıcısının içindeki yol arasındaki dosya sistemi bağlamasını açıklar. Bu durumda, yerel makinedeki "${workingDirectory}/src/test/resources/wiremock/mappings" dizini, WireMock konteynerinin içindeki /home/wiremock/mappings dizinine bağlanacaktır.


Aşağıda dizindeki dosya kompozisyonunu anlamak için proje yapısının ek bir kısmı bulunmaktadır:

 load-tests/ |-- src/ | |-- test/ | | |-- resources/ | | | |-- wiremock/ | | | | |-- mappings/ | | | | | |-- health.json | | | | | |-- forecast.json


Sahte yapılandırma dosyalarının WireMock tarafından doğru şekilde yüklendiğinden ve kabul edildiğinden emin olmak için bir yardımcı kapsayıcı kullanabilirsiniz:

 helper.execInContainer("wget", "-O", "-", "http://wiremock:8080/health").getStdout() == "Ok"


Yardımcı kapsayıcı şu şekilde açıklanmaktadır:

 def helper = new GenericContainer<>("alpine:3.17") .withNetwork(network) .withCommand("top")


Bu arada, IntelliJ IDEA 2024.1 sürümü WireMock desteğini sundu ve IDE, sahte yapılandırma dosyaları oluşturulurken öneriler sunuyor.

Hedef Hizmet Başlatma Yapılandırması

Sandbox hizmet konteynerinin bildirimi aşağıdaki gibi görünür:

 def javaOpts = ' -Xloggc:/tmp/gc/gc.log -XX:+PrintGCDetails' + ' -XX:+UnlockDiagnosticVMOptions' + ' -XX:+FlightRecorder' + ' -XX:StartFlightRecording:settings=default,dumponexit=true,disk=true,duration=60s,filename=/tmp/jfr/flight.jfr' def sandbox = new GenericContainer<>(image) .withNetwork(network) .withNetworkAliases("sandbox") .withFileSystemBind("${reportDirectory}/logs", "/tmp/gc", READ_WRITE) .withFileSystemBind("${reportDirectory}/jfr", "/tmp/jfr", READ_WRITE) .withEnv([ 'JAVA_OPTS' : javaOpts, 'app.weather.url' : 'http://wiremock:8080', 'spring.datasource.url' : 'jdbc:postgresql://postgres:5432/sandbox', 'spring.datasource.username' : 'sandbox', 'spring.datasource.password' : 'sandbox', 'spring.jpa.properties.hibernate.default_schema': 'sandbox' ]) .waitingFor(new LogMessageWaitStrategy().withRegEx(".*Started SandboxApplication.*")) .withStartupTimeout(Duration.ofSeconds(10)) sandbox.start()

Önemli parametreler ve JVM ayarları şunları içerir:

  • Çöp toplama olayı bilgilerinin toplanması.
  • JVM performans verilerini kaydetmek için Java Flight Recorder'ın (JFR) kullanılması.


Ayrıca dizinler, hizmetin tanılama sonuçlarını kaydetmek için yapılandırılmıştır.

Kerestecilik

Test senaryosu yazma ve yapılandırma aşamasında muhtemelen gerekli olan herhangi bir konteynerin loglarını bir dosyaya görmeniz gerekiyorsa, konteyneri tanımlarken aşağıdaki talimatı kullanabilirsiniz:

 .withLogConsumer(new FileHeadLogConsumer("${reportDirectory}/logs/${alias}.log"))


Bu durumda, bir dosyaya sınırlı miktarda günlük yazılmasına izin veren FileHeadLogConsumer sınıfı kullanılır. Bunun nedeni, yük testi senaryolarında günlüğün tamamına ihtiyaç duyulmaması ve hizmetin düzgün çalışıp çalışmadığını değerlendirmek için kısmi bir günlüğün yeterli olmasıdır.

Yük Testlerinin Uygulanması

Yük testi için birçok araç vardır. Bu makalede bunlardan üçünü kullanmayı düşünmeyi öneriyorum: Gatling, Wrk ve Yandex.Tank. Her üç araç da birbirinden bağımsız olarak kullanılabilir.

mitralyöz

Gatling, Scala'da yazılmış açık kaynaklı bir yük test aracıdır. Karmaşık test senaryolarının oluşturulmasına olanak tanır ve ayrıntılı raporlar sunar. Gatling'in ana simülasyon dosyası modüle bir Scala kaynağı olarak bağlanır ve sözdizimi vurgulama ve dokümantasyon referansı için yöntemler arasında gezinme dahil olmak üzere IntelliJ IDEA'nın tüm destek yelpazesini kullanarak çalışmayı kolaylaştırır.


Gatling için konteyner konfigürasyonu aşağıdaki gibidir:

 def gatling = new GenericContainer<>("denvazh/gatling:3.2.1") .withNetwork(network) .withFileSystemBind("${reportDirectory}/gatling-results", "/opt/gatling/results", READ_WRITE) .withFileSystemBind("${workingDirectory}/src/gatling/scala", "/opt/gatling/user-files/simulations", READ_WRITE) .withFileSystemBind("${workingDirectory}/src/gatling/resources", "/opt/gatling/conf", READ_WRITE) .withEnv("SERVICE_URL", "http://sandbox:8080") .withCommand("-s", "MainSimulation") .waitingFor(new LogMessageWaitStrategy() .withRegEx(".*Please open the following file: /opt/gatling/results.*") .withStartupTimeout(Duration.ofSeconds(60L * 2)) ); gatling.start()

Kurulum diğer kapsayıcılarla hemen hemen aynıdır:

  • reportDirectory raporlara yönelik dizini bağlayın.
  • Yapılandırma dosyalarının dizinini workingDirectory bağlayın.
  • Simülasyon dosyalarının dizinini workingDirectory bağlayın.


Ek olarak parametreler konteynere iletilir:

  • Sandbox hizmetinin URL değerini içeren SERVICE_URL ortam değişkeni. Bununla birlikte, daha önce de belirtildiği gibi, ağ takma adlarının kullanılması, URL'nin doğrudan senaryo koduna sabit kodlanmasına olanak tanır.


  • Belirli bir simülasyonu çalıştırmak için -s MainSimulation komutu.


Neyin nereye iletildiğini anlamak için proje kaynak dosyası yapısını burada hatırlatıyoruz:

 load-tests/ |-- src/ | |-- gatling/ | | |-- scala/ | | | |-- MainSimulation.scala # Main Gatling simulation file | | |-- resources/ | | | |-- gatling.conf # Gatling configuration file | | | |-- logback-test.xml # Logback configuration for testing

Bu son kapsayıcı olduğundan ve tamamlanmasının ardından sonuç almayı beklediğimizden, beklentiyi .withRegEx(".*Please open the following file: /opt/gatling/results.*") belirledik. Bu mesaj konteyner kayıtlarında göründüğünde veya 60 * 2 saniye sonra test sona erecektir.


Bu aracın senaryolarının DSL'sine girmeyeceğim. Kullanılan senaryonun kodunu proje deposundan kontrol edebilirsiniz.

Wrk

Wrk basit ve hızlı bir yük test aracıdır. Minimum kaynakla önemli bir yük oluşturabilir. Temel özellikler şunları içerir:

  • İstekleri yapılandırmak için Lua komut dosyaları desteği.
  • Çoklu iş parçacığı nedeniyle yüksek performans.
  • Minimum bağımlılıkla kullanım kolaylığı.


Wrk için konteyner yapılandırması aşağıdaki gibidir:

 def wrk = new GenericContainer<>("ruslanys/wrk") .withNetwork(network) .withFileSystemBind("${workingDirectory}/src/test/resources/wrk/scripts", "/tmp/scripts", READ_WRITE) .withCommand("-t10", "-c10", "-d60s", "--latency", "-s", "/tmp/scripts/getForecast.lua", "http://sandbox:8080/weather/getForecast") .waitingFor(new LogMessageWaitStrategy() .withRegEx(".*Transfer/sec.*") .withStartupTimeout(Duration.ofSeconds(60L * 2)) ) wrk.start()


Wrk'un Sandbox hizmetine yapılan isteklerle çalışmasını sağlamak için Lua betiği aracılığıyla istek açıklaması gereklidir, bu nedenle betik dizinini workingDirectory bağlarız. Komutu kullanarak, hedef hizmet yönteminin betiğini ve URL'sini belirterek Wrk'yi çalıştırıyoruz. Wrk, sonuçlarına göre günlüğe beklentileri belirlemek için kullanılabilecek bir rapor yazar.

Yandex.Tank

Yandex.Tank, Yandex tarafından geliştirilen bir yük test aracıdır. JMeter ve Phantom gibi çeşitli yük testi motorlarını destekler. Yük testi sonuçlarını saklamak ve görüntülemek için ücretsiz Overload hizmetini kullanabilirsiniz.


İşte konteyner yapılandırması:

 copyFiles("${workingDirectory}/src/test/resources/yandex-tank", "${reportDirectory}/yandex-tank") def tank = new GenericContainer<>("yandex/yandex-tank") .withNetwork(network) .withFileSystemBind("${reportDirectory}/yandex-tank", "/var/loadtest", READ_WRITE) .waitingFor(new LogMessageWaitStrategy() .withRegEx(".*Phantom done its work.*") .withStartupTimeout(Duration.ofSeconds(60L * 2)) ) tank.start()


Sandbox için yük testi yapılandırması iki dosyayla temsil edilir: load.yaml ve ammo.txt . Kapsayıcı açıklamasının bir parçası olarak, yapılandırma dosyaları, çalışma dizini olarak bağlanacak olan reportDirectory dosyasına kopyalanır. Neyin nereye iletildiğini anlamak için proje kaynak dosyalarının yapısı aşağıda verilmiştir:

 load-tests/ |-- src/ | |-- test/ | | |-- resources/ | | | |-- yandex-tank/ | | | | |-- ammo.txt | | | | |-- load.yaml | | | | |-- make_ammo.py

Raporlar

JVM performans kayıtları ve günlükleri de dahil olmak üzere test sonuçları build/${timestamp} dizinine kaydedilir; burada ${timestamp} her test çalışmasının zaman damgasını temsil eder.


Aşağıdaki raporlar incelemeye sunulacaktır:

  • Çöp Toplayıcı günlükleri.
  • WireMock günlükleri.
  • Hedef hizmet günlükleri.
  • Wrk günlükleri.
  • JFR (Java Uçuş Kaydı).


Gatling kullanılmışsa:

  • Toplama raporu.
  • Günlükleri toplama.


Wrk kullanılmışsa:

  • Wrk günlükleri.


Yandex.Tank kullanıldıysa:

  • Overload'a ek yüklemeyle birlikte Yandex.Tank sonuç dosyaları.
  • Yandex.Tank günlükleri.


Raporların dizin yapısı aşağıdaki gibidir:

 load-tests/ |-- build/ | |-- ${timestamp}/ | | |-- gatling-results/ | | |-- jfr/ | | |-- yandex-tank/ | | |-- logs/ | | | |-- sandbox.log | | | |-- gatling.log | | | |-- gc.log | | | |-- wiremock.log | | | |-- wrk.log | | | |-- yandex-tank.log | |-- ${timestamp}/ | |-- ...

Çözüm

Yük testi, yazılım geliştirme yaşam döngüsünde çok önemli bir aşamadır. Bir uygulamanın çeşitli yük koşulları altında performansını ve kararlılığını değerlendirmeye yardımcı olur. Bu makale, test ortamının kolay ve verimli bir şekilde kurulmasına olanak tanıyan Testcontainer'ları kullanarak bir yük testi ortamı oluşturmaya yönelik bir yaklaşım sundu.


Test kapsayıcıları, entegrasyon testleri için ortamların oluşturulmasını önemli ölçüde basitleştirerek esneklik ve izolasyon sağlar. Yük testi için bu araç, gerekli kapsayıcıların farklı hizmet ve veritaban sürümleriyle konuşlandırılmasına olanak tanıyarak testlerin yürütülmesini kolaylaştırır ve sonuçların tekrarlanabilirliğini artırır.


Gatling, Wrk ve Yandex.Tank için sağlanan yapılandırma örneklerinin yanı sıra konteyner kurulumu, çeşitli araçların etkili bir şekilde nasıl entegre edileceğini ve test parametrelerinin nasıl yönetileceğini gösterir.


Ek olarak, uygulama performansını analiz etmek ve geliştirmek için gerekli olan test sonuçlarının günlüğe kaydedilmesi ve kaydedilmesi süreci de açıklandı. Bu yaklaşım gelecekte daha karmaşık senaryoları desteklemek ve diğer izleme ve analiz araçlarıyla entegrasyonu sağlamak üzere genişletilebilir.


Bu makaleye gösterdiğiniz ilgi için teşekkür ederiz ve faydalı testler yazma çabanızda iyi şanslar!