Backstage-Netzwerke bei Kubernetes

Hinweis perev. : Der Autor des Originalartikels, Nicolas Leiva, ist ein Lösungsarchitekt von Cisco, der beschlossen hat, seinen Kollegen, Netzwerkingenieuren, die Funktionsweise des Kubernetes-Netzwerks von innen mitzuteilen. Zu diesem Zweck untersucht er die einfachste Konfiguration im Cluster und nutzt dabei aktiv den gesunden Menschenverstand, sein Wissen über Netzwerke und Standard-Linux / Kubernetes-Dienstprogramme. Es stellte sich voluminös heraus, aber sehr deutlich.



Zusätzlich zu der Tatsache, dass Kelsey Hightowers Kubernetes The Hard Way- Handbuch nur funktioniert ( sogar unter AWS! ), Gefiel mir, dass das Netzwerk sauber und einfach gehalten wurde. Dies ist eine großartige Gelegenheit, um beispielsweise die Rolle der Container Network Interface ( CNI ) zu verstehen. Trotzdem möchte ich hinzufügen, dass das Kubernetes-Netzwerk eigentlich nicht sehr intuitiv ist, insbesondere für Anfänger ... und vergessen Sie auch nicht, dass es einfach kein Netzwerk für Container gibt.

Obwohl es zu diesem Thema bereits gute Materialien gibt (siehe Links hier ), konnte ich kein solches Beispiel finden, dass ich alles Notwendige mit den Schlussfolgerungen der Teams kombinieren würde, die Netzwerktechniker lieben und hassen, um zu demonstrieren, was tatsächlich hinter den Kulissen passiert. Aus diesem Grund habe ich beschlossen, Informationen aus vielen Quellen zu sammeln. Ich hoffe, dies hilft und Sie verstehen besser, wie alles miteinander verbunden ist. Dieses Wissen ist nicht nur wichtig, um sich selbst zu testen, sondern auch, um die Diagnose von Problemen zu vereinfachen. Sie können dem Beispiel in Ihrem Cluster von Kubernetes The Hard Way folgen: Alle IP-Adressen und Einstellungen werden von dort übernommen (Stand der Commits für Mai 2018, bevor Nabla-Container verwendet werden ).

Und wir werden am Ende beginnen, wenn wir drei Controller und drei Arbeitsknoten haben:



Sie können feststellen, dass es hier auch mindestens drei private Subnetze gibt! Ein wenig Geduld, und sie werden alle berücksichtigt. Denken Sie daran, dass wir uns zwar auf sehr spezifische IP-Präfixe beziehen, diese jedoch einfach aus Kubernetes The Hard Way stammen , sodass sie nur lokale Bedeutung haben und Sie gemäß RFC 1918 einen anderen Adressblock für Ihre Umgebung auswählen können. Für den Fall von IPv6 wird es einen separaten Blog-Artikel geben.

Host-Netzwerk (10.240.0.0/24)


Dies ist ein internes Netzwerk, zu dem alle Knoten gehören. Definiert durch das --private-network-ip in GCP oder die Option --private-ip-address in AWS beim --private-ip-address Computerressourcen.

Initialisieren von Controller-Knoten in GCP


 for i in 0 1 2; do gcloud compute instances create controller-${i} \ # ... --private-network-ip 10.240.0.1${i} \ # ... done 

( controllers_gcp.sh )

