time_wait 소켓이 실제 서비스에서 문제가 되는 이유
- 실제 서비스에서의 time-wait 발생과 적용원리
- 실제 서비스에서 time-wait 소켓이 문제를 일으키는 이유
- [부록] Nginx, WAS(톰캣), 외부AI, 데이터베이스간 통신은 HTTP 통신일까 ?
모든 코드는 깃헙에 작성되어 있습니다.
1.실제 서비스에서의 time-wait 발생과 작용원리
사실 TCP 통신에서 TIME_WAIT 상태의 소켓이 발생하는것은 자연스러운 현상이다. 하지만, 이게 문제가 되는것은 너무많은 time_wait 상태의 소켓이 생성되어있을 때이다.
어느쪽이든 Client 입장(요청하는 쪽)에선 요청을 보내기 위해서는 커널에서 할당 받은 로컬 포트로 소켓 생성을 요청한다.
3-way handshake나 4-way handshake에서는 클라이언트와 서버단에 대한 설명을 마치 유저와 서비스 서버로 나누어 놓았지만, 이해상 이렇게 구분되어지게 설명된것이다. 실제로는 EC2인스턴스가 클라이언트단이 되고 유저들이 서버단이 될 수도 있다. 어떠한 단이 되는지에 대한 기준은 요청을 먼저 하는쪽이 클라이언트단, 받는쪽이 서버단이 되는것이다. 즉, EC2서버에서 먼저 요청을 보내겠다고 하면 EC2서버가 클라이언트단이 되는것이다. 그 예로,
(1).톰캣서버(WAS)와 DB 연동
(2).톰캣서버(WAS)와 외부 API 연동
(3).Nginx와 톰캣서버(WAS) 연동
의 경우 (1),(2)는 톰캣서버가 클라이언트단이 되고, (3)에서는 Nginx가 클라이언트단이 되는것이다.
클라이언트단과 서버단의 개념
혹시, TCP통신의 3-way handshake와 4-way handshake에 대해 모르고 있다면 이 글을 읽고 오도록 하자.
위에서 말한데로, client 입장(요청하는 쪽)에서 요청을 보낼때 소켓을 이용하는데, 이 때 가용한 로컬포트 즉, 사용가능한 로컬포트를 커널에서 임의로 배정받아서 사용하게 된다. (이때 배정되는 포트들은 서로 고유하다.)
문제는 여기서부터인데, 통신이 완전히 종료되고(여기서는 time_wait까지 완전히 종료되는것을 이야기한다.) 소켓이 커널로 돌아가기전까지는 해당 소켓에 할당된 고유 포트번호를 사용할 수가 없다는 것이다. 만약 time_wait 상태의 소켓이 많아지게 되면 사용가능한 로컬포트 또한 줄어들게 되고, 끝으로는 모든 로컬포트가 time_wait소켓에 묶여있어 더이상 할당할 수 있는 로컬포트가 없게되는경우에 새로운 통신을 위한 소켓을 생성할 수 없게된다. 그러면, 더 이상 클라이언트단과 서버단은 서로 통신을 할 수 없게되는 것이다.
또 한가지 알아야 할것은 이 time_wait 소켓은 클라이언트단이나 서버단이나 연결을 먼저 끊으려고 하는쪽에서 발생한다. 즉, 꼭 클라이언트단에서 생기는 소켓이 아니라는것이다. 하지만, 통상 클라이언트단에서 먼저 연결을 끊으려고 FIN을 서버단에 보내기에 대부분 클라이언트단에서 time_wait 소켓이 발생한다고 볼 수 있다.
time_wait 소켓의 발생 (1)
time_wait 소켓의 발생 (2)
예외적으로, HTTP 기반의 통신의 경우는 대부분 서버가 먼저 연결을 끊는 경우가 많기 때문에 서버에서 TIME_WAIT가 생긴다. 하지만, 주로 time_wait로 인해서 서비스에 문제가 생기는 경우는 로컬포트를 할당받은 클라이언트단에서 time_wait 발생으로 인해 로컬포트 할당 부족현상이니 클라이언트에서 발생하는 time_wait 소켓외에는 논외로 해두겠다.
HTTP 통신과 서버단에서의 time_wait 발생
time_wait 소켓과 서버단, 그리고 HTTP 기반 통신
이해를 돕기위한 실제 예를 보여주도록 하겠다. 필자는 빈스톡과 Nginx를 사용하여 스프링부트 톰캣(내장)과 연동하여 사용했다.
위의 자료는 실제 Nginx가 클라이언트단이 되고, 서버단은 톰캣(WAS)이 되어서 통신하는 과정을 나타낸다. time_wait 소켓이 발생한 목록을 보면, 왼쪽은 127.0.0.1:고유포트번호이고 오른쪽은 127.0.0.1:8080이다. 왼쪽이 클라이언트단인 Nginx의 소켓 고유 포트번호와 ip주소이고 오른쪽 127.0.0.1:8080이 스프링부트 어플리케이션이 실행중인 포트번호이다.
해당 time_wait 소켓(클라이언트단의 소켓)이 사용하는 포트번호가 모두 다른것을 볼 수 있다.
위의 연동과정은 하나의 EC2내에 리버스프록시로써 Nginx을 사용하고 외부에서 받은 80포트의 통신을 Nginx가 앞단에서 받아 이를 WAS(톰캣)으로 연결시켜주는 과정을 의미한다.
양쪽 다 ip주소가 127.0.0.1인 이유는 같은 EC2 인스턴스 내에서 이루어지는 통신이기 때문이다.
2.실제 서비스에서 time-wait 소켓이 문제를 일으키는 이유
위의 그림을 다시보면, 클라이언트(Nginx)단에서 로컬포트가 계속해서 사용되고 있는것이 보인다. 통신을 할 때, 로컬포트는 한 연결당 한 번 밖에 쓰이지 못한다.(그래서, 클라이언트와 서버단이 연결될 때 소켓의 출발지 IP,PORT - 목적지 IP,PORT가 연결당 유일하게 존재하게 되는거다.)
계속해서 소켓끼리 연결이 되었다가 끊어지고 다시 time_wait 상태에 빠지게된다. 하지만, time_wait상태에 빠진경우는 해당 소켓에 있는 로컬포트는 커널로 되돌아간것이 아니기 때문에 재사용이 불가한 상태다. 즉, 새로운 연결을 할 경우 다른 남아있는 로컬포트중에서 끌어다가 사용해야 한다. 이때 문제가 발생한다.
로컬포트는 무한정 있는게 아니다. 임의로 사용가능한 로컬 포트 범위를 확장시켜주어도 10240 ~ 65535사이에 있는 포트를 사용하기에 이 포트마저도 고갈이 되버리면 더이상 연결을 진행할 수 없게 되는것이다.
필자가 EC2 내부에 있는 Nginx와 WAS(톰캣)을 사용을 예로 들었었는데, 실제로 이러한 연결말고도 흔히 time_wait가 발생할 수 있는경우가 두가지 경우가 더 있다.
위에서 말했다시피,
- Tomcat 서버와 DB의 연동
- Tomcat 서버와 외부 API의 연동
- Nginx와 Tomcat의 연동
가 있다.
이 중에서 세번째는 우리가 방금 본 경우이고, Tomcat 즉, WAS에서 데이터베이스에 연결한다던지, 아니면 WAS에서 외부 API에 요청을 보내는 경우 모두 time_wait 소켓 발생으로 문제가 발생할 수 있는 경우들이다.(왜냐면 위 셋 모두 클라이언트단이 EC2 인스턴스 내부에 있는 Nginx 혹은 WAS이며 이로인해 로컬포트 부족발생 현상이 나타날 수 있기 때문이다.)
내장톰캣 기반인 스프링부트 프로젝트를 예로 들어서 설명을 했으나 기존의 노드를 사용하더라도 위의 개념은 동일하게 적용이 되니 꼭 알아두도록 하자.
추가로, 우리가 브라우저에 특정 URL을 입력하여 서버에 접근할때는 (위와같이 EC2에 배포된 스프링부트 프로젝트(WAS)와 리버스 프록시인 Nginx를 사용한다고 보겠다.) Nginx에서 WAS로 연결할때의 time_wait 소켓을 고려해야하는것을 물론이고 동시에 WAS에서 API서버, 아니면 WAS에서 데이터베이스에 연결할 때 생성되는 time_wait도 신경써야 한다. 즉, 더 풀어말하자면 우리가 웹서비스에서 정적파일을 요구하는 단순 요청을 보낼수도 있으나 실제로는 정적파일 외에 추가로 동적자원에 대한 요청을 보낼 수 있다. 이 과정에서 외부 API를 WAS에서 요청을 보내야 하는 경우도 있고 동시에 WAS에서 데이터베이스로 특정 자료를 요청할 수도 있다. 그렇기 때문에 브라우저에서 단 한번의 요청을 하더라도 실제로는 여러 연결을 동시에 고려해서 time_Wait 소켓을 관리해야 한다는 것이다.(결국은 이 모든게 EC2 인스턴스가 감수해야하는 부분인것이다.)
3.[부록] time_wait 소켓이 발생하는 Nginx, WAS, 데이터베이스, 외부API간의 통신, HTTP통신일까?
바로 위에서 우리는 Nginx, WAS, 데이터베이스, 외부API간의 통신에서 time_wait 소켓이 발생하고 이를 통해서 문제가 된다는것을 알았다. 그렇다면 과연 이들간의 통신은 HTTP 아래에서 이루어지는 통신일까?
답은 아니다 이다.
우선, time_wait 소켓은 TCP 통신기반(3-way handshake, 4-way handshake)에서 발생하는 소켓 상태이다.
그러나, 기본적으로 HTTP 통신은 TCP 기반으로 이루어져 있기에 time_wait 소켓이 발생하는 것이고 그 외에도 다양한
통신들이 TCP 통신기반으로 이루어지기 때문에 HTTP통신이 아니여도 time_wait 소켓이 발생하게 되는것이다.
HTTP 통신은 포트80, HTTPS 통신은 포트 443에서 이루어진다. Nginx가 WAS에 접근할때는 8080포트(물론 이것이 고정값은 아니다.) WAS에서 데이터베이스에 접근할때는 3306포트를 이용하기 때문에 HTTP 규약 아래에서 이루어지는 통신이라고는 볼 수 없다. 또한, HTTP 통신이라면 요청과 응답에 기본적으로 정해진 구조를 갖추고 있어야 한다.
각 프로토콜 별 기본 포트
HTTP 통신이라면 요청과 응답에 갖추어야 할 기본 구조
HTTP통신과 TCP통신에 관해 자세히 알고싶다면 해당 글을 참조하도록 하며, 3-way handshake와 4-way handshake에 관해서 알고싶다면 해당 글을 참조하도록 하자.
🚀 추가로, time_wait 소켓이 발생하여 문제가 생길경우 어떻게 튜닝작업을 할것인지 방안은 무엇인지에 대해서는 실제 스프링부트 프로젝트를 다루면서 [성능튜닝] 카테고리에서 다루도록 하겠다.
태그 : #time_wait 소켓, #커널, #로컬포트, #HTTP, #TCP