*경험 기반*

Django python 서버에는 api 개발 중 클라이언트에게 반환할 필요 없이 비동기로 처리하기 위한 기능이 구현되어있다. 

해당 기능이 뭔지, 어떻게 사용하는지는 여기선 중요하지 않으니 관련 글만 참조한다. 

https://realpython.com/asynchronous-tasks-with-django-and-celery/

 

Asynchronous Tasks With Django and Celery – Real Python

This tutorial shows how to integrate Celery and Django and create Periodic Tasks

realpython.com

 

백엔드 개발 중 해야할 작업을 나중으로 미루는 것은 성능 및 서비스 안전성에 긍정적인 효과를 줄 때가 많다. 마이크로서비스 패턴(MSA)의 구조 역시 이러한 철학을 바탕으로 되어있고, 메시징 큐(MQ)를 통한 비동기 구조로 채택하는 경우가 많다.

 

하지만 아주 작은 작업을 위해 별도의 서버와 프로젝트를 구성하지 못하는 경우도 있다. 그래서 기능 중 일부를 위와 같은 기능으로 비동기화하여 그 기능을 동일 서버에서 처리하도록 하는 경우가 있다. 그리고 이런 기능은 아마 당장은, 혹은 그냥저냥 잘 굴러갈 수 있다. 

 

잘 굴러가는데 왜 뻘글을 쓰고있냐면, 이게 장기적으로 매우 나쁜 선택지가 됐기 때문이다. 

 

발단은 서버의 성능 확인을 위한 부하 테스트 작업이었다. 

비동기로 등록된 작업의 스레드 작업 우선순위는 당장 서버로 진입하는 request보다 후순위로 밀리는 것이 당연하다. 이는 실제 테스트를 통해 확인하였다. 하지만 결국 예약된 비동기 작업 또한 처리가 되어야 하고, 서버 OS의 스케쥴링 규칙과 작업의 지연 시간 증가로 작업 실행 우선 순위는 점점 상승하게 된다. 이 순위가 당장 처리되어야 할 진입 request의 우선순위보다 밀리게 될 때 문제가 발생했다. 

 

0.00x~0.x 초 사이로 빠르게 처리되어야 할 reqeust의 작업이 대량의 비동기 작업들에 밀려 x초 단위로 지연이 발생한 것이다. 

 

쉬벌

 

서버의 부하가 많을 때 발생하는 조건부 지연이지만, 백엔드 개발이란게 원래 부하 받을 때 안전하게 돌아가라고 하는 개발인데 이게 고려가 안된 것이다. 

그리고 사실 이건 해결책을 내기도 힘든게 별도 비동기 처리용 서버를 당장 새로 만들 수도 없고, 그렇다고 이제와서 동기(synchronous) 처리를 하기에도 애매하다. 

 

그렇다. 이건 그냥 훗날 똥만 될 뿐이다. 

1. 사용하게 된 이유 

NGINX의 마이크로 캐싱을 통한 API 서버 성능 개선 작업을 진행하고 있다. 

이미 사용 중인 기능의 DB 스키마와 로직 개선에 대한 기능 테스트야 하던 대로 진행 했지만, 기존 방식으로는 확인하기 힘든 테스트가 생겨버렸다. 

NGINX 마이크로 캐싱을 통한 API 대량 요청에 대한 기대 성능을 측정해봐야 하는데, 기존의 부하 테스트는 테스트 할 때마다 외부에서 호출해주기 위한 배치 프로세스를 별도로 개발/배포하여 사용해야 했다.

개발하고 정리하는 것만 해도 시간 모자른데, 부하 테스트용 코드를 또 개발해야 하는 것은 너무 비효율적이었다. 

그러던 중 팀장님의 권유로 nGrinder에 대해 알게 되어 적용해보기로 했다.

 

2. nGrinder

부하 테스트용 오픈 소스 grinder를 Naver에서 가다듬은 버전이다. 

Web GUI를 제공하며 가상 유저 수와 API 호출 횟수, 그리고 테스트 결과에 대한 시각화를 제공한다. 

그런데 생각보다 제대로 정리된 글이 없다. 구버전 대상 설명이거나, 끝까지 설명하지 않은 경우가 많아서 정리한다. 

 

