cname-records

1 개의 포스트

CNAME이 먼저인가 A 레 (새 탭에서 열림)

Cloudflare의 DNS 서비스인 1.1.1.1은 메모리 사용량을 최적화하기 위해 DNS 응답 내 레코드 순서를 변경했다가 전 세계적인 접속 장애를 일으켰습니다. 대다수 현대 소프트웨어는 DNS 레코드 순서를 무시하지만, glibc와 같은 특정 구현체는 CNAME 레코드가 A 레코드보다 먼저 등장할 것을 전제로 작동하기 때문입니다. 결국 Cloudflare는 이전의 순서로 로직을 롤백하여 문제를 해결했습니다. ### CNAME 체인과 부분 캐싱 메커니즘 * **DNS 별칭 추적:** `www.example.com`을 조회할 때 리졸버는 최종 IP 주소에 도달할 때까지 여러 개의 CNAME(별칭)을 따라가며, 이 과정에서 발생하는 모든 중간 레코드를 캐싱합니다. * **부분 만료 처리:** 체인 내 레코드들은 각기 다른 TTL(유효 기간)을 가집니다. 일부 CNAME은 유효하지만 최종 A 레코드가 만료된 경우, 리졸버는 전체 체인을 다시 조회하는 대신 만료된 부분만 갱신하여 기존 캐시와 병합합니다. * **병합 과정의 중요성:** 갱신된 레코드와 기존 캐시 레코드를 하나의 응답으로 합칠 때, 이들의 배열 순서가 클라이언트의 해석 방식에 영향을 미칩니다. ### 성능 최적화를 위한 로직 변경 * **기존 방식 (CNAME 우선):** 새로운 리스트를 생성하여 캐시된 CNAME들을 먼저 넣고, 그 뒤에 새로 조회된 A 레코드를 추가했습니다. 이는 메모리 할당과 복사 비용이 추가로 발생합니다. * **변경 방식 (A 레코드 우선):** 메모리 사용량을 줄이기 위해 기존의 응답 리스트 끝에 CNAME 레코드를 단순히 덧붙이는(append) 방식으로 변경했습니다. * **결과:** 이 사소한 변경으로 인해 DNS 응답 데이터에서 CNAME이 최종 결과값인 A 레코드보다 뒤에 위치하게 되었습니다. ### glibc 등 DNS 클라이언트의 처리 방식 문제 * **순차적 탐색:** 리눅스에서 널리 사용되는 `glibc`의 `getaddrinfo`와 같은 구현체는 DNS 응답을 순차적으로 읽으며 '찾아야 할 이름'을 업데이트합니다. * **인식 실패:** 클라이언트가 CNAME을 먼저 발견하면 "다음 타겟 이름"을 갱신하고 이후에 나오는 A 레코드를 수락합니다. 하지만 A 레코드가 먼저 나오면 아직 CNAME 정보를 모르기 때문에 해당 레코드를 무관한 데이터로 간주하고 무시합니다. * **결과적 오류:** 모든 데이터를 읽었음에도 불구하고 클라이언트는 매칭되는 IP를 찾지 못해 최종적으로 응답이 비어 있다는 결론을 내리게 됩니다. ### 시사점 및 결론 40년 된 DNS 프로토콜의 모호성으로 인해 레코드 순서에 대한 엄격한 정의가 부족할 수 있지만, 실제 환경에서는 전통적인 순서(CNAME -> Answer)를 유지하는 것이 하위 호환성을 위해 필수적입니다. 시스템의 성능 최적화가 기존 생태계의 암묵적인 동작 원리를 깨뜨리지 않는지, 특히 표준 라이브러리 수준의 하위 호환성을 철저히 검증해야 함을 보여주는 사례입니다.