Go 언어로 Locust 성능테스트 작성하기

smpl published on
2 min, 390 words

Locust는 파이썬으로 작성된 성능테스트 툴이다. 성능테스트를 위한 코드 역시 파이썬으로 작성하는 것이 일반적인데, 혹시 Go로 Locust를 위한 성능테스트를 작성할 수 있을지 알아보다가, Boomer라는 패키지가 사용 가능함을 알게 되어 방법을 정리해둔다.

요약을 하면, Locust를 분산 모드의 master로 실행하고, Boomer 패키지를 이용해 작성된 프로그램이 그 slave agent가 되어 동작하는 방식이다.

Locust master 설치 및 실행하기

Locust master는 Python과 Poetry 패키지 관리자를 이용해 설치하고 실행하도록 한다.

먼저 적당한 디렉토리에 Poetry 프로젝트를 생성하고, 필요한 의존성들을 설치해준다. 아쉽게도 Boomer는 Locust 최신버전과 호환되지 않으며, Locust 1.6.0 버전과 호환되므로, Locust 또한 해당 버전을 준비해야 하며, Locust의 간접 의존 패키지들 또한 호환되는 버전으로 선택해서 설치해주어야 한다. 게다가 파이썬 최신버전이 지원되고 있지 않으므로, 3.8 버전을 사용하도록 설정해준다. 아래는 그 과정이다.

# 프로젝트 생성
$ poetry init
$ cd <projectname>

# python 3.8 사용
poetry env use 3.8

# 의존성 설치
$ poetry add locust@1.6.0 jinja2@2.10.1 markupsafe@2.0 itsdangerous@0.24 werkzeug@1.0.1

Locust master를 실행할 때, Locustfile이라는 테스트 소스파일을 요구하는데, 여기서는 Go로 테스트를 작성할 것이므로, 임의의 더미 파일을 만들어서 넘겨준다.

#!/usr/bin/env python3
# -*- coding: utf8 -*-

# dummy.py

from locust import User, task

class Dummy(User):
    @task
    def dummy(self):
        pass

이제 Locust master를 실행한다.

poetry run locust --master -f dummy.py

Locust master의 대시보드는 콘솔에도 출력되듯, localhost의 8089 포트로 접속하면 된다.

Boomer로 테스트 작성하기

Boomer v1.6.0은 현재 Task와 TaskSet을 지원한다. boomer github의 examples 디렉토리에 많은 예제 코드들이 있으므로 이를 참고하여 작성하면 된다.

만일 테스트할 API가 한 개가 아니고, API의 정해진 호출 순서가 있으면서 API 별로도 성능 측정이 필요할 경우, 아래 예시와 같이 하나의 Task 안에서 name을 구분하여 여러번 Record를 호출하는 방법으로 작성할 수 있다.

아래는 실제 API 호출 코드는 아니지만, 이러한 방법을 보이기 위한 예시이다.

먼저 boomer 의존성을 갖는 Go 프로젝트 모듈을 생성한다.

$ go mod init <packagename>

# 의존성을 설치해준다.
$ go get github.com/myzhan/boomer@v1.6.0

그리고 프로그램 main을 작성해준다.

package main

import (
	"time"

	"github.com/myzhan/boomer"
)

func foo() {
	start := time.Now()
	time.Sleep(100 * time.Millisecond)
	elapsed := time.Since(start).Nanoseconds() / int64(time.Millisecond)
	boomer.RecordSuccess("tcp", "foo1", elapsed, 100)

	start2 := time.Now()
	time.Sleep(150 * time.Millisecond)
	elapsed2 := time.Since(start2).Nanoseconds() / int64(time.Millisecond)
	boomer.RecordFailure("tcp", "foo2", elapsed2, "justerror")
}

func main() {
	tfoo := &boomer.Task{
		Weight: 100,
		Fn:     foo,
		Name:   "foo",
	}

	boomer.Run(tfoo)
}

작성된 코드를 일반적인 Go 프로그램처럼 빌드하고 실행하면, 스스로 로컬의 Locust master에 접속하여 slave agent로 동작하며, 대시보드에서 테스트를 작동시킬 수 있다.

만일 Locust master가 로컬 머신에 있지 않다면, slave agent 프로그램의 실행 인자에 Locust master의 주소와 포트 등을 지정해줄 수 있다.