[ 一日30分 인생승리의 학습법] Apache 웹서버 동작 방식과 처리 Thread 부족에 따른 증상 확인
2023.07.27 21:20
[ 一日30分 인생승리의 학습법] Apache 웹서버 동작 방식과 처리 Thread 부족에 따른 증상 확인
Apache WEB 서버의 동작 방식에 대해 알아보고 특히, 사용자 폭주 시에 Apache의 처리 Thread(worker,event기준)가 부족할 경우 어떤 증상이 발생하는지 그리고 이를 조치하기 위한 방법에 대해 알아보도록 하겠습니다.
이번 설명은 Apache WEB 서버 버전 2.4.33 기준으로 설명합니다.
그럼 먼저 Apache에서 사용자 요청 처리가 어떻게 이루어지는지 살펴보도록 하겠습니다.
위의 그림과 같이 Apache WEB 서버에는 기본적으로 두가지 구조의 저장 공간이 제공됩니다.
1. 사용자의 연결을 수립하기 위한 Queue(ListenBackLog)
2. 사용자 요청을 처리하기 위한 Work Queue
각 저장 공간은 설정한 옵션에 따라 동작하는 방식이 다르며, 업무적인 특성에 따라 적절히 설정하는 것이 필요합니다.
각 Queue에 대해 간단하게(?) 설명하도록 하겠습니다.
먼저 사용자의 연결을 수립하기 위한 Queue입니다. 이 부분에 대해 설명하기 위해서는 사전 지식 및 설명이 필요한데요.
간단하게 다음과 같은 역할을 한다고 보시면 됩니다.
위의 그림과 같이 Client/Server간 Socket 연결을 위한 3Way Handshaking 처리를 위한 각각 Listen/Backlog Queue가 존재하며 Socket Program이 정상적으로 동작했을 경우 Backlog Queue에 대기하는 연결 요청이 최종 Accept되어 연결이 최종 마무리 되는 것입니다.
좀 더 상세한 내용은 "Network(Socket) 오류 메시지부터 확인하자~"를 참고하시기 바랍니다.
Apache WEB 서버에서는 이러한 Backlog Queue에 해당하는 Queue가 Listen Backlog입니다.
이러한 Listen Backlog는 Apache WEB 서버가 수많은 사용자의 연결을 처리(Socket Accept) 시 Work Thread가 일시적으로 부족할 경우 버퍼의 역할을 수행합니다.
설정은 httpd.conf 파일의 "ListenBackLog" 옵션을 통해 설정 가능합니다.
사용자 요청을 처리하기 위한 Work Queue는 Apache WEB 서버에서 사용자 요청을 저장하기 위한 Queue입니다.
이 Queue는 worker MPM에 설정된 Server의 Thread들이 POP 대기를 하고 있으며, Accept Thread가 요청을 Queue에 저장하는 순간 Thread들이 해당 요청을 POP하여 처리하게 됩니다.
또한 모든 Thread가 처리 중이라면 Accept Thread는 Idle Queue를 바라보게 되면 Idle Queue는 Idle한 Thread가 발생할 경우 Work Queue를 바라보게 됩니다.
따라서 Work Queue는 우리가 일반적으로 생각하는 Queue와는 다르게 요청을 분배 할당하기 위해 살짝 거쳐가는 Queue이며, 따로 버퍼 역할은 없습니다.
아래는 Thread의 여유가 있을때와 Thread가 모든 처리를 담당하고 있을때의 Stack 입니다.
참고로 해당 Stack은 linux OS에서 제공하는 pstack 명령어(pstack [PID])를 사용합니다.
그럼 이제 테스트를 통해 사용자 처리 Thread가 부족할 때 어떤 증상이 발생하는지 확인해 보고 어떻게 조치할지에 대해 알아 보겠습니다.
해당 내용을 테스트를 통해 알아보도록 하겠습니다.
위의 그림과 같이 Listen Backlog(httpd.conf 파일)를 2개로 설정하고 Apache WEB 서버의 설정(httpd-mpm.conf 파일)인 StartServers 1개, Min/MaxSpareThread 2개, ThreadPerChild 2개로 설정하고,
테스트의 편의상 처리 지연을 발생시키기 위해 WAS의 처리 Thread에 Sleep 처리(1000초)하도록 하였습니다.
또한 클라이언트는 총 6개의 요청을 보낼 수 있도록 하여 Apache WEB 서버의 Worker Thread가 모두 소진될 경우 어떤 현상이 발생하는지 그리고 어떻게 모니터링 하는지를 설명하도록 하겠습니다.
테스트를 위한 클라이언트는 편의상 HTTP 요청을 위한 Socket 프로그램을 사용하도록 하겠습니다.
그리고 모니터링은 Apache WEB 서버 로그(error), 서버 프로세스 stack(pstack), 포트 연결 정보(netstat -an), Listen Backlog 정보(ss -lnt), listen queue overflow 정보(netstat -s)를
확인하도록 하겠습니다.
클라이언트를 2개 수행한 경우의 각종 네트워크 관련 정보 및 프로세스 Stack 입니다.
위의 그림과 같이 2개의 요청이 정상적으로 처리되고 있으며 Listen Backlog에 대기 수도 없으며 또한 Listen Queue overflow도 증가하지 않는 상태입니다.
그럼 이제 클라이언트를 5개 수행해 보겠습니다.
5개의 클라이언트를 수행할 경우 5개의 연결이 Established 되어 있으나, Listen Backlog의 대기 수가 3개로 증가하였습니다.
그런데 여기서 서버 Thread 수가 2개이고 Backlog가 2개인데 Listen Backlog에 대기 수가 3개 인것은 Backlog가 동작하는 방식이 설정한 값에 +1이 되기 때문입니다.
2개의 요청외에 모두 Backlog에 대기 상태라는 것을 알 수 있고 또한 서버의 Accept Thread도 Accept 상태가 아니고 Idle Queue를 바라보며
처리가 완료된 Thread를 감지하게 됩니다.
이제 Thread수와 Backlog(+1)수보다 많은 7개의 클라이언트를 수행해 보겠습니다.
7개의 클라이언트를 수행하게 되면 추가된 2개의 클라이언트에 대해 SYN_RECV 상태가 됩니다. 이는 Listen Queue에서 Backlog로 소켓 연결을 저장하지 못했기 때문입니다.
Backlog와 Stack 정보는 그대로입니다.
그런데 여기서 Listen Queue의 overflow된 수가 늘어나는 것을 알 수 있습니다.
이는 SYN_RECV 상태의 Socket에 대해 최종 클라이언트의 Ack를 처리 못하고 drop 시켰으며 이로 인해 서버에서 SYN_ACK를 다시 재전송하게되고 클라이언트는 다시 Ack를 보내는 상황이 발생하기 때문에 overflow수가 계속 증가합니다.
좀 더 상세한 내용은 "Network(Socket) 오류 메시지부터 확인하자~"를 참고하시기 바랍니다.
SYN_ACK를 재전송하는 횟수(tcp_synack_retries)만큼 진행하고도 처리가 안될 경우 서버에서는 RST(Reset)를 전송하고 클라이언트는 "Connection Reset" 오류가 발생하게 됩니다.
그런데 여기서 한가지 염두에 둘 부분은 OS의 종류(Window, Linux)와 Kernel Network Parameter, 그리고 프로그램의 동작 방식에 따라 "Connection Reset"이 발생하는 원인은 좀 다를 수 있습니다.
예를 들어 클라이언트가 Window일 경우는 Network 파라미터인 TcpMaxDataRetransmissions(Default 5)이 먼저 영향을 받아 서버가 아닌 클라이언트가 "Connection Reset"을 발생시킵니다.
이는 클라이언트 프로그램에서 Socket 연결을 하게 되면 서버는 SYN_RECV 상태이나 클라이언트는 Established 상태가 되어 요청 데이터 전송이 가능합니다.
이경우 서버에서는 Accept되지 못한 Socket에 대한 연결이여서 해당 수신 데이터를 Receive Buffer에 저장시킵니다.
Receive Buffer에 저장된 데이터는 처리가 되지 못하고 있으며 이렇게 되면 수신했다는 Ack를 클라이언트에 전송하지 못하게 됩니다.
클라이언트는 이 경우 RTO(Retransmission Time Out)으로 재전송을 하게 되고 이 상황이 5번 반복되면 클라이언트에서 "Connection Reset"을 발생시킵니다.
그러면 클라이언트가 Linux인 경우는 tcp_retries2 파리미터(Default 15)로 괘 긴시간이 소요되어 "Connction Reset"이 서버의 Listen Backlog Full에 의해 발생하게 됩니다.
이 부분은 다음에 좀더 심도있게 설명할 수 있도록 하겠습니다.
그런데 여기서 이런 상황에서도 Apache Web 서버에서는 아무런 에러 로그가 나오지 않습니다.
여기까지 극단적인 환경에서의 테스트를 해보았는데요.
Apache Web 서버의 Worker Thread가 Backend(WAS)단의 처리 성능 저하로 Thread가 부족하거나 하게 되면 실제로 Apache Web 서버 입장에서는 아무런 로그가 없다는 걸 알수 있습니다.
근본적인 문제의 원인은 성능 저하가 발생한 부분의 조치가 필요하겠지만 갑자기 사용자가 많아지면서, 처리 성능에 평상시 보다 지연되는 현상이 발생하는 경우는 사용자가 Web 서버의 간단한 index.html페이지에도 접속하지 못하고 아래처럼 브라우저 메시지를 보게 됩니다.
이런 내부적인 Flow들을 알고 있다면 문제를 해결하는데도 빠르게 접근이 가능합니다.
Apache Web 서버를 운영 중인 시스템에서는 이러한 문제를 해결하기 위해 다음과 같이 모니터링하고 확인하는게 필요 할 것 같습니다.
1. Listen Backlog 모니터링
- "ss -lnt" 명령어로 서비스 포트에 대한 Backlog 상황을 확인합니다. Accept를 대기하고 있는 수가 얼마나 많은지 확인하는 거죠. 그리고 평상시에도 Backlog의 상태를 확인함으로써 적정한 Worker Thread수를 산정할 수 가 있으니 주기적으로 모니터링하는게 좋습니다.
2. netstat 모니터링
- Listen Backlog를 모니터링해서 대기 수가 많다면 netstat을 통해 SYN_RECV 상태의 사용자 요청이 많은지 확인합니다.
그리고 netstat -s를 통해 overflow가 지속적으로 발생하는지 확인합니다.
3. 서버 프로세스 stack 확인
- Listen Backlog에 대기가 발생하고 SYN_RECV가 많아진다면, 서버 프로세스의 stack(pstack [PID])을 확인하여 현재 Thread들이 뭘하고 있고 있는지 확인하여 조치합니다.
여기까지 Apache 웹서버에 대한 처리 구조와 Thread 부족 현상에 대해 설명드렸습니다.
도움이 되셨기 바랍니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.

