Elasticsearch Performansını Test Etme: K6 Yük Testi ile Adımlar ve Deneyimler

Serdarcan Büyükdereli
Arabam Labs
Published in
5 min readMay 17, 2024

k6, web uygulamalarının performansını ölçme ve analiz etme konusunda mükemmel bir çözüm sunan açık kaynaklı bir yük testi aracıdır. Kullanıcının belirlediği çeşitli yük senaryoları altında bir uygulamanın performansını simüle ederken, oluşturulan yüklerin uygulamanın davranışı üzerindeki etkilerini anlamamıza yardımcı olur. Bu sayede, uygulamalarımızın performansını en iyi duruma getirebilmek için değerli içgörüler elde ederiz.

Bir linux server üzerinde yapacağımız testlerde öncesinde elasticsearch üzerinde yapacağımız test için k6 plugin şeklinde eklememiz gerekir.

docker run --rm -it -u "$(id -u):$(id -g)" -v "${PWD}:/xk6" grafana/xk6 build v0.43.1 --with github.com/elastic/xk6-output-elasticsearch --with github.com/grafana/xk6-notification@latest

Bulunduğunuz dosya dizini üzerinde k6 binary dosyasını görebilirsiniz. Bu şekilde k6 elasticsearch plugin entegre edilmiş şekilde görürüz.

Daha fazla pluginleri görebilmek için awesome reposundan bakabilirsiniz.

Elasticsearch için gerekli olan ENV’ler ;

export K6_ELASTICSEARCH_URL=https://<ELASTIC_IP>:9200
export K6_ELASTICSEARCH_INSECURE_SKIP_VERIFY=true
export K6_ELASTICSEARCH_USER=elastic
export K6_ELASTICSEARCH_PASSWORD=<ELASTIC_PASSWORD>

Elasticsearch plugin reposundaki example.js’i inceleyelim.

import http from "k6/http";
import { check } from "k6";

export const options = {
vus: 1,
duration: '1m',
thresholds: {
'http_reqs{expected_response:true}': ['rate>5'],
},
};

export default function () {
check(http.get("https://httpbin.test.k6.io/status/200"), {
"status is 200": (r) => r.status == 200,
});
}

Bu kod k6 yük testi aracını kullanarak bir web hizmetine HTTP GET istekleri gönderir. Bu istekler, belirli bir süre boyunca belirlenen kullanıcı sayısıyla (vus: 1) ve belirlenen bir süre boyunca (duration: 1 dakika) gönderilir.

HTTP GET istekleri, “https://httpbin.test.k6.io/status/200" URL’ye gönderilir ve her bir isteğin HTTP durum kodunun 200 (başarılı) olup olmadığı kontrol edilir. Eğer başarılı isteklerin oranı 5'ten düşükse, test başarısız sayılır.

Şimdi burada elasticsearch nerede diye sorabilirsiniz. :) output olarak çıktının verdiği logları elasticsearch e gönderecek ve yük testi başlayacak.

./k6 run example.js -o output-elasticsearch

Yukarıda verdiğimiz ENV ler sayesinde elasticsearch clusterımıza erişebiliyor. k6-metrics adında bir index oluşturuyor. Çalışması bittikten sonra bize cli üzerinde bir rapor veriyor.

curl -XGET -k 'https://elastic:<ELASTIC_PASSWORD>@<ELASTIC_IP>:9200/k6-metrics/_search?pretty' -H 'Content-Type: application/json' -d'
{
"sort": [
{
"Time": {
"order": "desc"
}
}
],
"size": 10
}'

Bir curl isteği ile index i kontrol edebilirsiniz.

Eğer Raporu daha güzel bir çıktı olarak almak istiyorsanız. Aşağıdaki repo ile alınabilir.

Read testimizi birde bu şekilde yapalım.

import http from 'k6/http';
import { check } from 'k6';
import { htmlReport } from "https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js";

export function handleSummary(data) {
return {
"summary.html": htmlReport(data),
};
}

export default function () {
const payload = JSON.stringify({
"sort": [
{
"Time": {
"order": "desc"
}
}
],
"size": 10
});

const params = {
headers: {
'Content-Type': 'application/json'
}
};

const response = http.post(
"https://elastic:<ELASTIC_PASSWORD>@<ELASTIC_IP>:9200/k6-metrics/_search?pretty",
payload,
params
);
//console.log(response.body); Body görünsün diye kontrol edebilirsiniz.

check(response, {
"status is 200": (r) => r.status === 200
});


}

export const options = {
vus: 10,
duration: '1m',
thresholds: {
'http_reqs{expected_response:true}': ['rate>5'],
},
};