Initialisieren von Controller-Knoten in AWS


 for i in 0 1 2; do declare controller_id${i}=`aws ec2 run-instances \ # ... --private-ip-address 10.240.0.1${i} \ # ... done 

( controllers_aws.sh )



Jede Instanz hat zwei IP-Adressen: privat vom Host-Netzwerk (Controller - 10.240.0.1${i}/24 , Worker - 10.240.0.2${i}/24 ) und eine öffentliche, vom Cloud-Anbieter festgelegte, über die wir später sprechen werden wie man zu NodePorts .

Gcp


 $ gcloud compute instances list NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS controller-0 us-west1-c n1-standard-1 10.240.0.10 35.231.XXX.XXX RUNNING worker-1 us-west1-c n1-standard-1 10.240.0.21 35.231.XX.XXX RUNNING ... 


Aws


 $ aws ec2 describe-instances --query 'Reservations[].Instances[].[Tags[?Key==`Name`].Value[],PrivateIpAddress,PublicIpAddress]' --output text | sed '$!N;s/\n/ /' 10.240.0.10 34.228.XX.XXX controller-0 10.240.0.21 34.173.XXX.XX worker-1 ... 

Alle Knoten müssen in der Lage sein, sich gegenseitig zu pingen, wenn die Sicherheitsrichtlinien korrekt sind (und wenn ping auf dem Host installiert ping ).

Herdnetzwerk (10.200.0.0/16)


Dies ist das Netzwerk, in dem die Pods leben. Jeder Arbeitsknoten verwendet ein Subnetz dieses Netzwerks. In unserem Fall ist POD_CIDR=10.200.${i}.0/24 für worker-${i} .



Um zu verstehen, wie alles konfiguriert ist, treten Sie einen Schritt zurück und sehen Sie sich das Kubernetes-Netzwerkmodell an , für das Folgendes erforderlich ist:

  • Alle Container können ohne Verwendung von NAT mit anderen Containern kommunizieren.
  • Alle Knoten können mit allen Containern kommunizieren (und umgekehrt), ohne NAT zu verwenden.
  • Die IP, die der Container sieht, muss mit der IP-Adresse übereinstimmen, die andere sehen.

All dies kann auf viele Arten implementiert werden, und Kubernetes übergibt das Netzwerk-Setup an das CNI-Plugin .

„Das CNI-Plugin ist dafür verantwortlich, dem Netzwerk-Namespace des Containers eine Netzwerkschnittstelle hinzuzufügen (z. B. ein Ende eines veth-Paares ) und die erforderlichen Änderungen auf dem Host vorzunehmen (z. B. das zweite Ende von veth mit einer Bridge zu verbinden). Dann muss er eine IP-Schnittstelle zuweisen und die Routen gemäß dem Abschnitt IP-Adressverwaltung konfigurieren, indem er das gewünschte IPAM-Plugin aufruft. “ (aus der Spezifikation der Containernetzwerkschnittstelle )



Netzwerk-Namespace


„Der Namespace verpackt die globale Systemressource in eine Abstraktion, die für Prozesse in diesem Namespace so sichtbar ist, dass sie über eine eigene isolierte Instanz der globalen Ressource verfügen. Änderungen in der globalen Ressource sind für andere in diesem Namespace enthaltene Prozesse sichtbar, für andere Prozesse jedoch nicht. “ ( von der Namespaces-Manpage )

Linux bietet sieben verschiedene Namespaces ( Cgroup , IPC , Network , Mount , PID , User , UTS ). Netzwerk-Namespaces ( CLONE_NEWNET ) definieren die Netzwerkressourcen, die dem Prozess zur Verfügung stehen: „Jeder Netzwerk-Namespace verfügt über eigene Netzwerkgeräte, IP-Adressen, IP-Routing-Tabellen, /proc/net Verzeichnis, Portnummern usw.“ ( aus dem Artikel „ Namespaces in Betrieb “) .

Virtuelle Ethernet-Geräte (Veth)


„Ein virtuelles Netzwerkpaar (veth) bietet eine Abstraktion in Form einer„ Pipe “, mit der Tunnel zwischen Netzwerk-Namespaces oder eine Brücke zu einem physischen Netzwerkgerät in einem anderen Netzwerkbereich erstellt werden können. Wenn der Namespace freigegeben wird, werden alle darin enthaltenen Geräte zerstört. “ (von der Manpage der Netzwerk-Namespaces )

Gehen Sie zu Boden und sehen Sie, wie sich alles auf den Cluster bezieht. Erstens sind Netzwerk-Plugins in Kubernetes vielfältig, und CNI-Plugins sind eines davon ( warum nicht CNM? ). Kubelet auf jedem Knoten teilt der Container- Laufzeit mit, welches Netzwerk-Plug-In verwendet werden soll. Die Container Network Interface ( CNI ) befindet sich zwischen der Container-Laufzeit und der Netzwerkimplementierung. Und schon baut das CNI-Plugin das Netzwerk auf.

„Das CNI-Plugin wird ausgewählt, indem die Befehlszeilenoption --network-plugin=cni an Kubelet übergeben wird. Kubelet liest die Datei aus --cni-conf-dir (der Standardwert ist /etc/cni/net.d ) und verwendet die CNI-Konfiguration aus dieser Datei, um das Netzwerk für jede Datei zu konfigurieren. " (aus den Anforderungen des Netzwerk-Plugins )

Die realen Binärdateien des CNI-Plugins befinden sich in -- cni-bin-dir (der Standardwert ist /opt/cni/bin ).

Bitte beachten Sie, dass die kubelet.service von --network-plugin=cni :

 [Service] ExecStart=/usr/local/bin/kubelet \\ --config=/var/lib/kubelet/kubelet-config.yaml \\ --network-plugin=cni \\ ... 

Zunächst erstellt Kubernetes einen Netzwerk-Namespace für den Herd, noch bevor Plugins aufgerufen werden. Dies wird mithilfe des speziellen pause implementiert, der „als„ übergeordneter Container “für alle Herdcontainer dient“ (aus dem Artikel „ Der allmächtige Pausencontainer “) . Kubernetes führt dann das CNI-Plugin aus, um den pause an das Netzwerk anzuhängen. Alle Pod-Container verwenden den netns dieses netns .

 { "cniVersion": "0.3.1", "name": "bridge", "type": "bridge", "bridge": "cnio0", "isGateway": true, "ipMasq": true, "ipam": { "type": "host-local", "ranges": [ [{"subnet": "${POD_CIDR}"}] ], "routes": [{"dst": "0.0.0.0/0"}] } } 

Die verwendete CNI-Konfiguration gibt die Verwendung des bridge Plugins zum Konfigurieren der Linux (L2) -Softwarebrücke im Root-Namespace cnio0 (der Standardname ist cni0 ) an, der als Gateway fungiert ( "isGateway": true ).



Ein veth-Paar wird ebenfalls konfiguriert, um den Herd mit der neu erstellten Brücke zu verbinden:



Um L3-Informationen wie IP-Adressen zuzuweisen, wird das IPAM-Plugin ( ipam ) aufgerufen. In diesem Fall wird der host-local Typ verwendet, "der den Status lokal im Hostdateisystem speichert, wodurch die Eindeutigkeit der IP-Adressen auf einem Host sichergestellt wird" (aus der host-local ) . Das IPAM-Plugin gibt diese Informationen an das vorherige Plugin ( bridge ) zurück, sodass alle in der Konfiguration angegebenen Routen konfiguriert werden können ( "routes": [{"dst": "0.0.0.0/0"}] ). Wenn gw nicht angegeben ist, wird es aus dem Subnetz übernommen . Die Standardroute wird auch im Netzwerk-Namespace des Herdes konfiguriert und zeigt auf die Bridge (die als erstes IP-Subnetz des Herdes konfiguriert ist).

Und das letzte wichtige Detail: Wir haben Maskerading ( "ipMasq": true ) für den Datenverkehr aus dem "ipMasq": true angefordert. Wir brauchen NAT hier nicht wirklich, aber dies ist die Konfiguration in Kubernetes The Hard Way . Der Vollständigkeit halber muss ich daher erwähnen, dass die Einträge in den iptables bridge Plugins für dieses spezielle Beispiel konfiguriert sind. Alle Pakete vom Herd, deren Empfänger nicht im Bereich 224.0.0.0/4 , befinden sich hinter NAT , was die Anforderung "Alle Container können mit jedem anderen Container ohne Verwendung von NAT kommunizieren" nicht ganz erfüllt. Nun, wir werden beweisen, warum NAT nicht benötigt wird ...



Herdführung


Jetzt können wir die Pods anpassen. Schauen wir uns alle Netzwerkbereiche der Namen eines der Arbeitsknoten an und analysieren Sie einen davon, nachdem Sie von hier aus die Bereitstellung von nginx haben . Wir werden lsns mit der Option -t , um den gewünschten Typ des Namespace (d. H. net ) auszuwählen:

 ubuntu@worker-0:~$ sudo lsns -t net NS TYPE NPROCS PID USER COMMAND 4026532089 net 113 1 root /sbin/init 4026532280 net 2 8046 root /pause 4026532352 net 4 16455 root /pause 4026532426 net 3 27255 root /pause 

Mit der Option -i zu ls können wir ihre Inode-Nummern finden:

 ubuntu@worker-0:~$ ls -1i /var/run/netns 4026532352 cni-1d85bb0c-7c61-fd9f-2adc-f6e98f7a58af 4026532280 cni-7cec0838-f50c-416a-3b45-628a4237c55c 4026532426 cni-912bcc63-712d-1c84-89a7-9e10510808a0 

Sie können auch alle Netzwerk-Namespaces mit ip netns :

 ubuntu@worker-0:~$ ip netns cni-912bcc63-712d-1c84-89a7-9e10510808a0 (id: 2) cni-1d85bb0c-7c61-fd9f-2adc-f6e98f7a58af (id: 1) cni-7cec0838-f50c-416a-3b45-628a4237c55c (id: 0) 

Um alle Prozesse cni-912bcc63–712d-1c84–89a7–9e10510808a0 , die im Netzwerkbereich cni-912bcc63–712d-1c84–89a7–9e10510808a0 ( 4026532426 ) ausgeführt werden, können Sie beispielsweise den folgenden Befehl ausführen:

 ubuntu@worker-0:~$ sudo ls -l /proc/[1-9]*/ns/net | grep 4026532426 | cut -f3 -d"/" | xargs ps -p PID TTY STAT TIME COMMAND 27255 ? Ss 0:00 /pause 27331 ? Ss 0:00 nginx: master process nginx -g daemon off; 27355 ? S 0:00 nginx: worker process 

Es ist zu sehen, dass wir zusätzlich zur pause in diesem Pod Nginx gestartet haben. Der pause teilt die net und ipc Namespaces mit allen anderen Pod-Containern. Erinnern Sie sich an die PID aus pause - 27255; wir werden darauf zurückkommen.

Nun wollen wir sehen, was kubectl über diesen Pod erzählt:

 $ kubectl get pods -o wide | grep nginx nginx-65899c769f-wxdx6 1/1 Running 0 5d 10.200.0.4 worker-0 

Weitere Details:

 $ kubectl describe pods nginx-65899c769f-wxdx6 

 Name: nginx-65899c769f-wxdx6 Namespace: default Node: worker-0/10.240.0.20 Start Time: Thu, 05 Jul 2018 14:20:06 -0400 Labels: pod-template-hash=2145573259 run=nginx Annotations: <none> Status: Running IP: 10.200.0.4 Controlled By: ReplicaSet/nginx-65899c769f Containers: nginx: Container ID: containerd://4c0bd2e2e5c0b17c637af83376879c38f2fb11852921b12413c54ba49d6983c7 Image: nginx ... 

Wir sehen den Namen des Pods - nginx-65899c769f-wxdx6 - und die ID eines seiner Container ( nginx ), aber über die pause nichts gesagt. Graben Sie einen tieferen Arbeitsknoten, um alle Daten abzugleichen. Denken Sie daran, dass Kubernetes The Hard Way Docker nicht verwendet. Einzelheiten zum Container finden Sie im Konsolen-Dienstprogramm Containerd - ctr (siehe auch Artikel " Integration von Containerd mit Kubernetes, Ersetzen von Docker, produktionsbereit " - ca. Übertragung ). ::

 ubuntu@worker-0:~$ sudo ctr namespaces ls NAME LABELS k8s.io 

Wenn Sie den Containerd- k8s.io ( k8s.io ) kennen, können Sie die nginx Container-ID k8s.io :

 ubuntu@worker-0:~$ sudo ctr -n k8s.io containers ls | grep nginx 4c0bd2e2e5c0b17c637af83376879c38f2fb11852921b12413c54ba49d6983c7 docker.io/library/nginx:latest io.containerd.runtime.v1.linux 

... und auch pause :

 ubuntu@worker-0:~$ sudo ctr -n k8s.io containers ls | grep pause 0866803b612f2f55e7b6b83836bde09bd6530246239b7bde1e49c04c7038e43a k8s.gcr.io/pause:3.1 io.containerd.runtime.v1.linux 21640aea0210b320fd637c22ff93b7e21473178de0073b05de83f3b116fc8834 k8s.gcr.io/pause:3.1 io.containerd.runtime.v1.linux d19b1b1c92f7cc90764d4f385e8935d121bca66ba8982bae65baff1bc2841da6 k8s.gcr.io/pause:3.1 io.containerd.runtime.v1.linux 

Die nginx Container-ID mit der Endung …983c7 stimmt mit der von kubectl . Mal sehen, ob wir herausfinden können, welcher pause zum nginx Pod gehört:

 ubuntu@worker-0:~$ sudo ctr -n k8s.io task ls TASK PID STATUS ... d19b1b1c92f7cc90764d4f385e8935d121bca66ba8982bae65baff1bc2841da6 27255 RUNNING 4c0bd2e2e5c0b17c637af83376879c38f2fb11852921b12413c54ba49d6983c7 27331 RUNNING 

Denken Sie daran, dass Prozesse mit PID 27331 und 27355 im Netzwerk-Namespace cni-912bcc63–712d-1c84–89a7–9e10510808a0 werden.

 ubuntu@worker-0:~$ sudo ctr -n k8s.io containers info d19b1b1c92f7cc90764d4f385e8935d121bca66ba8982bae65baff1bc2841da6 { "ID": "d19b1b1c92f7cc90764d4f385e8935d121bca66ba8982bae65baff1bc2841da6", "Labels": { "io.cri-containerd.kind": "sandbox", "io.kubernetes.pod.name": "nginx-65899c769f-wxdx6", "io.kubernetes.pod.namespace": "default", "io.kubernetes.pod.uid": "0b35e956-8080-11e8-8aa9-0a12b8818382", "pod-template-hash": "2145573259", "run": "nginx" }, "Image": "k8s.gcr.io/pause:3.1", ... 

... und:

 ubuntu@worker-0:~$ sudo ctr -n k8s.io containers info 4c0bd2e2e5c0b17c637af83376879c38f2fb11852921b12413c54ba49d6983c7 { "ID": "4c0bd2e2e5c0b17c637af83376879c38f2fb11852921b12413c54ba49d6983c7", "Labels": { "io.cri-containerd.kind": "container", "io.kubernetes.container.name": "nginx", "io.kubernetes.pod.name": "nginx-65899c769f-wxdx6", "io.kubernetes.pod.namespace": "default", "io.kubernetes.pod.uid": "0b35e956-8080-11e8-8aa9-0a12b8818382" }, "Image": "docker.io/library/nginx:latest", ... 

Jetzt wissen wir sicher, welche Container in diesem Pod ( nginx-65899c769f-wxdx6 ) und im Netzwerk-Namespace ( cni-912bcc63–712d-1c84–89a7–9e10510808a0 ) ausgeführt werden:

  • nginx (ID: 4c0bd2e2e5c0b17c637af83376879c38f2fb11852921b12413c54ba49d6983c7 );
  • Pause (ID: d19b1b1c92f7cc90764d4f385e8935d121bca66ba8982bae65baff1bc2841da6 ).



Wie ist dies unter ( nginx-65899c769f-wxdx6 ) mit dem Netzwerk verbunden? Wir verwenden die zuvor von pause empfangene PID 27255, um Befehle in ihrem Netzwerk-Namespace cni-912bcc63–712d-1c84–89a7–9e10510808a0 ( cni-912bcc63–712d-1c84–89a7–9e10510808a0 ):

 ubuntu@worker-0:~$ sudo ip netns identify 27255 cni-912bcc63-712d-1c84-89a7-9e10510808a0 

Für diese Zwecke verwenden wir nsenter mit der Option -t , die die Ziel-PID definiert, und -n ohne eine Datei anzugeben, um in den Netzwerk-Namespace des nsenter zu gelangen (27255). Hier ist, was ip link show sagen wird:

 ubuntu@worker-0:~$ sudo nsenter -t 27255 -n ip link show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 3: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default link/ether 0a:58:0a:c8:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0 

... und ifconfig eth0 :

 ubuntu@worker-0:~$ sudo nsenter -t 27255 -n ifconfig eth0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.200.0.4 netmask 255.255.255.0 broadcast 0.0.0.0 inet6 fe80::2097:51ff:fe39:ec21 prefixlen 64 scopeid 0x20<link> ether 0a:58:0a:c8:00:04 txqueuelen 0 (Ethernet) RX packets 540 bytes 42247 (42.2 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 177 bytes 16530 (16.5 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 

Dies bestätigt, dass die zuvor über kubectl get pod erhaltene IP-Adresse auf der eth0 Schnittstelle konfiguriert ist. Diese Schnittstelle ist Teil eines Veth-Paares , von dem sich ein Ende im Herd und das andere im Root-Namespace befindet. Um die Schnittstelle des zweiten Endes herauszufinden, verwenden wir ethtool :

 ubuntu@worker-0:~$ sudo ip netns exec cni-912bcc63-712d-1c84-89a7-9e10510808a0 ethtool -S eth0 NIC statistics: peer_ifindex: 7 

Wir sehen, dass der ifindex Festes 7 ist. Überprüfen Sie, ob er sich im Root-Namespace befindet. Dies kann über den ip link :

 ubuntu@worker-0:~$ ip link | grep '^7:' 7: veth71f7d238@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master cnio0 state UP mode DEFAULT group default 

Um sicherzugehen, lassen Sie uns sehen:

 ubuntu@worker-0:~$ sudo cat /sys/class/net/veth71f7d238/ifindex 7 

Großartig, jetzt ist mit dem virtuellen Link alles klar. brctl Sie uns mit brctl sehen, wer noch mit der Linux-Bridge verbunden ist:

 ubuntu@worker-0:~$ brctl show cnio0 bridge name bridge id STP enabled interfaces cnio0 8000.0a580ac80001 no veth71f7d238 veth73f35410 vethf273b35f 

Das Bild sieht also wie folgt aus:



Routing-Prüfung


Wie leiten wir den Verkehr tatsächlich weiter? Schauen wir uns die Routing-Tabelle im Netzwerk-Namespace-Pod an:

 ubuntu@worker-0:~$ sudo ip netns exec cni-912bcc63-712d-1c84-89a7-9e10510808a0 ip route show default via 10.200.0.1 dev eth0 10.200.0.0/24 dev eth0 proto kernel scope link src 10.200.0.4 

Zumindest wissen wir, wie man zum Root-Namespace kommt ( default via 10.200.0.1 ). Nun sehen wir uns die Host-Routing-Tabelle an:

 ubuntu@worker-0:~$ ip route list default via 10.240.0.1 dev eth0 proto dhcp src 10.240.0.20 metric 100 10.200.0.0/24 dev cnio0 proto kernel scope link src 10.200.0.1 10.240.0.0/24 dev eth0 proto kernel scope link src 10.240.0.20 10.240.0.1 dev eth0 proto dhcp scope link src 10.240.0.20 metric 100 

Wir wissen, wie Pakete an einen VPC-Router weitergeleitet werden (VPC verfügt über einen „impliziten“ Router, der normalerweise eine zweite Adresse aus dem Haupt-IP-Adressraum des Subnetzes hat). Jetzt: Weiß der VPC-Router, wie er zum Netzwerk jedes Herdes gelangt? Nein, das tut er nicht. Daher wird davon ausgegangen, dass die Routen vom CNI-Plug-In oder manuell (wie im Handbuch) konfiguriert werden. Anscheinend macht das AWS CNI-Plugin genau das für uns bei AWS. Denken Sie daran, dass es viele CNI-Plugins gibt , und wir betrachten ein Beispiel für eine einfache Netzwerkkonfiguration :



Tiefes Eintauchen in NAT


kubectl create -f busybox.yaml zwei identische busybox Container mit Replication Controller:

 apiVersion: v1 kind: ReplicationController metadata: name: busybox0 labels: app: busybox0 spec: replicas: 2 selector: app: busybox0 template: metadata: name: busybox0 labels: app: busybox0 spec: containers: - image: busybox command: - sleep - "3600" imagePullPolicy: IfNotPresent name: busybox restartPolicy: Always 

( busybox.yaml )

Wir bekommen:

 $ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE busybox0-g6pww 1/1 Running 0 4s 10.200.1.15 worker-1 busybox0-rw89s 1/1 Running 0 4s 10.200.0.21 worker-0 ... 

Pings von einem Container zum anderen sollten erfolgreich sein:

 $ kubectl exec -it busybox0-rw89s -- ping -c 2 10.200.1.15 PING 10.200.1.15 (10.200.1.15): 56 data bytes 64 bytes from 10.200.1.15: seq=0 ttl=62 time=0.528 ms 64 bytes from 10.200.1.15: seq=1 ttl=62 time=0.440 ms --- 10.200.1.15 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.440/0.484/0.528 ms 

Um die Bewegung des Datenverkehrs zu verstehen, können Sie Pakete mit tcpdump oder conntrack :

 ubuntu@worker-0:~$ sudo conntrack -L | grep 10.200.1.15 icmp 1 29 src=10.200.0.21 dst=10.200.1.15 type=8 code=0 id=1280 src=10.200.1.15 dst=10.240.0.20 type=0 code=0 id=1280 mark=0 use=1 

Die Quell-IP von Pod 10.200.0.21 wird in die IP-Adresse des Hosts 10.240.0.20 übersetzt.

 ubuntu@worker-1:~$ sudo conntrack -L | grep 10.200.1.15 icmp 1 28 src=10.240.0.20 dst=10.200.1.15 type=8 code=0 id=1280 src=10.200.1.15 dst=10.240.0.20 type=0 code=0 id=1280 mark=0 use=1 

In iptables können Sie sehen, dass die Anzahl steigt:

 ubuntu@worker-0:~$ sudo iptables -t nat -Z POSTROUTING -L -v Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination ... 5 324 CNI-be726a77f15ea47ff32947a3 all -- any any 10.200.0.0/24 anywhere /* name: "bridge" id: "631cab5de5565cc432a3beca0e2aece0cef9285482b11f3eb0b46c134e457854" */ Zeroing chain `POSTROUTING' 

