Kubernetes Networking Genel Bakis-1

Kubernetes yillar yili insanoglunun yazmis oldugu iptables, route tables, gibi bircok ana komponenti kendi icerisinde yasiyor ve tum network altyapisini bunun uzerine kurgulayabiliyor .

Kubernetes uzerinde Linux kosan herhangi bir sunucuda, cloud provider (saglayici) farketmeksizin agnostic bir soyutlama katmani haline donusturuyor .

Not: Bu yazida genel networking anlatiminda temel CNI calisma pratigi uzerinden anlatmaya calistim.CNI plugin olarak Calico secilmistir.Farkli pluginler ornek AWS-VPC-CNI, Flannel gibi farkli bir yaklasim ile bu islemleri yonetmektedir.

KUBEPROXY, CNI:

Her node uzerinde network otomasyonunu saglamak adina tum gereken islemleri control-plane’den gelen olaylara bakarak ilgili iptables ve routing ile ilgili kurallarini olusturmaktadir.

Kubernetes uzerinden network katmani disaridan gelen bir trafikte nodelardan podlara akacak sekilde dizayn edilmistir. Disaridan gelen tum trafigin ic networke aktarilmasi ve hangi paketin hangi poda yonlendirelecegini belirten kurallari kube-proxy generate etmektedir. Uzerine trafik alan tum nodelar uzerinde konumlanmaktadir.

Isin bir de CNI tarafi vardir. CNI (Container Networking Interface) asagidaki iki ana gorevlerden sorumludur:

  • Podun (container) ayaga kalktiginda sanal interfacelere baglanmasi

CNI araclari da yine kubeproxy gibi control-plane uzerinden gelen veya ona convert edilen eventleri dinler ;

ADD
DEL
GET
VERSION

Podlar arasi Iletisim

Kubernetes uzerinde en unutulmamasi gereken konularin basinda podlarin uzerinde bulundugu node configurasyonun podlarin yasamini direkt olarak etkiledigidir.

Iki pod ilk olarak ayni sunucuda ya da farkli sunucularda olsa bile bunu kendi K8S icerisinde nasil gerceklestiriyor.

Konteynir ve Interface

Ayaga kaldirilan her pod aslinda birer container ya da (podlar icerisinde birden fazla containter olabilir.) oldugundan her biri kendi icerisinde bir sanal interface’e sahip olmalidir.

Ilk olarak POD’un kendi icerisinde bir IP adresi aldigini gorebiliyoruz .

Pod icerisine baglanip (exec) ve ip a komutuyla baglandigimizda bu interface ile bagli bir IP adresinin tanimlanmis oldugunu goruyoruz.

$ ip a4: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether b6:81:2b:21:f8:f8 brd ff:ff:ff:ff:ff:ff
inet 100.127.249.201/32 scope global eth0

Yukaridaki 100.127.249.201 adresi surekli ayaga ayaga kalkan her bir pod icin bir interface’e baglama ve her node’da kurulan kubelete parametre olarak verdigimiz PodCidr blogu icerisinden set edilmekte ve makine icinde interface yaratilarak podlara baglanmaktadir. Anlatilan butun bu islemleri aslinda bizim icin CNI pluginleri gerceklestirmektedir.

Bir deployment yaptigimizda pod ayaga kalkmasi ve interface atanmasi islemleri acisindan suanki konumumuz asagidaki gibidir. Daha pod disina cikmadik.

Poda bir interface baglanmis ve uzerine bir pod IP adresi atanmis durumda node uzerinde asagidaki komutu calistirdigimda bunu da ayni degerlerde gorebiliyorum.

ip route ls |grep 100.101.135.85100.101.135.85 dev cali6e9890dac79 scope link

Ufak bir not olarak bulundugunuz her node uzerinde ip route ls ile baktiginizda bu IP’lerin ilgili interfaceler uzerinden yonlendirilecegini goreceksiniz K8S uzerindeki ayri katmanada yine ulasir Katman-3 seviyesindeki bir IP yonlendirmesi ile basliyor.

