2 minute read

개요

AWS fargate 환경에서 DNS가 일정 주기로 같은 region에서만 latency가 늦어지는 현상이 발생하였다.
결국 원인은 아마존이어서 fargate 전체가 패치 되었지만 dns lookup 및 속도 개선을 위해 사용자 입장에서 처리했던 내용을 적어본다.

설명

pod의 container base image로 alpine linux를 사용하고 있었고, dns 관련 설정을 하지 않았었는데 linux에서 dns 설정을 위해서는 2개 파일이 존재한다.

  • /etc/hosts
  • /etc/resolv.conf

브라우저등에 ip나 domain을 입력할 경우 먼저 hosts를 찾는다.
localhost등이 loopback 주소인 127.0.0.1에 대응하게 되는건 hosts에 정의되어 있기 때문이다.
먼저 hosts를 보았는데 내가 알지 못하는 주소라면 resolv.conf에 정의된 값을 참조하여 ns에 질의하게 된다.

shell
kubernetes의 경우 pod가 자동으로 위 이미지에 해당하는 값들이 생기게 된다.
(EKS이므로 fargate node의 alias 또한 정의되어 있다.)
search 값에 core라고 되어있는데, core namespace에 있어서 그렇고 default namespace에 있다면 default가 된다.
같은 kubernetes cluster 내에서 통신을 하게 될 경우 kubernetes가 편리하게 제공해주는 내부에서 사용가능한 dns를 사용하게 되는데, http://<service>.<namespace> 주소로 호출하는 경우가 있다.
kubernetes의 service는 yaml의 ref 설정등을 통해 endpoints 리소스를 생성(관리)하는데 이 resource는 각각의 pod ip를 가리키게 된다. 따라서 service는 해당하는 모든 pod의 ip를 알고 있고, service와 namespace의 조합으로 호출을 하게 되면 여러대의 pod중 한대의 pod로 트래픽을 서빙하게 된다.
만약 동일한 namespace의 경우 http://<service> 만으로도 통신이 가능한데, 이를 위 이미지의 search 쪽에서 가능하게 한다.

kubernetes는 최대한 클러스터내의 리소스에서 찾아본 뒤 안되면 외부 ns를 참조하게끔 하려고 하는데, 그게 search와 nameserver이다.
위 이미지를 기준으로 입력한 url로 못찾는 경우 search에 있는 주소를 뒤에 붙여서 하나씩 시도한다.
ex) http://aaa.com 의 경우 못찾는다면

  1. http://aaa.com.core.svc.cluster.local
  2. http://aa.com.svc.cluster.local
  3. http://aa.com.cluster.local
  4. http://aa.com.ap-northeast-2.compute.internal

사용자가 service 이름만 입력할지, kubernetes에서 지원하는 전체 도메인을 입력할지 알지 못하기 때문에 best effort로 찾아보려고 하는 의도가 담겨있는듯 하다..
위에서 보듯이 1~4번의 domain으로 172.20.0.10에 해당하는 name server에 질의하여 도메인을 어디서 찾을 수 있는지 질의한다.

172.20.0.10의 경우 EKS 내부에 떠있는 coredns pod의 service에 해당하는 가상(?) ip이다.
1.1.1.1, 8.8.8.8 처럼 아예 외부로 나가기전에 kubernetes cluster 내에서 domain을 처리해주는 ns 라고 보면 된다.

위에서 봤듯 그냥 aa.com 이라는 도메인을 호출하려고 해도 4번의 coredns를 호출하는 dns lookup 과정을 거친 뒤 외부 도메인으로 나가게 된다.

여기서 중요한게 ndots와 attempts이다. 추가적으로 timeout 시간 등도 설정할 수 있으나, 우선 ndots와 attempts만 변경하였다.
ndots는 점(.)이 몇개 이상부터 FQDN으로 판단할것이냐의 기준이고, attempts는 retry 횟수이다.
FQDN은 url의 맨 뒤에 .을 붙여서 호출하는 경우 해당 domain은 내가 의도한 완전한 domain이므로 1~4의 과정을 거치지 않고 찾지 못한다면 바로 외부 ns에 질의하라는 의미이다.

가장 좋은 방법은 URL을 호출시에 맨 뒤에 .을 붙이는 방법이나, 그러기엔 쉽지 않으므로 ndots의 수를 줄여 바로 FQDN 처리를 하는 방식이다.
무조건 작게 줄여버릴 경우 기존에 kubernetes domain을 사용하여 호출하는 다른 서비스들에서 오류가 발생하거나 의도하지 않은 목적지로 트래픽이 전송될 수 있으므로 주의해야 한다.

Deployment 내에서 다음 옵션을 주어 설정할 수 있다.

spec:
    template:
        spec:
            containers:
                ...
            dnsConfig:
                options:
                    - name: ndots
                      value: "2"
                    - name: attempts
                      value: "3"

결론

해당 옵션을 적용하고 외부로 나가는 URL을 가지고 위의 1~4 과정을 거치는 lookup 과정이 사라져서 좀 더 빠른 응답이 가능해졌다.
실제로 TCP dump를 떠서 확인해봐도 4번 질의할게 1번으로 줄었다.
많은 통신을 하는 서비스이고, RPS가 높은 경우 해당 옵션을 조절한다면 상당한 양의 트래픽을 절약할 수 있을지도 모른다.
API를 개발할때도 여러 API와 통신하는 경우가 흔하게 생기는데 FQDN을 이용하면 조금 더 성능 향상을 도모할 수 있을지도..