July
12th,
2021
- 설정에 대한 튜닝은 반드시 해야한다.
- 대개 기본값으로 최대한의 성능을 낼 수 있는 것은 없다.
설정해야 하는 대상
- 성능 문제를 진단하는 가장 좋은 방법은 성능 테스트를 통해 병목 지점을 미리 파악하는 것이다.
- 무조건 애플리케이션 위주로 병목을 찾는 것보다, 일단 문제가 될만한 셋팅 값을 먼저 진단하는 것이 가장 효율적이다.
- 웹 기반 시스템에서 성능에 영향을 줄만한 세팅을 나열해보면 다음과 같다 :
- 웹 서버 세팅
- WAS 서버 세팅
- DB 서버 세팅
- 장비 세팅
아파치 웹 서버 설정
- WAS를 웹 서버로 사용하지 말자.
- WAS는 웹에서 사용하는 애플리케이션 서버이지 웹 서버가 아니다. 정적인 부분은 웹 서버에서 처리해야 한다.
- 그렇지 않을 경우 이미지, CSS, 자바 스크립트, HTML등을 처리하느라 아까운 WAS서버의 스레드를 점유하게 된다. 반드시 상용 웹서버나 아파치 웹 서버를 WAS 앞단에 두고 운영하도록 하자.
- MPM : Multi-Processing Module의 약자로, 여러 개의 프로세싱 모듈 기반 서비스를 제공한다.
- 설치폴더 하단의 conf 디렉토리에 있는 httpd.conf 파일을 수정하므로서 파치 웹 서버의 설정을 바꿀 수 있다.
ThreadsPerChild 250
MaxRequestsPerChild 0
- ThreadsPerChild : 웹 서버가 사용하는 스레드의 개수(위 케이스의 경우, 아파치 프로세스 하나당 250개의 스레드가 만들어진다)
- MaxRequestsPerChild : 최대 요청 개수 지정, 0이면 그 수에 제한을 두지 않겠다는 의미이다.
- 스레드 방식을 사용하기 위해서는 httpd-mpm의 worker부분을 수정해주어야 한다.
<IfModule mpm_worker_module>
StartServers 2
MaxClients 150
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild 25
MaxRequestsPerChild 0
</IfModule>
- StartServers : 서버를 띄울 때 프로세스의 개수를 지정한다. child 프로세스의 개수를 말한다.
- MaxClients : 최대 처리 가능한 클라이언트의 수를 지정한다.
- MinSpareThreads : 최소 여유 스레드 수를 지정한다.
- MaxSpareThreads : 최대 여유 스레드 수를 지정한다.
- ThreadsPerChild : 프로세스당 스레드 수를 지정한다.(앞과 동일)
- MaxRequestsPerChild : 최대 요청 개수를 지정한다.(앞과 동일)
위 예제에서 프로세스 수는 2이고, 프로세스당 요청 처리 가능한 스레드는 25이므로 총 50개의 요청을 처리할 수 있다. 또한, 최대 여유 스레드 수가 75이기 때문에, 총 150번의 클라이언트 요청을 한 번에 처리할 수 있다.
어떤 서비스가 150명의 요청을 받고 있다고 가정해보자. 이 상황에서 웹 서버에서 150명의 요청을 받고, 그 요청을 전달받은 WAS도 마찬가지로 150명의 요청을 받는다. 그런데, 자바는 GC를 할 때 JVM이 멈춘다(Stop-the-World).
만약 이 GC가 2초 걸린다면, 아파치 웹 서버에 총 300명의 요청이 기다리게 될 것이다. GC를 하는 동안 WAS가 멈춰 새로운 연결을 할 수 없다. 이 경우 Tomcat에서는 AJP Connector라는 웹 서버와 WAS 사이 커넥터에 설정한 backlog 값의 영향을 받는다.
만약 이 값을 설정하지 않으면 기본 값은 100이다. 즉 WAS가 응답하지 않을 때 100개의 요청까지 큐에 담아둔다는 의미이다. 이 100개를 넘는 요청은 503(Service Unavailable)이라는 HTTP 헤더 코드 값을 리턴 받게 된다. 이를 방지하기 위해서는 아래와 같은 조치를 취해주는 것이 좋다.
- 서버를 늘린다(Scale-Up) : 가장 편한 방법이지만 가장 많은 비용이 든다.
- 서비스 튜닝 : 서비스가 응답이 안되는 원인을 찾고 튜닝한다. 하지만 원인분석에 많은 공수가 든다.
- GC 튜닝을 한다 : 만약 GC가 오래 소요되어 응답이 안될 경우 GC 튜닝을 한다.
- 각종 옵션 값을 튜닝한다 : 가장 간단한 방법이지만, 잘못 설정시 오히려 더 큰 문제가 야기될 수 있다.
웹 서버의 Keep Alive
- 아파치 웹 서버에서 httpd.conf 파일에 다음의 설정을 통해 Keep Alive 설정을 추가할 수 있다.
KeepAlive On
- 웹 서버와 웹 브라우저가 연결되었을 때, KeepAlive 기능이 켜져있지 않으면, 매번 HTTP 연결을 맺었다 끊었다 하는 작업을 반복한다.
- 초기 화면에서 많은 이미지와 CSS, 자바 스크립트 등의 파일을 받아야 하는 사이트에 KeepAlive 옵션이 적용되어 있지 않다면, 초기 화면을 띄우는 데 몇 분씩 소요될지 모른다. 즉, 이미지와 같은 정적 개체들도 서버에 매번 접속을 해야하는 상황이 발생한다.
- CDN : 별도의 URL에서 해당 컨텐츠들을 내려받도록 설정하고, 동적 컨텐츠들은 WAS에서 처리하도록 해놓으면 WEB-WAS 서버의 부담도 줄어들게 된다.
KeepAliveTimeout 15
- 초 단위로 KeepAlive가 끊기는 시간을 설정하기 위한 부분이다.
- 마지막 연결이 끝난 이후에 다음 연결이 될 때까지 얼마나 기다릴지를 지정한다.
DB Connection Pool 및 스레드 개수 설정
- WAS 에서 설정해야 하는 값은 너무 많지만, 그중 가장 성능에 많은 영향을 주는 DB Connection Pool과 스레드 개수에 대해 알아본다.
- 이 두 항목은 메모리와 관련이 있다.
- DB Connection Pool은 보통 4-50개로 지정하며, 스레드 개수는 이보다 10개 정도 더 지정한다.(스레드 개수가 DB Connection Pool의 개수보다 적으면 DB 커넥션은 더 놀고 있기 때문이다)
- 스레드의 개수가 DB 연결 개수보다 많아야 하는 이유는?
- 모든 애플리케이션이나 화면이 DB에 접속하는 것은 아니기 때문이다.
- 가장 적절한 DB Connection Pool과 스레드 개수는 몇 개일까?
- 서비스 및 서버의 상황에 맞게 값을 지정해야 한다.
- 가장 좋은 방법은 성능테스트를 통해 가장 적절한 값을 구하는 것이다.
케이스별 튜닝 방법
- DB Connection Pool이 40개인 상황에서, 40개의 커넥션을 전부 사용하고 DB의 CPU 사용량이 100%인 경우
- DB의 CPU를 점유하는 쿼리를 찾아 튜닝 수행
- 인덱스를 타지 않거나, 테이블을 풀 스캔하는 쿼리가 있는것은 아닌지 쿼리 플랜을 떠서 확인한다
- DB의 CPU를 점유하는 쿼리를 찾아 튜닝 수행
- DB의 CPU 사용량은 50%도 되지 않는 상황에서 WAS의 CPU 사용량은 100%에 도달하는 경우
- WAS의 애플리케이션을 튜닝한다.
- WAS 애플리케이션 튜닝이 완료된 상태라면, DB 커넥션 개수를 줄이자.
WAS 인스턴스 개수 설정
- 서버의 WAS 인스턴스 개수를 늘리면 늘릴수록 CPU가 처리해야 하는 양이 많아진다.
- WAS 인스턴스가 여러개인 경우, 이 여러 개의 인스턴스에서 경합을 하며 CPU를 차지하려고 한다.
- 보통은 1~2개의 CPU당 하나의 인스턴스를 지정하는 것이 좋다. 하지만 성능 테스트를 통해 적정 인스턴스 개수를 구하는 것이 제일 바람직하다.
WAS 인스턴스 개수 설정 케이스
- CPU Core 개수가 36개인 장비가 있다. 인스턴스 1개일 때 500 TPS가 나오고, 2개일 때 700 PS, 3개일 때 720TPS, 4개일 때 730 TPS가 나올 경우 몇개를 띄우는 것이 가장 적절할까?
- 저자는 2~3개의 인스턴스를 띄우는 것을 권장한다. 인스턴스를 더 늘린다고 TPS가 증가하지 않는 상황에서는 오히려 유지보수성만 떨어지기 때문이다.
- 만약 WAS 장비에 4GB의 여유 메모리가 있을 경우 하나의 인스턴스에 4GB의 메모리를 지정하는 것은 지양하도록 하자.
- Full GC 발생시 많은 시간이 소요된다.
- 단독 인스턴스 구성시, 서버 장애가 발생하면 서비스가 불가능해질 수 있다.
- 가급적 512MB~2GB사이에 메모리를 지정하자.
- 이 예시에서는 1GB를 메모리로 지정해 2개의 인스턴스를 사용하는 것이 좋은 방법일것 같다. (다른 애플리케이션 혹은 OS에서도 메모리를 사용하기 때문)
- 장비가 한 대여도, 두 개 이상의 인스턴스가 서로 클러스터링하도록 지정하여 사용자이 세션 정보를 공유하도록 하는 것이 좋다.
Session Timeout 시간 설정
- 정상적인 서버의 경우 세션 종료 시간을 몇 분 단위로 해놓지만, 어떠한 서버는 몇백분으로 설정을 해놓는 경우가 있다.(이럴 경우 OOM이 손쉽게 발생할 수 있다)
- 그럴 경우 WEB-INF/web.xml 파일에서 설정한 session-timeout이 적절한 값이 들어가있는지 확인해보자.
- WAS에서 따로 설정한 값이 없거나 세션 객체의 invlidate()가 수행되지 않으면 세션은 삭제되지 않는 점을 유의하자.