3. 구성

GUI를 제공하는 nGrinder-Controller

테스트 대상 API를 호출하여 부하를 발생 시키는 nGrinder-Agent

테스트 대상 API가 실행되는 Target 서버의 실시간 리소스(CPU, Memory, Network) 정보를 수집하는 nGrinder-Monitor

세 개로 구성되어있다. 

 

사진 1. nGrinder 시스템 아키텍쳐

4. nGrinder 실행 파일 

github.com/naver/ngrinder/tags

 

naver/ngrinder

enterprise level performance testing solution. Contribute to naver/ngrinder development by creating an account on GitHub.

github.com

nGrinder 또한 github에 공개되어 있고, 단독 실행 가능한 war 파일로 버전 별로 공개되어있다. 

 

5. nGrinder 설정 

5-1. nGrinder-Controller

제일 먼저 실행할 것은 nGrinder-Controller이다.

* Controller에서 제공하는 Web 페이지에서 Controller와 버전이 맞는 Agent와 Monitor의 실행 파일을 다운받을 수 있고, Agent의 경우 Controller가 실행되어있지 않다면 실행 되지 않는다.

 

4번의 링크에서 원하는 버전의 ngrinder-controller-(버전).war 파일을 다운받아서 실행할 서버로 업로드를 해준다. 

(아니면 wget으로 해당 서버에서 직접 다운로드를 받아줘도 된다. - 다운로드 링크 우클릭해서 주소 복사 -)

 

* 원하는 서버의 원하는 경로에 다운로드를 끝냈다면 실행만 시켜주면 된다. 별도의 톰캣 설정 없이 단독으로 실행 가능하도록 빌드 되어있다. 

사진 2. 4번의 링크를 들어가면 나오는 github 웹페이지
사진 3. 2번 사진에서 원하는 버전을 골라 들어가면 나오는 웹 페이지

사진 3의 하단에 ngrinder-controller.war 파일을 다운받으면 된다. 

 

사진 4. 해당 war파일을 다운받아 원하는 경로에 위치시켰다.

실행 파일을 받았으니 실행을 시켜보자. 

 

* 실행 스크립트는 아래와 같다. 

nohup java -XX:MaxPermSize=200m -jar ngrinder-controller-(버전).war --port (원하는 포트) &

예시) nohup java -XX:MaxPermSize=200m -jar ngrinder-controller-3.5.3.war --port 8080 &

 

사진 5. controller war 파일 실행 및 프로세스 확인
사진 6. Controller 화면이 나오는 것을 확인!

기본 계정은 

아이디 : admin

비밀번호 : admin

이다

그리고 이하 설명은 한국어 베이스로 진행할 것이기 때문에 언어는 한국어로 설정할 것이다. 

사진 7. 정상적으로 진행 됐다면, 이렇게 나온다. 아무코토 없다.

 

5-2. nGrinder-Agent

Controller가 준비됐으니, 부하 발생기인 Agent를 준비해보자.

Agent의 실행 파일은 Controller를 실행시키면 얻을 수 있다. 

사진 8. 우측 상단 계정을 (여기선 admin) 누르면, 여러 기능을 볼 수 있다.

사진 8을 따라 우측 상단의 계정을 클릭하면, 플로팅 메뉴가 나오는 것을 알 수 있다. 

해당 메뉴에서 "에이전트 다운로드"를 클릭하면, ngrinder-agent-(Controller의 IP).tar 압축 파일이 다운로드 되는 것을 알 수 있다. 

* 해당 압축 파일은 실행 가능한 자바 프로젝트의 압축 파일이기 때문에, 사용할 위치에서 압축만 풀어주면 된다. 

사진 9. Agent 파일을 원하는 위치에 놓고 압축을 풀어준다.
사진 10. 압축을 푼 후 보면 ngrinder-agent 폴더가 형성된 것을 확인할 수 있다. 
사진 11. 10번 사진에서 생성된 폴더에 들어가보면, 실행 파일이 들어가있는 lib 폴더와 리눅스/윈도우 용 스크립트 파일, 그리고 초기 설정용 __agent.conf파일이 있다.

