AWS ELB 의 TCP 패킷 블랙홀

ELB (Elastic Load Balancer)

AWS를 사용하다보면 굉장히 많이 사용하는 컴포넌트 중 하나가 ELB 이다. 이런저런 기능이 많은데, 여튼 제일 중요한 것은 백엔드에 여러 EC2 들을 두고 로드밸런싱을 해준다는 것이다.

근데 최근에 이 ELB 놈에 문제가 있어서 좀 고생을 했었다.

ELB TCP Listner

결론부터 말하자면. 개발하는 서버와 클라이언트가 만약 TCP 커넥션을 물어놓고 통신하는 방식이라면, ELB를 쓰면 안된다.

클라이언트와 서버가 통신하기 위해서 TCP 커넥션을 ESTABLISH 했다고 하자. 그러면 실제로는 클라이언트와 ELB 사이, 그리고 ELB와 서버 사이에 TCP 커넥션이 연결되어 있는 상태다. 아래 그림을 보면 이해하기 쉬울 것이다.

클라이언트가 ELB에 TCP 연결을 물면, 그 연결은 백엔드에 있는 EC2 중 한대에 연결이 물린다. 위 그림에서 1번 EC2와 ELB 사이에 TCP 연결이 물렸다.

클라이언트가 TCP 연결을 재생성하거나, 백엔드 서버에서 RST 패킷 등을 보내주지 않는 이상, 위 연결은 계속 유지된다. 1번 EC2를 제외한 나머지 애들은 그냥 놀고있음. 즉 로드밸런서를 쓰는 이유가 없는 것이다. 이것도 문제인데, 더 큰 문제는 다른데 있다.

TCP 블랙홀

이때, ELB에서 1번 EC2가 제외되었다고 하자. 제외되는 이유는 여러가지가 있을 수 있다.

  • 헬스체크 실패
  • AWS 콘솔에서 명시적으로 'remove from load balancer'
  • 기타 운영/장애 이슈로 인해

EC2가 제외되었다고 해도, 클라이언트는 여전히 ELB에 TCP 연결이 유지되어 있다. 이 상황에서 클라이언트가 ELB로 TCP 패킷을 쏘면, 당연히 ELB가 알아서 이 패킷을 2번 EC2로 보내줄 것이라고 생각할 것이다. failover도 ELB의 중요한 기능중 하나니까. 나도 그렇게 생각했다. (똑똑한 아마존 AWS 개발자들이 당연히 잘 해놓았겠지!) 하지만 그게 잘됐다면 이 포스팅을 쓰고있지 않을 것이다.

이 상황에서 클라이언트가 ELB로 쏘는 TCP 패킷은 아무데도 가지 않고 ELB에서 그냥 증발한다. ELB가 패킷을 먹어버리는 블랙홀이 된 것이다. 직접 눈으로 확인하고 싶다면 패킷이 왔다갔다하는 모든 곳에 tcpdump를 이용해서 확인해보도록 하자.

이렇게 패킷이 ELB에서 증발하는 것도 큰 문제인데, 제일 심각한 문제는 이런 상황을 인지하기가 어렵다는 것이다. AWS에서는 이 상황을 에러로 판단하지 않는다.

해결책 몇가지

  • 패킷을 하나 쏠때마다 TCP 연결을 다시 물린다.
  • TCP 대신 HTTP 를 사용한다.
  • ELB 를 사용하지 않는다.
    • 다른 로드밸런싱 방법을 사용한다.
    • produce-consume 방식으로 구조를 바꾼다.