Netwerk connecties binnen een Kubernetes omgeving kan een ingewikkelde zaak zijn. Pod networks, service networks, cluster IPs, container ports, host ports, node ports… soms zien we door de bomen het bos niet meer. Wellicht kan dit artikel wat licht doen schijnen op het ‘Kuberetes netwerk‘.
POD netwerk
Om bij de basis te beginnen, pods bevatten één of meerdere containers die samen op dezelfde node zijn geïnstalleerd en delen (dus) een gezamelijke netwerk. Dit houdt in dat alle containers in een pod elkaar kunnen bereiken via localhost. Als er bijvoorbeeld een WordPress container is die ‘luistert’ op TCP poort 8080 dan is deze voor alle overige containers bereikbaar via http://localhost:8080.
De default interface van Master is ‘eth0‘ met IP adres 192.168.1.10 en is verbonden met de interface genaamd ‘cni0‘ met IP adres 10.244.0.1
De default interface van Worker1 is ‘eth0‘ met IP adres 192.168.1.11 en is verbonden met de interface genaamd ‘cni0‘ met IP adres 10.244.1.1.
Worker2 heeft IP adres 192.168.1.12 op ‘eth0‘ en 10.244.2.1 op ‘cni0‘
Onderling kunnen de nodes dus via eth0 met elkaar communiceren en de pods communiceren onderling via een overlay-network. Deze gebruikt de ‘cni0‘ virtuele interface op Worker1 en dat overlay-network is dus 10.244.1.0/32. Op Worker2 is dat overlay-network 10.244.2.0/32. Alle pods krijgen een IP adres van het overlay-network en kunnen zo dus met elkaar communiceren. (CNI staat voor Container Network Interface) en wordt aangemaakt tijdens de initiële fase van de master(s) met de kubeadm parameter –pod-network-cidr
Een kubectl voorbeeld om pod IP’s te tonen:
$ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE hypriot-578c989b88-9plzz 1/1 Running 0 133m 10.244.1.102 worker1 nginx-654ddfd749-qqxq9 1/1 Running 0 138m 10.244.1.99 worker1 nginx2-57bbd47f4d-xbkfv 1/1 Running 0 3d5h 10.244.2.81 worker2
Zoals gezegd, kunnen alle pods communiceren met elkaar, zelfs als ze op een andere node zijn deployed. Flannel is (in dit geval) de default route voor dit netwerk en deze heeft eth0 weer als default gateway. Vanaf de Worker1 node:
$ ip route get 10.244.2.81 10.244.2.81 via 10.244.2.0 dev flannel.1 src 10.244.1.0 cache
Hierdoor is de nginx2 pod op worker2 bereikbaar voor de pods op worker1 en vice versa.
DNS
Met het aanmaken van de cluster wordt door kubeadm een dns-service geïnstalleerd genaamd ‘kube-dns‘ die een selector gebruikt ‘k8s-app=kube-dns‘. De deployment ‘coredns‘ in de kube-system namespace heeft deze labels en de pods fungeren als DNS service voor het cluster.
De masters en workers hebben deze DNS services echter niet standaard in /etc/resolv.conf maar die kunnen we er bijzetten. Ook de standaard domain-name van de cluster kunnen we als zoek-pad in de search plaatsen. We zoeken eerst de dns-service in de namespace kube-system met:
$ kubectl get svc --namespace=kube-system -l k8s-app=kube-dns kube-dns ClusterIP 10.96.0.10 53/UDP,53/TCP,9153/TCP 44h k8s-app=kube-dns
Vervolgens gebruiken we nslookup om het domain te vinden. Gebruik hiervoor het IP adres van de dns-service:
$ nslookup > server 10.96.0.10 Default server: 10.96.0.10 Address: 10.96.0.10#53 > 10.96.0.10 Server: 10.96.0.10 Address: 10.96.0.10#53 10.0.96.10.in-addr.arpa name = kube-dns.kube-system.svc.cluster.local.
Zoals je kunt zien wordt dit vertaald naar [SERVICE_NAME].[NAMESPACE_NAME].svc.cluster.local waarbij ‘svc.cluster.local de domain naam is. Deze gaan we als zoekpad in /etc/resolv.conf plaatsen en tevens zetten we het IP adres van de dns-service als erbij:
search svc.cluster.local nameserver 10.96.0.10 nameserver 8.8.8.8
Indien dit nu voor elke node zo ingesteld wordt, kan vanaf elke node een service aangeroepen worden via [SERVICE_NAME].[NAMESPACE_NAME], bijvoorbeeld:
$ curl http://app-v1.production
Congratulations! Version 1.0 of your application is running on Kubernetes.
Om de DNS te testen kan een pod gebruikt worden waarin NSLOOKUPs gedaan kunnen worden. Deze pod.yml:
apiVersion: v1 kind: Pod metadata: name: dnsutils namespace: default spec: containers: - name: dnsutils image: k8s.gcr.io/e2e-test-images/jessie-dnsutils:1.3 command: - sleep - "3600" imagePullPolicy: IfNotPresent restartPolicy: Always
Nadat deze applied wordt kan een nslookup gedaan worden met:
$ kubectl exec -it dnsutils -- nslookup <domeinnaam>
Eventuele logs kunnen dan opgevraagd worden. Voeg daarvoor ‘log’ toe aan de configmap via: met:
$ kubectl -n kube-system edit configmap coredns
Voeg dan ‘log’ toe aan de Corefile sectie (net boven ‘errors’). Binnen een aantal minuten worden coredns events gelogd en kunnen bekeken worden met:
$ kubectl logs -n kube-system -l k8s-app=kube-dns
(niet vergeten de log weer uit te zetten!)
Opmerking: Sommige Linux distributies (zoals Ubuntu) gebruiken ‘systemd-resolved.service‘ voor DNS requests en het bestand ‘/etc/resolv.conf‘ wordt op deze systemen overschreven (gelinked) door een stub-file. Deze resolved-service proxied de DNS requests naar de upstream DNS resolvers die geconfigureerd staan in ‘/etc/systemd/resolved.conf‘.
CoreDNS ConfigMap
De coredns configmap heeft standaard een verwijzing naar /etc/resolv.conf voor alle requests die buiten het Kubernetes netwerk vallen:
forward . /etc/resolv.conf
Dit kan gewijzigd worden naar een upstream DNS server, bijvoorbeeld:
forward . 8.8.8.8
Indien de DNS verzoeken voor stub-domains naar een andere DNS server dienen te worden gestuurd, kan een stub-domain-stanza toegevoegd worden aan de configMap. Bijvoorbeeld:
digitalinfo.local:53 { errors cache 30 forward . 192.168.178.1 }
De complete configmap ziet er dan alsvolgt uit:
apiVersion: v1 kind: ConfigMap metadata: name: coredns namespace: kube-system data: Corefile: | .:53 { errors health { lameduck 5s } ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } prometheus :9153 forward . 8.8.8.8 cache 30 loop reload loadbalance } # STUBDOMAINS digitalinfo.local:53 { errors cache 30 forward . 192.168.178.1 }
Als altternatief kunnen host-entries toegevoegd worden aan de coredns ConfigMap. Dat ziet er dan bijvoorbeeld zo uit:
apiVersion: v1 kind: ConfigMap metadata: name: coredns namespace: kube-system data: Corefile: | .:53 { errors health { lameduck 5s } hosts { 192.168.1.1 server1.domain.com 192.168.1.2 server2.domain.com fallthrough } ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } prometheus :9153 forward . "/etc/resolv.conf" cache 30 loop reload loadbalance }
NetworkPolicy
Uiteraard kan een NetworkPolicy gebruikt worden om te voorkomen dat pods van verschillende namespaces elkaar kunnen bereiken. Helaas werkt flannel niet met networkpolicies maar daar kan eventueel Calico op gezet worden of gebruik Weaver als CNI. Een voorbeeld: (networkpolicy.yml)
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-from-other-namespaces spec: podSelector: matchLabels: ingress: - from: - podSelector: {}
apply dit dan in de te isoleren namespace.
Service
In kubernetes kunnen services aangemaakt worden om pods/applicaties te kunnen bereiken van buiten de kubernetes cluster. Deze services zorgen dan, m.b.v. zogenaamde ‘selectors’, welke pods bereikt dienen te worden. Voor ‘service’ zijn veschillende ‘types’ mogelijk:

ClusterIP
ClusterIP wordt gebruikt om pods met elkaar te (laten) connecten. Dit netwerk is alleen beschikbaar binnen het cluster.
NodePort
NodePort genereert een poort op elke node zodat via deze poort de onderliggende applicaties bereikt kunnen worden. Het poort-nummer wordt random gegenereerd bij aanmaken van de service en heeft een nummer boven 32000. Dit en dan gelijk het nadeel, je weet van te voren niet welk poort-nummer gekoppeld wordt, tenzij je dit vaststeld in de configuratie van de service. Daarnaast zal het node-network niet via Internet bereikbaar zijn dus werkt dit alleen binnen het cluster-netwerk. Een proxy-service kan dit echter wel beschikbaar maken, bijvoorbeeld nginx of HAProxy.

LoadBalancer
Bijna hetzelfde als NodePort echter wordt nu gebruik gemaakt van een LoadBalancer om verkeer naar de applicaties te sturen. Een LoadBalancer maakt geen gebruik van een specifiek poort-nummer maar van een extern IP adres. Deze functie dient dan aanwezig te zijn in de kubernetes cluster, iets wat de cloud-providers standaard aanbieden. Het IP adres van deze load-balancers bevinden zich dan op een te configureren netwerk dat via Internet bereikbaar gemaakt kan worden.

Ingress
Ingress is een andere methode om http– en https-services te kunnen bereiken vanaf een externe locatie, buiten de kubernetes cluster. Ingress dient echter als controller toegevoegd te worden aan de cluster om Ingress objecten te herkennen. Een populaire Ingress Controller is ‘nginx-ingress’.

zie ook deze pagina over de installatie van Nginx Ingress.