Τα pod που εκτελούν ένα OpenVPN sidecar αποτυγχάνουν στην επίλυση ονομάτων με αναγνωρίσιμο μοτίβο: τα ονόματα του cluster κολλάνε, τα απομακρυσμένα ονόματα απαντούν μέχρι να επανεκκινήσει το pod, και το επεξεργασμένο αρχείο resolver επανέρχεται. Τίποτα σε αυτή την ακολουθία δεν είναι βλάβη του tunnel. Ένα pod έχει ένα αρχείο resolver και δύο κόσμους ονομάτων, και το αρχείο μπορεί να απαριθμήσει διακομιστές με σειρά αλλά δεν μπορεί να εκφράσει ζώνες. Η διαρκής λύση είναι ένας resolver που εκτελείται μέσα στο pod και κατέχει τον διαχωρισμό, ώστε το αρχείο να παραμένει απλό και καμία πολιτική να μην έχει λόγο να το ξαναγράψει.

Γιατί η αλλαγή που κάνατε εξαφανίζεται συνεχώς

Το αρχείο ανήκει στο kubelet, όχι σε εσάς. Κάτω από dnsPolicy: ClusterFirst το kubelet το δημιουργεί ώστε να δείχνει στο DNS του cluster και το αναδημιουργεί κάθε φορά που ξεκινά ένα container. Ότι αλλάζετε μέσω kubectl exec ζει στο εγγράψιμο επίπεδο του container, οπότε μια επανεκκίνηση, μια έξωση ή ένα rollout το πετάει. Η προδιαγραφή του pod εξακολουθεί να φέρνει την ίδια πολιτική, γι' αυτό επιστρέφει το ίδιο αρχείο.

Η ρύθμιση dnsPolicy: None σταματά την αναδημιουργία και αφαιρεί επίσης την επίλυση εντός cluster. Κερδίζετε ένα αρχείο που ελέγχετε και χάνετε τα ονόματα που χρειαζόσασταν. Αυτή η ανταλλαγή είναι όλο το πρόβλημα: το resolv.conf δεν έχει έννοια ζωνών, μόνο διακομιστών που δοκιμάζονται με σειρά, οπότε δεν μπορεί να στείλει το cluster.local σε μια διεύθυνση και μια ιδιωτική ζώνη σε άλλη.

Εκτελέστε έναν resolver στο pod, στο loopback

Στρέψτε το pod σε έναν μόνο διακομιστή ονομάτων στο loopback και μετά αφήστε ένα container CoreDNS που μοιράζεται τον χώρο ονομάτων δικτύου του pod να απαντά στη θύρα 53. Ορίστε την πολιτική σε None, δώστε στο pod αυτή τη μία διεύθυνση και προσθέστε το container 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

Και τα τρία container μοιράζονται έναν χώρο ονομάτων δικτύου, οπότε το 127.0.0.1:53 από το container της εφαρμογής φτάνει στο CoreDNS χωρίς υπηρεσία, χωρίς port-forward και χωρίς παράκαμψη μέσα από το δίκτυο του cluster. Η προσάρτηση του Corefile από ένα ConfigMap το κρατά ως έκδοση εισόδου αντί για αρχείο που επεξεργάζεται επί τόπου. Το παραγόμενο resolv.conf κρατά τότε έναν διακομιστή ονομάτων και τίποτα άλλο, μια κατάσταση για την οποία μπορείτε να συλλογιστείτε.

Δύο λεπτομέρειες δαγκώνουν εδώ. Η επίσημη εικόνα δεσμεύει τη θύρα 53 ως root και ορισμένες πολιτικές cluster απαιτούν να το γράψετε ρητά. Κάθε container στο pod εξαρτάται τώρα από το ότι αυτό το ένα container είναι ζωντανό, οπότε προσθέστε ένα readiness probe σε ένα ακίνδυνο όνομα και αφήστε την εφαρμογή να ανέχεται αποτυχίες εκκίνησης.

Δώστε σε κάθε ιδιωτική ζώνη το δικό της upstream

Ένα Corefile είναι μια λίστα ζωνών, καθεμία ονομάζει τον διακομιστή που μπορεί να την απαντήσει. Ένα μπλοκ καλύπτει τη ζώνη του tunnel και το μπλοκ που πιάνει τα πάντα έρχεται τελευταίο και παίρνει ό, τι δεν διεκδίκησε κανένα ρητό μπλοκ. Το μπλοκ που πιάνει τα πάντα πρέπει να στοχεύει πραγματικό upstream, ποτέ πίσω στο pod: το .:53 { forward . 10.100.0.10 } επαναχρησιμοποιεί τον resolver του cluster, επειδή αυτός ο resolver ήδη προωθεί τα δημόσια ονόματα στους resolver του ίδιου του κόμβου. Τίποτα άλλο στο pod δεν χρειάζεται να ξέρει σε ποιο δίκτυο ανήκει ένα όνομα.

.:53 {
  errors
  cache 30
  forward . 10.100.0.10
}

svc.acme.internal:53 {
  errors
  cache 30
  forward . 192.168.240.1
}

