<?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; kubernetes</title><atom:link href="https://nabozny.io/tag/kubernetes/index.xml" rel="self" type="application/rss+xml"/><link>https://nabozny.io/tag/kubernetes/</link><description>Blog o programowaniu i nie tylko</description><lastBuildDate>Tue, 12 Mar 2024 10:03:44 +0000</lastBuildDate><language>pl-PL</language><generator>Hugo 0.167.0</generator><item><title>Debugowanie kontenerów w kubernetesie – część 2</title><link>https://nabozny.io/debugowanie-kontenerow-w-kubernetesie-czesc-2/</link><dc:creator>maciej</dc:creator><pubDate>Tue, 12 Mar 2024 10:02:11 +0000</pubDate><category>Cloud</category><category>Linux</category><category>docker</category><category>linux</category><category>k8s</category><category>kubernetes</category><category>sieci</category><guid isPermaLink="true">https://nabozny.io/debugowanie-kontenerow-w-kubernetesie-czesc-2/</guid><description>Po poprzedniej części zapraszam na kolejną, pokazującą jak poradzić sobie z ograniczeniami które wprowadza Kubernetes podczas debugowania aplikacji sieciowych.
Wykonanie pojedynczego polecenia/shelll kubectl -n my_namespace exec -it pod -- /bin/some_command Gdzie zwykle /bin/some_command można …</description><content:encoded>&lt;p&gt;Po poprzedniej &lt;a href="https://nabozny.io/debugowanie-kontenerow-w-chmurze/"&gt;części&lt;/a&gt; zapraszam na kolejną, pokazującą jak poradzić sobie z ograniczeniami które wprowadza Kubernetes podczas debugowania aplikacji sieciowych.&lt;/p&gt;&#10;&lt;h3 id="wykonanie-pojedynczego-poleceniashelll"&gt;Wykonanie pojedynczego polecenia/shelll&lt;/h3&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl -n my_namespace exec -it pod -- /bin/some_command&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Gdzie zwykle &lt;em&gt;/bin/some_command&lt;/em&gt; można zastąpić &lt;em&gt;/bin/bash&lt;/em&gt;&lt;/p&gt;&#10;&lt;h3 id="kontener-do-debugowania"&gt;Kontener do debugowania&lt;/h3&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl debug my_pod_name -n my_namespace -it --image=ubuntu:latest /bin/bash&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Powyższe spowoduje uruchomienie nowego kontenera w tym samym podzie. Dzięki temu możemy połączyć się do localhost (127.0.0.1) i sprawdzić jak działają nasze serwisy za pomocą curl/wget. W trybie debug mamy też dostęp do roota, dzięki czemu hardening właściwej aplikacji nie jest aż tak straszny i nie ogranicza nas, np. w zainstalowaniu nowych paczek. Taki kontener nie współdzieli też systemu plików z oryginalnymi kontenerami z poda, dzięki temu nie namieszamy zbyt dużo.&lt;/p&gt;&#10;&lt;h3 id="lepszy-kontener-do-debugowania"&gt;Lepszy kontener do debugowania&lt;/h3&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl debug my_pod_name -n my_namespace -it --image=ubuntu:latest --share-processes -c container_name --copy-to=mg-debug&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jest to trochę bardziej rozbudowana wersja poprzedniego polecenia. Odpalając taki kontener do debugowania możemy wybrać jeden z istniejących kontenerów i kubectl skopiuje jego konfiguracje (zamontowane &lt;em&gt;volumes&lt;/em&gt;, sieć i przestrzeń procesów). Zabawa w takim środowisku daje już dość duże możliwości interakcji z innymi kontenerami. Można wysłać przyjacieski sygnał -KILL do procesów, pogrzebać w podmontowanych wolumenach lub sprawdzić sieć, również bez ograniczeń hardeningu.&lt;/p&gt;&#10;&lt;h3 id="shell-noda"&gt;Shell noda&lt;/h3&gt;&#10;&lt;p&gt;Link dla zdesperowanych: &lt;a href="https://github.com/kvaps/kubectl-node-shell"&gt;https://github.com/kvaps/kubectl-node-shell&lt;/a&gt;. Skrypt potrafi uruchomić shell na nodzie kubernetesa. Dzięki niemu możemy dostać się jako root na dowolnego noda z klastra jako root i wprost pogrzebać w systemie. Chyba najbardziej &amp;ldquo;wszechmocna&amp;rdquo; opcja do zabawy w szukanie dziury w całym.&lt;/p&gt;&#10;&lt;p&gt;Miłego debugowania!&lt;/p&gt;&#10;</content:encoded></item><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><item><title>Kubernetes – co w sieci piszczy</title><link>https://nabozny.io/kubernetes-co-w-sieci-piszczy/</link><dc:creator>maciej</dc:creator><pubDate>Sun, 24 Nov 2019 10:44:57 +0000</pubDate><category>Cloud</category><category>Linux</category><category>cloud</category><category>linux</category><category>k8s</category><category>kubernetes</category><category>sieci</category><guid isPermaLink="true">https://nabozny.io/kubernetes-co-w-sieci-piszczy/</guid><description>Zróbmy wycieczkę pakietem od poda, aż po default gateway całego klastra Kubernetesa. Post ma na celu ułatwienie debugowania ewentualych problemów sieciowych Kubernetesa z Flannelem.</description><content:encoded>&lt;p&gt;Zróbmy wycieczkę pakietem od poda, aż po default gateway całego klastra Kubernetesa. Post ma na celu ułatwienie debugowania ewentualych problemów sieciowych Kubernetesa z Flannelem.&lt;/p&gt;&#10;&lt;p&gt;Skoro to czytasz, to zakładam, że masz jako takie pojęcie czym się je Kubernetesa, czym są pody i interfejsy sieciowe. Dobrze, gdybyś też wiedział conieco o kontenerach Dockera. Na tapetę weźmiemy (nie)działającą instalację, która składa się z kilku kontenerów i sieci obsługiwanej przez Flannel. Zaczynajmy!&lt;/p&gt;&#10;&lt;h5 id="wejdźmy-do-poda"&gt;Wejdźmy do poda&lt;/h5&gt;&#10;&lt;p&gt;A zanim wejdziemy, zobaczmy jakie mamy pody:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;master# kubectl get pods -n cloudover&#10;NAME READY STATUS RESTARTS AGE&#10;cloudover-io-8xxxyyy886-6c86k 1/1 Running 2 10d&#10;cloudover-io-8xxxyyy886-jx479 1/1 Running 2 10d&#10;cloudover-org-xxxyyyc48-4j4j4 1/1 Running 2 10d&#10;cloudover-org-xxxyyyc48-jv59p 1/1 Running 2 10d&#10;dinemic-io-599xxxyyy8-86d49 1/1 Running 2 20d&#10;dinemic-io-599xxxyyy8-t4pq6 1/1 Running 2 20d&#10;dinemic-io-backup-15xxxyyy20-4t2xr 0/1 Completed 0 2d17h&#10;dinemic-io-backup-15xxxyyy20-2x7pp 0/1 Completed 0 41h&#10;dinemic-io-backup-15xxxyyy20-7k8zs 0/1 Completed 0 17h&#10;dinemic-io-mysql-79yyyxxx6d-wfdrh 1/1 Running 2 20d&#10;mnabozny-pl-56xxxyyy45-p9jp2 1/1 Running 2 20d&#10;mnabozny-pl-56xxxyyy45-plzfq 1/1 Running 2 20d&#10;mnabozny-pl-backup-157xxxyyy0-q76s7 0/1 Completed 0 125m&#10;mnabozny-pl-backup-157xxxyyy0-vrzgt 0/1 Completed 0 65m&#10;mnabozny-pl-backup-157xxxyyy0-nt5mm 0/1 Completed 0 5m20s&#10;mnabozny-pl-mysql-86xxxyyy55-q7nhn 1/1 Running 2 20d&#10;pkg-cloudover-org-84xxxyyy78-dxzxr 1/1 Running 2 10d&#10;pkg-cloudover-org-84xxxyyy78-tqb7w 1/1 Running 2 10d&#10;pkg-dinemic-io-xxxyyyfc77-2729k 1/1 Running 2 10d&#10;pkg-dinemic-io-xxxyyyfc77-4vjsq 1/1 Running 2 10d&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Mamy więc kilka podów z różnych moich serwisów oraz zakończone cron joby backupu danych (Completed). Weźmy na warsztat poda &lt;em&gt;pkg-dinemic-io-xxxyyyfc77-2729k&lt;/em&gt;, który sprawia problemy i zobaczmy jak wygląda jego sieć. Żeby było czym popatrzeć, musimy doinstalować potrzebne narzędzia (polecenie ip). Domyślnie, obraz dockera, z którego odpalony jest ten serwis, jest dość mocno okrojony:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;master# kubectl exec -n cloudover -it pkg-dinemic-io-... bash&#10;apt update&#10;apt install -yqq iproute2 traceroute&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Kolejną rzeczą jest sprawdzenie na którym nodzie uruchomiony jest pod, który chcemy zbadać i doinstalowanie tcpdumpa.&lt;/p&gt;&#10;&lt;p&gt;Mając (prawie) wszystko co potrzeba, możemy zobaczyć jak połączone są interfejsy poda i noda. Linux (docker i cała masa innych narzędzi) wykorzystuje mechanizm veth oraz sieciowe namespace (przestrzenie nazw), netns do oddzielania od siebie ruchu sieciowego kontenerów, wirtualnych maszyn itd. Namespace to grupa procesów (dotyczy to też dowolnego innego zasobu), która współdzieli te same interfejsy, tablice routingu, firewall i trochę innych rzeczy. Działa to prawie tak, jak osobny host w sieci lub wirtualna maszyna, z tą różnicą, że mamy doczynienia z jednym systemem operacyjnym. Jądro Linuksa odseparowuje te zasoby, aby można było używać np. nachodzących na siebie adresacji lub regułek iptables bez żadnych dodatkowych komplikacji.&lt;/p&gt;&#10;&lt;p&gt;Druga z przydatnych rzeczy, o której wspomniałem to interfejs typu veth. Najprościej jest porównać je do wirtualnego kabelka ethernetowego, który spina dwa interfejsy. Jeśli pakiet pojawi się na jego jednym końcu, to jest widoczny również na drugim (peer interface). Sparowane interfejsy veth można przekładać pomiędzy różnymi namespaces i przy nieco bardziej zaawansowanej konfiguracji, kształtować ruch sieciowy pomiędzy nimi. Korzysta z tego m.in. openstackowy neuron, flannel, corecluster i prawdopodobnie sporo innych narzędzi.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;# dodaj nowa namespace&#10;ip netns add test-ns&#10;&#10;# dodaj pare interfejsow veth - veth1 i veth1p, ktore sa wzajemnie sparowane&#10;ip link add veth1 type veth peer name veth1p&#10;&#10;# ustaw namespace interfejsu peer na nasza nowa namespace&#10;ip link set veth1p netns test-ns&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Powyższy przykład tworzy nazwaną namespace, którą możemy zobaczyć przez &lt;em&gt;ip netns show&lt;/em&gt; i usunąć przez &lt;em&gt;ip netns del test-ns&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;My za pomocą polecenia ip chcemy zobaczyć jak poukładane są interfejsy sieciowe na nodzie, gdzie jest uruchomiony pod. Sprawdźmy najpierw w samym podzie jakie mamy interfejsy:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;pod# ip link show&#10;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000&#10; link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&#10; inet 127.0.0.1/8 scope host lo&#10; valid_lft forever preferred_lft forever&#10;3: eth0@if12: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue state UP group default &#10; link/ether a2:62:74:d8:6e:1c brd ff:ff:ff:ff:ff:ff link-netnsid 0&#10; inet 10.244.1.80/24 scope global eth0&#10; valid_lft forever preferred_lft forever&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jak zawsze &lt;em&gt;lo&lt;/em&gt;, które jest po prostu lo. Eth0 za to prezentuje się nieco inaczej niż na &amp;ldquo;zwykłym&amp;rdquo; systemie. Po nazwie eth0 widzimy &lt;em&gt;@if12&lt;/em&gt;. Dwunastka po małpie oznacza id interfejsu peer, który jest sparowany z naszym eth0. Eth0 w tym przypadku veth w przestrzeni namespace, której tutaj nie widzimy, z netnsid 0.&lt;/p&gt;&#10;&lt;p&gt;Patrząc po drugiej stronie, czyli na nodzie mamy taką listę interfejsów:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;node# ip link show&#10;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000&#10; link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&#10;2: eth0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000&#10; link/ether aa:bb:cc:xx:yy:zz brd ff:ff:ff:ff:ff:ff&#10;3: wlan0: &amp;lt;BROADCAST,MULTICAST&amp;gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000&#10; link/ether aa:bb:cc:xx:yy:zz brd ff:ff:ff:ff:ff:ff&#10;...&#10;12: veth7b2f355b@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default &#10; link/ether 92:04:f7:a2:90:f0 brd ff:ff:ff:ff:ff:ff link-netnsid 5&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ostatni z nich to dźwięcznie brzmiący &lt;em&gt;veth7b2f355b&lt;/em&gt; z dopiskiem &lt;em&gt;@if3&lt;/em&gt; Patrząc na pierwszy listing, który zrobiliśmy w podzie, z &lt;em&gt;eth0@if12&lt;/em&gt; widać, że eth0 poda miało id 3. Wszystko wię zatem zgadza. Ostatnią rzeczą, na którą warto rzucić okiem to parametr &lt;em&gt;link-netnsid&lt;/em&gt; oznaczający ID namespace, do którego przypisany jest peerowany interfejs. Tak więc, w &lt;em&gt;ip link show&lt;/em&gt; bezpośrednio z noda widzimy, że interfejs &lt;em&gt;veth7b2f355b&lt;/em&gt; jest speerowany (sparowany? może pożeniony? :) z interfejsem o id 3 z namespace o id 5. W &lt;em&gt;ip link show&lt;/em&gt; uruchomionym na podzie, widzimy, że interfejs &lt;em&gt;eth0&lt;/em&gt; jest speerowany z interfejsem o id 12, z namespace 0.&lt;/p&gt;&#10;&lt;p&gt;Wszystko się zgadza. Żeby sprawdzić możemy puścić pinga lub traceroute z poda i nasłuchiwać na odpowiadającym mu interfejsie sieciowym, na nodzie. Pakiety powinny się pojawić.&lt;/p&gt;&#10;&lt;h3 id="dalej-w-świat-po-opuszczeniu-poda"&gt;Dalej, w świat po opuszczeniu poda&lt;/h3&gt;&#10;&lt;p&gt;Pakiety wychodzące z poda są adresowane przez &amp;ldquo;system operacyjny&amp;rdquo; poda z adresu interfejsu eth0 (src):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;pod# ip route show&#10;default via 10.244.1.1 dev eth0 &#10;10.244.0.0/16 via 10.244.1.1 dev eth0 &#10;10.244.1.0/24 dev eth0 proto kernel scope link src 10.244.1.80&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;W tym przypadku będzie to 10.244.x.y/24, który skonfigurował automatycznie Kubernetes. Po stworzeniu takiego pakietu i zaadresowaniu, jego warstwa IP nie powinna się zmieniać (chyba, że przejdzie przez magiczne regułki iptables, o czym będzie później). Zatem sam pod odpowiada za przygotowanie naszego pakietu w warstwie trzeciej.&lt;/p&gt;&#10;&lt;p&gt;Po przejściu na drugą stronę, czyli do &amp;ldquo;systemu&amp;rdquo; noda, pakiet taki pojawia się na interfejsie speerowanym z podem. Patrząc na adresacje tego interfejsu po stronie noda, nie zobaczymy już na nim adresu IP:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;node# ip a s&#10;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000&#10; link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&#10; inet 127.0.0.1/8 scope host lo&#10; valid_lft forever preferred_lft forever&#10; inet6 ::1/128 scope host &#10; valid_lft forever preferred_lft forever&#10;2: eth0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc pfifo_fast state UP group default qlen 1000&#10; link/ether b8:27:eb:46:06:69 brd ff:ff:ff:ff:ff:ff&#10; inet 10.0.0.103/24 brd 10.0.0.255 scope global eth0&#10; valid_lft forever preferred_lft forever&#10; inet6 fe80::ba27:ebff:fe46:669/64 scope link &#10; valid_lft forever preferred_lft forever&#10;...&#10;5: flannel.1: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue state UNKNOWN group default &#10; link/ether 9e:2b:2b:16:e2:73 brd ff:ff:ff:ff:ff:ff&#10; inet 10.244.1.0/32 scope global flannel.1&#10; valid_lft forever preferred_lft forever&#10; inet6 fe80::9c2b:2bff:fe16:e273/64 scope link &#10; valid_lft forever preferred_lft forever&#10;6: cni0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue state UP group default qlen 1000&#10; link/ether 5a:a8:5d:84:d7:ee brd ff:ff:ff:ff:ff:ff&#10; inet 10.244.1.1/24 scope global cni0&#10; valid_lft forever preferred_lft forever&#10; inet6 fe80::58a8:5dff:fe84:d7ee/64 scope link &#10; valid_lft forever preferred_lft forever&#10;...&#10;12: veth7b2f355b@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP group default &#10; link/ether 92:04:f7:a2:90:f0 brd ff:ff:ff:ff:ff:ff link-netnsid 5&#10; inet6 fe80::9004:f7ff:fea2:90f0/64 scope link &#10; valid_lft forever preferred_lft forever&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;W zasadzie żaden z interfejsów speerowanych z podami nie ma przypisanego adresu. Jak jest zatem organizowany routing i przekazywanie pakietów? Tu zaczyna się nieco trudniejsza zabawa.&lt;/p&gt;&#10;&lt;p&gt;Każdy z interfejsów przyłączonych do podów jest zmostkowany z cni0 (żeby wyświetlić, doinstaluj pakiet br-utils):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;node# brctl show&#10;bridge name&#9;bridge id&#9;&#9;STP enabled&#9;interfaces&#10;cni0&#9;&#9;8000.5aa85d84d7ee&#9;no&#9;&#9;veth67f9f314&#10;&#9;&#9;&#9;&#9;&#9;&#9;&#9;veth7b2f355b&#10;&#9;&#9;&#9;&#9;&#9;&#9;&#9;veth96d5a04b&#10;&#9;&#9;&#9;&#9;&#9;&#9;&#9;vethe25b43a7&#10;&#9;&#9;&#9;&#9;&#9;&#9;&#9;vethed432234&#10;docker0&#9;&#9;8000.02421143bd1a&#9;no&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nasz interfejs &lt;em&gt;veth7b2f355b&lt;/em&gt; również tam jest. Zatem pakiety, o ile nie zostaną wycięte przez iptables, są przekazywane przez mostek pomiędzy interfejsami z tego samego noda. Jeśli nie możesz sobie tego wyobrazić, to pomyśl, że cni0 jest switchem, a każdy zmostkowany interfejs jest kabelkiem (veth) prowadzącym do innego komputera (tutaj to namespace).&lt;/p&gt;&#10;&lt;p&gt;Dokładnie to samo można sprawdzić przez polecenie &lt;em&gt;ip&lt;/em&gt; z pakietu &lt;em&gt;iproute&lt;/em&gt;:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;node# ip link show&#10;...&#10;6: cni0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue state UP mode DEFAULT group default qlen 1000&#10; link/ether 5a:a8:5d:84:d7:ee brd ff:ff:ff:ff:ff:ff&#10;7: veth67f9f314@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default &#10; link/ether ce:53:d6:38:84:5c brd ff:ff:ff:ff:ff:ff link-netnsid 0&#10;8: vethe25b43a7@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default &#10; link/ether a6:16:c1:f9:ae:02 brd ff:ff:ff:ff:ff:ff link-netnsid 1&#10;10: vethed432234@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default &#10; link/ether 82:a5:3c:69:61:39 brd ff:ff:ff:ff:ff:ff link-netnsid 3&#10;11: veth96d5a04b@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default &#10; link/ether 7e:af:3a:8a:29:91 brd ff:ff:ff:ff:ff:ff link-netnsid 4&#10;12: veth7b2f355b@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default &#10; link/ether 92:04:f7:a2:90:f0 brd ff:ff:ff:ff:ff:ff link-netnsid 5&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Każdy z interfejsów zmostkowanych z cni0 (lub innym mostkiem) ma parametr &lt;em&gt;master cni0&lt;/em&gt;, który odpowiada za &amp;ldquo;bycie w bridgu&amp;rdquo; z cni0.&lt;/p&gt;&#10;&lt;p&gt;Ok, mamy zatem interfejsy, które są ze sobą połączone warstwą drugą, natomiast nie mamy jeszcze interfejsu, który odpowiadałby za wypchnięcie pakietu z noda lub przekazanie do innego poda. O ile w podzie wszystko jest jasne i routing mówi: wszystko co nie do mnie, idzie przez default gw, to na nodzie tak różowo nie jest.&lt;/p&gt;&#10;&lt;p&gt;Pakiety w obrębie jednego noda są routowane pomiędzy nimi, przez cni0 – każdy pod na nodzie ma swój IP z sieci przypisanej do noda – w tym przypadku 10.244.1.0/24. Jeśli pakiet ma być dostarczony do innego noda, to zaczynają się schodki.&lt;/p&gt;&#10;&lt;p&gt;Tablica rouringu noda wygląda następująco:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;node# ip route show&#10;default via 10.0.0.1 dev eth0 onlink &#10;10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.103 &#10;10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink &#10;10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1 &#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.244.5.0/24 via 10.244.5.0 dev flannel.1 onlink &#10;10.244.6.0/24 via 10.244.6.0 dev flannel.1 onlink &#10;172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Cała sieć 10.244.1.0/24 jest routowana do naszego noda przez cni0. Natomiast sieci z innych nodów (10.244.2-6.0/24) są przesyłane przez interfejs flannel.1. Dopisek onlink mówi kernelowi, aby przesyłał ruch do tych sieci, nawet nie mając na tym interfejsie przypisanego adresu ip (inaczej musiałby mieć trasę przez konkretny host).&lt;/p&gt;&#10;&lt;p&gt;Interfejs flannel.1 jest tunelem vxlan (&lt;em&gt;ip -d link show&lt;/em&gt; pokaże przy nim ID vlanu 1, mówi o tym też dopisek .1 w nazwie). Zatem adresując pakiet z naszego poda z adresem 10.244.1.68, przechodzi on przez interfejsy veth do domyślnego namespace, i pojawia się jako pakiet przychodzący przez port cni0 (łapie się na regułki z łancucha FORWARD z iptables), ale nie adresowany do naszego noda (zatem nie będzie to iptables INPUT). Ponieważ kernel w domyślnym namespace zna regułki mówiące o tym jak dostać się do innych sieci na innych nodach, przesyła taki pakiet przez interfejs flannel.1. Tam już na podstawie zapytań arp odnajnowany jest host z sieci innego noda, obsługującego danego poda, np. 10.244.6.0/32:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;node# arp -an | grep flannel.1&#10;? (10.244.4.0) at 2e:ed:ad:yy:aa:xx [ether] PERM on flannel.1&#10;? (10.244.0.0) at d2:79:b7:yy:aa:xx [ether] PERM on flannel.1&#10;? (10.244.3.0) at 1e:42:c2:yy:aa:xx [ether] PERM on flannel.1&#10;? (10.244.6.0) at 5a:2d:9a:yy:aa:xx [ether] PERM on flannel.1&#10;? (10.244.2.0) at c6:52:d3:yy:aa:xx [ether] PERM on flannel.1&#10;? (10.244.5.0) at ce:ec:b4:yy:aa:xx [ether] PERM on flannel.1&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sieć stworzona z interfejsów flannel.1 różnych nodów można znowu wyobrazić sobie jako switcha spinającego po jednym kabelku z różnych nodów. Jednak zamiast par veth mamy tutaj tunele VXLAN, które tworzą wirtualną warstwę 2, niezależną od fizycznej sieci. W powyższym przypadku vxlan stworzony przez flannel.1 jest wypuszczany do fizycznej sieci przez eth0 noda. W vxlanie node za pomocą pakietów ARP poznaje inne hosty w sieci i na ich podstawie jest w stanie poprawnie zaadresować mac adres karty odpowiadającej docelowej sieci, w której będzie pod. Flaga onlink w tablicy routingu trochę to zadanie ułatwia, gdyż normalnie kernel musiałby mieć podanego hosta we wspólnej sieci, łączącej wszystkie nody. Tutaj ten etap jest pominięty i przeniesiony na vxlan i odkrywanie hostów poniekąd przez arp.&lt;/p&gt;&#10;&lt;p&gt;Po osiągnięciu docelowego hosta przez vxlan, pakiet przebywa analogiczną drogę do poda.&lt;/p&gt;&#10;&lt;h3 id="iptables"&gt;iptables&lt;/h3&gt;&#10;&lt;p&gt;Po wpisaniu &lt;em&gt;iptables-save&lt;/em&gt; dostajemy całkiem sporo regułek i łańcuchów, które odpowiadają za filtrowanie ruchu i natowanie go. Z ważniejszych rzeczy, na które warto zwrócić uwagę, to na wszystkich nodach mamy mimo wszystko ustawioną politykę FORWARD na DROP z dopuszczeniem przekazywania pakietów z sieci flanella:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;:FORWARD DROP [0:0]&#10;...&#10;-A FORWARD -s 10.244.0.0/16 -j ACCEPT&#10;-A FORWARD -d 10.244.0.0/16 -j ACCEPT&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Do debugowania ruchu może też się przydać poniższy zestaw regułek:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;-A POSTROUTING -s 10.244.0.0/16 -d 10.244.0.0/16 -j RETURN&#10;-A POSTROUTING -s 10.244.0.0/16 ! -d 224.0.0.0/4 -j MASQUERADE --random-fully&#10;-A POSTROUTING ! -s 10.244.0.0/16 -d 10.244.1.0/24 -j RETURN&#10;-A POSTROUTING ! -s 10.244.0.0/16 -d 10.244.0.0/16 -j MASQUERADE --random-fully&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Określa on skąd i gdzie pakiety mają być natowane oraz jaki ruch powinien zostać zablokowany.&lt;/p&gt;&#10;&lt;p&gt;Dodatkowo w dumpie z iptables-save znajdziemy też całą listę (dość długą) reguł, określających dostęp do klastra k8s, jego serwisów i dns&amp;rsquo;a.&lt;/p&gt;&#10;&lt;h3 id="uff"&gt;Uff&amp;hellip;&lt;/h3&gt;&#10;&lt;p&gt;Podsumowując, inżynierowie integrujący flanella zrobili kawał dobrej roboty, który niekoniecznie dobrze się debuguje w razie problemów :) Natomiast ostatnie kilka lub kilkanaście lat pracy nad wirtualizacją i kontenerami daje coraz lepsze narzędzia pozwalające coraz lepiej kontrolować ruch sieciowy w tak zagmatwanych środowiskach jakim jest na przykład kubernetes.&lt;/p&gt;</content:encoded></item></channel></rss>