Sådan deler du DNS mellem Kubernetes og en OpenVPN-sidecar
En sidecar-pod har brug for to navneverdener. Peg én loopback-resolver mod klyngen og tunnelen, og hold op med at stole på en redigeret resolv.conf.
Pods, der kører en OpenVPN-sidecar, fejler navneopslag efter et genkendeligt mønster: klyngenavne hænger, fjernnavne svarer, indtil podden genstartes, og din redigerede resolver-fil vender tilbage. Intet i den sekvens er en tunnel-fejl. En pod har én resolver-fil og to navneverdener, og filen kan liste servere i rækkefølge, men kan ikke udtrykke zoner. Den holdbare løsning er en resolver, der kører inde i podden og ejer opdelingen, så filen forbliver triviel, og ingen politik har grund til at overskrive den.
Hvorfor den ændring, du lavede, bliver ved med at forsvinde
Filen tilhører kubelet, ikke dig. Under dnsPolicy: ClusterFirst genererer kubelet den til at pege på klyngens DNS og genererer den igen, hver gang en container starter. Alt, hvad du ændrer via kubectl exec, lever i containerens skrivbare lag, så en genstart, en udsmidning eller en rollout kasserer det. Pod-specifikationen bærer stadig den samme politik, og derfor kommer den samme fil tilbage.
At sætte dnsPolicy: None stopper gendannelsen og fjerner også klyngeopslag. Du får en fil, du kontrollerer, og mister de navne, du havde brug for. Den udveksling er hele problemet: resolv.conf kender ikke zoner, kun servere, der skal prøves i rækkefølge, så den kan ikke sende cluster.local til én adresse og en privat zone til en anden.
Kør én resolver i podden, på loopback
Peg podden mod én enkelt navneserver på loopback, og lad derefter en CoreDNS-container, der deler poddens netværksnamespace, svare på port 53. Sæt politikken til None, giv podden den ene adresse, og tilføj DNS-containeren ved siden af VPN'en og applikationen:
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-corefileAlle tre containere deler ét netværksnamespace, så 127.0.0.1:53 fra applikationscontaineren når CoreDNS uden service, uden port-forward og uden omvej gennem klyngenettet. Montering af Corefile fra et ConfigMap holder den som et versioneret input i stedet for en fil, der redigeres på stedet. Den genererede resolv.conf indeholder derefter én navneserver og intet andet, hvilket er en tilstand, du kan ræsonnere om.
To detaljer bider her. Det officielle image binder port 53 som root, og nogle klyngepolitikker kræver, at du skriver det eksplicit. Hver container i podden afhænger nu af, at den ene container er oppe, så tilføj en readiness-probe mod et harmløst navn, og lad applikationen tåle opstartsfejl.
Giv hver privat zone sin egen upstream
En Corefile er en liste af zoner, der hver navngiver den server, der kan svare på den. Én blok dækker tunnelzonen, og blokken, der fanger alt, kommer sidst og tager alt, som ingen eksplicit blok gjorde krav på. Blokken, der fanger alt, skal pege på en rigtig upstream, aldrig tilbage på podden: .:53 { forward . 10.100.0.10 } genbruger klyngeresolveren, fordi den resolver allerede videresender offentlige navne til nodens egne resolvere. Intet andet i podden behøver at vide, hvilket netværk et navn hører til.
.:53 {
errors
cache 30
forward . 10.100.0.10
}
svc.acme.internal:53 {
errors
cache 30
forward . 192.168.240.1
}Læs klyngeadressen fra kube-dns-servicen i kube-system i stedet for at kopiere en fra en artikel, fordi service-CIDR'er er forskellige. Tunneladressen kommer fra dhcp-option DNS-værdien, som OpenVPN-serveren sender, eller fra den autoritative server for zonen. Hvis din klyngeresolver ikke svarer på offentlige navne, så sæt nodens resolveradresse i blokken, der fanger alt, læst på noden og ikke inde i podden.
Peg ikke blokken, der fanger alt, på /etc/resolv.conf, som er den fristende én-linje-løsning og en fælde i netop denne opsætning. Under dnsPolicy: None indeholder poddens egen fil den eneste navneserver 127.0.0.1, så målet er CoreDNS selv, og hver uafklaret forespørgsel gentages, indtil den udløber. En blok ved navn svc.acme.internal matcher det navn og hvert navn under det, hvilket dækker db.svc.acme.internal og udelukker svc.acme.internal.example.net. Det afsluttende punktum er kosmetisk, fordi zonens navne normaliseres til fuldt kvalificeret form; suffiksgrænsen er det, der afgør, hvor en forespørgsel lander.
Afgør, om videresendelsen hører hjemme i klyngeresolveren
Hvis hver workload i namespacet har brug for tunnelzonen, er en videresendelsesblok i CoreDNS ConfigMap under kube-system billigere end en sidecar per pod. Den ændring er på klyngeniveau: den kræver administratorrettigheder og en genstart af et deployment, som alle afhænger af. Begræns den til den ene zone, aldrig til roden. En enkelt workloads tunneladresse bliver derefter hver pods resolver for zonen, og den dag tunnelen falder, mister hele klyngen navnet.
Sidecar'en holder eksplosionsradiusen inde i én pod, hvilket er det bedre standardvalg, når kun få tjenester har brug for det eksterne netværk. Uanset hvad du vælger, så skriv ned, hvilken resolver der ejer zonen, fordi den næste, der fejlfinder en timeout, vil spørge.
Navne, der slår op, mens forbindelser stadig fejler
Opslag er en af de to ting, tunnelen skal bære; den anden er returvejen, og poddens netværk skjuler den godt. Hvis selve OpenVPN-serveren kører som en pod, styrer regler, du tilføjer i den container, ikke trafik, der forlader gennem noden. Den fejl er snæver: kun pods på noden, der også kørte serveren, fik svar, og alt andet udløb.
At køre serveren med hostNetwork: true sætter den tilbage i nodens namespace, hvor en FORWARD-regel og kildetranslation virker. Oversæt klientpuljen på vej ud, ellers har ingen ekstern node en rute tilbage til en tunneladresse som 10.8.0.6. Kontroller begge retninger, før du rører Corefile igen. En tunnel, der bærer forespørgslen og ikke svaret, giver præcis samme timeout som en resolver, der aldrig modtog forespørgslen.
Verificér fra en ny pod, ikke fra den session, du rettede
Spørg eksplicit til begge zoner, og gentag derefter forespørgslen, efter at podden er udskiftet. Konfiguration, der kun findes i en kørende container, er ikke konfiguration:
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 +shortHvis det første par svarer, og parret efter udskiftningen ikke gør, er Corefile i orden, og pod-specifikationen er ikke. Hvis begge svarer, og applikationen stadig ikke kan forbinde, så stop med at fejlfinde DNS og test TCP-vejen med nc -vz db.svc.acme.internal 5432 inde fra containeren. Gem dig-outputtet i billetten, fordi det er det artefakt, der skiller et navneproblem fra et routingproblem.