Wenn Sie andererseits "ipMasq": true aus der CNI-Plugin-Konfiguration entfernen, sehen Sie Folgendes (dieser Vorgang wird ausschließlich zu Bildungszwecken ausgeführt - wir empfehlen, die Konfiguration in einem funktionierenden Cluster nicht zu ändern!):

 $ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE busybox0-2btxn 1/1 Running 0 16s 10.200.0.15 worker-0 busybox0-dhpx8 1/1 Running 0 16s 10.200.1.13 worker-1 ... 

Ping sollte noch bestehen:

 $ kubectl exec -it busybox0-2btxn -- ping -c 2 10.200.1.13 PING 10.200.1.6 (10.200.1.6): 56 data bytes 64 bytes from 10.200.1.6: seq=0 ttl=62 time=0.515 ms 64 bytes from 10.200.1.6: seq=1 ttl=62 time=0.427 ms --- 10.200.1.6 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.427/0.471/0.515 ms 

Und in diesem Fall - ohne NAT:

 ubuntu@worker-0:~$ sudo conntrack -L | grep 10.200.1.13 icmp 1 29 src=10.200.0.15 dst=10.200.1.13 type=8 code=0 id=1792 src=10.200.1.13 dst=10.200.0.15 type=0 code=0 id=1792 mark=0 use=1 

