[AWS] EC2 DNS 질의 제한으로 인해 발생하는 네트워크 성능 저하
2019. 02. 12
2019. 02. 12
일반적으로 Linux 는 nscd 등과 같은 별도의 DNS Cache Service 를 사용하지 않을 경우 DNS 질의를 수행할 때 마다 네트워크 통신이 발생합니다. 이는 AWS EC2 를 Linux 기반 운영체제로 사용해도 마찬가지인데 자체 구축한 Linux 환경과 AWS EC2 Linux 환경은 아주 중요한 차이점이 존재합니다.
각 Amazon EC2 인스턴스는 Amazon에서 제공하는 DNS 서버로 전송할 수 있는 패킷 수를 네트워크 인터페이스당 초당 최대 1024 패킷으로 제한합니다. 이 한도는 늘릴 수 없습니다. Amazon에서 제공하는 DNS 서버가 지원하는 초당 DNS 쿼리 수는 쿼리 유형, 응답 크기 및 사용 중인 프로토콜에 따라 다릅니다. 확장 가능한 DNS 아키텍처에 대한 자세한 내용과 권장 사항은 Hybrid Cloud DNS Solutions for Amazon VPC 백서를 참조하십시오.
Amazon Virtual Private Cloud 사용 설명서 중
https://docs.aws.amazon.com/ko_kr/vpc/latest/userguide/vpc-dns.html
AWS VPC 사용시 할당된 서브넷의 게이트웨이 + 1 에 해당하는 주소를 AWS 에서 제공하는 네임서버로 사용[1]하는데 해당하는 네임서버로 DNS 질의시 초당 최대 1024 패킷까지만 전송이 가능하고 이 한도는 Hard Limit[2] 이기 때문에 증설이 불가능하며 이 제한은 EC2 하드웨어 사양에 관계 없이 모두 동일합니다. 본 포스트에서는 이로 인해 발생하는 네트워크 지연 문제와 해결 방법을 설명하고자 합니다.
[1] 172.31.0.1 의 경우 172.31.0.2 주소를 네임서버로 사용
[2] Hard Limit 의 경우 증설이 불가능한 수치이며 Soft Limit 의 경우 요청시 증설 가능
- AWS EC2 (Amazon Linux2)
- AWS ElastiCache
- PHP 7
우선 EC2 DNS 질의 제한으로 인해 발생하는 네트워크 성능 저하 현상을 재현할 것 입니다.
본 포스트에서는 바로 위 테스트 환경에 적힌 EC2, ElastiCache 로 테스트를 진행했으나 이 현상은 AWS 에서 Endpoint 형태로 제공하는 다른 서비스, 혹은 다른 도메인에 대한 DNS 질의시 모두 해당되는 문제입니다.
SSH 접속 후 sar 명령어로 네트워크 상태를 확인합니다. 정상적인 상태이므로 이상할 것이 없는 수치들이 표시되고 있습니다.
[prayer@imweb-tech /]# sar -n SOCK 1 07:00:01 PM totsck tcpsck udpsck rawsck ip-frag tcp-tw 07:00:02 PM 200 4 4 0 0 0 07:00:03 PM 200 4 4 0 0 0 07:00:04 PM 200 4 4 0 0 0 07:00:05 PM 200 4 4 0 0 0 07:00:06 PM 200 4 4 0 0 0 |
화면 1 - 정상적인 네트워크 상태
쉘 스크립트를 작성하여 ElastiCache 에서 제공해준 Endpoint 대한 DNS 질의 시간도 확인합니다.
(google.com 등 다른 도메인으로도 확인이 가능합니다.)
#!/bin/bash START_TIME=`echo $(($(date +%s%N)/1000000))` dig imweb-tech.xxxxxx.0001.apn2.cache.amazonaws.com END_TIME=`echo $(($(date +%s%N)/1000000))` echo "Elapsed time: `expr $END_TIME - $START_TIME` ms" |
화면 2 - DNS 질의 시간 측정을 위한 쉘 스크립트
[prayer@imweb-tech /]# ./check_dns_query_time ; <<>> DiG 9.9.4-RedHat-9.9.4-61.amzn2.1.1 <<>> imweb-tech.xxxxxx.0001.apn2.cache.amazonaws.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12109 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; QUESTION SECTION: ;imweb-tech.xxxxxx.0001.apn2.cache.amazonaws.com. IN A ;; ANSWER SECTION: imweb-tech.xxxxxx.0001.apn2.cache.amazonaws.com. 60 IN A 192.168.0.200 ;; Query time: 1 msec ;; SERVER: 192.168.0.2#53(192.168.0.2) ;; MSG SIZE rcvd: 55 Elapsed time: 5 ms |
화면 3 - 정상적인 DNS 질의 시간
화면 2 에서 기억해야 할 것은 Elapsed time: 5 부분입니다. DNS 질의 한 번에 5 밀리세컨드가 소요됐으므로 지극히 정상적인 속도라고 볼 수 있으며 추가로 SERVER 부분을 보면 192.168.0.2 IP 에 53번 포트로 요청한 부분도 확인 할 수 있습니다. 그럼 이제 현상 재현을 위해 데이터 캐시 목적으로 ElastiCache 를 사용하는 페이지를 작성합니다.
1 2 3 4 5 6 7 | <?php /* dns_query_test.php */ $redis = new Elasticache(Elasticache::REDIS); $key_name = 'DNS_QUERY_TEST'; $redis->setValue($key_name, 1, 3600); $value = $redis->getValue($key_name); echo json_encode($value); | cs |
화면 4 - ElastiCache 와 Connect 하는 페이지 (PHP 7 로 작성)
Elasticache 클래스는 객체 생성시 ElastiCache 의 redis 인스턴스에 Connect 하도록 내부적으로 구현되어있는 상태이며 Connect 가 발생할 때 마다 ElastiCache Endpoint 에 대한 DNS 질의를 수행합니다. 이제 해당 페이지를 실제 서비스 환경에 맞게 비동기로 호출할 페이지를 작성합니다. 어렵게 작성할 필요 없이 javascript ajax 호출만으로도 충분합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> <script> for(var i = 0; i < 1000; i++){ $.ajax({ type: 'GET', url: ('https://imtech.me/dns_query_test.php'), dataType: 'json', cache: false, success: function(res){ console.log(res); } }); } </script> | cs |
화면 5 - ElastiCache Connect 페이지를 ajax 비동기 호출하는 페이지
테스트 페이지 작성이 완료되었으면 SSH 에서 sar 명령어를 실행시킨 후 테스트 페이지를 실행시켜 봅니다.
[prayer@imweb-tech /]# sar -n SOCK 1 07:00:01 PM totsck tcpsck udpsck rawsck ip-frag tcp-tw 07:00:02 PM 500 200 16 0 0 1695 07:00:03 PM 586 225 22 0 0 2374 07:00:04 PM 686 321 32 0 0 4231 07:00:05 PM 586 242 22 0 0 5213 07:00:06 PM 678 319 32 0 0 6198 07:00:07 PM 878 419 32 0 0 8177 07:00:08 PM 902 457 32 0 0 8532 |
화면 6 - 비정상적인 네트워크 상태
페이지 요청이 증가했으니 당연히 TCP 소켓 사용량은 증가할 것 입니다. 그러나 우리가 주목해야 할 부분은 UDP 소켓[1]입니다.
접속 요청이 증가함에 따라 TCP 소켓 사용량은 계속해서 증가하는 반면, UDP 소켓 사용량은 일정 수치에서 고착되는 상황을 볼 수 있습니다. 정확한 패킷 전송량은 tcpdump 등을 이용해 알 수 있겠으나 위에서 설명한 초당 1024 의 패킷이 32개 정도의 소켓을 사용하는 것으로 추측 할 수 있습니다. 이제 위에서 DNS 질의 시간 측정을 위해 작성했던 쉘 스크립트(화면 2)를 실행시켜 봅니다.
[prayer@imweb-tech /]# ./check_dns_query_time ; <<>> DiG 9.9.4-RedHat-9.9.4-61.amzn2.1.1 <<>> imweb-tech.xxxxxx.0001.apn2.cache.amazonaws.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12109 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; QUESTION SECTION: ;imweb-tech.xxxxxx.0001.apn2.cache.amazonaws.com. IN A ;; ANSWER SECTION: imweb-tech.xxxxxx.0001.apn2.cache.amazonaws.com. 60 IN A 192.168.0.200 ;; Query time: 1 msec ;; SERVER: 192.168.0.2#53(192.168.0.2) ;; MSG SIZE rcvd: 55 elapsed time: 2101 ms |
화면 7 - 비정상적인 DNS 질의 시간
DNS 질의 한 번에 2초 이상이 소요되는 것이 확인됩니다. 클라이언트에서 페이지 요청시 DNS 질의 한 번 마다 2초 이상이 소요된다면 애플리케이션의 성능은 심각하게 저하될 것 입니다. 거기에 ElastiCache 뿐 아니라 RDS 등 Endpoint 형태로 제공되는 서비스를 추가로 요청하는 페이지라면 서비스 자체가 불가능 할 것 입니다.
[1] 화면 6 의 udpsck 부분, DNS 질의는 UDP 방식으로 통신한다.
본 포스트 제일 앞에 힌트가 있습니다. 결론은 DNS 질의에 대한 응답 결과를 캐시해서 사용하면 캐시된 응답이 없는 경우에만 AWS 에서 제공하는 네임서버에 요청할 것이고 요청 빈도는 현저하게 낮아질 것 입니다. 아래에서 DNS 캐시 사용 방법을 알아보고 DNS 캐시 후의 성능을 확인해보겠습니다. 우선 Linux 에 DNS 캐시 서비스를 설치합니다. (본 포스트의 경우 dnsmasq 사용)
설치 방법은 링크를 참고합니다. 링크를 통해 dnsmasq 설치 후 설정까지 완료되었다면 위에서 작성한 테스트 스크립트를 실행시켜 네트워크 성능이 어떻게 달라졌는지 살펴보겠습니다.
[prayer@imweb-tech /]# sar -n SOCK 1 07:00:01 PM totsck tcpsck udpsck rawsck ip-frag tcp-tw 07:00:02 PM 503 192 4 0 0 1342 07:00:03 PM 521 201 4 0 0 2593 07:00:04 PM 562 252 14 0 0 4101 07:00:05 PM 546 233 4 0 0 4921 07:00:06 PM 537 231 13 0 0 6101 07:00:07 PM 585 267 7 0 0 7810 07:00:08 PM 604 299 4 0 0 8133 |
화면 8 - 로컬 DNS 캐시 적용 후 네트워크 상태
아까와 같이 TCP 소켓 사용량은 증가하였으나 UDP 소켓 사용량은 미미하게 증가하며, 증가하더라도 빠르게 감소하는 것을 확인할 수 있습니다. 위에서 DNS 질의 시간 측정을 위해 작성한 쉘 스크립트도 실행시켜 봅니다.
[prayer@imweb-tech /]# ./check_dns_query_time ; <<>> DiG 9.9.4-RedHat-9.9.4-61.amzn2.1.1 <<>> imweb-tech.xxxxxx.0001.apn2.cache.amazonaws.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65521 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; QUESTION SECTION: ;imweb-tech.xxxxxx.0001.apn2.cache.amazonaws.com. IN A ;; ANSWER SECTION: imweb-tech.xxxxxx.0001.apn2.cache.amazonaws.com. 60 IN A 192.168.0.200 ;; Query time: 1 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; MSG SIZE rcvd: 55 elapsed time: 7 ms |
[화면 9 - 로컬 DNS 캐시 적용 후 DNS 질의 시간 측정]
정상적인 속도로 DNS 질의를 수행하는 것을 확인할 수 있습니다. 이제 아래의 DNS 질의 메커니즘을 보며 로컬 DNS 캐시 후의 프로세스를 정리합니다.
화면 10 - DNS 질의 메커니즘
[1] EC2 (애플리케이션) 으로 사용자가 특정 페이지(본 포스트의 경우 ElastiCache 와 Connect 하는 페이지)를 요청
[2] ElastiCache 의 IP 주소를 알아내기 위해 DNS 서버에 질의
[3] DNS 서버는 질의에 대한 응답을 EC2 로 전송
-> EC2 에 캐시된 IP 주소가 있을 경우 2, 3번 단계를 건너뛰고 4번 단계 진행 (이로 인해 초당 1024 패킷 제한에서 자유로워진다.)
[4] 알아낸 IP 주소를 통해 ElastiCache 에 연결 요청
[5] 연결이 완료되면 EC2 와 통신
[6] ElastiCache 와의 통신이 완료되면 사용자의 요청에 대해 응답
이런 제한이 존재하는 이유에 대한 설명이 정확하게 없고 AWS 공식 메뉴얼에도 이에 대한 설명이 두 줄로 간략하게만 나와있어 놓치기 쉬운 부분입니다. 아마 UDP Flood 등의 DoS 공격을 원천차단하기 위해 제한했을 거라고 추측됩니다. 충분한 서버 사양에도 불구하고 지속적으로 네트워크 성능이 저하된다면 본 포스트에서 재현된 현상을 의심해봐야합니다.
by 개발 태웅