티스토리 뷰

반응형

intro

  • 이번에 Kserve 로 서비스를 배포하면서 locust 테스트를 이용했는데, 좀더 잘 알고 싶어서, 해당 글을 작성하게 되었으나, 예상보다도 테스트라는 분야가 넓고 깊고 방대해서 감동받고 후일 스터디를 계획하게됐다는 이야기로 시작을 해보려합니다.. 아니 이전에 그라파나부터 공부해야겠네요

Why we test?

Testing Applications on the Web 이라는 책을 제가 하고 싶은 말을 너무 잘 정리해놓아주셔서 인용해왔습니다.

웹 애플리케이션의 장점은 여러 사용자가 동시에 시스템 리소스에 액세스 할 수 있다는 것이다.(동시에 서로 다른 서비스를 요청하고 다양한 기능에 액세스 할 수 있음). 다중사용자 지원(multiuser support)이 웹 애플리케이션의 성공을 판가름하는 핵심 요인이므로, 시스템이 사용자 액티비티가 최고조에 달하는 피크 기간 동안 주요 기능을 수행할 수 있는지를 신중히 평가해야 한다.

즉, 이렇게 피크타임이나, 언제든 우리가 안심하고 서비스를 운영하기 위해선 주요 기능을 수행하는지 테스트를 반드시 수행해야합니다.

테스트 종류와 부하테스트

테스트는 종류와 목적에 따라 다양합니다.

  • Load Testinging(부하테스트)
    • 부하 테스트는 일반적으로 프로그램에 동시에 액세스하는 여러 사용자를 시뮬레이션하여 소프트웨어 프로그램의 예상 사용을 모델링하는 방법을 의미합니다.
    • 사전 정의된 부하 수준으로 시스템 성능을 평가합니다. 부하 테스트는 정상 조건 또는 사전 정의된 조건 하에서 시스템이 다양한 프로그램 태스크 및 기능을 수행하는데 걸리는 시간을 측정합니다. 만약 태스크가 제한 시간 내에 실행되지 못하면 버그 보고서가 제출됩니다. 시스템 성능이 부하 요구사항(load requirements)을 충족하는지 확인하는 것이 부하 테스트의 목적입니다.
  • Stress Testing(스트레스 테스트)
    • 지정된 운영 한계(operational limits)를 넘도록 압박이 가해진 시스템 한계를 초과하는 폭발적인 피크 액티비티에 어떻게 반응하는지 평가합니다.
    • 과부하 조건에서 시스템 크래시가 발생하는지 또는 매끄럽게 복구되는지 확인하는 것이 스트레스 테스트의 주요 목표입니다. 스트레스 테스트는 시스템의 약한 고리가 드러날 때까지 시스템 리소스를 한계로 몰아 가도록 설계해야 합니다.
  • (Enduranace, Soak Testing)내구성 테스트, (Spike Testing)최고점 부하테스트 등이 있다.

테스트는 또한 다음과 같은 시스템 에러도 식별 할 수 있습니다.

  • 소프트웨어 실패 (예. 하드웨어 인터럽트)
  • 메모리 런타임 에러(메모리릭, 덮어 쓰기, 포인터 에러 등)
  • 데이터베이스 교착상태(deadlocks)
  • 멀티스레딩 문제(multithreading problems)

테스트 툴 종류

테스트 툴

  • jmeter :
    • 1998 년 부터 시작된 프로젝트로서 오랫동안 기능 강화를 지속해오고 있는 Java 기반의 부하 테스트 툴 입니다.. 실제 워크로드를 시뮬레이션 하기 위해 다양한 방법으로 커스터마이징이 가능합니다.
  • ngrinder
  • Goad, Vegeta, Tsung, Gatling
  • 이 외에도 다양한 항목을 aws사이트에서 확인할 수 있습니다.

그 중에 locust

  • https://locust.io/
  • 사실 다른 툴들을 딥하게 이용해본 적이 없기때문에 비교하기는 어려운 점이 있습니다.
  • 한 가지 확실한 점은 저같은 파이썬 유저에게 파이썬 기반이고, 사용자가 스크립트를 작성해 작동시키는 부하테스트툴인 locust가 아주 매력적이었습니다.
    • 실제로 탄생 배경에도 나오는 이야기인데,
      • ""대부분의 페이지에 사용자마다 다른 콘텐츠가 있는 동적 웹 사이트에 대해 실제 로드를 생성할 수 있는 기존 로드 테스트 도구가 없었고, 기존 도구에서는 투박한 인터페이스나 장황한 구성 파일을 사용하여 테스트를 선언했한 것과 달리, 구성 파일 설정이나 UI 대신 Python 코드를 사용하여 사용자의 동작을 정의할 수 있는 Python 프레임워크"가 Locust 의 탄생배경이라고 합니다.

locust 특징

locust를 이해하는데 도움이 될 것같아, 공식 도큐먼트에서 들고왔습니다.

  • 일반 Python으로 테스트 시나리오 작성

