Kubernetes와 OpenVPN 사이드카 사이에서 DNS를 분할하는 방법
사이드카 파드에는 두 개의 이름 세계가 필요합니다. 하나의 루프백 리졸버를 클러스터와 터널로 향하게 하고, 편집한 resolv.conf에 의존하지 마세요.
OpenVPN 사이드카를 실행하는 파드는 이름 해석에서 알아볼 수 있는 패턴으로 실패합니다. 클러스터 이름은 멈추고, 원격 이름은 파드가 재시작될 때까지 응답하며, 편집한 리졸버 파일은 원래대로 돌아갑니다. 그 순서 어디에도 터널 결함은 없습니다. 파드에는 리졸버 파일 하나와 두 개의 이름 세계가 있고, 파일은 서버를 순서대로 나열할 수 있지만 영역을 표현할 수는 없습니다. 지속적인 해결책은 파드 안에서 실행되며 분할을 소유하는 리졸버이고, 그러면 파일은 단순하게 남고 어느 정책도 다시 쓸 이유가 없습니다.
당신이 만든 편집이 계속 사라지는 이유
그 파일은 kubelet의 것이지 당신의 것이 아닙니다. dnsPolicy: ClusterFirst 아래에서 kubelet은 클러스터 DNS를 가리키도록 파일을 생성하고 컨테이너가 시작될 때마다 다시 생성합니다. kubectl exec로 바꾼 것은 컨테이너의 쓰기 가능 계층에 살기 때문에 재시작, 축출, 롤아웃이 그것을 버립니다. 파드 명세는 여전히 같은 정책을 담고 있고, 그래서 같은 파일이 돌아옵니다.
dnsPolicy: None를 설정하면 재생성이 멈추고 클러스터 해석도 사라집니다. 통제하는 파일을 얻는 대신 필요했던 이름을 잃습니다. 이 교환이 문제의 전부입니다. resolv.conf에는 영역 개념이 없고 순서대로 시도할 서버만 있으므로, cluster.local를 한 주소로, 사설 영역을 다른 주소로 보낼 수 없습니다.
파드 안에서 루프백에 리졸버 하나를 실행하세요
파드를 루프백의 단일 네임서버로 향하게 하고, 파드의 네트워크 네임스페이스를 공유하는 CoreDNS 컨테이너가 포트 53에서 응답하게 하세요. 정책을 None로 설정하고, 그 하나의 주소를 파드에 주고, VPN과 애플리케이션 옆에 DNS 컨테이너를 추가하세요:
spec:
dnsPolicy: None
dnsConfig:
nameservers:
- 127.0.0.1
options:
- name: ndots
value: "5"
containers:
- name: dns
image: coredns/coredns
args: ["-conf", "/etc/coredns/Corefile"]
volumeMounts:
- {name: corefile, mountPath: /etc/coredns, readOnly: true}
- name: vpn
image: your-openvpn-client-image
- name: app
image: your-app-image
volumes:
- name: corefile
configMap:
name: split-dns-corefile세 컨테이너가 하나의 네트워크 네임스페이스를 공유하므로, 애플리케이션 컨테이너의 127.0.0.1:53는 서비스도, port-forward도, 클러스터 네트워크를 도는 우회도 없이 CoreDNS에 도달합니다. ConfigMap에서 Corefile을 마운트하면 현장에서 편집하는 파일이 아니라 버전 관리되는 입력으로 유지됩니다. 생성된 resolv.conf는 그다음 하나의 네임서버만 담고 다른 것은 없으며, 그것에 대해 추론할 수 있는 상태입니다.
여기서 두 가지가 문제가 됩니다. 공식 이미지는 포트 53을 root로 바인딩하고, 일부 클러스터 정책은 그것을 명시적으로 쓰도록 요구합니다. 이제 파드의 모든 컨테이너가 그 하나의 컨테이너가 뜼 있는지에 의존하므로, 무해한 이름에 대한 readiness 프로브를 추가하고 애플리케이션이 시작 실패를 견디게 하세요.
각 사설 영역에 자체 upstream을 주세요
Corefile은 영역 목록이고, 각 항목은 그 영역에 응답할 수 있는 서버를 지정합니다. 하나의 블록이 터널 영역을 담당하고, 모든 것을 잡는 블록은 마지막에 와서 어느 명시적 블록도 주장하지 않은 모든 것을 가져갑니다. 모든 것을 잡는 블록은 실제 upstream을 향해야 하며 결코 파드로 돌아가서는 안 됩니다. .:53 { forward . 10.100.0.10 }는 클러스터 리졸버를 재사용하는데, 그 리졸버가 이미 공개 이름을 노드 자체의 리졸버로 전달하기 때문입니다. 파드의 다른 무엇도 이름이 어느 네트워크에 속하는지 알 필요가 없습니다.
.:53 {
errors
cache 30
forward . 10.100.0.10
}
svc.acme.internal:53 {
errors
cache 30
forward . 192.168.240.1
}클러스터 주소는 기사에서 복사하는 대신 kube-system의 kube-dns 서비스에서 읽으세요. 서비스 CIDR이 다르기 때문입니다. 터널 주소는 OpenVPN 서버가 밀어 넣는 dhcp-option DNS 값이나 해당 영역의 권위 서버에서 오며, 클러스터 리졸버가 공개 이름에 응답하지 않으면 노드의 리졸버 주소를 모든 것을 잡는 블록에 넣으세요. 파드 안이 아니라 노드에서 읽은 값이어야 합니다.
모든 것을 잡는 블록을 /etc/resolv.conf로 향하게 하지 마세요. 그것은 유혹적인 한 줄이고 바로 이 구성에서 함정입니다. dnsPolicy: None 아래에서 파드 자신의 파일은 유일한 네임서버 127.0.0.1를 담고 있으므로 그 대상은 CoreDNS 자체이고 일치하지 않는 모든 질의는 시간 초과까지 재귀합니다. svc.acme.internal라는 블록은 그 이름과 그 아래의 모든 이름에 일치하며, db.svc.acme.internal를 포함하고 svc.acme.internal.example.net를 제외합니다. 끝의 점은 겸모양일 뿐이고 영역 이름은 완전 수식 형식으로 정규화됩니다. 질의가 어디에 떨어지는지를 결정하는 것은 접미사 경계입니다.
전달을 클러스터 리졸버에 둘 지를 결정하세요
네임스페이스의 모든 워크로드에 터널 영역이 필요하면, kube-system 아래의 CoreDNS ConfigMap에 전달 블록을 두는 것이 파드마다 사이드카를 두는 것보다 저렴합니다. 그 변경은 클러스터 규모이고 관리자 권한과 모두가 의존하는 디플로이멘트의 재시작이 필요합니다. 그 하나의 영역으로 제한하고 결코 루트로 향하지 마세요. 한 워크로드의 터널 주소가 그러면 그 영역에 대한 모든 파드의 리졸버가 되고, 터널이 끊기는 날 클러스터 전체가 이름을 잃습니다.
사이드카는 폭발 반경을 한 파드 안에 유지하며, 소수의 서비스만 원격 네트워크가 필요할 때 더 나은 기본값입니다. 무엇을 선택하든 어느 리졸버가 영역을 소유하는지 적어 두세요. 다음에 타임아웃을 디버깅할 사람이 물을 것입니다.
연결이 아직 실패하는데 해석되는 이름
해석은 터널이 운반해야 할 두 가지 중 하나이고, 다른 하나는 돌아오는 경로이며 파드 네트워크가 그것을 잘 숨깁니다. OpenVPN 서버 자체가 파드로 실행되면 그 컨테이너에 추가한 규칙은 노드를 통해 나가는 트래픽을 제어하지 못합니다. 그 실패는 좁아서, 서버도 실행한 노드의 파드만 응답을 받았고 나머지는 모두 시간 초과되었습니다.
서버를 hostNetwork: true로 실행하면 노드의 네임스페이스로 돌아가고, 거기서 FORWARD 규칙과 소스 변환이 효력을 발휘합니다. 나갈 때 클라이언트 풀을 변환하지 않으면 어느 원격 노드도 10.8.0.6 같은 터널 주소로 돌아오는 경로를 갖지 못합니다. Corefile을 다시 건드리기 전에 양방향을 확인하세요. 질의는 운반하고 응답은 운반하지 않는 터널은 질의를 한 번도 받지 못한 리졸버와 똑같은 타임아웃을 만드닙니다.
고친 세션이 아니라 새 파드에서 검증하세요
두 영역에 모두 명시적으로 질의하고, 파드가 교체된 뒤 질의를 반복하세요. 실행 중인 컨테이너에만 존재하는 구성은 구성이 아닙니다:
kubectl exec -it deploy/app -- dig @127.0.0.1 kubernetes.default.svc.cluster.local +short
kubectl exec -it deploy/app -- dig @127.0.0.1 db.svc.acme.internal +short
kubectl delete pod -l app=your-app
kubectl wait --for=condition=Ready pod -l app=your-app --timeout=90s
kubectl exec -it deploy/app -- dig @127.0.0.1 db.svc.acme.internal +short첫 쌍이 응답하고 교체 후 쌍이 응답하지 않으면 Corefile은 맞고 파드 명세는 틀립니다. 둘 다 응답하는데 애플리케이션이 여전히 연결하지 못하면 DNS 디버깅을 멈추고 컨테이너 안에서 nc -vz db.svc.acme.internal 5432로 TCP 경로를 시험하세요. dig 출력을 티켓에 남기세요. 그것이 이름 문제와 라우팅 문제를 가르는 산출물입니다.