4 min read

Locust둜 λΆ€ν•˜ ν…ŒμŠ€νŠΈν•˜κΈ°

Table of Contents

Locust

ν˜„μž¬ μ§„ν–‰ν•˜κ³  μžˆλŠ” ν”„λ‘œμ νŠΈμ—μ„œ 졜근 λΆ€ν•˜ ν…ŒμŠ€νŠΈλ₯Ό λ‹΄λ‹Ήν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•œ λ„κ΅¬λŠ” μ—¬λŸ¬ κ°€μ§€κ°€ μžˆμ§€λ§Œ μ €ν¬λŠ” λΉ λ₯΄κ³  κ°„λ‹¨ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλŠ” Locustλ₯Ό μ‚¬μš©ν•˜κΈ°λ‘œ ν–ˆμŠ΅λ‹ˆλ‹€.

생각보닀 Locust μ‚¬μš© 사둀가 λ§Žμ§€ μ•Šμ•„μ„œ, 이 κ³Όμ •μ—μ„œ κ²ͺμ—ˆλ˜ κ³Όμ •λ“€κ³Ό λ¬Έμ œλ“€μ„ 정리해 보렀고 ν•©λ‹ˆλ‹€.

κ°„λ‹¨ν•œ μ‚¬μš©λ²•

LocustλŠ” Python μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆλ‹€λ©΄ κ°„λ‹¨ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Locustμ—μ„œ μ€‘μš”ν•œ νŒŒμΌμ€ 크게 2κ°€μ§€μž…λ‹ˆλ‹€.

  1. locust.conf : ν…ŒμŠ€νŠΈ ν™˜κ²½μ— λŒ€ν•œ μ„€μ • νŒŒμΌμž…λ‹ˆλ‹€.
  2. locustfile.py : ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” νŒŒμΌμž…λ‹ˆλ‹€.

λ¨Όμ € locust.conf νŒŒμΌμ€ λ‹€μŒκ³Ό 같은 ν˜•νƒœλ‘œ μž‘μ„±ν•©λ‹ˆλ‹€.
(주석은 μ‹€μ œ μ‚¬μš©ν•  λ•ŒλŠ” μ§€μš°λ©΄ λ©λ‹ˆλ‹€.)

locustfile = locustfile.py           # μ‹€ν–‰ν•  파일
headless = true                      # ν—€λ“œλ¦¬μŠ€ λͺ¨λ“œ μ‚¬μš© (μ›Ή μΈν„°νŽ˜μ΄μŠ€ μ—†μŒ)
expect-workers = 10                  # μ›Œμ»€ 수
users = 10                           # μ΅œλŒ€ λ™μ‹œ μ‚¬μš©μž 수
spawn-rate = 2                       # μ΄ˆλ‹Ή μƒμ„±λ˜λŠ” μ‚¬μš©μž 수
run-time = 5m                        # ν…ŒμŠ€νŠΈ μ‹€ν–‰ μ‹œκ°„
loglevel=INFO

# html = "output/sample.html"
# csv = "output/sample.csv"
# logfile = "output/sample.log"

host = "https://www.example.com"
# host = "http://app.sample-namespace.svc.cluster.local:8000"

λ‹€λ₯Έ 섀정은 상황에 따라 자유둭게 λ³€κ²½ν•˜λ©΄ 되고, μ€‘μš”ν•œ 뢀뢄은 host λΆ€λΆ„μž…λ‹ˆλ‹€.
이 λΆ€λΆ„μ—λŠ” ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•  μ„œλ²„μ˜ URL을 μž…λ ₯ν•©λ‹ˆλ‹€.

μ™ΈλΆ€μ—μ„œ μ„œλ²„μ— μ ‘κ·Όν•  수 μžˆλŠ” 상황이라면, μ ‘κ·Όν•  수 μžˆλŠ” URL을 μž…λ ₯ν•˜λ©΄ λ©λ‹ˆλ‹€.
λ§Œμ•½ κ·Έλ ‡μ§€ μ•Šλ‹€λ©΄ λ‚΄λΆ€μ—μ„œ μ ‘κ·Ό κ°€λŠ₯ν•œ 값을 μ„€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.