사진 10과 11을 보면 압축을 풀었을 때 nGrinder-agent 폴더가 생성된 것과 폴더 하위 경로에 실행 스크립트/실행 파일/__agent.conf 파일이 있는 것을 확인할 수 있다. 

여기서 먼저 해야할 것은 __agent.conf 파일의 수정이다. 

사진 12. __agent.conf 파일 내부 내용

하단에 주석이 되어있는 것은 사용하지 않는다. (추가적인 기능이 있는 것이겠지만, 기본적으로 사용하지 않았다. 자료도 적고 테스트 진행에 있어서 필요하지 않았다.)

필요한 것은 상단의 agent.controller_host 이다. 

 

현재 작성하고 있는 예시는 시험용 예시이기 때문에 Controller와 Agent가 하나의 기기(노트북)에서 함께 실행하지만, 

본래의 목적 달성을 위해서는 Controller와 Agent는 다른 서버 머신에 있어야 한다. 이 때 실행되는 Agent가 Controller를 바라보게 되는데 그것을 위해 Controller_host, 즉 Controller의 IP와 Controller_port를 필요로 하게 된다. 

* Controller가 설정 수정 없이 디폴트로 수정되었다면, Web 접근용 포트 이외에 Agent 접근용 16001 포트와 부하 테스트 사용을 위한 12000~12009 포트가 함께 개방된다. 

 

지금 예시에서는 localhost로 실행되기 때문에 그냥 실행하겠다. 

실행 방법은 윈도우의 경우 run_agent.bat 혹은 run_agent_bg.bat을 실행하고, 리눅스의 경우 run_agent.sh 혹은 run_agent_bg.sh를 실행하면 된다. (_bg는 백그라운드로 실행하겠다는 의미이다. 실제 동작 확인을 하고 싶다면 run_agent를, 아니면 run_agent_bg를 실행시키면 된다.)

사진 13. nGrinder-agent의 실행 및 프로세스 확인

예시에서는 _bg파일로 스크립트를 실행하였고, Agent 자바 프로세스가 정상적으로 실행된 것을 확인할 수 있다. 

그럼 Controller에서 정상적으로 인식했는지 확인해보자

사진 14. 우측 상단 계정을 클릭 후 에이전트 관리

 

우측 상단 계정 클릭 후, 에이전트 관리로 들어가보자 

사진 15. Agent가 등록되어 있는 것을 확인

Agent의 정보가 등록된 것을 확인할 수 있다. 

주의) 내 경우 실무 적용 시 aws subnet 환경에서 해당 작업을 진행했는데, Controller의 외부 접근용 IP를 __agent.conf에 설정 후 실행 시 Connection refused 에러가 발생했었다. 정확한 이유는 모르겠지만 내부 IP로 재설정 후 실행했더니 해결되었다. 

 

여기까지만 해도 부하 테스트를 충분히 진행할 수 있기 때문에 보통 여기서 설명이 끊긴 경우가 대부분이었다. 

하지만 어림도 없지. 팀장님께선 리소스의 모니터링도 볼 수 있어야 한다고 하셨다. 

팀장님은 모니터가 좋다고 하셨어

 

5-3. nGrinder-Monitor

마지막 Monitor를 보자. 

Monitor는 nGrinder-architecture 구성 중에서 target server, 부하를 받을 서버에 설치되어야 한다. 

Agent에 의해 부하를 받는 동안에 부하 대상의 CPU, Memory, Network receive/send Queue를 시각화하여 함께 볼 수 있기 때문에 적용하여 손해보는 것은 단 하나도 없다. 

하지만 개인적으로 처음 설정할 때 가장 고통 받았던 것이 Monitor다. 

 

설정 이전에 이해를 돕기 위한 설명!

Agent는 Agent가 Controller를 보는 것이다. 그렇기에 Agent에 Controller를 등록했다. 

그렇다면 Monitor는 어떨까? 사진 1의 nGrinder architecture를 보면 알 수 있듯이, Controller가 Monitor를 본다고 되어있다. 

 

그럼 이제 Monitor 설치를 해보자 

사진 15. 사진 14와 똑같은 사진이다.

* 사진은 위에 올려보기 싫으니까 똑같은거 다시 올렸다. 

