현재 나는 "프리티어로 10000명의 사용자 감당하기"에 도전중이다..!!!
이전 서버는 한개의 컨테이너 서버였고 약 900명의 사용자를 버티며 RPS는 450 으로 측정 되었다
가장 서버성능을 획기적으로 올리는 거는 서버를 막연히 늘리는 거라고 생각했다.
따라서 많이 사용하는 엔진엑스 로드밸런싱을 도전했고 그 과정에서 시행착오와 인상적인 부분을 정리해볼것이다....
모든 인프라(DB, Redis, Server)가 도커 컨테이너 위에서 동작하고 엔진엑스만 Ec2 임베디드이다.
기본적으로 AWS는 2개이상의 Ec2부터 유료전환💵이다...
따라서 프리티어 계정을 많이 만들어 ScaleOut + 엔진엑스 로드밸런싱을 해보려고 한다..!!
기존에 임베디드로 관리하는 거보다 유연한 Docker컨테이너로 NginX를 관리해보려고 한다.
(참고 강의 -> 매우 좋음 👍)
강의만 잘 따라 간다면 문제없이 https로 Ec2를 관리 및 docker-compose로도 가능할 것이다..!!
이후 로드벨런싱을 위해 nginx.conf
파일의 upstream속성만 넣어주면 된다..!!
내 nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
server_tokens off;
charset utf-8;
upstream honorsParkingBackend {
server honorsParking-master:8080;
#server honorsParking-slave:8081;
(... ip값 있어서 제외!!)
}
server {
listen 80 default_server;
server_name _;
location ~ /.well-known/acme-challenge/ {
root /var/www/certbot;
}
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/gyural.shop/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/gyural.shop/privkey.pem;
server_name gyural.shop;
root /var/www/html;
index index.php index.html index.htm;
location / {
proxy_pass http://honorsParkingBackend;
}
location ~ /.well-known/acme-challenge/ {
root /var/www/certbot;
}
}
}
첫번째 성능을 측정한 아키텍처이다. 3개의 Server를 사용한 것이 특징이다.
Ec2 1️⃣ : NginX + Spring-boot + Redis + MySQL
Ec2 2️⃣ : Spring-boot x 2
User
: 1100
RPS
: 500 ~ 510
한개 컨테이너 서버보다 900 -> 1100명 약 20% 성능이 증가하였다...
하지만 이상하지 않은가?
서버가 3개이면 900 -> 900 * 3 == 2700이 되기를 기대했는데 그러지 못했다...!!
결국 병목이 발생한다는 것이었다..!!!
따라서 병목을 찾아보겠다...!!
1) 리소스 부족
모니터링 결과 NginX만 CPU점유율 70퍼 이상 차치하고 서버 컨테이너 1개도 70퍼 이상을 차지할 때가 있었다..!!
그러면 T2-Micro
스펙으로는 1개의 서버를 할당해주는게 적당해 보인다.
2) DB및 Redis 컨테이너
현재 한 EC2에서 엔진엑스 + MySQL + Redis + Srping이 다 돌아간다..!!
즉 DB와 Redis를 바깥으로 뺀다면 서버나 엔진엑스의 리소스가 남게 될 것으로 예상된다.
두 가지 가설을 세우기 위해서의 전략은 2가지 이다..!!
1) Ec2인스턴스 하나당 서버 컨테이너 최대 1개로 둔다
2) DB 및 Redis를 외부에 위치시킨다.
Redis-Cloud : Redis
AWS RDS : mySQL
Ec2 1️⃣ : NginX
Ec2 2️⃣ : Spring Boot
User
: 1200
RPS
: 650
기존에 엔진엑스가 돌아가던 EC2에서 DB와 Redis를 각각 클라우드에서 실행시켰더니 단일 서버 컨테이너로 서버 성능이 압도적으로 상승했다.
따라서 첫번째 병목은 엔진엑스 Ec2에서 다른 인프라사용으로 인한 엔진엑스 자원 부족이었던 것이다.
또 해결까지 해줬다..!!
단일 서버로도 1500명을 감당해서 2개의 서버로 한다면 엄청난 성능 상승을 기대하고 측정해본다.
서버 컨테이너 2개를 로드벨런싱을 해보겠다.
Redis-Cloud :Redis
Ec2 1️⃣ : NginX
Ec2 2️⃣ : Spring Boot
Ec2 3️⃣ : Spring Boot
RDS : mySQL
컨테이너를 두개로 늘려도 성능이 좋아지지는 않았다. 따라서 엔진엑스의 자원사용량을 찾아봤더니
CPU사용율이 75% ~ 95%사이로 엔진엑스 EC2 컴퓨터에서 병목이 발생으로 예상된다...!!
이 말은 프리티어인 컴퓨터로는 로드벨런싱의 수혜를 보기는 어렵다는 뜻이다.
엔진엑스 병목을 해소하기 위해 엔진엑스 EC2를 스케일 업을 해보겠다..!!
이번에는 CPU 코어 수가 많은 T2.medium으로 로드밸런싱을 했고 결과는 좋았다..!!
이번에야말로 의미있는 로드밸런싱의 성능상승의 결과가 나왔다...!!!
RPS : 900 ~ 950
User : 2500
사용자는 2000명 정도까지 버틸만 한거 같다..!!
Ec2 1️⃣(T2.medium) --> NginX
Ec2 2️⃣ --> Spring Boot
Ec2 3️⃣ --> Spring Boot
Ec2 4️⃣ --> Spring Boot
이번에는 서버를 3개로 돌려보겠다..!!
하지만 결과는 2개린 아키텍처-3 보다 성능이 하나도 좋아지지 않았다..!!
여기서도 엔진엑스 병목이라고 판단해 vcpu가 4개인 마지막 스케일업한 아키텍처를 성능 테스트해보겠다..!!
엔진엑스 Ec2를 4vCpu인 T2.xlarge를 사용했는데 똑같은 문제인
2개 -> 3개 서버를 늘렸을 때 성능 변화가 없었다...!!
좌절감.. 이 느껴졌다..!! 비싼 T2 xLarge를 써도 문제가 해결 되지 않았다..!!!
결국 병목은 엔진엑스일 것이고 왜 엔진엑스 내부에서 병목이 발생할지를 고민해본 결과..!! 핵심 열쇠는 바로..!! Keep-Alive
keep-alive는 연결 지연으로 TCP 통신에서 연결 생성 종료에서 발생할 수 있는 병목을 해결해줄 수 있는 기술이다...!!
또한 엔진엑스에서 upstream으로 연결된 백엔드 서버는 기본적으로 keep-alive가 활성화되어있지 않다..!!
이제 정말 마지막 기대를 안고 keep-alive를 가진 아키텍처를 고안해보고자 한다..!!!
엔진엑스 Ec2는 T2.medium이다.
먼저 keep-alive의 부재로 인한 병목이 있다는 가설을 세운 후 실험을 해봤다 서버는 1->3개까지 차근차근 늘리면서 성능을 테스트 할 것이다.
RPS가 900이 넘었다 이전에는 1개의 서버만으로는 RPS가 최대 700까지밖에 측정이 안되었지만 1개에 서버에서도 효과가 나타났다..!!
최종적으로 3개의 로드밸런싱에서도 효과가 있기를 기대해본다...!!!
하지만 기대는 무너졌다..!!
서버를 두개 돌렸는데도 RPS는 차이가 아예없었다..!! 따라서 Nginx.conf의 worker_connections
를 1024 -> 2048로 올렸더니..
RPS가 1000 -> 1200으로 올랐다..!!
측정은 했지만 항상 서버 2개 -> 3개 에서의 성능상승은 발생하지 않는다....
이 병목을 찾아보려고 열심히 했지만 모든 엔진엑스 파라메터도 수정하고 RDS 모니터링에도 흔적을 찾을 수는 없었다..
T2.micro 엔진엑스는 서버를 2개로 확장해도 엔진엑스에서 병목이 발생함
T2.medium 엔진엑스는 서버를 2개로 확장하면 RPS가 750 -> 1000으로 30% 상승함
서버를 2개 -> 3개로 확장했을 때는 vCpu4인 T2.xlarge에서도 성능은 안올랐음
-성능은 오른다는 거는 이론적으로 실험적으로 맞다..
--> 하지만 서버를 확장할 때마다 병목이 발생하고 해당 병목을 해결하는 Skill이 필요하다!
먼저 서버 컴퓨터는 모두 프리티어인 Ec2(T2.micro)를 사용
엔진엑스 컴퓨터만 변화가 있었습니다...!!
아키텍처 | EC2 1️⃣ (Nginx) | EC2 2️⃣ (SpringBoot) | EC2 3️⃣ (SpringBoot) | EC2 4️⃣ (SpringBoot) | RDS | Redis | 성능 (Users → RPS) | 특징 |
---|---|---|---|---|---|---|---|---|
1 | Nginx+Spring+Redis+MySQL(T2.medium) | T2.micro SpringBoot x2 | - | - | EC2 내부 | EC2 내부 | 1100 → 500~510 | Nginx 자원 부족, 많은 인프라가 EC2-1에 집중됨 |
2 | Nginx(T2.micro) | SpringBoot(T2.micro) | - | - | AWS RDS | RedisCloud | 1200 → 650 | Nginx에 자원을 집중하자 성능 향상 |
3 | Nginx(T2.micro) | SpringBoot(T2.micro) | SpringBoot(T2.micro) | - | AWS RDS | RedisCloud | 1600 → 680~700 | 서버 추가했지만 성능 상승 미미, Nginx CPU 90% 이상 |
4-1 | Nginx (T2.medium) | SpringBoot(T2.micro) | SpringBoot(T2.micro) | - | AWS RDS | RedisCloud | 2500 → 900~950 | Nginx 스케일 업으로 성능 상승 |
4-2 | Nginx (T2.medium) | SpringBoot(T2.micro) | SpringBoot(T2.micro) | SpringBoot(T2.micro) | AWS RDS | RedisCloud | 2500 → 900~950 | 서버 한 대 더 추가했지만 성능 변화 없음 |
5 | Nginx (T2.xlarge) | SpringBoot(T2.micro) | SpringBoot(T2.micro) | SpringBoot(T2.micro) | AWS RDS | RedisCloud | 2500 → 1200 | vCPU 증가로 인한 Nginx 성능 20% 향상 |
6 | Nginx (T2.medium) | SpringBoot(T2.micro) | SpringBoot(T2.micro) | SpringBoot(T2.micro) | AWS RDS | RedisCloud | 3000 → 1200 | Nginx 설정 튜닝(worker_connections, keep-alive 등)으로 성능 개선 |
1) Nginx EC2에는 Nginx만..
Nginx자체가 가벼운 연산을 하더라도 결국 여러개의 서버의 관제탑역할을 하기 때문에 자원을 모두 사용할 수 있게 해줘야한다.
2) Nginx Ec2에는 vCPU2이상 사용
vCPU가 2개 이상이어야지 2개이상 로드밸런싱에서 의미있는 성능상승이 나타났다.
3) worker_connections 및 keep-alive
Nginx EC2 모니터링 이후 부하가 걸리고 성능이 안오를 때 Mem의 공간의 여유가 있다면 두 파라매터를 최대한 올리는게 좋다..!!
-> Nginx는 Mem를 많이 사용안하기 때문에 오버헤드도 발생 X
Prob1
RDS 퍼브릭 엑세스(IPv4 할당 비용)
Prob2
Ec2간 통신(엔진엑스 - 서버) 1Gig초과당 요금 발생
Prob3
모든 EC2가 병목이 없는데도 성능이 좋아지지가 않음 가설로 로그인을 새로할때마다 세션이 증가하고 레디스 저장공간을 위협할 정도로 세션이 늘어남
Prob3
서버가 Failure가 발생하면 부하가 걸리는 상태에서는 계속해서 failure가 발생함 -> 서버의 오류가 있다면 로그를 딸깍으로 확인할 수 있으면 좋을 거 같음
Prob1
해결 방법먼저 RDS 퍼블릭 엑세스 비용이 켜두기만 해도 시간단위로 발생한다.
-> 부하테스트 시에만 사용하고 아키텍처 설정이나 비 개발 시에는 꺼두기로 한다
-> 이를 쉽게 끄고 킬수있게 고안해내야한다
-> 현재 아키텍쳐 .env
파일 내부에 DB의 Private-url과 Public-url을 주석처리/비처리 해서 해결할 수 있다
예시 RDS (퍼블릭 활성화 시)
#DB_URL=jdbc:mysql://ec2-private.compute.amazonaws.com:3306/parking
DB_URL=jdbc:mysql://db-instance-public-samle.rds.amazonaws.com:3306/parking
#DB_USERNAME=user
DB_USERNAME=user
이런식으로 주석을 전환하고 docker컨테이너를 껐다가 꺼준다.
Prob2
해결 방법먼저 RDS 퍼블릭 엑세스 비용이 켜두기만 해도 시간단위로 발생한다.
30기가 초과시 2.7달러 발생
이런식으로 주석을 전환하고 docker컨테이너를 껐다가 꺼준다.