λ‹€μŒμœΌλ‘œ, locustfile.py νŒŒμΌμ—λŠ” ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€.

from locust import HttpUser, between, task

class TestApi(HttpUser):
    wait_time = between(1, 2)

    @task
    def test_api(self):
        self.client.get("/hello")

μ•„μ£Ό κ°„λ‹¨ν•˜κ²Œ μž‘μ„±ν•œ μ˜ˆμ‹œμž…λ‹ˆλ‹€. μ§€κΈˆμ€ λ‹¨μˆœ GET μš”μ²­μ΄μ§€λ§Œ, νŒŒλΌλ―Έν„°λ₯Ό μΆ”κ°€ν•˜κ±°λ‚˜ νŒŒμΌμ„ μ—…λ‘œλ“œν•˜κ³ , μΆ”κ°€ λ‘œμ§μ„ μž‘μ„±ν•˜κ±°λ‚˜ 둜그λ₯Ό μΆ”κ°€ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

wait_time 뢀뢄은 μ‚¬μš©μžκ°€ μš”μ²­μ„ λ³΄λ‚΄λŠ” μ‹œκ°„ 간격을 μ˜λ―Έν•©λ‹ˆλ‹€.
μ˜ˆμ‹œμ—μ„œλŠ” 1~2초 μ‚¬μ΄μ˜ λ¬΄μž‘μœ„ 간격을 가지도둝 μ„€μ •ν•œ κ²ƒμž…λ‹ˆλ‹€.

λΆ€ν•˜ ν…ŒμŠ€νŠΈμ—μ„œ κ²ͺ은 λ¬Έμ œλ“€

μœ„μ—μ„œ μž‘μ„±ν•œ κ²ƒλ§Œ 보면 Locust μ‚¬μš©λ²•μ€ κ°„λ‹¨ν•©λ‹ˆλ‹€.
ν•˜μ§€λ§Œ μ‹€μ œλ‘œ μ μš©ν•  λ•ŒλŠ” λͺ‡ κ°€μ§€ λ¬Έμ œκ°€ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

λ‚΄λΆ€ νμ‡„λ§μ—μ„œ ν…ŒμŠ€νŠΈν•˜κΈ°

μ œκ°€ ν…ŒμŠ€νŠΈν•΄μ•Ό ν•˜λŠ” APIλŠ” 격리된 Kubernetes ν™˜κ²½μ— κ΅¬μ„±λ˜μ–΄ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
λ‹Ήμ—°νžˆ λ‚΄λΆ€λ§μ—μ„œλ§Œ API에 μ ‘κ·Όν•  수 μžˆμ—ˆκ³ , λ˜ν•œ ν΄λŸ¬μŠ€ν„° μ™ΈλΆ€μ—μ„œλŠ” 개발 ν™˜κ²½μ„ ꡬ성할 수 μ—†μ—ˆμŠ΅λ‹ˆλ‹€.

이 μ‘°κ±΄μ—μ„œ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•˜κΈ° μœ„ν•΄ Locust ν™˜κ²½ 전체λ₯Ό μ΄λ―Έμ§€λ‘œ λ§Œλ“€κ³ , κ·Έ 이미지λ₯Ό Pod둜 배포해 ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν–ˆμŠ΅λ‹ˆλ‹€. μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€μ—μ„œλŠ” Kubernetes μ„œλΉ„μŠ€μ™€ 포트λ₯Ό 톡해 호좜이 κ°€λŠ₯ν–ˆκ³ , κ·Έ μ™Έμ—λŠ” λΉ„μŠ·ν•œ λ°©μ‹μœΌλ‘œ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν–ˆμŠ΅λ‹ˆλ‹€.

μ›ν•˜λŠ” κ²°κ³Όλ₯Ό 파일둜 μ €μž₯ν•˜κΈ°

