Как да разделим DNS между Kubernetes и OpenVPN сайдкар
Един под със сайдкар се нуждае от два свята на именуване. Насочете един loopback resolver към клъстера и тунела и спрете да разчитате на редактиран resolv.conf.
Подовете, които пускат OpenVPN сайдкар, се провалят при разрешаването на имена по разпознаваем модел: имената на клъстера забиват, отдалечените имена отговарят, докато подът не се рестартира, а редактираният от вас resolver файл се връща към старото. Нищо в тази последователност не е дефект на тунела. Един под има един resolver файл и два свята на именуване, а файлът може да изброи сървърите по ред, но не може да изрази зони. Трайното решение е resolver, който работи вътре в пода и притежава разделянето, така че файлът остава тривиален и никоя политика няма причина да го преписва.
Защо промяната, която направихте, продължава да изчезва
Файлът принадлежи на kubelet, а не на вас. Под dnsPolicy: ClusterFirst kubelet го генерира, за да сочи към клъстерния DNS, и го генерира наново при всяко стартиране на контейнер. Всичко, което промените чрез kubectl exec, живее в записваемия слой на контейнера, затова рестарт, изваждане или rollout го изхвърлят. Спецификацията на пода все още носи същата политика, поради което същият файл се връща.
Задаването на dnsPolicy: None спира повторното генериране и също премахва разрешаването в клъстера. Печелите файл, който контролирате, и губите имената, които ви трябваха. Тази размяна е целият проблем: resolv.conf не познава зони, а само сървъри, които да опитва по ред, затова не може да изпрати cluster.local към един адрес, а частна зона към друг.
Пуснете един resolver в пода, на loopback
Насочете пода към един сървър за имена на loopback, после оставете CoreDNS контейнер, който споделя мрежовото пространство на пода, да отговаря на порт 53. Задайте политиката на None, дайте на пода този един адрес и добавете DNS контейнера до VPN и приложението:
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 от контейнера на приложението достига до CoreDNS без услуга, без port-forward и без обиколка през клъстерната мрежа. Монтирането на Corefile от ConfigMap го пази като версиониран вход вместо файл, редактиран на място. Генерираният resolv.conf тогава държи един сървър за имена и нищо друго, което е състояние, за което можете да разсъждавате.
Два детайла жилят тук. Официалният образ свързва порт 53 като root, а някои клъстерни политики изискват да го напишете изрично. Всеки контейнер в пода сега зависи от това този един контейнер да е работещ, затова добавете readiness проверка срещу безобидно име и оставете приложението да понася провали при стартиране.
Дайте на всяка частна зона собствен upstream
Corefile е списък от зони, всяка посочва сървъра, който може да й отговори. Един блок покрива тунелната зона, а блокът, който хваща всичко, идва последен и взема всичко, което никой изричен блок не е заявил. Блокът, който хваща всичко, трябва да сочи към истински upstream, никога обратно към пода: .:53 { forward . 10.100.0.10 } използва отново клъстерния resolver, защото той вече препраща публичните имена към resolver-ите на самия възел. Нищо друго в пода не трябва да знае към коя мрежа принадлежи дадено име.
.:53 {
errors
cache 30
forward . 10.100.0.10
}
svc.acme.internal:53 {
errors
cache 30
forward . 192.168.240.1
}Прочетете адреса на клъстера от услугата kube-dns в kube-system, вместо да копирате такъв от статия, защото услуговите CIDR-и се различават. Адресът на тунела идва от стойността dhcp-option DNS, която изпраща OpenVPN сървърът, или от авторитативния сървър за тази зона. Ако клъстерният ви resolver не отговаря на публични имена, сложете адреса на resolver-а на възела в блока, който хваща всичко, прочетена на възела, а не вътре в пода.
Не насочвайте блока, който хваща всичко, към /etc/resolv.conf, това е примамливият едноредов вариант и капан в точно тази настройка. Под dnsPolicy: None файлът на самия под съдържа единствения сървър за имена 127.0.0.1, затова тази цел е самият CoreDNS и всяка несъответстваща заявка рекурсира, докато изтече времето. Блок с име svc.acme.internal съвпада с това име и с всяко име под него, което покрива db.svc.acme.internal и изключва svc.acme.internal.example.net. Завършващата точка е козметична, защото имената на зоните се нормализират до пълно квалифициран вид; границата на суфикса решава къде попада заявката.
Решете дали препращането принадлежи на клъстерния resolver
Ако всяко натоварване в namespace-а се нуждае от тунелната зона, блок за препращане в CoreDNS ConfigMap под kube-system е по-евтин от сайдкар на под. Тази промяна е в обхвата на клъстера: тя изисква администраторски права и рестарт на deployment, от който всички зависят. Ограничете я до тази една зона, никога до корена. Тунелният адрес на едно натоварване тогава става resolver на всеки под за зоната и в деня, в който тунелът падне, целият клъстер губи името.
Сайдкарът държи радиуса на взрива в един под, което е по-добрият вариант по подразбиране, когато само няколко услуги се нуждаят от отдалечената мрежа. Каквото и да изберете, запишете кой resolver притежава зоната, защото следващият човек, който ще отстранява проблем с изтичане на времето, ще попита.
Имена, които се разрешават, докато връзките още се провалят
Разрешаването е едно от двете неща, които тунелът трябва да носи; другото е обратният път, а мрежата на пода го скрива добре. Ако самият OpenVPN сървър работи като под, правилата, които добавите в този контейнер, не управляват трафика, който излиза през възела. Този провал е тесен: само подовете на възела, който също е пускал сървъра, получаваха отговори, а всичко останало изтече.
Пускането на сървъра с hostNetwork: true го връща в namespace-а на възела, където правило FORWARD и транслацията на източника действат. Транслирайте клиентския pool на изхода, или нито един отдалечен възел няма маршрут обратно до тунелен адрес като 10.8.0.6. Проверете двете посоки, преди отново да пипнете Corefile. Тунел, който носи заявката, но не отговора, дава точно същото изтичане на времето като resolver, който никога не е получил заявката.
Проверете от нов под, а не от сесията, която поправихте
Попитайте двете зони изрично, после повторете заявката, след като подът бъде заменен. Конфигурация, която съществува само в работещ контейнер, не е конфигурация:
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 и тествайте TCP пътя с nc -vz db.svc.acme.internal 5432 отвътре в контейнера. Запазете изхода от dig в тикета, защото това е артефактът, който отделя проблем с именуването от проблем с маршрутизацията.