{"id":"IL-0455","org":"Woowa Brothers","date":"2020-11-11","docs":[],"title":{"en":"Calls from an AWS service through NAT Gateway to an on-premises service returned 5xx errors every busy weekend","ko":"AWS 의 A 서비스가 NAT Gateway 와 IDC L4 를 거쳐 온프레미스 B 서비스를 부를 때 주말마다 5xx 오류가 늘었다"},"domain":["networking","aws"],"impact":{"scope":"API requests from service A in AWS to on-premises service B failed with 5xx errors during weekend traffic, and user requests failed","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"The fix is often easy to find once the cause is analysed, but without confirming the root cause a service keeps running with the risk still in it, so Woowa Brothers treats finding the root cause as essential for preventing recurrence, alongside resolving the failure first.","ko":"장애를 해결하는 방법은 원인을 분석하다 보면 쉽게 보일 때가 많다. 그러나 원인을 정확히 확인하지 않으면 잠재적인 위험을 안은 채 서비스를 운영하게 된다. 우아한형제들은 장애 해결을 최우선으로 다루면서도, 재발을 막으려고 근본 원인을 찾아내는 과정을 중요하게 여긴다."},"related":[],"sources":[{"url":"https://techblog.woowahan.com/2700/","kind":"blog","archived":null}],"summary":{"en":"While testing the network path, the team found that through NAT Gateway the failure appeared only when service A did not use a connection pool. Four tests narrowed it down: with a connection pool through NAT Gateway, the public internet, IDC firewall and L4, it did not reproduce; over a VPN between AWS and the IDC, skipping NAT and the public internet, it did not reproduce; from service A in one specific zone through NAT Gateway it reproduced; and with the L4 removed from the path it did not reproduce. The problem was therefore in the IDC L4.\n\nWith DAM (Direct access mode) enabled, the L4 translates client ports, but the range did not match the 2000 to 65000 the team knew. In an L4 load test, checking the session table with the /i/slb/sess/du command showed no service ports above 15000, only 2048 to 16383, and once the TEST VIP had more than about 28,000 sessions from client overload, the Allocation fail count in the AUX Table rose. The team then mapped AWS availability zones A, B and C one to one to NAT Gateways, spread service A over the zones and ran a load test expecting the L4 to allocate about 14,000 ports per source IP (NAT Gateway IP), so the symptom should appear at about 42,000 TIME_WAIT sockets on the service B test server. The test server reached about 42,000 TIME_WAIT sockets and service A got 5xx errors, confirming the limit of about 14,000 per NAT Gateway IP, 14,000 x 3 = 42,000 with three NAT gateways.\n\nTo prevent a recurrence, the team set out to switch service A to a connection pool together with its team, and to survey the services that use NAT Gateway between AWS and the IDC and switch them to connection pools.","ko":"우아한형제들에서 AWS EC2 에 있는 웹 애플리케이션 A 서비스는 온프레미스 환경의 웹 애플리케이션 B 서비스와 REST API 로 통신한다. 요청은 A 서비스에서 NAT Gateway, 인터넷, IDC, 방화벽, L4 를 차례로 거쳐 B 서비스로 간다. 트래픽이 많은 주말마다 A 서비스의 5xx 오류와 B 서비스의 L4 세션 카운트가 함께 올라 사용자 요청이 실패했다. 팀은 매번 A 서비스 인스턴스를 늘리고 교체해 장애를 넘겼지만 원인은 몰랐다. 근본 원인을 찾으려고 SRE 팀, 클라우드인프라개발팀, 도메인팀, 정보보안팀이 함께 나섰다.\n\nB 서비스의 패킷 덤프에는, NAT Gateway 가 보낸 SYN 에 B 서비스 서버가 잘못된 sequence number 의 ACK 만 보내고 요청자가 SYN 을 다시 보내는 흐름이 있었다. 팀은 출발지, 목적지, 네트워크 구간에 가설을 세워 차례로 시험했다. 커널 튜닝과 서버 증설로는 증상이 그대로였다. 반면 A 서비스가 connection pool 을 쓰거나, VPN 으로 NAT 와 인터넷 구간을 빼거나, L4 를 빼면 재현되지 않았다. L4 는 DAM, 곧 Direct access mode 가 켜져 있으면 Client Port 를 변환하는데, 실제로 쓰는 포트 범위가 알고 있던 2000~65000 과 달랐다.\n\n팀은 AWS 가용 영역 A, B, C 를 NAT Gateway 와 1:1 로 짝지어 부하 테스트를 했다. B 서비스 테스트 서버에 TIME_WAIT 가 약 42,000개 쌓이자 A 서비스에서 5xx 오류가 났다. L4 는 NAT Gateway IP 하나당 약 14,000개의 포트만 할당했고, NAT Gateway 3개에서는 14,000 × 3 = 42,000 이 한계였다. 팀은 재발을 막으려고, A 서비스와 NAT Gateway 를 거쳐 AWS 와 IDC 사이를 오가는 다른 서비스들이 connection pool 을 쓰도록 바꾸기로 했다."},"trigger":{"en":"High weekend traffic from service A, which called service B without a connection pool, through the NAT gateways.","ko":"트래픽이 많은 주말에 connection pool 을 쓰지 않는 A 서비스가 NAT Gateway 를 거쳐 B 서비스로 새 TCP 연결을 계속 열었다."},"severity":"unknown","symptoms":["high-error-rate","outbound-connections-exhausted"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"Rising 5xx errors on service A and a rising L4 session count on service B.","ko":"A 서비스의 5xx 오류 증가와 B 서비스의 L4 세션 카운트 증가로 알아챘다."},"components":["nat-gateway","on-premises-load-balancer"],"root_cause":"capacity","remediation":[{"text":{"en":"Added service A instances, which lowered but did not end the errors","ko":"A 서비스 인스턴스를 늘렸다."}},{"text":{"en":"Replaced the service A instances, which brought errors and the L4 session count back to normal","ko":"5xx 오류와 L4 세션 카운트가 평소 수치로 돌아오지 않자 A 서비스 인스턴스를 교체했다."}}],"action_items":[{"text":{"en":"Switch service A to a connection pool","ko":"A 서비스 팀과 함께 A 서비스가 connection pool 을 쓰도록 바꾼다."}},{"text":{"en":"Find the services that use NAT Gateway between AWS and the IDC and switch them to connection pools","ko":"AWS 와 IDC 사이에서 NAT Gateway 를 쓰는 서비스를 조사해 connection pool 을 쓰도록 바꾼다."}}],"contributing":["limit-unknown"],"recurrence_of":null,"symptoms_text":{"en":"5xx errors rose on API requests from service A, running in AWS, to service B, running on premises, and user requests failed. At the same time the L4 session count of service B went up.\n\nAdding service A instances lowered the 5xx errors and the L4 session count, and service A's latency and errors fell, but they did not return to normal; replacing the service A instances brought both back to normal values. The cause stayed unclear, and the failure came back every weekend, when traffic is high. The SRE team and the development teams cleared it the same way each weekend.\n\nService A's EC2 instances call service B's servers over a REST API, through NAT Gateway, the public internet, the data center (IDC) firewall and an IDC L4 load balancer. In service B's packet dump a normal connection from 13.125.xx.xx (AWS NAT Gateway), source port 10141, to 58.229.xxx.xx port 443 went through the three-way handshake, data transfer and the four-way close. In a failing one the requester's SYN got only an ACK(2812932054) with a wrong sequence number instead of SYN,ACK, and the requester retransmitted the SYN 3 times. The team suspected that, as source ports were reused, TCP sockets on the service B side had not been closed properly.\n\nThe team tested three hypotheses: the source not closing TCP sockets (number of instances, timeouts, AWS zones A and C), the destination not closing them (number of servers, kernel tuning such as net.core.netdev_max_backlog=30000, net.ipv4.tcp_max_syn_backlog=65536, net.core.somaxconn=16384, net.ipv4.tcp_fin_timeout=5 and net.ipv4.tcp_max_tw_buckets=\"1800000\"), and the network path in between (NAT Gateway performance, more NAT gateways, the IDC firewall, the IDC L4). Kernel tuning and more servers did not change the symptom.","ko":"AWS 에 있는 A 서비스가 온프레미스 환경의 B 서비스로 보내는 API 요청에서 5xx 오류가 늘고 사용자 요청이 실패했다. 같은 때 B 서비스 앞단 L4 의 세션 카운트도 올라갔다.\n\nA 서비스 인스턴스를 늘리자 5xx 오류와 L4 세션 카운트는 줄었지만 평소 수치로 돌아가지는 않았다. 인스턴스를 교체하고 나서야 평소로 돌아왔다. 원인을 모르는 채로 장애는 트래픽이 많은 주말마다 되풀이됐고, SRE 팀과 개발팀은 주말마다 같은 조치로 장애를 넘겼다.\n\nB 서비스 서버에서 뜬 패킷 덤프에는 정상과 다른 흐름이 있었다. 정상일 때는 AWS NAT Gateway 의 13.125.xx.xx 에서 B 서비스 서버 443 포트로 3way-handshake, 데이터 전송, 4way-handshake 가 이어졌다. 비정상일 때는 같은 출발지 포트 10141 에서 SYN 이 왔는데, B 서비스 서버는 SYN,ACK 대신 sequence number 가 2812932054 로 잘못된 ACK 만 보냈다. 요청자는 SYN 을 3회 다시 보냈다.\n\n팀은 포트 재사용이 반복되다가 B 서비스 서버에서 TCP 소켓이 제대로 닫히지 않았다고 의심했다. 그리고 출발지, 목적지, 그 사이 네트워크 구간에 가설을 하나씩 세워 시험했다. 출발지에서는 인스턴스 수, 타임아웃, AWS 가용 영역 A 와 C 를 보았다. 목적지에서는 서버 수와 net.core.somaxconn=16384, net.ipv4.tcp_fin_timeout=5 같은 커널 튜닝을 보았다. 네트워크 구간에서는 NAT 증설, IDC 방화벽, L4 를 보았다. 커널 튜닝과 서버 증설로는 같은 증상이 그대로 났다.\n\n구간을 나눠 재현해 보니 세 경우에는 증상이 나지 않았다. A 서비스가 connection pool 을 쓸 때, VPN 으로 NAT 와 인터넷 구간을 뺐을 때, L4 를 뺐을 때였다. 그래서 의심 범위를 IDC 의 L4 로 좁혔다. L4 부하 테스트 중에 /i/slb/sess/du 로 Session table 을 보니, 알고 있던 포트 범위 2000~65000 과 달리 2048~16383 만 보였다. TEST VIP 의 세션이 약 28,000 을 넘자 AUX Table 의 Allocation fail 수치가 올라갔다."},"updated_at":"2026-11-14T12:34:10.409556Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0455","org_slug":"woowa-brothers"}