[AWS] EC2 DNS 질의 제한으로 인해 발생하는 네트워크 성능 저하

2019. 02. 12

blog cover image

Overview


일반적으로 Linux 는 nscd 등과 같은 별도의 DNS Cache Service 를 사용하지 않을 경우 DNS 질의를 수행할 때 마다 네트워크 통신이 발생합니다. 이는 AWS EC2 를 Linux 기반 운영체제로 사용해도 마찬가지인데 자체 구축한 Linux 환경과 AWS EC2 Linux 환경은 아주 중요한 차이점이 존재합니다.




DNS 제한


각 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 와의 통신이 완료되면 사용자의 요청에 대해 응답




Finish


이런 제한이 존재하는 이유에 대한 설명이 정확하게 없고 AWS 공식 메뉴얼에도 이에 대한 설명이 두 줄로 간략하게만 나와있어 놓치기 쉬운 부분입니다. 아마 UDP Flood 등의 DoS 공격을 원천차단하기 위해 제한했을 거라고 추측됩니다. 충분한 서버 사양에도 불구하고 지속적으로 네트워크 성능이 저하된다면 본 포스트에서 재현된 현상을 의심해봐야합니다.


by 개발 태웅