LocustλŠ” 기본적으둜 CSV와 HTML ν˜•μ‹μœΌλ‘œ κ²°κ³Όλ₯Ό μ €μž₯ν•©λ‹ˆλ‹€.
ν•˜μ§€λ§Œ 이 νŒŒμΌλ“€μ€ 응닡 μ‹œκ°„κ³Ό 성곡λ₯  μ •λ„λ§Œ ν‘œμ‹œν•˜κ³  있고, μΆ”κ°€λ‘œ μ›ν•˜λŠ” 정보가 μžˆλ‹€λ©΄ μΆ”κ°€ μž‘μ—…μ΄ ν•„μš”ν•©λ‹ˆλ‹€. λ˜ν•œ, 이 ν…ŒμŠ€νŠΈ νŒŒμΌμ„ μž…λ§›λŒ€λ‘œ μˆ˜μ •ν•  수 μžˆλŠ” 방법이 λ§ˆλ•…μΉ˜ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

Locust κΈ°λ³Έ κ²°κ³Ό 파일 μ˜ˆμ‹œ

μ—¬λŸ¬ 방면으둜 μ‘°μ‚¬ν•œ κ²°κ³Ό, Locust의 Event hooksλ₯Ό μ‚¬μš©ν•˜λ©΄ κ°€λŠ₯ν•˜λ‹€λŠ” 것을 μ•Œκ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€.
λ‹€λ§Œ 곡식 μ˜ˆμ œμ™€ 달리 μ‹€μ œλ‘œλŠ” CSV νŒŒμΌμ„ λ”°λ‘œ μ €μž₯ν•΄μ•Ό ν–ˆκ³ , μ€‘κ°„κ°’μ΄λ‚˜ λΆ„μœ„μˆ˜ λ“±μ˜ 정보도 계산해야 ν–ˆμŠ΅λ‹ˆλ‹€.

κ·Έλž˜μ„œ μ „μ—­μœΌλ‘œ 리슀트 λ³€μˆ˜λ₯Ό μ„ μ–Έν•˜κ³ , μš”μ²­μ΄ μ™„λ£Œλ  λ•Œλ§ˆλ‹€ 데이터λ₯Ό μΆ”κ°€ν•œ λ‹€μŒ ν…ŒμŠ€νŠΈ μ’…λ£Œ μ΄λ²€νŠΈμ—μ„œ μ΅œμ’… νŒŒμΌμ„ μ €μž₯ν•˜λŠ” λ°©μ‹μœΌλ‘œ κ΅¬ν˜„ν–ˆμŠ΅λ‹ˆλ‹€.
곡식 μ˜ˆμ œμ—μ„œλ„ μ „μ—­ λ³€μˆ˜λ₯Ό μ‚¬μš©ν–ˆκ³ , 이 μ •λ„μ˜ 둜직이 λΆ€ν•˜ ν…ŒμŠ€νŠΈμ— 큰 영ν–₯을 λ―ΈμΉ˜μ§€λŠ” μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

μ˜ˆμ‹œ μ½”λ“œλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

from typing import Generator
from locust import HttpUser, task, between, events
from locust.runners import MasterRunner
from datetime import datetime
import logging

# import csv
# import os
# import numpy as np

API_KEY = "------"

stats = []

class TestApi(HttpUser):
    wait_time = between(1, 2)

    @task
    def test_api(self):
        data = {
            # ...
        }
        headers = {"content-type": "application/json", "API-KEY": API_KEY}
        res = self.client.post(
            "/foo/bar",
            json=data,
            headers=headers
        )
        # Add the data to the stats