Daher haben wir überprüft, dass "alle Container ohne Verwendung von NAT mit anderen Containern kommunizieren können".

 ubuntu@worker-1:~$ sudo conntrack -L | grep 10.200.1.13 icmp 1 27 src=10.200.0.15 dst=10.200.1.13 type=8 code=0 id=1792 src=10.200.1.13 dst=10.200.0.15 type=0 code=0 id=1792 mark=0 use=1 

Clusternetzwerk (10.32.0.0/24)


Möglicherweise haben Sie im busybox Beispiel busybox dass die der busybox zugewiesenen IP-Adressen jeweils busybox waren. Was wäre, wenn wir diese Container für die Kommunikation von anderen Herden zur Verfügung stellen wollten? Man könnte die aktuellen IP-Adressen des Pods nehmen, aber sie werden sich ändern. Aus diesem Grund müssen Sie die Serviceressource konfigurieren, die Anfragen an viele kurzlebige Herde weiterleitet.

"Service in Kubernetes ist eine Abstraktion, die den logischen Satz von Herden und die Richtlinien definiert, über die auf sie zugegriffen werden kann." (aus der Dokumentation zu Kubernetes Services )

Es gibt verschiedene Möglichkeiten, einen Dienst zu veröffentlichen. Der Standardtyp ist ClusterIP , der die IP-Adresse aus dem CIDR-Block des Clusters festlegt (d. h. nur über den Cluster zugänglich). Ein solches Beispiel ist das in Kubernetes The Hard Way konfigurierte DNS-Cluster-Add-on.

 # ... apiVersion: v1 kind: Service metadata: name: kube-dns namespace: kube-system labels: k8s-app: kube-dns kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile kubernetes.io/name: "KubeDNS" spec: selector: k8s-app: kube-dns clusterIP: 10.32.0.10 ports: - name: dns port: 53 protocol: UDP - name: dns-tcp port: 53 protocol: TCP # ... 