IPTABLES :

Yukarida bahsetmis oldugumuz butun islemler podun hayatina basladigi ilk andan itibaren CNI tarafindan tamamlandi ancak burada K8S ic networkunde bir POD’a herhangi bir trafik nasil gelir bu tarz yonlendirmeleri yapacagimiz husus onemli.

Paket seviyesindeki islemler icin yonlendirme netfilter module ile ile calisan bir arayuz olan IPTABLES ile yapilmaktadir. IPTABLES kurallari ise kubernetes uzerinde worker nodelardan ve uzerinde pod iletisimi saglayan nodelar uzerinde de konumlandirilabilen kube-proxy objesi tarafindan generate edilmektedir.

Kube-proxy ic networkteki paket yonetimini IPTABLES uzerinden yapmaktadir ancak ipvs uzerinden de yonetebilme opsiyonumuz vardir.

Simdi asagidaki gorseli inceleyelim.

CNI tarafindan tanimlanmis bir Interface ve bir IP adresi mevcut, bildiginiz uzere bu pod objeleri nodelar uzerinde konumlandirilmis durumda.Ilk olarak makinenin kendi uzerindeki ag arayuzunden gelen paketler hangi pod IP’ye yonlendirecegini tanimlamamiz gerekiyor.

Burada Kubernetes seviyesinde onemli bir konu daha olan servis objeleri karsimiza cikiyor.

KUBERNETES SERVICE

Podlar kapanabilir, arti veya eksi yonde olceklenebilir buradaki her bir islem her bir poda rastgele bir IP atanacak sekilde davranmaktadir.

Bu sebeple K8S tarafinda ilgili label uzerinden yonetilen pod grubuna yonlendirme icinde kullanilan hem sanal tek bir IP adresi saglayan hem de DNS tarafinda soruldugunda tek bu IP uzerinden yonlendirme ve loadbalancing yapmamizi saglayan objeler butunudur.

kubectl get svcNAME      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
web-svc ClusterIP 100.68.152.235 <none> 80/TCP 41d

Yukaridaki IP adresi benim web-svc objesinde tek bir IP tanimim asagida tanimda ilgili labela sahip tum podlara dagilimi saglamaktadir.

apiVersion: v1
kind: Service
metadata:
name: web-svc
spec:
selector:
app: web-service
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80

Ilgili servis tanimi benim hem tek bir IP uzerinden yonlendirme yapabilme mi hem de DNS uzerinde de yine servis adlari ile gidebilmemi saglamaktadir.

Simdi ana konumuz olan iptables kurallarina donelim. Her bir servis IP’si ilgili podIp’leri ile ilgili bir hedef yonlendirmesi yani DNAT tanimi yapmaktadir.Bu tanim sayesinde DNS uzerinden gidilen sanal servis Ip adresi uzerinden ilgili podIPlara yonlendirme yapilmaktadir.

Bunu daha net gormek adina herhangi bir worker node uzerinde asagidaki komut ile NAT kurallarini listeleyelim.

iptables -t nat -nL

Bu cok genis uzun ciktilar bize sunabilir ama su sekilde coklu deployment yaptigimiz podlarin Ip’lerini sirasiyla asagidaki gibi grep ile aratalim.

iptables -t nat -nL |grep -A 3 -B 3 100.101.135.81 => KUBE-SEP-QQFKKCBX2PLEDZRJ
iptables -t nat -nL |grep -A 3 -B 3 100.101.135.80 => KUBE-SEP-MOC4GDVGPEPVJSDN
iptables -t nat -nL |grep -A 3 -B 3 100.101.135.76 => KUBE-SEP-CYRXLGKEYPPWA5W5

Uc adet chain karsimiza cikiyor ve bun chainlerden herhangi birini tekrar alip baktigimizda ornek CHAIN asagidaki gibi karsimiza cikiyor.

