시작은 미미하나 끝은 쥬쥬하리라.

DevOps/Knowledge - 네트워크

[네트워크] DNS(Domain Name System)과 현장 DNS 관련 이슈

코딩뚜벅이 2026. 4. 5. 19:43
반응형

목차

  • DNS
  • 응용/심화
  • 요약

1. DNS

DNS(Domain Name System)은 인터넷 전화번호를 의미.

 

컴퓨터는 통신할 때, 142.250.207.14 같은 IP 주소를 사용.

<-> 사람은 google.com과 같은 도메인 이름을 사용.

=> DNS는 사용자가 도메인을 입력하면 이를 컴퓨터가 이해할 수 있는 IP 주소로 변환해주는 시스템과 규칙.

 

 

DNS 구성 요소

1. 도메인 네임 스페이스(Domain Name Space)

데이터가 저장되는 계층적 구조, '.'일 기준으로 계층이 구분.

 

2. 네임 서버(Name Server)

특정 도메인이 특정 IP라는 정보를 실제로 저장하고 관리하는 데이터베이스 서바

 

3. 리졸버(Reslover)

사용자의 요청을 받아서 네임 서버에 값을 요청/반환 받는 중개 역할, 통신사(ISP) DNS가 이 역할을 담당.

 

 

DNS의 계층적 구조

도메인은 뒤에서부터 앞으로 읽으면서 계층이 깊어지도록 설계.

ex) www.google.com 

-> '.'(마지막에 루트를 의미하는 .이 숨어 있음) -> .com -> .google -> www

 

Root(루트)

-> 최상위 단계, 모든 조회의 시작점.

-> 톻신사 기지국 DNS 서버에 IP 주소가 없을 때는 루트 DNS 서버부터 하위까지 탐색 시작.

 

TLD(최상위 도메인)

-> .com, .net, .kr 등 국가나 성격을 나타냄.

-> .org : 비영리 기관, .net : 네트워크 관리 기관 등등.

 

SLD(2차 도메인)

-> naver, google 등 직접 등록한 이름.

 

SubDomain(서브 도메인)

-> www, blog, mail 등 서비스별로 구분하는 이름.

-> 최하위 도메인.

 

FQDN(전체 도메인 이름)

-> 호스트 이름과 도메인 이름을 합친 전체 주소를 의미.

 

 

DNS의 동작 순서

(1) 브라우저 -> 리졸버

-> www.naver.com  주소 검색해줘.

 

(2) 리졸버 -> Root 서버

-> naver.com 주소 알고 있어?

 

(3) Root 서버 -> 리졸버

-> 나는 모르지만 .com이 관리하는 TLD 서버 주소를 줄게.

 

(4) 리졸버 -> TLD 서버

-> naver.com 주소 알고 있어?

 

(5) TLD 서버 -> 리졸버

-> 나는 모르지만 네이버가 사용하는 Authoritative(권한 있는) 서버 주소를 줄게.

 

(6) Authoritative 서버 -> 리졸버

-> 222.122.195.6이야.

 

(7) 리졸버 -> 브라우저

-> 여기로 접속해.

 

 

Resolver 쿼리의 두 가지 방식

1. Recursive Query(재귀적 질의)

결과(IP)가 나올 때까지 리졸버가 책임지고 찾아와서 알려주는 작업.

ex) 사용자 <-> 리졸버

 

2. Iterative Query(반복적 질의)

리졸버가 각 네임 서버를 직접 돌아다니며 "모르면 다음 서버 알려줘"라고 반복해서 질의하는 작업.

ex) 리졸버 <-> 각 네임 서버

 

 

리소스 레코드(RR) 종류

네임 서버가 제정하는 데이터 형식

 

A : 도메인을 IPv4 주소로 매핑.

CNAME : 도메인에 별칭을 부여.

MX : 메일 서버 주소 정보

NS : 도메인을 관리하는 권한 있는 네임 서버 정보.

 


2. 응용/심화

다중 A 레코드

DNS 서버에 호스트 이름과 IP를 매핑할 때, 이 행의 타입 분류가 A.

-> A 레코드라는 것은 도메인을 IPv4 주소에 연결하는 것을 의미.

-> 다중 A 레코드는 하나의 도메인에 여러 IPv4 주소를 연결하는 것을 의미.

 

위와 같이 하나의 도메인에 여러 IP를 연결하면 DNS 서버는 라운드 로빈 방식으로 두 개의 IP를 다 반환해주거나, 두 IP 값을 번갈아가면서 반환.