( kube-dns.yaml )

kubectl zeigt, dass der Service Endpunkte kubectl und übersetzt:

 $ kubectl -n kube-system describe services ... Selector: k8s-app=kube-dns Type: ClusterIP IP: 10.32.0.10 Port: dns 53/UDP TargetPort: 53/UDP Endpoints: 10.200.0.27:53 Port: dns-tcp 53/TCP TargetPort: 53/TCP Endpoints: 10.200.0.27:53 ... 

Wie genau? .. wieder iptables . Lassen Sie uns die für dieses Beispiel erstellten Regeln durchgehen. Ihre vollständige Liste kann mit dem Befehl iptables-save angezeigt werden.

Sobald Pakete vom Prozess erstellt werden ( OUTPUT ) oder auf der Netzwerkschnittstelle ankommen ( PREROUTING ), durchlaufen sie die folgenden iptables Ketten:

 -A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES -A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES 

Die folgenden Ziele entsprechen TCP-Paketen, die um 10.32.0.10 an den 53. Port gesendet und mit dem 53. Port an den Empfänger 10.200.0.27 übertragen werden:

 -A KUBE-SERVICES -d 10.32.0.10/32 -p tcp -m comment --comment "kube-system/kube-dns:dns-tcp cluster IP" -m tcp --dport 53 -j KUBE-SVC-ERIFXISQEP7F7OF4 -A KUBE-SVC-ERIFXISQEP7F7OF4 -m comment --comment "kube-system/kube-dns:dns-tcp" -j KUBE-SEP-32LPCMGYG6ODGN3H -A KUBE-SEP-32LPCMGYG6ODGN3H -p tcp -m comment --comment "kube-system/kube-dns:dns-tcp" -m tcp -j DNAT --to-destination 10.200.0.27:53 