플로팅 메뉴에서 모니터 다운로드를 확인할 수 있다. 다운받자.

사진 16. Agent와 동일하게, 압축 파일을 받았다

이후 Agent와 동일하게 tar -xvf 명령어로 압축을 풀어서 생성되는 ngrinder-monitor 폴더로 들어가보자.

사진 17. Agent를 잘못 들어온 것이 아니다. Monitor 맞다

Agent와 거의 비슷해보이고 __agent.conf 파일이 있어 잘못들어왔나 싶겠지만, Monitor경로로 제대로 들어온 것이 맞다!

사진 18. Monitor의 __agent.conf 내용

수정하지 않은 __agent.conf의 내용이다. 

실행 모드는 monitor로 되어있는데, 이것은 Monitor가 원래 Agent와 동일한 프로젝트였던 것으로 추측한다. 

* 증거 없이 이렇게 추측하는 이유는, 구버전의 설정을 설명한 다른 글들에서 볼 때 Monitor의 __agent.conf 포맷이 사진 18과는 다르게 Agent의 그것과 포맷이 동일하기 때문이다. 뻘소리니 그냥 Monitor설정이구나 하고 넘어가자 

그 아래 주석 처리 되어있는 monitor.binding_ip는 리소스 모니터링을 할 대상 서버 머신의 IP이다. 주석처리가 되어있다면 디폴트로 Monitor가 실행되고 있는 서버 머신의 리소스를 보게된다. 

monitor.binding_port는 monitor가 실행될 때 개방하는, nGrinder-Controller가 nGrinder-Monitor로 접근하기 위한 port이다. nGrinder-Controller의 기본 설정으로 지정되어있기 때문에 수정은 하지 않겠다. 

 

그럼 실행을 해보자 

사진 19. Controller, Agent, Monitor 모두 실행 중인 것을 확인

여기까지 했으면 거의 다 된 것이다. 

 

마지막으로 남은 Controller에 Monitor를 등록하는 과정을 진행해보자.

사진 20. Controller 성능 테스트 페이지

Controller 상단의 "성능 테스트" 탭을 클릭하면, 위와 같은 페이지에 진입하게 된다. 

우측에 있는 "테스트 생성" 파랑색 버튼을 눌러보자

사진 21. 테스트 설정 페이지 

위와 같은 테스트 설정 페이지가 나오는데, 우리는 여기서 테스트를 하는 방법은 보지 않을 것이다. 

사실 작성은 하고 싶은데, 부하 테스트에 사용할 서버를 올려놓은게 없다. 

여기서 봐야하는 것은 왼쪽 아래에 "테스트 대상 서버"라고 쓰여진 텍스트 박스이다. 텍스트 박스 오른쪽 아래 "+ 추가"를 눌러보면 도메인과 IP를 추가할 수 있는데, IP만 추가해주면 설정이 끝나게 된다.

 

여기까지 설정한 후 테스트를 진행하면, 상세 보고서에서 좌측 하단에 타겟 서버 당 시각화된 리소스 모니터 데이터를 확인할 수 있다. 

Multi-AZ

AWS에서 권장하는 데이터를 AZ(Availability Zone) 여러 곳에 분산하여 저장하는 정책이다.
ex) AWS에서 제공하는 Cache 서버를 구성하는데 단일 클러스터로 구성하였다. 이런 상황에서 AWS 오류로 Cache 서버가 죽어서 서비스에 차질이 생길 수 있으니, 서울-Region-a와 서울-Region-b 두 곳에 분산시켜 Fail Over 대책을 세워야 한다는 것이다

서비스 안정성을 위해서 HA를 위해 Multi-AZ를 시키는 것은 맞지만 이 정책에는 일단 함정이 있다. AWS의 서비스 안정성은 99.99%(서비스 별로 다름)로 AWS측에서 일부 서비스를 재시작하거나 내부 오류가 발생하여 서비스 기능이 일시 중지 될 수 있다. 해당 내용은 공식 매뉴얼에 기재된 것으로 실제 발생할 수 있는 상황이다. 이 때 AWS 오류로 인해 일부 기능이 작동을 하지 않는 경우, Multi-AZ를 적용하지 않아 피해를 본 고객사에게는 피해 보상을 하지 않는다. (물론 Multi-AZ-AZ는 추가적인 인스턴스 혹은 AWS EBS를 대여하는 것이므로 비용이 늘어난다.)

