External DNS는 Kubernetes 클러스터에서 실행 중인 서비스와 인그레스 리소스에 대한 DNS 레코드를 자동으로 생성하고 관리해 주는 도구이다. 외부에서 트래픽을 받아주는 리소스와 도메인을 동시에 관리할 수 있기 때문에 많은 사람들이 External DNS를 사용하고 있다. External DNS는 기본적으로 DNS를 레코드 생성/수정/삭제를 관리하는데, AWS Route53, Google Cloud DNS, AzureDNS 등을 비롯해 다양한 프로바이더를 지원한다.
그러면 External DNS는 어떻게 레코드를 생성, 수정, 삭제할까?
이번 포스트에서는 External DNS가 레코드를 관리하는 방법에 대해 알아보고, 유용한 옵션이 어떤 것들이 있는지 알아보고자 한다.
동작 방식
기본적으로 External DNS를 실행하면, 다음과 같이 컨트롤러가 실행된다.
먼저, 컨트롤러는 프로그램이 종료할 때까지 반복문을 지속적으로 수행한다. 반복문은 1초에 한 번씩 수행된다. ShouldRunOnce 함수는 이전 실행 시간을 기준으로 사용자가 전달한 interval 시간이 지났는지를 체크한다. 기본값은 1분이다.
RunOnce는 다음과 같은 과정을 거친다.
1. 현재 자신이 관리하고 있는 레코드를 전부 가져온다.
만약 캐시 설정이 되어 있다면(txt-cache-interval), 캐시에서 불러오고, 그렇지 않다면 새롭게 프로바이더에서 정보를 가져온다.
자신이 관리하는 모든 Zone에서 모든 레코드 정보를 불러온다.
불러온 레코드 중에서 TXT와 Non-TXT 레코드를 분리한다.
TXT 레코드 중에서 heritage=external-dns 값이 있는 경우에만 키, 값을 파싱 해서 labelMap에 저장한다.
Non-TXT 중에서 labelMap에 해당하는 실제 엔드포인트를 찾아서 매핑한다. 즉, External DNS가 관리하는 TXT 레코드와 매칭되는 엔드포인트가 있으면, 해당 엔드포인트 라벨에 txt 값에서 추출한 라벨을 복사한다.
2. 현재 Sources(service or ingress...)에서 설정해야 하는 모든 정보를 조회한다.
Service 중에서 external-dns.alpha.kubernetes.io/hostname 혹은 external-dns.alpha.kubernetes.io/internal-hostname annotation이 있는 서비스를 추출한다.
Ingress 중에서 Rule.Host가 비어있지 않는 Ingress와 external-dns.alpha.kubernetes.io/hostname annotation이 있는 Ingress를 추출한다. 만약 external-dns.alpha.kubernetes.io/ingress-hostname-source 값이 존재한다면, 그 값이 따라 Spec 기반 혹은 annotation 기반 Ingress 중 하나를 반환한다.
위와 같은 방식으로 사용자가 --source로 지정한 모든 엔드포인트 정보를 가져와서 Merge 한다.
3. 현재 관리하고 있던 레코드와 Source로부터 조회한 결과를 비교하여, Create, Update, Delete 변화를 계산한다.
내부적으로 PlanTable을 구성해서, 변경사항을 추적한다.
PlanTable은 각 DNS 네임을 기준으로 Current(현재 상태), Desired(Source에 명시된 정보)를 나열한다.
Current가 없고, Desired 가 있으면 -> Create
Current가 있고, Desired가 없으면 -> Delete
둘 다 있는데, 정보가 다르면 -> Update
단, Update 시 Type이 변경되는 경우, 삭제 후 생성하는 과정으로 변경사항을 반영한다.
Policy에 따라서 Create, Update, Delete 선택
sync: 모든 변경
upsert-only: Create, Update
create-only: Create
4. 새로운 레코드가 생기는 경우, TXT 레코드도 추가한다.
기존 버전은 DNS 이름이랑 동일하게 TXT 레코드 설정한다.
새로운 버전은 DNS이름 앞에 Record Type- 이 자동으로 설정한다. 이때, Alias는 cname으로 자동 변경되어 붙는다.
현재는 기존 버전과 새로운 버전이 동시에 생성된다.
실제로 테스트해 보면 다음과 같이 cname- 이 붙은 것과 붙지 않은 것이 동시에 생성된 것을 볼 수 있다.
6. 프로바이더에 실제 레코드를 생성한다.
AWS의 경우 Batch 작업을 통해서 Route53 ResourceRecord를 생성한다.
Batch를 통해서 레코드를 생성하면, 내부적으로 Transactional 하게 동작하므로, 모두 성공 혹은 실패로 Atomic 한 작업을 보장한다.
만약 실패한 경우에는 다음 interval에서 다시 시도할 수 있도록 failedqueue에 저장한다.
테스트를 해보면, 다음과 같이 ChangeResourceRecordSets API를 호출할 때, ChangeBatch로 변경사항을 한 번에 적용하는 것을 볼 수 있다.
알아두면 유용한 설정
--txt-prefix
TXT Registry를 사용하는 경우, TXT 레코드 앞에 붙이는 prefix를 의미한다. 만약 해당 값에 "%{record_type}" 이 들어있으면, record_type이 붙지 않는 기존 버전의 TXT 레코드는 생성하지 않는다. 단, 해당 기능은 버전
다시 ingress를 생성해 보면, 다음과 같이 cname이 붙은 TXT 레코드 하나만 생성되는 것을 볼 수 있다.
--txt-cache-interval
External DNS는 매 Interval마다 TXT Registry를 조회해서 현재 설정된 내역을 조회하는데, --txt-cache-interval 값이 설정되어 있으면, 해당 내용을 캐시 해서 사용한다. 따라서 AWS Route53에 레코드를 조회하는 빈도를 줄일 수 있다. 만약 interval 값을 매우 작게 설정한 경우에는 위 설정을 통해 조금이나마 API 횟수를 줄여볼 수 있다.
--aws-zones-cache-duration
AWS Route53을 사용하는 경우에, Zone에 대한 정보를 캐싱할 수 있는 옵션이다. 대부분의 경우 Zone 자체를 자주 수정하지 않기 때문에, 해당 값을 추가하면, Zone 정보를 불러오는 API 수를 줄일 수 있다. 다만, 지나치게 높게 설정하는 경우에는 새롭게 만들었을 때 반영이 늦게 되므로, 상황에 맞게 설정하는 것이 좋다.