Chain KUBE-SEP-QQFKKCBX2PLEDZRJ (1 references)
target prot opt source destination
KUBE-MARK-MASQ all -- 100.101.135.81 0.0.0.0/0 /* web-svc/web-svc: */
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 /* kuard/kuard: */ tcp to:100.101.135.81:80

Goruldugu uzere bagli olan ust chain kendisine gelen istegi `100.101.135.81` IP adresine yonlendiriyor , bunlari da kubernetes servis seviyesinden cagiran CHAIN ise hangisi ?

KUBE-SEP-QQFKKCBX2PLEDZRJ isimli chain ilk olarak karsimiza cikiyor her bir pod icin tanimlanmis bu chainler rastgele bir isimle olsuturluyor kube-proxy tarafindan. Bu chain aratildiginda yonlendirilmes kisimina bakacak olursak ve nat kurallari arasinda aradigimiz bu sefer karsimiza KUBE-SVC-TBXKZ2STP32JFDA3 isimli bir chain cikiyor yani ozetle.

Ben burada outputlarda -A ile ust chain bir altchaine yonlendirilerek DNAT uzerinden post-routing yapilmasi saglaniyor asagidaki iptables-save ciktisinda goruldugu uzere.

-A KUBE-SVC-TBXKZ2STP32JFDA3-m comment --comment "robot-shop/mysql:mysql" -j KUBE-SEP-QQFKKCBX2PLEDZRJ

Pod to Service Iletisim (DNS)

Kubernetes uzerinde service ismi uzerinden bir iletisim saglamaktadir, servis isimleri dns kaydi olarak cluster icerisinde konumlanan bir dns podunda konumlanir burada su sekilde bir durum var :

kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
.... ...... ...
clusterDomain: "cluster.local"
clusterDNS:
- "10.32.0.237"

clusterDNS parametresi yine nodelar icerisindeki CIDR bloguna sadik kalinarak verilmis bir Ip adresidir, bu adres dns servisinin clusterIP’si olarak ayaga kalkacaktir. Bu servis IP adresi her bir podun /etc/resolv.conf`un altina islenmektedir, pod tum dns sorgularini eger direkt HostNetwork’e bagli degilse buradaki adres uzerinden yurutur.

Pod to Node to Pod Iletisim

Podlar bir node uzerinde degilde birden fazla node uzerinde schedule edilebilir burada her bir Node kendi icerisiden ana PODCIDR bloguna sadik kalarak kendileri icin bir IP blogu allocate ederler.

Burada kubernetes uzerinde eger ki bir pod baska node uzerine gonderilmesi gerekiyorsa ki bunun kararini Iptables callback sonucu verir akabinde ilgili node’a yine route ciktisina gore yonlendirilir .

100.102.245.192/26 via   NODE1   dev tunl0 proto bird onlink
100.109.199.0/26 via NODE2 dev tunl0 proto bird onlink
100.120.175.0/26 via NODE3 dev tunl0 proto bird onlink

Ilk baska bir paket network uzerinde kendi icindeki CIDR blogunda bulamayip ilgili routinglere bakarak paketleri yonlendirecegi node’u bulur (yukaridaki kurallar) paket ilgili node’a ulastiktan sonra yapilacak islemler yine her node uzerindeki bu chainler ile paketi ilgili poda ulastirmaktir.

Sonuc

Kubernetes uzerinde ilk baslarda gercek ciddi sekilde nasil yuruyor diye sorguladigimiz DNS basta olmak uzere bircok komponent aslinda bugune kadar yazilmis olarak netfilter, iptables gibi ve butun bunlarin uzerine kurulu onlarca kernel modul uzerine kurulu bir calismanin sonucunda karsimiza cikan otomasyon ve soyutlama katmani olarak karsimiza cikmaktadir.

Bazi noktalari gostermeye calistim bu yazida, eksik gordugunuz veya eklemek istenilen noktalari da konusursak bu yaziyi guncelleyebiliriz.

Kaynakca

https://linux.die.net/man/8/route

PythonRubyLinux(❤)