Locust 와 ecs 를 이용한 부하테스트

문석·2022년 8월 6일
1

내가 만든 서버는 몇 명의 유저를 수용할 수 있을까? 어느 정도 부하를 견딜 수 있을까? 이런 의문점을 해소하기 위해서는 부하테스트가 필요해요. 시스템에 부하를 가하여 어느 정도의 RPS 를 서버가 견딜 수 있을지 테스트 결과 데이터를 기반으로 예측하는 것이 목표가 되는 것이죠. 그래서 이번엔 Locust, ECS(docker, fargate) 를 이용한 부하테스트를 해보려해요.

ECS를 사용하기 전에는 Locust 를 커스텀하여 MacOS, Window 실행가능한 파일로 만들고 여러대의 로컬 머신에서 부하를 주는 방법을 사용했지만 부하테스트 수정 시에 다시 모든 로컬 머신에 실행파일을 설치해주어야 하는 단점이 있었어요

그전에 Locust 와 ECS 를 간략히 짚고 넘어갈게요.

Locust 는 python 으로 부하 스크립트를 작성할 수 있는 부하테스트 툴이에요.
Locust Homepage
위 이미지에서 알 수 있듯이 User의 '행동' 을 코드로 작성할 수 있고, 분산 및 확장 가능한 테스트가 가능해요. 자세한 내용은 문서 를 확인해주세요!

ECS 는 AWS 에서 제공하는 컨테이너 호스팅 서비스에요. EC2 와 차이가 뭐야? 라고 생각할 수 있는데 포함관계에 있다는 표현이 맞을 것 같아요. EC2 에서 ECS 를 운용할 수 있으니 EC2 는 ECS 가 제공하는 모든 기능을 이용할 수 있는 셈이죠. 부하머신에는 많은 기능이 필요하지 않고 Docker 를 사용하여 보다 간편하고 확장, 축소를 쉽게 하기위해 ECS를 선택했어요.

먼저 locust 스크립트를 작성해요. 스크립트 작성 방법은 이 포스트에서 다루지는 않을게요. 그 다음 작성한 스크립트를 ECS 에서 운용하기 위해 Docker 이미지를 빌드하고 DockerHub 에 업로드해야해요. 기본 이미지는 Locust DockerHub 을 사용했어요.

DockerFile

FROM locustio/locust
WORKDIR $PWD
ADD locustfile.py ./
docker build -t moonseoklee/locust .
docker push moonseoklee/locust

이제 ECS 환경을 구축해야해요. 로컬머신에 aws cli 가 설치된 것을 전제로 하고 최초에 ecs-cluster 를 생성하는 부분은 넘어가겠습니다.

먼저 ECS 에서 운용할 task 정의를 생성해줘요. json 형식으로 파일을 생성해요. 분산 부하 테스트를 위해서는 Locust 의 분산부하테스트에서 두 종류의 서버 (master, worker) 가 필요하기 때문에 두 개의 task definition 을 생성할게요.

task-definition.json