사용자가 루프를 실행하거나, 일부 조건부 동작을 수행하거나 일부 계산을 수행하도록 하려면 Python에서 제공하는 일반 프로그래밍 구조를 사용합니다.
Locust는 자체 greenlet(경량 프로세스/코루틴) 내에서 모든 사용자를 실행합니다.
이를 통해 콜백이나 다른 메커니즘을 사용하지 않고 일반(차단) Python 코드처럼 테스트를 작성할 수 있습니다.
시나리오가 "pure 파이썬"이기 때문에 일반 IDE를 사용할 수 있고 테스트를 일반 코드로 버전 제어할 수 있습니다(cf. XML 또는 바이너리 형식을 사용하는 다른 도구).

  • 분산 및 확장 가능 - 수십만 명의 동시 사용자 지원

Locust를 사용하면 여러 시스템에 분산된 부하 테스트를 쉽게 실행할 수 있습니다.
단일 프로세스가 수천 명의 동시 사용자를 처리할 수 있는 event-based입니다 .( gevent 사용)
지정된 하드웨어에서 초당 더 많은 요청을 수행할 수 있는 다른 도구가 있을 수 있지만 각 Locust 사용자의 오버헤드가 낮기 때문에 동시 작업 부하를 테스트하는 데 매우 적합합니다.

  • 웹 기반 UI

Locust에는 테스트 진행 상황을 실시간으로 보여주는 사용자 친화적인 웹 인터페이스가 있습니다.
테스트가 실행되는 동안 로드를 변경할 수도 있습니다. 또한 UI 없이 실행할 수 있어 CI/CD 테스트에 쉽게 사용할 수 있습니다.

  • 모든 시스템 테스트 가능

Locust는 주로 웹 사이트/서비스와 함께 작동하지만 거의 모든 시스템이나 프로토콜을 테스트하는 데 사용할 수 있습니다.
테스트하려는 항목에 대한 클라이언트를 작성하거나 커뮤니티에서 만든 항목을 탐색하면됩니다 .

  • 해킹 가능

Locust는 작고 매우 유연하며 앞으로도 계속 그렇게 할 것입니다.
원하는 데이터베이스 및 그래프 시스템으로 보고 데이터를 보내거나 REST API에 대한 호출을 래핑하여 시스템의 세부 사항을 처리하거나 완전히 사용자 정의 로드 패턴을 실행하려는 경우 막을 수 있는 것은 없습니다

what we test?

저희는 크게 1. 인프라 모니터링 지표, 2. 예상 사용자 동접자수 , 3. 예상 유저 시나리오, 4. 제한 조건 (제한 응답시간,예상응답코드 등)을 테스트 지표로 설정해야합니다.

인프라 모니터링 지표 && 대시보드 설정

  • 대시보드와 함께 실시간 리소스 지표를 확인해야합니다.
  • 기본적으로 cpu, memory 부터,tps(Transaction per Seconds), 응답시간 등의 지표를 세팅해야합니다.

monitoring dashboards

이미지 출처 : https://grafana.com/solutions/kubernetes/

테스트 지정

locust 를 통해 우리가 테스트 하고 싶어하는 플로우를 정해야합니다.

  • 최대 동접자 수를 산정합니다.
  • 사용자 시나리오를 정합니다.
    • 예를들어 주문-결제의 경우
    • 로그인을 하고 -> 장바구니에서 선택해서 -> 결제를 하고 -> 주문배송창에 가기까지라던지 등의 개발자가 테스트하고 싶은 흐름을 지정합니다.
  • 실제 서버 기준, 동접자수, 응답시간을 정합니다.

when we test?

  • 주로 테스트를 수행하는 시기는, (서비스 시작 전, 혹은 대규모 버전 업데이트) 배포 전이라고 볼 수 있는데요, 예상 tps 가 존재하고, 우리가 어느정도 자원을 투자하면, (cpu, memory, cache 등..) 충분히 우리가 예상 유저를 허용 가능한 자원을 줄지를 알기 위해 이용합니다.
  • 뿐만 아니라, 서비스 시작할때, 부하가 어느정도 발생하는지, 시작 이후에 동접자가 증가했을때, 리소스 현황에 문제가 생기지 않는지 체크해줘야합니다.

where we test?

  • 우리는 locust 설치를 아주 단순하게 pip install locust 처럼, python library 로 설치할 수 있습니다.
  • 부하테스트 도구의 성능을 위해서라면, docker 기반 혹은, k8s helm chat 구성또한 방법입니다.

How we test?

  • 저희는 "what we test" 목차에서 1. 인프라 모니터링 지표, 2. 예상 사용자 동접자수 , 3. 예상 유저 시나리오, 4. 제한 조건 (제한 응답시간,예상응답코드 등)을 정해야한다는 이야기를 나눴었는데요, 그것을 기반으로 파이썬으로 스크립트를 작성하면 됩니다.
  • 모든 이야기보다, 주석 잘달린 코드가 나을 것같아, 해당 파트 일부는 코드로 대체하겠습니다.
  • https://docs.locust.io/en/stable/writing-a-locustfile.html

