Core DNS
개요
코어 DNS는 쿠버네티스에서 사용되는 DNS 서버 애드온이다.
원래는 별개의 CNCF 프로젝트이나, 실질적으로 이거 말고 쓰이는 다른 대안이 없어 그냥 쿠버네티스 DNS라고 해도 상관 없다.
그래서 이 문서에서는 쿠버네티스 dns 방식에 대한 일체의 설명을 곁들인다.[1]
클러스터에서 서비스에도, 파드도 고유한 ip를 부여받는다.
근데 통신할 때 ip 쓰는 찐따송구스럽다가 어딨냐 ㅋㅋ
당연히 쿠버네티스도 내부에서 통신할 때 DNS를 활용할 수 있도록 설계됐다.
원래 쿠버네티스에는 kube-dns라는 dns 서버가 어플리케이션이 있었다.
근데 이걸 조금 더 확장하는 애드온이 현재 거의 표준이 돼버린 것이다.
그래서 kube-system
에서 확인해보면 kube-dns
라는 이름의 서비스를 확인할 수 있다.
이를 통해 추후에 다른 dns 애드온을 사용한다고 해도 클러스터의 모든 dns는 kube-dns 서비스를 사용하므로 문제가 없게 된다.
파드는 kubelet이 생성 시점에 dns을 지정해준다.
이게 싫다면 kubelet 플래그에 --resolv-conf=""
로 설정해주고, 다른 경로를 쓰고 싶을 때도 이걸로 지정해주면 된다.
서비스도 만들어질 때 쿠버가 dns를 만들어준다.
도메인 질의 구조
(참고로 초록색 2번 플로우에서 파드 B는 절대로 저런 도메인을 가질 리 없지만 보기 편하라고 저렇게 적었다.)
아주 간단하게 도메인 질의 과정을 보자면 이런 식으로 이뤄진다.
클러스터의 컨테이너들은 자신 파일시스템에 /etc/resolve.conf
의 내용을 바탕으로 질의할 DNS 서버를 정한다.
근데 파드 생성 시 해당 파일에는 항상 10.96.0.10
과 같은 식으로 무조건 Core dns의 서비스 IP가 적힌다.
그래서 모든 도메인 질의는 무조건 Core DNS를 통해 이뤄지게 된다.
이때 Core DNS는 받은 도메인에 따라 크게 두 가지 동작을 하게 된다.
- 클러스터 내부 주소
- 클러스터 내부 도메인은 클러스터 생성 시점에 설정할 수 있음(기본값:
*.cluster.local
) - 평소 core dns는 kube-apiserver를 감시하면서 서비스나 파드의 정보를 감시하고 있다.
- 그래서 이를 기반으로 파드나 서비스의 도메인이 들어왔을 때 해당 ip를 알려줄 수 있다.
- 클러스터 내부 도메인은 클러스터 생성 시점에 설정할 수 있음(기본값:
- 클러스터 외부 주소
- 클러스터 도메인이 아니면 노드의
/etc/resolve.conf
정보를 이용한다. - 여기에는 노드가 사용하는 도메인 서버가 적혀있을 거라 결과적으로 업스트림 네임 서버로 트래픽을 forward한다.[2]
- 클러스터 도메인이 아니면 노드의
도메인 네임 구조
기본적으로 사용되는 방식부터 본다.
참고로, 클러스터에서 사용할 도메인을 생성 시점에 결정할 수 있는데 기본값은 cluster.local
이다.
파드에 대해서는 네임스페이스를 지정하지 않아도 된다.
다만 서비스는 무조건 네임스페이스를 지정해야 한다.
파드의 /etc/resolv.conf
에 kubelet이 클러스터에서 활용할 수 있는 정보를 넣어준다.
nameserver 10.32.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
이런 식이다.
서비스에 대해서는 이런 방식으로 도메인 네임이 지정된다.
{서비스 이름}.{네임스페이스}.svc.cluster.local
Service#헤드리스 서비스도 당연히 도메인 네임이 부여되는데, 이걸로 쿼리를 날리면 연결된 엔드포인트의 ip들이 나오게 된다.
헤드리스는 클러스터 ip가 None이니 당연한 결과다.
SRV 레코드에 대해서도 나온다.
_{포트이름}._{포트 프로토콜}.{서비스 이름}.{네임스페이스}.svc.cluster.local
아직 DNS 문서를 안 만들어서 남기는데, 이건 사용되는 프로토콜과 포트까지 함께 리턴한다.
한 도메인에 여러 서비스가 있을 때 사용하면 좋다.
ip 별 가중치도 부여할 수 있는, 일반 a 레코드의 확장 버전이랄까.
파드는 조금 다르다.
{파드-ip}.{네임스페이스}.pod.cluster.local
가령 파드가 10.10.10.10
의 ip를 가졌다면 10-10-10-10
과 같은 식이 된다.
서비스를 통해 노출되는 파드라면,
{파드-ip}.{서비스 이름}.{네임스페이스}.pod.cluster.local
으로도 접근 가능하다.
파드에 .spec.hostname
필드가 명시된다면 파드의 호스트네임은 이것으로 된다.
아니라면 그냥 파드의 이름이 호스트네임이다.
또한 .spec.subdomain
필드가 있다면 말 그대로 서브도메인도 설정된다.
그래서 .spec.hostname=foo
에 .spec.subdomain=bar
인 파드는 foo.bar.{네임스페이스}.svc.cluster.local
를 또 가진다.
만약 같은 네임스페이스에 같은 서브도메인을 가진 파드가 있다면 여기에 A레코드도 돌려준다.
hostname 없이 subdomain만 지정하면 a레코드만 만들어준다.
T-스테이트풀셋과 연결되는 헤드리스 서비스에 관한 실험에서 내가 맘대로 실험한 게 그래서 실패한 것이다.
.spec.setHostnameAsFQND=True
스펙이 돼있으면 파드 내부에서 hostname을 조회했을 때 FQDN이 나온다.
근데, 원래 리눅스에서는 호스트네임이 64글자 이상 될 수 없기 때문에 FQDN이 이상 넘어간다면 에러가 난다.
이런 걸 체크할 때는 Admission Webhook을 사용하는 게 좋다.
파드 DNS 정책
파드마다 DNS 정책을 지정할 수 있다.
.spec.dnsPolicy
필드를 작성하면 된다.
- Default - 파드의 resolv.conf가 노드의 파일을 기본적으로 사용한다.
- ClusterFirsrt - 클러스터 도메인에 맞지 않는 도메인에 대해서는 상위 dns 서버로 보낸다.
- 클러스터 구성에 따라 상위 네임 서버를 사용하는 경우가 있을 수 있다.
- ClusterFirstWithHostNet - 호스트 네트워크로 돌아가는 파드에 대해서 적용.
- 호스트 네트워크 파드에 위의 옵션들을 주면 Default로 동작하게 된다.
- None - 쿠버 환경의 dns 세팅을 무시하고 파드
.spec.dnsConfig
필드의 값으로 설정된다.
아니 근데 헷갈리면 안 되는 게, 아무 설정 안 하면 기본값이 ClusterFirst
다;;
Default
에 낚이면 안 된다..
파드 DNS 설정
파드에 resolve.conf
에 추가되는 문자열 설정인데, 위의 정책이 None
일 때는 필수적으로 설정돼야 한다.
dnsPolicy: "None"
dnsConfig:
nameservers:
- 192.0.2.1 # this is an example
searches:
- ns1.svc.cluster-domain.example
- my.dns.search.suffix
options:
- name: ndots
value: "2"
- name: edns0
이 이렇게 된 예시는 아래처럼 resolv.conf
에 추가된다.
nameserver 1.2.3.4
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0
options 부분은 조금 설명이 필요할 수 있겠다.
ndots는 FQDN으로 간주할 범위를 말한다.
2로 돼있으니, foo.bar.
라는 식의 쿼리가 나오면 뒤에 추가 도메인을 붙이지 않고 바로 네임서버로 질의를 날린다.
edns0은 dns 프로토콜을 확장할 수 있게 해주는 기능이라고 한다.
dns 검색 도메인 리스트 제한
쿠버에서는 도메인 리스트가 32개를 넘거나, 전체 도메인의 길이가 2048자를 넘기전까지는 설정에 대한 제한을 크게 하지 않는다.
이 제한은 노드, 파드, dns 설정 파일에 각각 적용된다.
근데 컨테이너 런타임마다 이에 대한 제한이 개별 적용돼있을 수 있어서, 이 제한과 충돌이 일어나는 경우가 있었다고 한다.
Containerd 1.5.5, CRI-O 1.21에서 이런 이슈가 있었다고 한다.
core dns 설정
이 친구는 플러그인처럼 동작하기에 동적으로 설정이나 기능을 넣을 수 있다.[3]
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
이런 식으로 kube-system
네임스페이스에 coredns
라는 이름으로 ConfigMap을 넣어주면 된다.
각 절에 대한 설명을 조금 해보자면..
- errors
- 에러에 대한 표준 출력 정의
- health
- 코어dns 서버에 대한 헬스체크로,
http://localhost:8080/health
경로로 수행된다. - lameduck은 프로세스가 종료될 때 unhealthy로 체크 후 5초 대기해준다는 것이다.
- 코어dns 서버에 대한 헬스체크로,
- ready
- 8181 포트가 열려있을 때 준비 상태로 둔다.
- kubernetes
- 쿠버네티스의 서비스, 파드에 대한 쿼리 설정 블록.
- ttl은 쿼리 캐싱으로, 기본 5초이며 0~3600 사이의 값으로 설정할 수 있다.
- pods
- insecure는 kube-dns의 호환성을 위한 옵션이다.
- verified를 주면 같은 네임스페이스의 요청은 무조건 A레코드를 준다.
- disabled는 파드 레코드를 쓰지 않을 때 사용하면 된다.
- Prometheus는 말 그대로
http://localhost:9153/metrics
에 프로메테우스 형식의 메트릭을 노출해준다. - forward는 core dns가 처리할 수 없는 도메인에 대해 외부로 쿼리를 보낼 resolve.conf 파일 경로를 지정한다.
- 그냥 노드의 resolve.conf 파일 경로를 제공하면 된다.
- cache는 프론트엔드 캐시를 가능케 한다...?
- loop는 루프가 발생했을 때 이를 중단시켜준다.
- reload는 이 파일이 수정됐을 때 동적으로 리로딩을 가능케 하는 옵션이다.
- 최대 2분까지 걸릴 수 있다.
- loadbalance는 dns 로드밸런싱을 해주는 기능이다.
여기다 추가 설정을 해도 된다.
consul.local:53 {
errors
cache 30
forward . 10.150.0.1
}
Consul을 사용한다면 10.150.0.1에 dns 서버가 새로 서는데, 이쪽으로 dns 쿼리를 설정해주는 방식이다.
참고사항.
coredns는 스텁도메인 및 네임서버에 대한 fqdn을 지원하지 않는다.
fqdn 네임서버 설정은 전부 생략된다고 한다.
관련 문서
이름 | noteType | created |
---|---|---|
Core DNS | knowledge | 2025-01-09 |
S-flannel dns 질의 실패 | topic/shooting | 2024-09-11 |