{
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "inferenceAccelerators": [],
  "containerDefinitions": [
    {
      "name": "locust-task",
      "image": "moonseoklee/locust",
      "portMappings": [
        {
          "containerPort": 8089,
          "protocol": "tcp"
        }
      ],

      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/locust-task",
          "awslogs-region": "ap-northeast-2",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ],
  "networkMode": "awsvpc",
  "memory": "2048",
  "cpu": "1024",
  "executionRoleArn": "ecsTaskExecutionRole",
  "family": "locust-task",
  "taskRoleArn": ""
}

task-definition-master.json

{
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "inferenceAccelerators": [],
  "containerDefinitions": [
    {
      "name": "locust-task-master",
      "image": "moonseoklee/locust",
      "portMappings": [
        {
          "containerPort": 8089,
          "protocol": "tcp"
        },
{
                    "containerPort": 5557,
                    "protocol": "tcp"
                },
                {
                    "containerPort": 5558,
                    "protocol": "tcp"
                }
      ],
      "environment": [ # 환경변수를 통해 master locust node 를 가동시키는 방법은 최신 locust 버전에서 지원되지 않아요.
        {
          "name": "LOCUST_MODE",
          "value": "master"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/locust-task",
          "awslogs-region": "ap-northeast-2",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ],
  "networkMode": "awsvpc",
  "memory": "2048",
  "cpu": "1024",
  "executionRoleArn": "ecsTaskExecutionRole",
  "family": "locust-task-master",
  "taskRoleArn": "",
}

이제 생성한 task-definition 을 aws 에 등록해줘요.

aws ecs register-task-definition --cli-input-json file://./task-definition.json
aws ecs register-task-definition --cli-input-json file://./task-definition-master.json

그리고 ecs 운용을 위해 ecs-cli 를 설치해 줄게요.

sudo curl -o /usr/local/bin/ecs-cli https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-darwin-amd64-latest

ecs-cli 를 이용해 service 와 task 를 생성해줄게요. 저는 ecs task 를 locust master 머신으로, service 를 locust worker 머신으로 사용했어요. ecs service 로 생성한 task 는 ecs ui console 에서 숫자를 지정하여 쉽게 확장할 수 있고 master 서버는 scalable 할 필요가 없기 때문이에요.

먼저 master task 를 기동시켜 줄게요. master 를 먼저 기동하는 이유는 worker 노드는 master ip 가 필요하기 때문이고 이를 위해서 master 노드를 먼저 생성 후에 master 의 public ip 를 가져올게요. 먼저 docker-compose 파일을 작성해줘요.

version: "3"
services:
  locust:
    ports:
     - "8089:8089"
     - "5557:5557"
    command: "--master"
    image: "moonseoklee/locust:latest"
    ulimits:
      nofile:
        soft: 100000
        hard: 100000
    logging:
      driver: awslogs
      options:
        awslogs-group: /ecs/locust-task
        awslogs-region: ap-northeast-2
        awslogs-stream-prefix: ecs

간단한 docker-compose.yml 인데 ulimits 가 추가되었어요. 이는 ubuntu 에서 일정 수 이상의 connection 이 file open 하는 행위를 제한하기 때문인데 부하머신에서 ubuntu 의 기본 제한 수치는 꽤 낮아 OsError 가 발생할 수 있기 때문이에요. 실제로 저 수치를 조정하지 않고 부하 스크립트를 시작하면 too many open files 라는 os error 를 볼 수 있어요.
이제 master task 를 시작할게요

ecs-cli compose --project-name locust up --create-log-groups --cluster-config locust

그 다음으로 worker service, task 를 시작해야하는데 그전에 docker-compose 에 생성된 master task 의 ip 를 기입하고 command를 조금 수정해주겠습니다.

version: "3"
services:
  locust:
    ports:
     - "8089:8089"
     - "5557:5557"
    command: "--worker --master-host=1.2.3.4"
    image: "moonseoklee/locust:latest"
    ulimits:
      nofile:
        soft: 100000
        hard: 100000
    logging:
      driver: awslogs
      options:
        awslogs-group: /ecs/locust-task
        awslogs-region: ap-northeast-2
        awslogs-stream-prefix: ecs

ecs-cli compose --project-name locust service up --create-log-groups --cluster-config locust

이제 master 서버로 접속해서 locust 대시보드를 확인해볼게요. 별도의 도메인을 할당하지 않았다면 위에 적었던 {master_public_ip}:8089 로 접속해주세요!

그럼 위와 같은 화면이 나타나게 되는데 ECS에 설정한 service 의 scale 수와 우측 상단의 worker 수치가 동일하면 성공이에요! 참고로 scale 수는 ecs-cli 혹은 aws console 에서도 변경할 수 있어요.

테스트가 끝나면 locust dashboard 를 통해 결과를 확인하고 dashboard 를 html 파일로 저장할 수도 있어요. locust dashboard 뿐만 아니라 부하 대상이 되는 서버들의 시스템 수치도 당연히 확인해야합니다. 저같은 경우에는 웹서버가 구축된 서버는 확장이 용이하여 해당 서버들을 확장하며 스토리지 엔진들이 어느정도 커버가 되나까지 테스트 할 수 있었습니다.

2개의 댓글

comment-user-thumbnail
2023년 3월 21일

중간에 만든 task-definition을 fargate를 생성할 때 사용해야 되는거 같은데 ecs-cli compose만 하면 등록 해놓은 task-definition을 사용을 못합니다. 등록한 task-definition을 이용해서 생성하려면 어떻게 해야되나요?

1개의 답글