1. 스크립트 작성

  1. 예상 유저 시나리오와 , 제한조건, 그리고, 예상 사용자 동접자 수를 이용해, locust 스크립트를 작성합니다.
from locust import HttpUser, task, constant_throughput
from json import JSONDecodeError
from locust import events

class RequestUser(HttpUser):  
  #  HttpUser : User who use  http request 
  # User : 사용자 클래스는 시스템에 대한 사용자/시나리오의 한 유형을 나타냅니다. 테스트 실행을 수행할 때 시뮬레이션할 동시 사용자 수를 지정하면 Locust가 사용자당 인스턴스를 생성합니다. 이러한 클래스/인스턴스에 원하는 속성을 추가할 수 있지만 Locust에 특별한 의미가 있는 속성이 있습니다.
    wait_time = constant_throughput(
        100)  #  number of task runs per second execution equal to the time specified by the task_runs_per_second argument  # 태스크를 돌리는 실행간격을 조절합니다. (tps 와 연관지어서 고려하시면 좋을 것같습니다. )
    # https://docs.locust.io/en/stable/api.html#locust.wait_time.constant_throughput
    @task
    def hello_world(self):
        self.client.get("/hello")
        self.client.get("/world")

    @tag('tag1', 'tag2')  # tag 개념으로, 내가 원하는 태그만 테스트를 수행할 수 있습니다.
    @task # 태스크를 등록하는 형태로 진행됨. 원하는 테스트 환경이 있다면,  task 작업을 추가해주면 됩니다.
    # @task 메서드는 locust 파일의 핵심입니다. 실행 중인 모든 사용자에 대해 Locust는 해당 메서드를 호출하는 greenlet(마이크로 스레드)을 생성합니다.
    def request1(self):
        q_num_req = 40
        with self.client.post("/pay", json={
     "order_list":["사과","배"],
          "card":"삼성",
          "card_num":"01202020"

        }, catch_response=True) as response:
            # https://docs.locust.io/en/stable/writing-a-locustfile.html#validating-responses

            """
<Validating responses> 
HTTP 응답 코드가 OK(<=400)이면 요청이 성공한 것으로 간주되지만 응답에 대한 추가 유효성 검사를 수행하는 것이 유용한 경우가 많습니다.
catch_response 인수, with -statement 및 response.failure () 호출을 사용하여 요청을 실패한 것으로 표시할 수 있습니다..
            """
            try:
                if response.json()["status"] != "success":
                    response.failure("Did not get expected value in 'status':'success'")
                if response.elapsed.total_seconds() > 0.4:
                    response.failure("Request took too long")
            except JSONDecodeError:
                response.failure("Response could not be decoded as JSON")
            except KeyError:
                response.failure("Response did not contain expected key 'sentiment'")
    @tag('tag1', 'tag3')
    @task(3) # 가중치를 줄 수 있습니다.  RequestUser 가 선언된 태스크중에 임의로 골라 run 시키는 중에, 확률을 3배 증가시키는 효과를 냅니다
    @task(3)
    def view_items(self):
        for item_id in range(10):
            self.client.get(f"/item?id={item_id}", name="/item")
            time.sleep(1)
    def on_start(self):
        self.client.post("/login", json={"username":"foo", "password":"bar"})
        """
        Additionally we’ve declared an on_start method. A method with this name will be called for each simulated user when they start. For more info see on_start and on_stop methods.
        """

2. 로커스트 ui 설정 및 모니터링

로커스트를 직접 실행하면, 해당 화면이 나오는데,

  1. num of users : 피크 동접자수
  2. spawn rate : 유저 스폰 시간간경
  3. host 를 지정해줍니다._images/webui-splash-screenshot.png
  4. 다음 스크린샷은 40명의 concurrent users를 사용하여 0.5 users/s의 증가 속도로 성능이 다소 저하된 서버에 대해 이 테스트를 실행할 때의 모습을 보여줍니다. 탭별로 차트들을 보여줍니다.

_images/webui-running-statistics.png

)

_images/total_requests_per_second.png

)

_images/number_of_users.png

)

_images/response_times.png

3. 그라파나 등의 인프라 모니터링

실제 부하테스트를 동작시키면서, 예상 tps 에서 cpu 상태가 문제가 되지는 않는지, 메모리릭은 없는지, 현재 리소스가 충분한지(혹은 더 필요한지) 등을 체크합니다.

monitoring dashboards


outro

  • 처음에는 Locust test 에 대해 이해해보자! 라는 마음으로 시작했다가, 생각보다 테스트의 넓은 세계를 보고 놀랐습니다.. 테스트라는 분야가 넓고 깊고 방대하네요, 다른 툴도 이용해보고 싶고, 아직 locust 의 모든 것을 알았다! 라기 보다는 이제 테스트를 내가 원하는 대로 진행할 수 있다 정도의 수준인 것같아, 좀더 분발해보겠습니다..

출처

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함