Διαβάστε τη διεύθυνση του cluster από την υπηρεσία kube-dns στο kube-system αντί να αντιγράψετε μία από ένα άρθρο, επειδή τα CIDR των υπηρεσιών διαφέρουν. Η διεύθυνση του tunnel προέρχεται από την τιμή dhcp-option DNS που στέλνει ο διακομιστής OpenVPN ή από τον authoritative διακομιστή για αυτή τη ζώνη. Αν ο resolver του cluster σας δεν απαντά σε δημόσια ονόματα, βάλτε τη διεύθυνση resolver του κόμβου στο μπλοκ που πιάνει τα πάντα, διαβασμένη στον κόμβο και όχι μέσα στο pod.

Μην στρέψετε το μπλοκ που πιάνει τα πάντα στο /etc/resolv.conf, που είναι η δελεαστική μονόγραμμη λύση και μια παγίδα σε αυτήν ακριβώς τη ρύθμιση. Κάτω από dnsPolicy: None το αρχείο του ίδιου του pod περιέχει τον μοναδικό διακομιστή ονομάτων 127.0.0.1, οπότε αυτός ο στόχος είναι το ίδιο το CoreDNS και κάθε αναπάντητο ερώτημα αναδρομεί μέχρι να λήξει. Ένα μπλοκ με όνομα svc.acme.internal ταιριάζει με αυτό το όνομα και κάθε όνομα κάτω από αυτό, που καλύπτει το db.svc.acme.internal και αποκλείει το svc.acme.internal.example.net. Η τελεία στο τέλος είναι κοσμητική, επειδή τα ονόματα ζωνών κανονικοποιούνται σε πλήρως πιστοποιημένη μορφή. Το όριο του επιθήματος αποφασίζει πού προσγειώνεται ένα ερώτημα.

Αποφασίστε αν η προώθηση ανήκει στον resolver του cluster

Αν κάθε φόρτος εργασίας στον namespace χρειάζεται τη ζώνη του tunnel, ένα μπλοκ προώθησης στο CoreDNS ConfigMap κάτω από kube-system είναι φθηνότερο από ένα sidecar ανά pod. Αυτή η αλλαγή είναι σε κλίμακα cluster: απαιτεί δικαιώματα διαχειριστή και επανεκκίνηση ενός deployment από το οποίο εξαρτώνται όλοι. Περιορίστε την σε αυτή τη μία ζώνη, ποτέ στη ρίζα. Η διεύθυνση tunnel ενός μεμονωμένου φόρτου εργασίας γίνεται τότε ο resolver κάθε pod για τη ζώνη, και την ημέρα που πέφτει το tunnel ολόκληρο το cluster χάνει το όνομα.

Το sidecar κρατά την ακτίνα έκρηξης μέσα σε ένα pod, που είναι η καλύτερη προεπιλογή όταν μόνο λίγες υπηρεσίες χρειάζονται το απομακρυσμένο δίκτυο. Ό, τι κι αν επιλέξετε, γράψτε ποιος resolver κατέχει τη ζώνη, επειδή το επόμενο άτομο που θα αποσφαλματώσει ένα timeout θα ρωτήσει.

Ονόματα που επιλύονται ενώ οι συνδέσεις ακόμη αποτυγχάνουν

Η επίλυση είναι ένα από τα δύο πράγματα που πρέπει να μεταφέρει το tunnel. Το άλλο είναι η διαδρομή επιστροφής, και το δίκτυο του pod την κρύβει καλά. Αν ο ίδιος ο διακομιστής OpenVPN εκτελείται ως pod, οι κανόνες που προσθέτετε μέσα σε αυτό το container δεν διέπουν την κίνηση που φεύγει μέσω του κόμβου. Αυτή η αποτυχία είναι στενή: μόνο τα pod στον κόμβο που εκτελούσε και τον διακομιστή έπαιρναν απαντήσεις, και όλα τα άλλα έληξαν.

Η εκτέλεση του διακομιστή με hostNetwork: true τον επαναφέρει στον χώρο ονομάτων του κόμβου, όπου ένας κανόνας FORWARD και η μετάφραση πηγής έχουν ισχύ. Μεταφράστε το pool πελατών στην έξοδο, αλλιώς κανένας απομακρυσμένος κόμβος δεν έχει διαδρομή επιστροφής σε μια διεύθυνση tunnel όπως η 10.8.0.6. Ελέγξτε και τις δύο κατευθύνσεις πριν αγγίξετε ξανά το Corefile. Ένα tunnel που μεταφέρει το ερώτημα και όχι την απάντηση παράγει ακριβώς το ίδιο timeout με έναν resolver που δεν έλαβε ποτέ το ερώτημα.

Επαληθεύστε από ένα νέο pod, όχι από τη συνεδρία που διορθώσατε

Ρωτήστε ρητά και τις δύο ζώνες και μετά επαναλάβετε το ερώτημα αφού αντικατασταθεί το pod. Η διαμόρφωση που υπάρχει μόνο σε ένα container που εκτελείται δεν είναι διαμόρφωση:

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 είναι εντάξει και η προδιαγραφή του pod δεν είναι. Αν και τα δύο απαντούν και η εφαρμογή ακόμη δεν μπορεί να συνδεθεί, σταματήστε να αποσφαλματώνετε το DNS και δοκιμάστε τη διαδρομή TCP με το nc -vz db.svc.acme.internal 5432 μέσα από το container. Κρατήστε την έξοδο του dig στο ticket, επειδή είναι το τεκμήριο που χωρίζει ένα πρόβλημα ονομάτων από ένα πρόβλημα δρομολόγησης.

Σχετικά άρθρα