출처: https://m.blog.naver.com/tmondev/220731906490
그렇다보니 일시적으로 트래픽이 몰리게 되고 속도가 느려지면서 구매처리가 지연되는 경우도 간혹 발생한다. 이러한 상황을 개선하기 위해서 먼저 코드 측면의 불필요한 부분, 개선할 부분을 찾는 것도 중요하지만, 그 외의 측면에서 최적화된 Web Server / WAS(Web Appliaction Server) 설정을 하는 것도 중요한 포인트가 될 수 있다.
본 글에서는 티몬의 일부 서비스에서 사용 중인 아파치 웹 서버(Apache Web Server) + 톰캣 서버(Tomcat Server) 환경에 대해 간략히 알아보고 주요 설정 값들에 대한 의미와 일시적으로 트래픽이 몰리는 상황에서 보다 최적화된 성능을 내려면 어떻게 해야 하는지 알아보겠다.
서비스 환경 이해하기
아파치 웹 서버 + 톰캣 서버로 구성된 환경
먼저 아래 그림으로 일반적인 아파치 웹 서버와 톰캣 서버로 구성된 웹 서비스 환경을 대략적으로 한눈에 살펴보자.
일반적으로 사용자가 웹 브라우저를 통해 접근하게 되고 해당 브라우저에서 발생하는 사용자 요청은 80포트로 Apache Web server를 거쳐 Tomcat Connector를 통해 Tomcat Server까지 도달하게 된다.
Apache Web Server + Apache Tomcat Server로 구성된 웹 서비스 환경 1)
Apache Web Server 모듈
* 버전은 2.2.X 기준으로 작성
1) MPM (Multi Processing Module : 다중처리모듈)
MPM은 받아들인 요청을 처리하기 위해 자식들에게 분배하는 방식으로 대표적으로 Prefork / Worker 방식이 있다. 리눅스의 경우는 Prefork가 기본 방식이며, 컴파일 시 옵션을 -with-mpm=worker로 줄 경우 Worker 방식으로 설치된다.
* Tip
- rpm으로 설치하게 되면 Prefork 방식으로 기본 설정되어 설치된다.
- 현재 아파치가 어떤 모듈로 설치되었는지 확인하려면 "httpd -V" 또는 "httpd -l" 명령어로 확인이 가능하다.
1-1)Prefork MPM
Prefork MPM의 동작 방식 2)
실행중인 프로세스를 복제하여 실행한다. 쓰레드가 한개인 자식 프로세스를 N개 사용하며, 각 프로세스는 한번에 한 연결을 담당한다. (프로세스 : 쓰레드 = 1:1관계) 여러 시스템에서 Prefork의 속도는 Worker와 비슷하지만, 더 많은 메모리를 사용한다. 메모리 영역까지 복제하여 동작하기 때문에 프로세스간 메모리 공유가 없어 안정적이다.
1-2)Worker MPM
Worker MPM의 동작 방식 3)
여러 개의 자식 프로세스가 각각 여러 쓰레드를 사용한다. (요청이 쓰레드 단위로 처리됨) 각 쓰레드가 한번에 한 연결을 담당하며, 쓰레드간의 메모리를 공유한다. Prefork 방식보다 기본 메모리는 많이 사용하지 않는다.
동시 접속이 많은 경우, Prefork 방식보다 Worker 방식이 조금 더 나을 수 있는 반면, Worker 방식은 하나의 쓰레드가 문제가 생길 경우, 해당 쓰레드에 접근 중인 모든 프로세스에 영향을 미치게 되므로 안정성이 떨어지는 문제가 있다. 따라서 안정성에서 우수한 Prefork 방식이 많은 기업에서 주로 사용되고 있다.
Apache Web Server 주요 설정
아파치 웹 서버에서 가장 중요하게 볼 설정은 MaxClient 와 ServerLimit 이다. MaxClient는 "동시에 접속 가능한 클라이언트의 최대 수"를 의미하며 ServerLimit은 "프로세스 수의 최대 수"를 의미한다. 값의 범위는 ServerLimit >= MaxClient 로 설정되면 된다. 참고로 이들의 기본값은 모두 256이다.
이 두 가지 설정이 없었다거나 너무 크다면? 메모리 점유율이 커지면서 OS단에서 문제가 되거나, CPU 100% 점유로 동작불능 상태가 되는 불상사가 일어날지도 모른다. 위 설정 값의 임계치는 서버의 실제 메모리 용량과 아파치 프로세스의 평균 메모리 소비량의 이 두 가지 값에서 최대 어느 정도까지 프로세스를 생성할 수 있는지 계산할 수 있다.
예를 들어 메모리 용량은 4GB, httpd 프로세스 하나당 10MB라고 메모리를 평균적으로 사용한다고 가정해보자. 기타 시스템에서 기본적으로 점유하는 메모리를 200MB정도에 WAS에서 메모리를 1GB로 잡았다고 하고 나머지는 대략 2.8GB 정도를 아파치가 모두 사용할 수 있다고 본다면, 2.8GB / 10MB = 약 280개 정도로 최대로 설정이 가능하다.
실제 설정 값은 위에서 계산한 10MB라는 값이 아파치 1개 프로세스의 평균적으로 사용하는 값이므로, 여유 있게 기본값인 256 정도로 설정하는 것이 좋을 것이다. 그 외 설정 값들은 공식 사이트를 참고하여 기본 값 혹은 권장하는 값을 참고하기 바란다.
Tomcat Server 주요 설정
* 버전은 7.X 를 기준으로 작성.
아래는 주요 설정이 포함된 $TOMCAT_HOME/conf/server.xml 의 Connector 설정이다.
설정(있는 경우 기본값) | 설명 |
connectionTimeout (Default : 5000) | 클라이언트와 서버간 I/O가 설정한 시간동안 발생하지 않을 경우 타임아웃을 발생시킨다. |
minSpareThreads | Tomcat 실행시 생성되는 초기 쓰레드 사이즈. |
maxThreads (Default : 150) | 서버가 허용할수 있는 최대 요청(쓰레드) 수. 실제 Active User 수를 의미함. |
acceptCount | 쓰레드가 full 상태일 경우 요청을 대기하는 queue의 사이즈. |
maxConnection | 최대 커넥션 수. |
maxKeepAlive (Default : 1 = 비활성화) | 종료되지 않고 동시에 대기할 수 있는 TCP 연결 수. |
문제 발생 당시의 현상 재현
다음은 트래픽이 많이 몰림으로 인해 속도 지연의 현상이 발생했던 당시 환경을 유사하게 재현했다.
[아파치 설정값]
* 참고 : https://httpd.apache.org/docs/2.2/en/mod/prefork.html
1
2
3
4
5
6
7
8
|
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
ServerLimit 256
MaxClients 256
MaxRequestsPerChild 10000
</IfModule>
|
[톰캣 설정값]
톰캣은 최초 설정 값들을 공식 사이트를 참고하여 기본 값 혹은 권장하는 값으로 설정하였다.
* 참고 : http://tomcat.apache.org/tomcat-7.0-doc/config/http.html
1
2
3
4
5
6
|
<Connector port="9009" protocol="AJP/1.3"
connectionTimeout="20000"
minSpareThreads="10"
maxThreads="200"
acceptCount="100"
redirectPort="8443" />
|
[현상 재현]
- Apache Jmeter로 특정 jsp (Thread Sleep 10초 발생) 페이지로 접근하여 초당500건의 부하를 발생시킴.
(캡쳐 참조 / 부하량은 최초 서버 설정에서 에러 없이 전체건 처리 가능한 범위로 설정)
- 쓰레드 상태는 아래 두 가지 방법으로 모니터링
1) 톰캣 JMX 활성화 후 Jconsole로 모니터링
2) http://{serverIP}/manager/status 페이지에서 실시간 모니터링
[Request 결과]
- Client (Jmeter) : 평균 약 9.5 TPS (Transactions Per Seconds / 1초당 처리 가능한 트랜잭션 건수)
- Server (Jconsole) : max Thread 도달
* 관리자 status 페이지에서 current busy Thread가 maxThreads값인 200 도달 확인
최적화된 설정값 찾기
Prefork 방식은 Apache MaxClients : Tomcat MaxThreads = 1:1관계로 볼 수 있다. 따라서 최소 256개 이상으로 설정을 해야하기 때문에 maxThreads값을 기존 200 -> 256으로 변경하였다.
[Test 1. maxThreads 값을 256 으로 설정했을 때]
1
2
3
4
5
6
|
<Connector port="9009" protocol="AJP/1.3"
connectionTimeout="20000"
minSpareThreads="10"
maxThreads="256"
acceptCount="100"
redirectPort="8443" />
|
[Request 결과]
- Client (Jmeter) : 평균 약 16.8 TPS (이전 테스트 대비7.3 TPS 증가)
- Server (Jconsole) : max Thread 도달
추가로 maxThreads를 512로 세팅하여 256 이상일 경우에 성능에 변화가 있을지 테스트해 본 결과, TPS를 비롯하여 사용하는 쓰레드 최대치 모두 256을 설정했을 때와 유사한 결과를 보였다.
+ 번외로 웹 서버의 maxClients 설정을 maxThreads보다 적게 잡았을 때도 참고용으로 보면 좋을 것 같아 테스트해본 결과를 아래 추가하였다.
[Test 3. maxClients 설정을 maxThreads보다 적게 잡았을 때]
1
2
3
4
5
|
<IfModule mpm_prefork_module>
…
ServerLimit 128
MaxClients 128
</IfModule>
|
[Request 결과]
- Client (Jmeter) : 평균 약 10.6 TPS
- Server (Jconsole) : maxClient 설정값 만큼만 쓰레드 사용
위와 같이 가장 중요한 최대 쓰레드와 동시접속에 대해 메모리와 평균 메모리 사용량을 기반으로 계산하여 설정하고 WAS 설정 또한 그에 맞게 조정해 놓은 상황에서, 다른 설정 중 최적화 할 수 있는 부분은 없을까?
예전에 개발을 담당했던 서비스 중에 특정시간을 기점으로 선착순 100% 당첨이 되는 이벤트가 있었다. 이벤트가 오픈되는 시점에 트래픽이 몰리는 현상과 함께 많은 수의 요청에 대한 응답시간이 오래 걸리는 현상이 발생했다. 해당 현상을 해결하고자 당시 아파치 설정의 MinSpareServers / MaxSpareServers / MaxRequesstsPerChild 값을 아래와 같이 변경하였다.
1
2
3
4
5
6
7
8
|
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 100 // 변경
MaxSpareServers 200 // 변경
ServerLimit 256
MaxClients 256
MaxRequestsPerChild 15000 // 변경
</IfModule>
|
변경한 값 중 MinSpareServers 설정은 앞에 설명했던 바와 같이 Prefork 방식에서의 만큼 "유지하고자 하는 최소의 자식 프로세스 수" 이다. 평소엔 무리가 없지만 갑자기 트래픽이 증가할 경우에는 MinSpareServers 보다 많은 자식 프로세스가 필요하기에 위와 같은 방식으로 미리 생성하였다. 그리고 MaxRequestsPerChild는 "재생산까지 처리해야 하는 횟수"이므로 가능한 큰 숫자를 설정해야 cpu사용량이 좋아지므로 프로세스가 더 많은 요청을 처리할 수 있도록 변경하였다.
결국 종합적으로 프로세스의 생성 횟수를 최소화 하는 목적으로 위와 같이 설정을 변경한 결과, 특정 시점에서의 응답시간은 이전보다 현저히 줄어든 것을 확인할 수 있었다. 다만 24시간 내내 트래픽이 몰리는 것이 아니기 때문에, 평소에도 100개의 프로세스가 유지되어야 하는 점이 하나의 Trade-Off가 된다고 볼 수 있다.
정리하며
이 글에서는 아파치 웹 서버와 톰캣 서버에 대해 간략히 알아보고, 몇 가지 가정된 상황에서 적절한 설정 값을 찾는 테스트를 해보았다. 또한 추가로 실제 경험했었던 설정 값 튜닝 사례도 살펴보았다. 아파치 웹 서버 + 톰캣의 구성은 아파치 웹 서버가 어느 정도 검증이 되어있기에 안정적인 측면에서 아직도 많이 쓰여지고 있다. 하지만 이와 같은 구조는 프로세스 + 쓰레드 구조이므로 병목이 발생할 수 있으며, 병목현상으로 가용한 자원을 다 못쓰는 경우가 생긴다. 이러한 단점을 보완하여 아파치 웹 서버를 대체할 수 있는 NginX가 티몬에서는 많이 사용되고 있고, 현재도 계속적으로 변경을 진행하고 있다. (글을 작성하고 있는 동안 글을 쓰게된 계기가 되었던, 이슈가 있었던 서버도 NginX로 변경이 완료되었다)
이어서 다음 2부에서는 아파치 웹 서버를 대신하고 있는 NginX에 대해서 알아보도록 하겠다.
[참고]
http://httpd.apache.org/docs/2.0/mpm.html
http://httpd.apache.org/docs/current/misc/perf-tuning.html
https://httpd.apache.org/docs/2.2/en/mod/prefork.html
https://httpd.apache.org/docs/2.2/en/mod/core.html
https://www.apachelounge.com/viewtopic.php?t=5836
http://stackoverflow.com/questions/105754/best-practices-for-configuring-apache-tomcat
아파치 톰캣 7 따라잡기 (에이콘)
네이버를 만든 기술, 읽으면서 배운다 - 자바편 (위키북스)
서버/인프라를 지탱하는 기술 (Jpub)
이미지 출처 1) https://documentation.cpanel.net/display/EA/EasyApache
이미지 출처 2) http://www.slideshare.net/KarthikeyanBalasubra10/apache-50878068
이미지 출처 3) http://www.slideshare.net/KarthikeyanBalasubra10/apache-50878068
'프로그램 활용 > 웹서버' 카테고리의 다른 글
tomcat의 구동 스크립트에 환경변수를 추가하여 java 메모리튜닝 및 외부 라이브러리 경로 설정 (1) | 2023.11.14 |
---|---|
리눅스 tomcat 설치 (0) | 2023.11.13 |
로드 밸런싱 & 인프라 이중화 구축 (1) | 2023.10.24 |
세션(Session) 불일치 문제 및 해결 방법 (0) | 2023.10.24 |
Session Clustering 방식 (0) | 2023.10.24 |