ELASTIC_PASSWORD= Elasticsearch passwordu yazmamız gerekiyor.

ELASTIC_IP= Elasticsearch ip yazmamız gerekiyor.

summary.html şeklinde bir html file çıktısı verir.

Buna ek olarak eğer k6 ile testlerinizi slack e rapor şeklinde göndermek isterseniz. Javascript kodunuzda yine bir değişiklik ile yapabilirsiniz.

import http from 'k6/http';
import { check } from 'k6';
import { sleep } from 'k6';
import { htmlReport } from 'https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js';

// Read a token from the environment variables
const token = '<BOT_TOKEN>';

// Load the file as binary data in the init stage
const file = open('summary.html', 'b');

export default function () {
http.get('https://test.k6.io');
sleep(1);
}

// Initialize
export function sendSlackMessages(data, testCount) {
// Get the average values for each metric
let avgResponseTime = data.metrics.http_req_duration.values['avg'].toFixed(2);
let iterationsCount = data.metrics.http_reqs.values.count;
let p90ResponseTime = data.metrics.http_req_duration.values['p(90)'].toFixed(2);
let p95ResponseTime = data.metrics.http_req_duration.values['p(95)'].toFixed(2);
let failedRequests = data.metrics.http_req_failed.values.passes;
let testRunDuration = Math.floor((data.state['testRunDurationMs'] / 1000) % 60);

// Create the payload for the Slack message
const payload = {
channel: '<CHANNEL>',
attachments: [
{
color: '#9205a2',
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: '#*test passed in ' + testRunDuration + ' sec*',
},
},
{
type: 'section',
fields: [
{
type: 'mrkdwn',
text: '*http_req_duration Average:*\n :large_green_circle: ' + avgResponseTime + ' ms',
},
{
type: 'mrkdwn',
text: '*Iterations:*\n :large_green_circle: ' + iterationsCount,
},
{
type: 'mrkdwn',
text: '*90th Percentile:*\n :large_green_circle: ' + p90ResponseTime + ' ms',
},
{
type: 'mrkdwn',
text: '*95th Percentile:*\n :large_green_circle: ' + p95ResponseTime + ' ms',
},
{
type: 'mrkdwn',
text: '*Failed Requests:*\n :red_circle: ' + failedRequests,
},
],
},
{
type: 'divider',
},
{
type: 'context',
elements: [
{
type: 'image',
image_url: 'https://raw.githubusercontent.com/k6io/awesome-k6/master/assets/bert.png',
alt_text: 'k6 croc',
},
{
type: 'mrkdwn',
text: '*K6* has approved this message.',
},
],
},
],
},
],
};

// Send a message to Slack
const res = http.post(
'https://slack.com/api/chat.postMessage',
JSON.stringify(payload),
{
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + token,
},
}
);

// Check the response
check(res, {
'is status 200': (r) => r.status === 200,
});
// console.log(totalIterations);
}

export function sendReportToSlack(reportData) {
const url = 'https://slack.com/api/files.upload';
const data = {
channels: '<CHANNEL_ID>',
initial_comment: 'Test summary report',
};

const params = {
headers: {
Authorization: `Bearer ${token}`,
},
};

// Prepare the payload
const payload = {
content: reportData,
channels: data.channels,
initial_comment: data.initial_comment,
filetype: 'html',
filename: 'summary.html',
};

const res = http.post(url, payload, params);

// Check the response
check(res, {
'is status 200': (r) => r.status === 200,
});

console.log('Report upload response: ', res.body);
}

export function handleSummary(data) {
const reportData = htmlReport(data);

sendSlackMessages(data);
sendReportToSlack(reportData);

return {
'summary.html': reportData,
};
}

ELASTIC_PASSWORD= Elasticsearch passwordu yazmamız gerekiyor.

ELASTIC_IP= Elasticsearch ip yazmamız gerekiyor

CHANNEL= #monitor-rapor gibi bir channel vermemiz gerekiyor.

BOT_TOKEN= xoxb- cinsiden slack bot token giriyoruz.

CHANNEL_ID= girdiğimiz #monitor-rapor slack idsi atamamız gerekiyor.

Bu şekilde başlattığınız testlerin raporlarını slack ile alabilir sonuçlarını rahat şekilde görebilirsiniz.

Okuduğunuz için teşekkürler :))

Kaynaklar;

--

--

Serdarcan Büyükdereli
Arabam Labs

Linux ,Bulut sistemleri hayranı paylaşımcı bir Jedi . Docker, Kubernetes , Linux …