Gleiches gilt für UDP-Pakete (Empfänger 10.32.0.10:53 → 10.200.0.27:53):

 -A KUBE-SERVICES -d 10.32.0.10/32 -p udp -m comment --comment "kube-system/kube-dns:dns cluster IP" -m udp --dport 53 -j KUBE-SVC-TCOU7JCQXEZGVUNU -A KUBE-SVC-TCOU7JCQXEZGVUNU -m comment --comment "kube-system/kube-dns:dns" -j KUBE-SEP-LRUTK6XRXU43VLIG -A KUBE-SEP-LRUTK6XRXU43VLIG -p udp -m comment --comment "kube-system/kube-dns:dns" -m udp -j DNAT --to-destination 10.200.0.27:53 

Es gibt andere Arten von Services in Kubernetes. Insbesondere NodePort Kubernetes The Hard Way über NodePort - siehe Smoke Test: Services .

 kubectl expose deployment nginx --port 80 --type NodePort 

NodePort veröffentlicht den Dienst unter der IP-Adresse jedes Knotens und platziert ihn an einem statischen Port (er heißt NodePort ). NodePort kann NodePort von außerhalb des Clusters zugegriffen werden. Sie können den dedizierten Port (in diesem Fall - 31088) mit kubectl :

 $ kubectl describe services nginx ... Type: NodePort IP: 10.32.0.53 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 31088/TCP Endpoints: 10.200.1.18:80 ... 

