Linux Network Internal
이번 글에서는 TCP retransmission과 그로 인해 발생할 수 있는 애플리케이션의 타임아웃 문제에 대해 다뤄보겠습니다.
TCP는 신뢰성 있는 통신을 위해 자신이 보낸 패킷에 대해 ACK를 확인합니다. 보낸 쪽에서는 반드시 받는 쪽이 잘 받았다는 신호를 주어야 다음 전송을 하게 됩니다.
위 그림에서 보이는 것처럼 Client가 보낸 SYN에 대한 ACK를 받지 못하면 보냈던 SYN을 다시 보내게 됩니다. 그리고 이런 과정은 Retransmission Timer라는 커널 타이머에 의해 동작하게 됩니다.
TCP retransmission를 살펴보다 보면 아주 중요한 단어들을 만나게 되는데요 바로 RTO와 RTT입니다. RTO는 Retransmission Timeout 을 뜻하는 말로 타이머가 작동하는 시간을 의미합니다. RTT는 Round Trip Time을 뜻하는 말로 네트워크 통신을 하는 두 노드 간에 패킷이 전달되는데 소요된 시간을 의미합니다. 종단 간 거리가 멀면 멀수록 RTT 값을 커지게 됩니다.
이 두 값은 어떤 연관이 있을까요?
RTT는 RTO의 값을 결정하는데 아주 중요한 요소가 됩니다.
RTO는 내부적으로 복잡한 계산식을 거친 후 동적으로 설정이 되는데요, 이 때 RTT 값을 기반으로 계산이 됩니다.
여기서 한 가지 의문이 생기는데요.. 두 종단 간 통신이 이루어지는 첫 번째 SYN 패킷에 대한 RTO는 어떻게 결정되는가 입니다. TCP Handshake를 하기 위한 첫 번째 SYN 패킷은 상대방과의 RTT에 대한 정보가 전혀 없기 때문에 계산이 불가능한데요, 그래서 지정된 값을 사용합니다. 이 값을 InitRTO라고 부르며 OS 마다 값이 조금씩 다르지만, Linux에서는 1초로 되어 있습니다. 1초 --> 2초 --> 4초 이런 식으로 InitRTO 값은 2의 제곱수로 늘어 납니다. (참고 자료 : https://www.rfc-editor.org/rfc/rfc6298.txt)
위에 설명한 것처럼 InitRTO의 경우는 1초로 설정되어 있기 때문에 애플리케이션의 타임아웃은 1초보다 큰 값으로 설정해야 합니다. 이미 맺어진 세션에서의 재전송은 RTT를 기반으로 생성되기 때문에 대부분 1초를 넘기지 않겠지만, TCP Handshake를 맺는 과정에서의 재전송은 최소 1초는 소요되기 때문에 애플리케이션에서 타임아웃을 1초로 설정한다면 재전송으로 커버 가능한 통신을 타임아웃 에러로 끊어 버리게 됩니다.
커넥션 풀 방식으로 네트워크 세션을 미리 만들어 두고 통신하는 경우에는 InitRTO가 발생할 일이 없기 때문에 더 작은 값으로 설정해도 됩니다.
이렇게 RTO는 RTT를 기반으로 동적으로 생성이 되는데요, RTO 값에 영향을 주는 다른 요소는 없을까요?
네. 있습니다. RTO_MIN 값이 바로 그 주인공인데요, 단어 그대로 RTO의 최솟값을 의미합니다. 별도로 설정해서 쓰지 않는다면 디폴트로 200ms로 되어 있습니다. 이는 RTT가 아무리 작아도 RTO값은 200ms 밑으로 내려갈 수 없음을 의미합니다.
그럼, 현재 설정되어 있는 RTO를 확인하고 RTO_MIN을 수정해보도록 하겠습니다.
Linux에서는 ss -i 명령을 이용해서 현재 통신 중인 세션의 RTO 값을 확인할 수 있습니다.
RTO_MIN 변경은 ip route 명령을 통해할 수 있습니다.
ip route change default via <default gw> dev <ethernet> rto_min 100
위 명령을 통해서 100ms로 RTO_MIN을 변경한 후 ss-i 명령을 입력하면 RTO 값이 변해있는 것을 확인할 수 있습니다.
요즘같이 서버와 네트워크 장비의 성능이 좋은 환경에서는 사실 200ms의 RTO_MIN 값은 너무 큰 값이라고 생각됩니다. (개인적인 의견입니다. ^^) 그럼 어느 정도의 값이 효과적일까요?
RTO_MIN이 너무 작으면 재전송이 너무 자주 일어나게 됩니다. 이는 또 다른 부하를 일으켜 시스템의 성능과 서비스 응답 속도에 악영향을 끼칠 수 있습니다. 사실 정답은 없지만, 개인적으로는 50 ~ 100ms 정도의 값이 적당하지 않나 라는 생각이 듭니다. 관련해서는 테스트를 해볼 예정이고요. 브런치에도 꼭 공유를 하겠습니다.
재전송은 TCP 메커니즘에 의해서 반드시 발생할 수밖에 없습니다.
재전송이 일어나지 않게 하는 것이 중요한 게 아니고, 재전송이 빨리 일어날 수 있도록 해주는 것이 중요합니다.
따라서 환경에 맞는 RTO_MIN 값을 찾아서 적용하는 것이 서비스의 응답 속도를 향상하는 데 도움이 될 수 있습니다.