AWS가 죽어봤자 얼마나 죽겠어! 라고 생각할 수 있지만 의외로 잘 죽는다. 2019년 AWS 도쿄 리전의 일부 AV에 속한 캐싱 서버가 오류를 일으켜, 해당 Region과 AZ에 단일 캐싱을 두고 있던 기업들이 피해를 본 적이 있다. (내 기억으론 그 때 넥슨 일부 게임들도 오류가 났었던 꽤 큰 이슈로 알고있다.)

정리하게 된 사유 : ...서버 개발을 하고 있었는데 이젠 aws 운영 환경도 신경써야한다고 반 강제로 하게 되었다. 공부하면서 정리했던 필기를 정리한다

요즘 일하면서 공부하는게 평소 공부하는 것보다 많은 듯 (사실 공부 안함)

AWS 운영 환경을 구성할 때 알아야 할 용어 

AWS Region

- AWS 클라우드 센터가 있는 나라/지역

VPC (Virtual Private Cloud)

- 직역 그대로 "가상의 사설 클라우드"

- 논리적인 AWS Region의 하위 구성 

- 사용자가 정의한 aws 계정 전용 가상 네트워크 

- 가상의 개념으로 개인/기업 전용으로 대여되는 클라우드

AV (Availability Zone)

- 가용 영역

- 물리적인 AWS Region의 하위 구성  

- ex) AWS 서울 Region에는 [서울-Region-a, 서울-Region-b] 과 같이 하나의 Region에서도 여러 지역에 물리적으로 분산을 시켜 놓는다. 

Subnet

- VPC 영역의 IP 범위를 칭한다

라우팅 테이블

- 네트워크 트래픽을 전달할 위치를 결정하는데 사용하는 규칙의 집합

EC2 (Elastic Compute Cloud)

- 흔히들 인스턴스Instance 라고 부르는 것이 EC2 Instance다

- 클라우드에 설치, 실행되는 가상 서버

- 기본적으로 생성만 했다면, Subnet 내 IP만 할당되어 외부에선 접근할 수 없다

- "탄력적 IP 할당" 기능을 통해 외부 접근 가능 IP를 발행, 맵핑하여야 외부 접근이 가능하다

인터넷 게이트웨이

- VPC의 리소스와 인터넷 간의 통신을 활성화하기 위해 VPC에 연결하는 게이트웨이 

- 질럿나옴

AMI (Amazon Machine Image)

- 쉽게 생각하면 컴퓨터에 운영체제 설치할 때 사용하는 ISO 파일이다

- EC2 인스턴스에 설치할 소프트웨어(운영체제)의 설치 이미지 파일이자, 소프트웨어 구성이 기재된 Template이다

- EC2 인스턴스 생성 과정에서 AMI를 선택하는 구간이 있다

AWS EBS (Elastic Block Store)

- 교육방송

- Block 단위로 관리되는 동적 저장 공간

- AWS에서 관리하는 S3에 EBS Volume째로 스냅샷을 백업할 수 있다

- EC2 인스턴스에 저장 공간을 할당할 때 기본값으로 설정하는 저장 방식이다

AWS S3 (Simple Cloud Storage)

- 파일 저장소라고 생각하면 된다

- 나도 이렇게만 알고있다

Route 53

- 도메인 설정 기능

AWS 환경 구성 시 기본 순서 및 구성 방식

AWS Region > VPC > AV > Subnet > EC2 Instance

1) 물리적 근본적 지역을 지정

2) 지역 아래에 사용할 가상 대역(VPC)를 설정

3) 가상 대역에 할당할 Region에 속해있는 데이터 센터(IDC 센터)를 지정

4) 물리적(AV)으로 지정된 것을 VPC 할당을 하여 논리적으로 묶었다면, 이제 논리적 공간(VPC) 내에서 사용할 Subnet(내부 IP)를 구성한다

5) 사설 IP까지 구성이 되었다면, EC2 Instance를 생성할 때 사용할 사설 내부 IP를 할당하여 생성한다

+ Recent posts