-> 만약 하나의 서버가 죽은 경우, 클라이언트는 다중 A 레코드로 등록된 다른 IP로 접속을 시도하여 가용성을 높이기 위함.

 

 

DNS 캐시 오염 및 불일치

TTL(Time To Live)은 캐시가 유지되는 시간을 의미.

-> 만약 서버의 IP를 변경했는데 이전 서버의 캐시가 살아있다면 클라이언트는 여전히 이전 IP로 접속을 시도하고 실패.

-> TTL이 만료되기 이전까지 클라이언트는 새로운 IP를 물어보지 않음.

 

void ConnectToPLC(const std::string& domain) {
    struct addrinfo hints = {0}, *res = NULL;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    if (getaddrinfo(domain.c_str(), "5000", &hints, &res) == 0) {
        if (connect(sock, res->ai_addr, res->ai_addrlen) == SOCKET_ERROR) {
            std::cout << "Connection failed. Retrying after cache check..." << std::endl;
            system("ipconfig /flushdns");
        }
        freeaddrinfo(res);
    }
}

 

- connect로 접속을 시도했는데 실패하는 경우 flushdns 등의 명령어를 사용하여 DNS 캐시를 통째로 비워버릴 수 있음.

-> 실제로는 권한 문제도 있을 수 있고 더 정교한 로직 필요.

-> 요점은 재시도 로직 혹은 캐시 우회 등의 로직으로 코드 레벨에서 대응 가능.

 

 

폐쇄망에서의 DNS

보안상의 이유로 외부 DNS 접속이 차단된 환경에서는 Reslover가 Root 서버를 찾아가지 못하기 때문에 도메인 해석 불가.

-> DNS 서버에 의존하지 않는 메커니즘을 구축하는 것이 필요.

-> 수동으로 도메인을 등록하는 것도 확실한 해결책.

 

bool SmartConnect(const std::string& domain) {
    if (!ResolveAndConnect(domain)) { 
        std::string fallbackIp = "192.168.0.100"; 
        return ConnectByIp(fallbackIp);
    }
    return true;
}

 

- 만약 DNS 서버 연결 실패 시 고정된 IP로 연결하는 등의 로직 삽입.

 

도메인 수동 등록

C:\Windows\System32\drivers\etc

-> 위 경로에 위치한 "hosts" 파일을 열면 아래와 같이 표시됨.

 

 

- IP 주소 도메인명 형식으로 입력.

-> ex) 192.168.0.100 mydomain.com

- 만약 DNS 캐시를 확인해보고 없다면 외부 DNS에 요청하기 전에 무조건 해당 파일을 먼저 읽음.

-> 위 파일에도 없는 도메인일때만 TLD -> Authoritative 서버 등의 외부 요청.

-> 사전에 위 파일에 도메인을 등록해두면 폐쇄망이건 DNS 서버가 죽었건 즉시 IP로 연결.

 

 

다중 A 레코드 상황에서의 특정 서버 장애

다중 A 레코드는 여러 IP를 반환하나, 프로그램이 반환 받은 리스트의 첫 번째 IP만을 고집하는 등의 동작을 하면 첫 번째 서버가 죽었을 때 시스템 전체가 멈춘 것처럼 보임.

-> DNS는 살아있는 IP만 선별하여 반환하는 기능은 없기 때문에 죽은 IP도 반환할 수 있음.

 

bool ConnectWithFailover(const std::string& domain) {
    struct addrinfo hints = {0}, *result = NULL, *ptr = NULL;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    if (getaddrinfo(domain.c_str(), "5000", &hints, &result) != 0) return false;
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
        SOCKET s = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
        if (connect(s, ptr->ai_addr, (int)ptr->ai_addrlen) != SOCKET_ERROR) {
            std::cout << "Connected to one of the A records!" << std::endl;
            freeaddrinfo(result);
            return true;
        }
        closesocket(s);
    }

    freeaddrinfo(result);
    return false;
}

 

- getaddrinfo()를 통해 반환받은 result 리스트를 순회하며 모든 IP의 연결 성공 여부를 확인.

-> 만약 하나라도 연결에 성공하면 즉시 리턴.

- 모든 레코드 서버가 응답이 없는 경우 false 리턴.

 


3. 요약

DNS(Domain Name System)

: DNS는 사용자가 도메인을 입력하면 이를 컴퓨터가 이해할 수 있는 IP 주소로 변환해주는 시스템과 규칙.

 

 

반응형