<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Maciej[cc] &#187; iptables</title><atom:link href="https://nabozny.io/tag/iptables/index.xml" rel="self" type="application/rss+xml"/><link>https://nabozny.io/tag/iptables/</link><description>Blog o programowaniu i nie tylko</description><lastBuildDate>Sun, 29 Dec 2019 13:27:08 +0000</lastBuildDate><language>pl-PL</language><generator>Hugo 0.167.0</generator><item><title>Kubernetes – jak działa cluster IP</title><link>https://nabozny.io/kubernetes-jak-dziala-cluster-ip/</link><dc:creator>maciej</dc:creator><pubDate>Sun, 29 Dec 2019 13:27:08 +0000</pubDate><category>Cloud</category><category>Linux</category><category>linux</category><category>kubernetes</category><category>iptables</category><guid isPermaLink="true">https://nabozny.io/kubernetes-jak-dziala-cluster-ip/</guid><description>Niedawno pisałem o implementacji sieci opartej o Flannel. Zabrakło tam jednak jednej istotnej rzeczy – clusterIP. W tym poście przeczytasz jak dokładnie jest to zaimplementowane.</description><content:encoded>&lt;p&gt;Niedawno &lt;a href="https://nabozny.io/kubernetes-co-w-sieci-piszczy/"&gt;pisałem&lt;/a&gt; o implementacji sieci opartej o Flannel. Zabrakło tam jednak jednej istotnej rzeczy – clusterIP. W tym poście przeczytasz jak dokładnie jest to zaimplementowane.&lt;/p&gt;&#10;&lt;p&gt;Jeśli patrzyłeś na organizację interfejsów, adresów itd., to pewnie rzuciło Ci się w oczy, że nigdzie nie było routingu ani żadnego innego śladu po adresach IP przydzielonych do serwisów. Jeśli korzystasz z domyślnej konfiguracji, jaką Kubernetes daje, to jest to pula 10.96.0.0/16. Z niej można wybrać dowolny adres IP który będzie dostępny w całym klastrze Kubernetesa i routowany do konkretnego serwisu:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;apiVersion: v1&#10;kind: Service&#10;metadata:&#10; name: dinemic-io&#10; labels:&#10; app: dinemic-io&#10;spec:&#10; ports:&#10; - port: 80&#10; selector:&#10; app: mnabozny-pl&#10; clusterIP: 10.96.0.123&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Serwis za pomocą definicji z pola &lt;em&gt;selector&lt;/em&gt; wyszukuje odpowiednie aplikacje, do których będzie przekierowany ruch na port 80. Będąc w dowolnym miejscu klastra (node lub K8s master) zawsze będziemy mogli połączyć się z tym adresem, na port 80. Patrząc natomaist w tablicę routingu, nie znajdziemy ani jednego wpisu wskazującego na taką pulę adresów:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;maciek&amp;gt; ip route show&#10;default via 192.168.1.1 dev eth0 onlink &#10;10.0.0.0/24 dev eth1 proto kernel scope link src 10.0.0.1 &#10;10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1 &#10;10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink &#10;10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink &#10;10.244.3.0/24 via 10.244.3.0 dev flannel.1 onlink &#10;10.244.4.0/24 via 10.244.4.0 dev flannel.1 onlink &#10;...&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pula adresów &lt;em&gt;10.0.0.0/24&lt;/em&gt; to fizyczna sieć klastra, w którym działa Kubernetes. &lt;em&gt;10.244.X.0/24&lt;/em&gt; to pula adresów, które Kubernetes przydziela poszczególnym podom na nodach (gdzie X odpowiada nodowi, a konkretny numerek podowi).&lt;/p&gt;&#10;&lt;p&gt;Traceroute do adresu powyższego serwisu wychodzi zawsze poza moją sieć, zgodnie z tym, co mówi tablica routingu. Wget na http://10.96.0.123:80 zawsze magicznie dociera do celu. Gdzie więc czai się ta magia?&lt;/p&gt;&#10;&lt;h3 id="iptables-dla-clusterip"&gt;Iptables dla clusterIP&lt;/h3&gt;&#10;&lt;p&gt;Popatrzmy w ipsables. Za pomocą &lt;em&gt;sudo iptables-save&lt;/em&gt; można wylistować wszystkie regułki, które są aktualnie załadowane do jądra. Jest tego dość sporo, dla tego można wspomóc się grepem:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;maciek&amp;gt; sudo iptables-save | grep 10.96.0.123&#10;-A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.96.0.123/32 -p tcp -m comment --comment &amp;#34;cloudover/dinemic-io: cluster IP&amp;#34; -m tcp --dport 80 -j KUBE-MARK-MASQ&#10;-A KUBE-SERVICES -d 10.96.0.123/32 -p tcp -m comment --comment &amp;#34;cloudover/dinemic-io: cluster IP&amp;#34; -m tcp --dport 80 -j KUBE-SVC-AA7UXNSLITSSEFAY&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Do łańcucha &lt;em&gt;KUBE-SERVICES&lt;/em&gt; jest przekierowywany cały ruch z łańcucha &lt;em&gt;INPUT&lt;/em&gt;. Pierwsza z powyższych reułek oznacza pakiety, które mają być oznaczone do MASQUERADE. Druga natomiast przekazuje je do konkretnego łańcucha – KUBE-SVC-AA7UXNSLITSSEFAY. Obie reguły działają dla docelowego adresu 10.96.0.123, na docelowym porcie 80.&lt;/p&gt;&#10;&lt;h3 id="łańcuch-dla-konkretnego-serwisu"&gt;Łańcuch dla konkretnego serwisu&lt;/h3&gt;&#10;&lt;p&gt;W powyższym outpucie z iptables-save można zobaczyć, że druga regułka przekierowuje druch do konkretnego łańcucha, odpowiadającego serwisowi – KUBE-SVC-AA7UXNSLITSSEFAY. Przyjżyjmy się mu bliżej:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;maciek&amp;gt; sudo iptables -nL KUBE-SVC-AA7UXNSLITSSEFAY -t nat&#10;Chain KUBE-SVC-AA7UXNSLITSSEFAY (1 references)&#10;target prot opt source destination &#10;KUBE-SEP-Q3TQA7ZS25JCTF3F all -- 0.0.0.0/0 0.0.0.0/0&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Powyższy serwis przekierowuje cały ruch (100%) do łańcucha odpowiadającemu konkretnemu podowi, na port 80, robiąc przy okazji DNAT:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;maciek&amp;gt; sudo iptables -nL KUBE-SEP-Q3TQA7ZS25JCTF3F -t nat&#10;Chain KUBE-SEP-Q3TQA7ZS25JCTF3F (1 references)&#10;target prot opt source destination &#10;KUBE-MARK-MASQ all -- 10.244.3.115 0.0.0.0/0 &#10;DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp to:10.244.3.115:80&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Czyli, podsumowując, dowolny pakiet wchodzący do noda (INPUT) lub wychodzący z niego (OUTPUT) jest procesowany przez łańcuch KUBE-SERVICES. W nim natomiast K8S dodaje pojedyncze regułki, które wyłapują ruch kierowany do poszczególnych clusterIP przydzielonych do serwisów. Każdy serwis i clusterIP posiada swój osobny łańcuch KUBE-SVC-&amp;hellip;, w którym jest rozdzielany ruch do poszczególnych podów i ich łańcuchów, w których jest realizowany DNAT.&lt;/p&gt;&#10;&lt;h3 id="serwis-dla-wielu-podów"&gt;Serwis dla wielu podów&lt;/h3&gt;&#10;&lt;p&gt;Powyższy przykład pokazywał wpisy dla serwisu, który przekazuje ruch do jednego poda. Co jeśli przeskalujemy go do dwóch instancji? Szybkie &lt;em&gt;kubectl apply -k .&lt;/em&gt; i po chwili mamy kilka zmian! Chwila oznacza dokładnie tyle, ile trzeba aby nowy pod się uruchomił i zaczął działać. Popatrzmy jeszcze raz w całe iptables. KUBE-SYSTEM pozostaje bez zmian, dalej kieruje ruch dla clusterIP 10.96.0.123 do naszego serwisu, w łańcuchu &lt;em&gt;KUBE-SVC-AA7UXNSLITSSEFAY&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;Natomiast sam łańcuch &lt;em&gt;KUBE-SVC-AA7UXNSLITSSEFAY&lt;/em&gt; ma już nowe wpisy:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;gt; sudo iptables -nL KUBE-SVC-AA7UXNSLITSSEFAY -t nat&#10;Chain KUBE-SVC-AA7UXNSLITSSEFAY (1 references)&#10;target prot opt source destination &#10;KUBE-SEP-Q3TQA7ZS25JCTF3F all -- 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.50000000000&#10;KUBE-SEP-OCALVIKCSC2PZKNJ all -- 0.0.0.0/0 0.0.0.0/0&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dodana została na początku nowa regułka kierująca 50% ruchu (popatrz na komentarz – statistic mode, z probability 0.5) do nowego poda, którego ruch jest kształtowany przez łancuch &lt;em&gt;KUBE-SEP-Q3TQA7ZS25JCTF3F&lt;/em&gt;. Pozostałe 50% ruchu wpada w ostatnią regułkę, która jest tam od początku. Jeśli przeskalujemy nasz deployment do większej ilości podów, to udział procentowy odpowiednio spadnie do 100/N.&lt;/p&gt;&#10;&lt;h3 id="skalowalność-samego-cluster-ip"&gt;Skalowalność samego cluster IP&lt;/h3&gt;&#10;&lt;p&gt;Można mieć wrażenie, że każdy pod w klastrze K8S jest w pewnym stopniu single point of failure i jest to prawdą. Natomiast dołożenie nad podami i naszymi aplikacjami abstrakcyjnej warstwy serwisów, które są czymś w rodzaju load balancera, pozwala bardzo łatwo ograniczyć ten słaby punkt. Jeśli nasze pody z aplikacją będą odpowiednio rozproszone po klastrze, w dużej ilości instancji, to awaria dowolnego poda nie wpłynie na dostępność naszego serwisu.&lt;/p&gt;&#10;&lt;p&gt;A co z samym clusterIP? Zauważ, że to konkretne IP, na które tu patrzyliśmy nie jest nigdzie &amp;ldquo;fizycznie&amp;rdquo; widoczne. Każdy host w klastrze K8S posiada swój własny zestaw regułek, zwykle spójny z innymi, przynajmniej pod kątem clusterIP. Próbując dostać się do konkretnego serwisu, właśnie przez clusterIP tak na prawdę iptables losuje nam IP poda, na który będziemy przekierowani i DNAT&amp;rsquo;owani.&lt;/p&gt;&#10;&lt;p&gt;Również dzięki temu można przekierować/przeroutować ruch np. z VPN lub zewnętrznej sieci do dowolnego noda, który stanie się wtedy gateway&amp;rsquo;em. Taki node po otrzymaniu pakietu do jednego z clusterIP przekieruje takie zapytanie dalej, do odpowiedniego poda.&lt;/p&gt;&#10;&lt;h3 id="troubleshooting"&gt;Troubleshooting&lt;/h3&gt;&#10;&lt;p&gt;A wszystko to, bo ostatnio pomieszałem labelki w selektorze aplikacji www i mysql&amp;rsquo;a :) około połowa połączeń była odrzucana, przez co strona częściej nie działała niż działała. Jeśli doświadczasz losowego connection refused, to prawdopodobnie właśnie przy load balancingu/losowaniu poda K8S przekierowuje Twoje zapytanie do innego poda, który w danej chwili ma uruchomioną inną usługę. Po sprawdzeniu, zmianie labelek i przeładowaniu całego serwisu wszystko zaczyna działać.&lt;/p&gt;</content:encoded></item></channel></rss>