V síťařině jsem působil mnoho let a vždycky to bylo docela složité. Virtualizace a cloud situaci díky nástupu overlay sítí a mikrosegmentaci zrovna nezjednodušují a dnes se pak musí řešit otázka síťařiny v kontejnerech běžících nad virtualizací. Azure Container Service odladila a připravila robustní řešení pro open source orchestrátor dle vaší volby - Docker Swarm, Kubernetes, DC/OS.
Dnes se společně podíváme na síťování v Kubernetes v Azure.
Kubernetes přichází s konceptem podů, tedy shluků kontejnerů (ale velmi často lidé používají jediný kontejner v podu), které sdílejí IP adresu. Nicméně každý pod pak má adresu svou a s ostatními pody komunikuje napřímo a bez NAT místo sdílení IP adresy hostitele a pekla překládání portů (tak původně fungoval Mesos, interní Google Borg i Docker, ale v zásadě všichni zmínění začínají nabízet i koncept IP per kontejner).
Aby bylo možné toho dosáhnout existuje řada síťových řešení postavených na standardu CNI. Některé přidávají koncept overlay sítí (VXLAN) podobně, jako je tomu u DC/OS implementace. Zmínit můžeme Contiv, Weave, Flannel, Nuage nebo OVN. Jiné implementace využívají přímé L3 adresovatelnosti (tedy bez overlay) jako je případ Calico, jistých nastavení Contiv apod.
Azure Container Service v tomto ohledu využívá přímou adresaci a směrování (tedy L3 řešení), takže na úrovni kontejnerů nejsou overlay sítě potřeba (samozřejmě samotný Azure na úrovni VM je používá, ale to je interní záležitost, kterou nemusíte ani znát - pro vás je to možnost vytvořit objekt VNet a v něm subnety, jak si to Azure zařídí, je vám jedno). Azure přidělí každému hostiteli (VM s Kubernetes) samostanou /24 síť:
Vlastní hostitelé mají /16 subnet, ale pro účely kontejnerů dostal každý hostitel jinou /24 síť a mezi nimi je zajištěno směrování.
Zkusme si nastartovat dva deploymenty (v našem testovacím případě s jedním podem, potažmo jedním kontejnerem) tak, aby se v nich reálně nic nedělo, ale zůstaly běžet.
kubectl run c1 --image busybox --command -- tail -f /dev/null kubectl run c2 --image busybox --command -- tail -f /dev/null
Najdeme si jména podů.
# kubectl get pods NAME READY STATUS RESTARTS AGE c1-240622296-djsx2 1/1 Running 0 <invalid> c2-411540186-n3tbn 1/1 Running 0 <invalid>
Vevnitř kontejeru si vypíšeme adresu - všimněte si, že zapadá do /24 rozsahu svého hostitele.
# kubectl exec "c1-240622296-djsx2" -- ip a ... 3: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 0a:58:0a:f4:03:05 brd ff:ff:ff:ff:ff:ff inet 10.244.3.5/24 scope global eth0 valid_lft forever preferred_lft forever ... # kubectl exec "c2-411540186-n3tbn" -- ip a ... 3: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 0a:58:0a:f4:02:05 brd ff:ff:ff:ff:ff:ff inet 10.244.2.5/24 scope global eth0 valid_lft forever preferred_lft forever ...
Můžeme si ověřit, že ping funguje - máme tedy přímo dosažitelnost mezi kontejnery.
# kubectl exec "c2-411540186-n3tbn" -- ping -c1 10.244.3.5 PING 10.244.3.5 (10.244.3.5): 56 data bytes 64 bytes from 10.244.3.5: seq=0 ttl=62 time=2.067 ms --- 10.244.3.5 ping statistics --- 1 packets transmitted, 1 packets received, 0% packet loss round-trip min/avg/max = 2.067/2.067/2.067 ms
Jak je patrné z předchozího odstavce, pokud vypnete pod a pustíte ho na jiném hostiteli, nastartujte s jinou IP adresou. Pokud někdo chce na službu přistupovat, musel by se o změně IP dozvědět. Navíc co když chceme pody replikovat, tedy z výkonnostních důvodů jich provozovat několik a balancovat na ně? Kubernetes má koncept service, který našemu podu (nebo několika podům v rámci deploymentu) přiřadí neměnnou IP adresu, na kterou pak balancuje. Nicméně bude to mít jeden háček, podívejme se jaký.
Představme si, že na našem deploymentu c1 běží nějak služba a vytvořme si konstrukt service.
# kubectl get service NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE c1 10.0.127.42 <none> 80/TCP <invalid> kubernetes 10.0.0.1 <none> 443/TCP 5h
Co se stalo? Kubernetes vytvořil virtuální IP aresu (CLUSTER-IP) a na portu 80 na ní poslouchá a provoz balancuje na všechny pody v deploymentu c1. Zajišťuje tedy load balancing a neměnnou IP pro naší službu.
Nicméně jak je patrné, adresa 10.0.127.42 není veřejná (nebo řekněme externí či reálná). Service je výborná pro balancing služeb využívaných uvnitř clusteru, ale nedostaneme se na ni z okolního světa (tedy od uživatelů). Koncept service je ale možné integrovat s externím load balancerem mimo cluster, tedy v infrastruktuře (typicky virtualizované IaaS), v které cluster běží. Tou je v našem případě Azure a Kubernetes dokáže žádat Azure Load Balancer o zajištění tohoto externího balancingu. Ukažme si jak to funguje.
Spustíme tři deploymenty jednoduché webové aplikace a každý ve třech replikách.
kubectl run web --image yeasy/simple-web --replicas 3 kubectl run web2 --image yeasy/simple-web --replicas 3 kubectl run web3 --image yeasy/simple-web --replicas 3
Teď tuto službu "vystrčíme" ven jako service, ale nejen interní v rámci clusteru, ale i jako zvenku dostupnou balancovanou service.
kubectl expose deployments web --port=80 --type=LoadBalancer kubectl expose deployments web2 --port=80 --type=LoadBalancer kubectl expose deployments web3 --port=80 --type=LoadBalancer
Nějakou dobu bude trvat, než si Kubernetes a Azure vymění potřebné informace, ale po pár minutách uvidíme na výpisu služeb něco takového:
# kubectl get service NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE c1 10.0.127.42 <none> 80/TCP <invalid> kubernetes 10.0.0.1 <none> 443/TCP 5h web 10.0.103.19 13.95.8.111 80/TCP <invalid> web2 10.0.25.153 13.95.12.182 80/TCP <invalid> web3 10.0.15.37 52.174.188.30 80/TCP <invalid>
Všimněte si externích IP adres. Podívejme se do Azure portálu. V zdrojích najdeme nový balancer a public IP zdroje.
IP adresy jsou front end v balanceru.
Směřují na IP adresy hostitelů.
Když se podíváme na balancovací pravidla a taky health sondy, zjistíme, že Kubernetes vybral pro každou naší službu jiný TCP port a Azure balancer automaticky nastavil tak, že z unikátní IP na portu, který jsme chtěli (v našem případě 80) balancuje na agenty na tento jiný TCP port.
Síťařina v Kubernetes je možná trochu komplikovaná, ale ne pokud používáte Azure Container Service. Nejen, že cluster sestavíte jednoduše a spolehlivě, ale máte perfektní integraci se sítí v Azure, takže napojit vaše uživatele na reálné balancované služby ve vašem clusteru je snadné.