Com dividir el DNS entre Kubernetes i un sidecar OpenVPN
Un pod sidecar necessita dos mons de noms. Apunteu un sol resolutor de loopback al clúster i al túnel, i deixeu de dependre d'un resolv.conf editat.
Els pods que executen un sidecar OpenVPN fallen la resolució de noms amb un patró recognoscible: els noms del clúster es bloquegen, els noms remots responen fins que el pod es reinicia, i el fitxer de resolutor que heu editat torna enrere. Res d'aquesta seqüència no és una fallada del túnel. Un pod té un fitxer de resolutor i dos mons de noms, i el fitxer pot llistar servidors en ordre però no pot expressar zones. La solució duradora és un resolutor que s'executa dins del pod i posseeix la divisió, de manera que el fitxer queda trivial i cap política no té motiu per reescriure'l.
Per què l'edició que heu fet no para de desaparèixer
El fitxer pertany al kubelet, no a vosaltres. Amb dnsPolicy: ClusterFirst el kubelet el genera per apuntar al DNS del clúster, i el regenera cada vegada que s'inicia un contenidor. Tot el que canvieu mitjançant kubectl exec viu a la capa d'escriptura del contenidor, així que un reinici, un desallotjament o un desplegament el descarten. L'especificació del pod continua portant la mateixa política, i per això torna el mateix fitxer.
Posar dnsPolicy: None atura la regeneració i també elimina la resolució del clúster. Guanyeu un fitxer que controleu i perdeu els noms que necessitàveu. Aquest intercanvi és tot el problema: resolv.conf no té noció de zones, només de servidors per provar en ordre, així que no pot enviar cluster.local a una adreça i una zona privada a una altra.
Executeu un sol resolutor dins del pod, al loopback
Apunteu el pod a un sol servidor de noms al loopback, i després deixeu que un contenidor CoreDNS que comparteix l'espai de noms de xarxa del pod respongui al port 53. Poseu la política a None, doneu al pod aquesta única adreça i afegiu el contenidor DNS al costat de la VPN i de l'aplicació:
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-corefileEls tres contenidors comparteixen un sol espai de noms de xarxa, així que 127.0.0.1:53 des del contenidor de l'aplicació arriba a CoreDNS sense servei, sense port-forward i sense desviament per la xarxa del clúster. Muntar el Corefile des d'un ConfigMap el manté com una entrada versionada en lloc d'un fitxer editat al lloc. El resolv.conf generat aleshores conté un sol servidor de noms i res més, que és un estat sobre el qual podeu raonar.
Dos detalls mosseguen aquí. La imatge oficial enllaça el port 53 com a root, i algunes polítiques del clúster us obliguen a escriure-ho explícitament. Cada contenidor del pod ara depèn que aquest únic contenidor estigui en marxa, així que afegiu una sonda de disponibilitat contra un nom inofensiu i deixeu que l'aplicació toleri fallades d'inici.
Doneu a cada zona privada el seu propi upstream
Un Corefile és una llista de zones, i cadascuna nomena el servidor que la pot respondre. Un bloc cobreix la zona del túnel, i el bloc que ho atrapa tot ve al final i pren tot el que cap bloc explícit no ha reclamat. El bloc que ho atrapa tot ha d'apuntar a un upstream real, mai de tornada al pod: .:53 { forward . 10.100.0.10 } reutilitza el resolutor del clúster perquè aquest resolutor ja reenvia els noms públics als resolutors del propi node. Res més al pod no ha de saber a quina xarxa pertany un nom.
.:53 {
errors
cache 30
forward . 10.100.0.10
}
svc.acme.internal:53 {
errors
cache 30
forward . 192.168.240.1
}Llegiu l'adreça del clúster del servei kube-dns a kube-system en lloc de copiar-ne una d'un article, perquè els CIDR dels serveis difereixen. L'adreça del túnel ve del valor dhcp-option DNS que envia el servidor OpenVPN, o del servidor autoritatiu d'aquella zona. Si el vostre resolutor del clúster no respon noms públics, poseu l'adreça del resolutor del node al bloc que ho atrapa tot, llegida al node i no dins del pod.
No apunteu el bloc que ho atrapa tot a /etc/resolv.conf, que és la línia única temptadora i una trampa en aquesta configuració exacta. Amb dnsPolicy: None el fitxer del propi pod conté l'únic servidor de noms 127.0.0.1, així que aquest objectiu és el mateix CoreDNS i cada consulta no coincident recurreix fins que expira. Un bloc anomenat svc.acme.internal coincideix amb aquest nom i amb cada nom per sota, cosa que cobreix db.svc.acme.internal i exclou svc.acme.internal.example.net. El punt final és cosmètic, perquè els noms de zona es normalitzen a la forma plenament qualificada; la frontera del sufix és el que decideix on cau una consulta.
Decidiu si el reenviament pertany al resolutor del clúster
Si cada càrrega de treball de l'espai de noms necessita la zona del túnel, un bloc de reenviament al ConfigMap de CoreDNS sota kube-system és més barat que un sidecar per pod. Aquest canvi és d'abast de clúster: necessita drets d'administrador i un reinici d'un desplegament del qual tothom depèn. Limiteu-lo a aquella zona, mai a l'arrel. L'adreça del túnel d'una sola càrrega de treball es converteix aleshores en el resolutor de cada pod per a la zona, i el dia que el túnel cau, tot el clúster perd el nom.
El sidecar manté el radi d'explosió dins d'un sol pod, que és el millor valor per defecte quan només uns quants serveis necessiten la xarxa remota. Triïu el que triïu, anoteu quin resolutor posseeix la zona, perquè la propera persona que depuri un temps d'espera ho preguntarà.
Noms que es resolen mentre les connexions encara fallen
La resolució és una de les dues coses que el túnel ha de portar; l'altra és el camí de tornada, i la xarxa del pod l'amaga bé. Si el mateix servidor OpenVPN s'executa com a pod, les regles que afegiu dins d'aquest contenidor no governen el trànsit que surt pel node. Aquesta fallada és estreta: només els pods del node que també executaven el servidor obtenien respostes, i tota la resta va expirar.
Executar el servidor amb hostNetwork: true el torna a l'espai de noms del node, on una regla FORWARD i la traducció d'origen tenen efecte. Traduïu el pool de clients a la sortida, o cap node remot tindrà ruta de tornada a una adreça de túnel com 10.8.0.6. Comproveu les dues direccions abans de tocar de nou el Corefile. Un túnel que porta la consulta i no la resposta produeix exactament el mateix temps d'espera que un resolutor que no va rebre mai la consulta.
Verifiqueu des d'un pod nou, no des de la sessió que heu arreglat
Pregunteu explícitament per les dues zones, i després repetiu la consulta després que el pod sigui substituït. La configuració que només existeix en un contenidor en marxa no és configuració:
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 +shortSi el primer parell respon i el parell després de la substitució no, el Corefile està bé i l'especificació del pod no. Si tots dos responen i l'aplicació encara no es pot connectar, atureu la depuració del DNS i proveu el camí TCP amb nc -vz db.svc.acme.internal 5432 des de dins del contenidor. Guardeu la sortida de dig al tiquet, perquè és l'artefacte que separa un problema de noms d'un problema d'encaminament.