@events.test_stop.add_listener
def on_test_stop(environment, **kwargs):
    if not isinstance(environment.runner, MasterRunner):
        # CSV file processing
        logging.info(f"ν…ŒμŠ€νŠΈκ°€ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
    else:
        logging.info("Cleaning up test data")

κ·Έ μ™Έ

  • LocustλŠ” 슀트리밍 응닡을 κ³΅μ‹μ μœΌλ‘œ μ§€μ›ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 이슈λ₯Ό 보면 ν΄λΌμ΄μ–ΈνŠΈλ₯Ό 직접 μž‘μ„±ν•˜λΌκ³  λ˜μ–΄ μžˆλŠ”λ°, μ €μ˜ 경우 일반 API 호좜둜 해결을 ν–ˆμŠ΅λ‹ˆλ‹€. 슀트리밍 ν…ŒμŠ€νŠΈκ°€ ν•„μš”ν•˜λ‹€λ©΄, λ‹€λ₯Έ 도ꡬλ₯Ό 생각해야 ν•  μˆ˜λ„ μžˆμ„ 것 κ°™μŠ΅λ‹ˆλ‹€.
  • λ‹Ήμ—°ν•˜μ§€λ§Œ Locust λͺ…λ Ήμ–΄λ₯Ό ν†΅ν•΄μ„œλ„ 섀정값을 μ§€μ •ν•΄ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ €μ˜ κ²½μš°μ—λ„ μ—¬λŸ¬ μ‘°κ±΄μ—μ„œ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•΄μ•Ό ν–ˆλŠ”λ°, λ‚΄λΆ€λ§μ—μ„œλŠ” μ½”λ“œ μž‘μ—…μ΄ 쉽지 μ•ŠκΈ° λ•Œλ¬Έμ— 미리 슀크립트λ₯Ό μž‘μ„±ν•΄ 두고 λͺ…λ Ήμ–΄λ₯Ό 톡해 ν…ŒμŠ€νŠΈ 쑰건을 μ§€μ •ν•˜λŠ” λ°©μ‹μœΌλ‘œ μž‘μ—…μ„ μ§„ν–‰ν–ˆμŠ΅λ‹ˆλ‹€.

마치며

λŒμ•„λ³΄λ©΄ μ§€κΈˆκΉŒμ§€ ν…ŒμŠ€νŠΈλ‹€μš΄ ν…ŒμŠ€νŠΈλ₯Ό ν•΄ λ³Έ 적이 λ§Žμ§€ μ•Šμ•˜λ˜ 것 κ°™μŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ 더 μ œλŒ€λ‘œ ν•˜κ³  싢은 λ§ˆμŒλ„ μžˆμ—ˆκ³ μš”.
ν…ŒμŠ€νŠΈ 방법을 μ •λ¦½ν•˜κΈ° μœ„ν•΄μ„œ 저도 λ§Žμ€ μ‹œν–‰μ°©μ˜€λ₯Ό κ±°μ³€μ§€λ§Œ, API μžμ²΄μ—μ„œ μ‹œκ°„μ΄λ‚˜ 데이터λ₯Ό μ–»κΈ° μœ„ν•œ 섀정도 ν•„μš”ν–ˆκ³ , νŒ€μ›λ“€μ΄ κ΄€λ ¨ μž‘μ—…μ„ 지원해 μ£Όμ—ˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ 방법도 λ°©λ²•μ΄μ§€λ§Œ, λ‹€λ₯Έ ν”„λ‘œμ νŠΈμ—μ„œλ„ λΉ„μŠ·ν•œ λ°©λ²•μœΌλ‘œ λΆ€ν•˜ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•  수 μžˆλ„λ‘ 기쀀을 μ •ν•˜κ³ , μ •λ¦¬ν•˜λŠ” 것이 쉽지 μ•Šμ•˜λ˜ 것 κ°™μŠ΅λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈλ₯Ό μˆ˜ν–‰ν•˜λŠ” 과정이 μˆœνƒ„ν•˜μ§€λŠ” μ•Šμ•˜μ§€λ§Œ, κΈ°ν•œ 내에 잘 λ§ˆλ¬΄λ¦¬ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
μ•žμœΌλ‘œλ„ ν…ŒμŠ€νŠΈλ₯Ό 포함해 λ§Žμ€ μž‘μ—…μ„ μ§„ν–‰ν•˜κ²Œ 될 텐데, 이 κ³Όμ •μ—μ„œ μ •λˆλœ ν”„λ‘œμ„ΈμŠ€λ₯Ό λ§Œλ“€μ–΄ λ‚˜κ°ˆ 수 μžˆλ„λ‘ ν•˜λŠ” 것이 λͺ©ν‘œμž…λ‹ˆλ‹€.