Under ist jetzt im Internet unter http://${EXTERNAL_IP}:31088/ . Hier ist EXTERNAL_IP die öffentliche IP-Adresse einer Arbeitsinstanz . In diesem Beispiel habe ich die öffentliche IP-Adresse von worker-0 verwendet . Die Anforderung wird von einem Host mit einer internen IP-Adresse von 10.240.0.20 empfangen (der Cloud-Anbieter ist an öffentlichem NAT beteiligt). Der Dienst wird jedoch tatsächlich auf einem anderen Host gestartet ( Worker-1 , erkennbar an der IP-Adresse des Endpunkts - 10.200.1.18):

 ubuntu@worker-0:~$ sudo conntrack -L | grep 31088 tcp 6 86397 ESTABLISHED src=173.38.XXX.XXX dst=10.240.0.20 sport=30303 dport=31088 src=10.200.1.18 dst=10.240.0.20 sport=80 dport=30303 [ASSURED] mark=0 use=1 

Das Paket wird von Worker-0 an Worker-1 gesendet, wo es seinen Empfänger findet:

 ubuntu@worker-1:~$ sudo conntrack -L | grep 80 tcp 6 86392 ESTABLISHED src=10.240.0.20 dst=10.200.1.18 sport=14802 dport=80 src=10.200.1.18 dst=10.240.0.20 sport=80 dport=14802 [ASSURED] mark=0 use=1 

Ist eine solche Schaltung ideal? Vielleicht nicht, aber es funktioniert. In diesem Fall lauten die programmierten iptables Regeln wie folgt:

 -A KUBE-NODEPORTS -p tcp -m comment --comment "default/nginx:" -m tcp --dport 31088 -j KUBE-SVC-4N57TFCL4MD7ZTDA -A KUBE-SVC-4N57TFCL4MD7ZTDA -m comment --comment "default/nginx:" -j KUBE-SEP-UGTFMET44DQG7H7H -A KUBE-SEP-UGTFMET44DQG7H7H -p tcp -m comment --comment "default/nginx:" -m tcp -j DNAT --to-destination 10.200.1.18:80 

Mit anderen Worten, die Adresse für den Empfänger von Paketen mit Port 31088 wird am 10.200.1.18 gesendet. Der Port sendet auch von 31088 bis 80.

Wir haben keinen anderen LoadBalancer - LoadBalancer - der den Dienst mithilfe eines Load Balancers eines Cloud-Anbieters öffentlich verfügbar macht, aber der Artikel hat sich bereits als umfangreich herausgestellt.

Fazit


Es scheint, dass es viele Informationen gibt, aber wir haben nur die Spitze des Eisbergs berührt. In Zukunft werde ich über IPv6, IPVS, eBPF und einige interessante aktuelle CNI-Plugins sprechen.

PS vom Übersetzer


Lesen Sie auch in unserem Blog:

Source: https://habr.com/ru/post/de420813/


All Articles