<?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; Cloud</title><atom:link href="https://nabozny.io/category/cloud/index.xml" rel="self" type="application/rss+xml"/><link>https://nabozny.io/category/cloud/</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>Debugowanie kontenerów w chmurze</title><link>https://nabozny.io/debugowanie-kontenerow-w-chmurze/</link><dc:creator>maciej</dc:creator><pubDate>Fri, 24 Sep 2021 08:29:21 +0000</pubDate><category>Cloud</category><category>Debugowanie</category><category>Linux</category><guid isPermaLink="true">https://nabozny.io/debugowanie-kontenerow-w-chmurze/</guid><description>Czasami coś nie działa i przyczyna tego za diabła nie chce dać się znaleźć. Poniżej kilka pomocnych rad w debugowaniu w środowisku chmury i kontenerów, czyli “u mnie działa, a na cudzym komputerze nie”</description><content:encoded>&lt;p&gt;Czasami coś nie działa i przyczyna tego za diabła nie chce dać się znaleźć. Poniżej kilka pomocnych rad w debugowaniu w środowisku chmury i kontenerów, czyli &amp;ldquo;u mnie działa, a na cudzym komputerze nie&amp;rdquo;&lt;/p&gt;&#10;&lt;h3 id="problem"&gt;Problem&lt;/h3&gt;&#10;&lt;p&gt;Moja strona nie działa, a devops skomplikował sprawę. Na wejściu do naszego serwisu stoi ELB, ALB lub inny load balancer, który przekierowuje ruch do kontenera zarządzanego przez ECS lub kubernetes. Żeby było trudniej, nie mamy dostępu do czeluści load balancera i wejście na noda klastra też jest ograniczone. Możemy jedynie dostać się do kontenera.&lt;/p&gt;&#10;&lt;p&gt;Porblem pojawia się gdy frontend (UI) wysyła żądanie do backendu. Nie dostajemy poprawnej odpowiedzi, a jedynie błąd 503 Service Unavailable.&lt;/p&gt;&#10;&lt;h3 id="rozwiązanie"&gt;Rozwiązanie&lt;/h3&gt;&#10;&lt;p&gt;Znajdź problem i napraw :)&lt;/p&gt;&#10;&lt;h3 id="rozwiązanie-2"&gt;Rozwiązanie 2&lt;/h3&gt;&#10;&lt;p&gt;Zanim zaczniesz grzebać, upewnij się, że zepsucie danego kontenera nie pociągnie za sobą negatywnych konsekwencji. W przypadku Kubernetesa, ECS lub innych orchestratorów (jakkolwiek to się tłumaczy) usunięcie zepsutego kontenera powinno spowodować automatyczne utworzenie nowego, takiego samego. Oprócz tego dobrą rzeczą będzie kawałek papieru lub gedit do notowania co robimy, aby łatwo dało się zrobić rollback zmian, które są trwałe lub odtworzyć całą ścieżkę.&lt;/p&gt;&#10;&lt;p&gt;Ponadto pomyśl czy żądanie/request, które będziemy naprawiać nie niszczy czegoś po stronie backendu. Jeśli problem sprawia guzik &amp;ldquo;skasuj konto&amp;rdquo; to dwukrotne debugowanie tego samego requestu będzie problematyczne&amp;hellip;&lt;/p&gt;&#10;&lt;h5 id="sprawdź-co-nie-bangla"&gt;Sprawdź co nie bangla&lt;/h5&gt;&#10;&lt;p&gt;Na początek znajdź z użyciem developer tools jakie żądanie sprawia problem. Otwórz &lt;em&gt;developer tools&lt;/em&gt; w przeglądarce i popatrz co się dzieje gdy strona wysyła do backendu request. W dalszej części bardzo pomocne będzie skopiowanie polecenia cURL z dokładnymi parametrami żądania, które wysyła przeglądarka. W zakładce z listą połączeń sieciowych z &lt;em&gt;developer tools&lt;/em&gt; możesz wybrać skopiowanie requestu m.in. jako polecenie programu cURL.&lt;/p&gt;&#10;&lt;p&gt;Ponadto, w ulubionym edytorze tekstu, jakim jest oczywiście &lt;em&gt;pico&lt;/em&gt; albo &lt;em&gt;gedit&lt;/em&gt;, zapisz powyższe polecenie. Często bedzie ono zawierało całą masę nagłówków, id sesji, ciasteczek i innych rzeczy, które weryfikuje backend w celu uwierzytelnienia człowieka. Jako użyteczny dodatek możesz zamienić w takim poleceniu nagłówek &lt;em&gt;User Agent&lt;/em&gt; na własny string. W większości logów user agent pojawia się w każdej linijce logowanego requestu i dzięki temu będzie nam łatwiej przeszukać logi tylko z naszego curla.&lt;/p&gt;&#10;&lt;p&gt;Mając powyższe sprawdź czy curl dostaje tak samo niepoprawną odpowiedź od serwera. Możliwe że już na tym etapie otrzymasz komunikat z błędem i dalsze debugowanie stanie się zbędne.&lt;/p&gt;&#10;&lt;h5 id="logi"&gt;Logi&lt;/h5&gt;&#10;&lt;p&gt;Chyba podstawowa rzecz, dużo grepa, a jeśli to AWS to dużo scrollowania i szukania w cloudwatch. Jeśli nie ma w logach aplikacji z backendu, to możliwe że jest w logach load balancera. Jeśli nie, to&amp;hellip;&lt;/p&gt;&#10;&lt;h3 id="czas-na-grubszy-kaliber"&gt;Czas na grubszy kaliber&lt;/h3&gt;&#10;&lt;p&gt;Czasami błąd jest tak uciążliwy, że ani w logach ani w odpowiedzi backendu nie da się znaleźć jego przyczyny. Pozostaje więc kilka cięższych dział:&lt;/p&gt;&#10;&lt;h5 id="wewnątrz-kontenera--netstat"&gt;Wewnątrz kontenera – netstat&lt;/h5&gt;&#10;&lt;p&gt;Odpal &lt;em&gt;netstat&lt;/em&gt; z pakietu &lt;em&gt;net-tools&lt;/em&gt; w Ubuntu/Debianie. Polecenie &lt;em&gt;netstat -ntap&lt;/em&gt; powinno wypisać wszystkie nawiązane połączenia i otwarte porty. Sprawdź czy wszystko jest tak jak powinno, tj. czy serwer nasłuchuje na odpowiednim porcie, czy widać połączenie z bazą etc.&lt;/p&gt;&#10;&lt;p&gt;Jeśli wszystko jest ok, to spróbuj wykonać to samo podczas wykonywania żądania przez cURL. Jeśli takie żądanie wykonuje się za szybko i nie nadążasz klikać, to wrzuć gdzieś &lt;em&gt;sleep 10&lt;/em&gt;, tylko nie zapomnij go usunąć :) Ponieważ często po zmodyfikowaniu kodu potrzebny jest restart serwisu, a ubicie głównego procesu spowoduje restart kontenera, spróbuj &lt;em&gt;kill -HUP [proces]&lt;/em&gt;. Dawno dawno temu, gdy komputery obsługiwało się bez myszki (i nie przez touch screen) zdarzało się, że coś zerwało połączenie z terminalem, na którym mieliśmy uruchomiony interaktywny proces. Przez &lt;em&gt;SIGHUP&lt;/em&gt; można było poinformować taką odłączoną aplikacje, że straciliśmy z nią połączenie i chcemy aby się zakończyła. Serwery zwykle nie reagowały na to, jednak przez lata &lt;em&gt;SIGHUP&lt;/em&gt; stał się sygnałem do reloadu konfiguracji i często również skryptów, które są wykonywane przez serwer. Dzięki temu prawdopodobnie nie zrestartujesz kontenera, w którym pracujesz.&lt;/p&gt;&#10;&lt;p&gt;Wracając do tematu – mając już wrzuconego gdzieś &lt;em&gt;sleep 10&lt;/em&gt; spróbuj netstat ponownie i zobacz czy nic nie wygląda podejrzanie&lt;/p&gt;&#10;&lt;h5 id="curl-do-kontenera"&gt;cURL do kontenera&lt;/h5&gt;&#10;&lt;p&gt;Kolejnym punktem do odhaczenia jest wysłanie żądania przez cURL bezpośrenio z kontenera do niego samego. Możemy wykorzystać wcześniejsze polecenie cURL. Możemy też dodać opcje wymuszającą na cURL&amp;rsquo;u wyświetlenie nagłówków. Jeśli się powiedzie, to problem prawdopodobnie pojawia się gdzieś wcześniej, np. między load balancerem a kontenerem. Jeśli masz taką możliwość, wykonaj to samo z load balancera.&lt;/p&gt;&#10;&lt;h5 id="nc-do-innych-usług"&gt;nc do innych usług&lt;/h5&gt;&#10;&lt;p&gt;Jeśli to zadziała, dobrze jest sprawdzić połączenie z bazą lub innymi wymaganymi serwisami bezpośrednio z kontenera: &lt;em&gt;nc nazwa_hosta port&lt;/em&gt; ( &lt;em&gt;apt install netcat&lt;/em&gt;). W przypadku bazy można doinstalować jej klienta i spróbować się połączyć, przy okazji wykonując jakiś prosty select na tabelach, do których aplikacja ma mieć dostęp.&lt;/p&gt;&#10;&lt;h5 id="udawanie-innych-serwisów"&gt;Udawanie innych serwisów&lt;/h5&gt;&#10;&lt;p&gt;Kolejnym trikiem, który może się przydać jest odpalenie czegoś, co udaje bazę lub inny serwis. Pozwoli to poznać nam jakie żądania są wysyłane do sąsiednich usług. Może to być przydatne gdy nie mamy możliwości zainstalowania &lt;em&gt;tcpdump&lt;/em&gt; lub jest to zabronione.&lt;/p&gt;&#10;&lt;p&gt;Jak to zrobić aby stało się to tylko lokalnie, bez psucia całej infrastruktury? Sprawdź jaki host jest ustawiony jako np. mikroserwis, który chcesz podsłuchać (jeśli używasz po IP, to sorry&amp;hellip;). Następnie wpisz w &lt;em&gt;/etc/hosts&lt;/em&gt; nowy wpis: &lt;em&gt;127.0.0.1 [nazwa hosta]&lt;/em&gt; i odpal w kontenerze &lt;em&gt;nc -l [port usługi]&lt;/em&gt;. W Linuksie pierwszeństwo w ustalaniu adresów przypisanych do hostów ma plik /etc/hosts, dzięki czemu dodając do niego nowy wpis wymusimy aby nasz kontener połączył się z localhost zamiast właściwego hosta. Uruchomiony program &lt;em&gt;nc&lt;/em&gt; pozwoli wypisać to, co miało zostać wysłane przez aplikację z naszego kontenera do serwisu. Pamiętaj, aby usunąć później wpis z &lt;em&gt;/etc/hosts&lt;/em&gt; :)&lt;/p&gt;&#10;&lt;h5 id="podglądnięcie-zmiennych-środowiskowych"&gt;Podglądnięcie zmiennych środowiskowych&lt;/h5&gt;&#10;&lt;p&gt;Często zanim połączymy się z bazą lub podglądniemy ruch sieciowy do danego hosta warto sprawdzić jakie zmienne znają aplikacje, które są już uruchomione w kontenerze. Czasami przez błąd w konfiguracji aplikacja może nie mieć możliwości uzyskania tych zmiennych, bo np. zabrakło uprawnienia IAM do odpytania Secret Parameter Store. Może też to wynikać ze zmienionego API lub biblioteki – zamiast jednego requestu o listę wszystkich parametrów odpytuje o każdy z osobna, do czego IAM już nie uprawnia.&lt;/p&gt;&#10;&lt;p&gt;Wykonaj &lt;em&gt;ps aux&lt;/em&gt;. Zwykle pierwszy proces będzie znał tylko te parametry, które są przekazywane bezpośrednio przez dockera. Wszelkie inne, uzyskiwane z chmury są znane późniejszym procesom, po pełnej inicjalizacji środowiska. Możesz podglądnąć jeden z wyższych PID&amp;rsquo;ów (ale nie z naszej sesji!) przez wpis w &lt;em&gt;/proc/[pid]/environ&lt;/em&gt;:&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;sudo cat /proc/123/environ | tr &amp;lsquo;\0&amp;rsquo; &amp;lsquo;\n&amp;rsquo;&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;Poszczególne zmienne są rozdzielone zerami (znakami o kodzie zero), stąd &lt;em&gt;tr&lt;/em&gt;, który zmieni zera na nowe linie. Jeśli wygląda ok, to pijemy kolejną kawę i szukamy dalej&lt;/p&gt;&#10;&lt;h5 id="ponownie-netstat-lsof"&gt;Ponownie netstat, lsof&lt;/h5&gt;&#10;&lt;p&gt;Sprawdź ulimit i porównaj z ilością połączeń i otwartych plików w kontenerze. W zależności od konfiguracji ten parametr przy dużej ilości połączeń może stwarzać problemy&lt;/p&gt;&#10;&lt;h5 id="podglądanie-z-czym-się-łączy-aplikacja"&gt;Podglądanie z czym się łączy aplikacja&lt;/h5&gt;&#10;&lt;p&gt;Sprawdzisz to przez &lt;em&gt;tcpdump udp port 53&lt;/em&gt;. W trakcie działania sniffera możesz wykonać żądanie przez cURL i sprawdzić co się dzieje i z czym się łączy aplikacja. Jeśli używasz IP, to zapytanie tcpdump będzie trochę inne.&lt;/p&gt;&#10;&lt;h5 id="strace-do-czytania-logów"&gt;strace do czytania logów&lt;/h5&gt;&#10;&lt;p&gt;Jeśli cloudwatch szczególnie uwiera tu i ówdzie, przez co nie możesz znaleźć logów, warto sprawdzić co aplikacja wypisuje na standardowe wyjście. Nightmare w kontenerze? Nie :) Spróbuj:&lt;br&gt;&#10;&lt;em&gt;strace --attach=[pid serwera] -e write=1,2&lt;/em&gt;&lt;br&gt;&#10;Powyższe podłączy &amp;ldquo;debuger&amp;rdquo; strace do procesu i będzie wyłapywało jedynie wywołania systemowe funkcji write do plików o fd=1 i 2. Oznaczają one odpowiednio &lt;em&gt;stdout&lt;/em&gt; i &lt;em&gt;stderr&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;Nie jest to najwygodniejsza metoda oglądania logów, ale pozwala użyć ukochanego grepa i awk&amp;rsquo;a, co w przypadku cloudwatch nie zawsze jest najwygodniejsze i najszybsze.&lt;/p&gt;&#10;&lt;p&gt;Dla przykładowego &lt;em&gt;ls&lt;/em&gt; wykonanego przez ssh:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://nabozny.io/uploads/2021/09/strace-ssh.png" alt="" width="635" height="61" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;/p&gt;&#10;&lt;p&gt;otrzymamy wynik ze strace:&lt;br&gt;&#10;&lt;img src="https://nabozny.io/uploads/2021/09/strace-out.png" srcset="https://nabozny.io/uploads/2021/09/strace-out_hu_ff789cf743115bfc.png 600w, https://nabozny.io/uploads/2021/09/strace-out.png 1004w" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" alt="" width="1004" height="690" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;br&gt;&#10;Nie jest to najczytelniejsze, ale odpowiednio okraszając to grep&amp;rsquo;em i wycinając wszystkie znaki sterowania terminalem (kolory, znaki specjalne etc.) możemy uzyskać dość czytelny wgląd w to, co się dzieje. Powyższa metoda zadziała też na połączeniach sieciowych, w tym na tych, któ©e są już odszyfrowane i idą np. do fcgi po systemowym sockecie.&lt;/p&gt;&#10;&lt;h3 id="na-koniec"&gt;Na koniec&lt;/h3&gt;&#10;&lt;p&gt;Jeśli nie udało się nic znaleźć, a aplikacja dalej się sypie, to pozostaje stara dobra metoda&amp;hellip; wyłącz i włącz kontener, później instancje, a na koniec pogadaj z frontendowcami i backendowcami którzy muszą wysyłać to problematyczne żądanie. Chociaż pogadać i zrozumieć jest dobrze przed całym powyższym szukaniem dziury w całym ;)&lt;/p&gt;</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><item><title>Jak trzymać hasła w pamięci i dlaczego cstringi są czasem lepsze od std::stringów?</title><link>https://nabozny.io/jak-trzymac-hasla-w-pamieci-i-dlaczego-cstringi-sa-czasem-lepsze-od-stdstringow/</link><dc:creator>maciej</dc:creator><pubDate>Sat, 16 Feb 2019 08:32:37 +0000</pubDate><category>Programowanie</category><category>Cloud</category><category>Linux</category><category>kryptografia</category><category>programowanie</category><category>linux</category><guid isPermaLink="true">https://nabozny.io/jak-trzymac-hasla-w-pamieci-i-dlaczego-cstringi-sa-czasem-lepsze-od-stdstringow/</guid><description>“Poznaj sekret cstringów. Std::stringi ich za to nienawidzą”! A na serio, nie zgapiając od pomponików, będzie trochę na temat trzymania wrażliwych danych w pamięci RAM i o zapobieganiu ich zapisowi na dysk.</description><content:encoded>&lt;p&gt;&amp;ldquo;Poznaj sekret cstringów. Std::stringi ich za to nienawidzą&amp;rdquo;! A na serio, nie zgapiając od pomponików, będzie trochę na temat trzymania wrażliwych danych w pamięci RAM i o zapobieganiu ich zapisowi na dysk.&lt;/p&gt;&#10;&lt;p&gt;Na początek krótkie ćwiczenie: napisałeś program do przechowywania haseł. Taki keepass w wersji DIY. Po uruchomieniu wczytuje zaszyfrowany plik do pamięci, pyta Cię o hasło, odszyfrowuje i pokazuje wybrane hasło. Działa dopóki go nie zamkniesz, na wszelki wypadek, jakbyś potrzebował jeszcze innego hasła. Niby bezpieczne, bo zaszyfrowane, na hasło&amp;hellip; ale nie do końca.&lt;/p&gt;&#10;&lt;h3 id="co-może-pójść-nie-tak"&gt;Co może pójść nie tak?&lt;/h3&gt;&#10;&lt;p&gt;Trzymasz odszyfrowany pęk kluczy w pamięci – to pierwszy problem. Dowolne nadpisanie/odczytanie takiej pamięci, np. przez /dev/mem albo /proc/X/mem może skompromitować wszystkie hasła z Twojego pęku – dowolna osoba z uprawnieniami root&amp;rsquo;a będzie mogła wczytać treść z Twojej pamięci. To może odszyfrowywać tylko gdy tego potrzebujemy? Ok, wtedy trzymamy w pamięci jedynie nasze główne hasło i zaszyfrowane przez prawie cały czas dane.&lt;/p&gt;&#10;&lt;p&gt;Osoba atakująca będzie mogła wtedy dość łatwo poznać hasło, którego możesz też przecież używać gdzieś indziej (tak, są tacy ludzie i jest ich dość dużo :) ). Popatrzmy na inny przykład: Linux Kernel posiada wbudowany system szyfrowania dysków – LUKS. Do odszyfrowania jednego dysku można przypisać kilka różnych haseł dla różnych osób. Dzięki temu można uniknąć przekazywania ich i zapisywania na karteczkach. Każdy może użyć wtedy swojego ulubionego hasła :) Jeśli natomiast je zgubi, można zmienić tylko to jedno hasło do dysku, bez zmiany innych haseł. Co więcej zmiana dowolnego hasła nie wymusza re-szyfrowania całego dysku. Jak to działa? Nie ma za tym jakiejś specjalnej magii i matematyki. Każdy z użytkowników, którzy mają swoje hasła tak na prawdę deszyfruje główny klucz do dysku. Dopiero za pomocą tego klucza jest przeprowadzane pełne (de)szyfrowanie.&lt;/p&gt;&#10;&lt;p&gt;Podobny mechanizm możesz też zastosować w Twoim pęku kluczy lub podobnej aplikacji. Za pomocą hasła użytkownik może odszyfrować tak na prawdę klucz symetryczny, który posłuży do odczytania pozostałej części danych. Przechowywanie go w pamięci, jeśli to konieczne, jest wtedy nieco bardziej bezpiecznym rozwiązaniem niż ciągłe przechowywanie hasła, lub co gorsze pełnego pęku kluczy.&lt;/p&gt;&#10;&lt;h3 id="gdzie-zapisać-tymczasowe-hasło"&gt;Gdzie zapisać tymczasowe hasło?&lt;/h3&gt;&#10;&lt;p&gt;Mimo wszystko Twoje hasło lub klucz deszyfrujący musi być tak czy tak chociaż na chwilę wczytane do pamięci. Jak zabezpieczyć się przed jego wyciekiem? Oprócz możliwości odczytania hasła bezpośrednio z pamięci jest jeszcze jedna możliwość. Atakujący może zaalokować tak dużo pamięci w systemie, że w końcu Twój DIY Keepass zostanie przeniesiony do swapu. A stąd już prosta droga do wyciągnięcia i przeanalizowania danych. Jeśli nie jako dump pamięci z poziomu roota, to śrubokrętem, przenosząc dysk do innego komputera lub bootując komputer z live cd.&lt;/p&gt;&#10;&lt;p&gt;Jak się przed tym obronić? Linux posiada trzy funkcje pozwalające Twojej aplikacji określić jak ma się zachowywać jej wirtualna pamięć. Pierwsza z nich to &lt;a href="https://linux.die.net/man/2/mprotect"&gt;&lt;em&gt;mprotect&lt;/em&gt;&lt;/a&gt;. Podając adres pamięci (np. blok zwrócony przez &lt;em&gt;malloc&lt;/em&gt;) możesz sprawić, że dany obszar będzie dostępny tylko do odczytu, do zapisu lub w cale. Flaga &lt;em&gt;PROT_WRITE&lt;/em&gt; jest zwykle stosowana do zabezpieczenia obszaru pamięci, w którym znajduje się Twój plik wykonywalny, aby przypadkowe (lub złośliwe) nadpisanie kodu instrukcji Twojej aplikacji nie było możliwe. &lt;em&gt;PROT_EXEC&lt;/em&gt; jest natomiast stosowane do zabezpieczenia pamięci przed wykonywaniem jej kodu, np. w obszarach pamięci przechwujących zmienne. Zapobiega do przypadkowemu uruchomieniu takiego kodu. Wbrew pozorom bardzo użyteczną flagą może być &lt;em&gt;PROT_NONE&lt;/em&gt;. Takiego obszaru nie da się ani odczytać, ani zmodyfikować ani wykonać. Jedną stronę takiej pamięci możemy ustawić np. za obszarem, do którego będą zapisywane dane od użytkownika, o nieznanym rozmiarze. Jeśli nasze programowe zabezpieczenia zawiodą i na przykład zaczniemy zapisywać do takiej tablicy bez opamiętania (bez if&amp;rsquo;a :) to finalnie program spróbuje zapisać coś na stronie pamięci oznaczonym &lt;em&gt;PROT_NONE&lt;/em&gt;. W takiej sytuacji zadziała już sprzętowe zabezpieczenie i nasza aplikacja otrzyma stosowny, zwykle bolesny sygnał od systemu operacyjnego.&lt;/p&gt;&#10;&lt;p&gt;Drugą funkcją służącą do modyfikowania zachowania pamięci jest &lt;a href="http://man7.org/linux/man-pages/man2/madvise.2.html"&gt;&lt;em&gt;madvise&lt;/em&gt;&lt;/a&gt;. Podobnie jak &lt;em&gt;mprotect&lt;/em&gt; przyjmuje ona adres i rozmiar obszaru pamięci, który chcemy zmodyfikować. Za jej pomocą możemy powiedzieć systemowi (doradzić) jak będziemy korzystać z tego obszaru. Czy na przykład będą to losowe odczyty i cache&amp;rsquo;owanie na wyrost nie ma sensu, czy też podczas forka procesu ten obszar pamięci ma pozostać nieskopiowany. Flag jest sporo, warto przeglądnąć je w przyjaznym manualu.&lt;/p&gt;&#10;&lt;p&gt;Ostatnia funkcja, warta wspomnienia to &lt;em&gt;mlock&lt;/em&gt;. Ponownie, przyjmuje jako pierwsze parametry adres i rozmiar obszaru pamięci. Zapobiega ona przenoszeniu stron pamięci na swap. Oznaczając w ten sposób obszar gdzie trzymamy klucz deszyfrujący oraz sam keychain, możemy zabezpieczyć się przed wspomnianym wcześniej wymuszeniem zrzutu pamięci na swap.&lt;/p&gt;&#10;&lt;p&gt;Korzystając z wszystkich trzech powyższych funkcji możemy wymusić na systemie operacyjnym nieco lepsze potraktowanie naszych danych przesiadujących w pamięci. Za pomocą &lt;em&gt;mprotect&lt;/em&gt; możemy przez większość czasu zapobiegać dostępowi do pamięci, ustawiając jej flagę &lt;em&gt;PROT_NONE&lt;/em&gt;, i &amp;ldquo;odbezpieczając&amp;rdquo; tylko gdy użytkownik potrzebuje hasła. Za pomocą &lt;em&gt;madvise&lt;/em&gt; mona zapobiec skopiowaniu tej pamięci przez fork (i prawdopodobnie zabezpieczyć się też na kilka innych okoliczności), a z &lt;em&gt;mlock&lt;/em&gt; zapobiegniemy wyrzuceniu na swap.&lt;/p&gt;&#10;&lt;h3 id="cstring-vs-stdstring"&gt;Cstring vs. std::string&lt;/h3&gt;&#10;&lt;p&gt;Przy okazji wychodzi czemu czasami zarządzanie pamięcią przez &lt;em&gt;malloc&lt;/em&gt; i standardowe stringi z C są bardziej użyteczne. Korzystając z &lt;em&gt;std::string&lt;/em&gt; nie mamy tak dokładnej kontroli nad tym, gdzie będą trzymane dane ze stringa, jak również nie mamy gwarancji że po dopisaniu kolejnych znaków nie zostanie on w całości przeniesiony w inne miejsce. Trudniejsze staje się też zabezpieczenie strony tuż za stringiem. Nie jest to jednak niemożliwe, jednak wymaga dodatkowej pracy i dogłębnej wiedzy jak jest alokowana w tym przypadku pamięć. Dodatkowo, gdy std::string zwalnia pamięć, musimy też ręcznie posprzątać po nim, aby inny obiekt nie dostał obszaru z &lt;em&gt;PROT_NONE&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;W przypadku c stringów, wszystkie operacje przenoszenia, zabezpieczania i odbezpieczania musimy zrobić tak czy tak ręcznie, jednak z punktu widzenia czytelności kodu i jego obsługi jest to znacznie czytelniejsze.&lt;/p&gt;&#10;&lt;h3 id="swap-też-jest-szyfrowany"&gt;Swap też jest szyfrowany&lt;/h3&gt;&#10;&lt;p&gt;Tak, od jakiegoś czasu dystrybucje wspierają to domyślnie. Strony pamięci zrzucane do swapu są w locie szyfrowane, także odczytanie ich po wyłaczeniu komputera nieco się komplikuje. Przykładowy wpis z Arch wiki:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;swap /dev/sdX# /dev/urandom swap,cipher=aes-xts-plain64,size=256&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Przy każdym starcie system generuje losowy klucz do zaszyfrowania swapu z /dev/urandom. Nawet jeśli coś się na swapie pojawi, to będą to tylko losowe znaki dla osoby, która nie zna klucza. Problem pojawia się jednak przy wsparciu dla disk suspend – uśpienia komputera i zrzucenia stanu pamięci na dysk. W tym przypadku sprawy się nieco komplikują, ale to historia na inny wpis.&lt;/p&gt;&#10;&lt;h3 id="wirtualne-maszyny"&gt;Wirtualne maszyny&lt;/h3&gt;&#10;&lt;p&gt;O ile wszystkie powyższe sposoby mają się całkiem dobrze na &amp;ldquo;gołym&amp;rdquo; PC, to już korzystając z wirtualizacji nie mamy gwarancji, że ktoś nie zrobi zrzutu pamięci oraz dysku w locie i nie będzie próbował analizować tego offline. W takim wypadku jedynym rozwiązaniem minimalizującym ryzyko przechwycenia cennych danych jest tylko chwilowe odszyfrowywanie ich. Nie gwarantuje to jednak pełnego bezpieczeństwa. Na własnych systemach można zapobiegać temu odpowiednimi opcjami dla wirtualnych maszyn, dzięki którym gospodarz nie będzie przenosił ich stron na swap. Służy do tego m.in. opcja &lt;a href="https://libvirt.org/formatdomain.html#elementsMemoryBacking"&gt;&lt;em&gt;locked&lt;/em&gt;&lt;/a&gt; w libvircie. Jednak w publicznych chmurach nie mamy takiej gwarancji.&lt;/p&gt;&#10;&lt;h3 id="bezpieczeństwo-różnych-algorytmów"&gt;Bezpieczeństwo różnych algorytmów&lt;/h3&gt;&#10;&lt;p&gt;Na koniec warto wspomnieć o bezpieczeństwie samych algorytmów szyfrujących. Co, jeśli któryś okaże się podatny na ataki? Często aplikacje oferujące szyfrowanie pozwalają na złożenie kilku algorytmów na raz. O ile Twoje hasło pozostaje prawdopodobnie najsłabszym punktem, to szansa na skompromitowanie dwóch algorytmów szyfrujących na raz znacznie spada. Pomyśl, że lata temu używałeś MD5 do potwierdzania integralności plików. Algorytm się &amp;ldquo;zepsuł&amp;rdquo;. Wykonanie SHA1(MD5(&amp;hellip;)) nieco ratuje sprawę. W ten sposób zabezpieczają się np. adresy portfeli z Ethereum. Skompromitowanie jednego algorytmu (co powodowałoby że łatwo wygenerować inny portfel z tym samym adresem) znacznie maleje.&lt;/p&gt;</content:encoded></item><item><title>Krótki wstęp do AWS i boto3</title><link>https://nabozny.io/krotki-wstep-do-aws-i-boto3/</link><dc:creator>maciej</dc:creator><pubDate>Thu, 14 Feb 2019 16:59:45 +0000</pubDate><category>Cloud</category><guid isPermaLink="true">https://nabozny.io/krotki-wstep-do-aws-i-boto3/</guid><description>Jest sporo poradników na temat tego jak korzystać z chmury AWS. Oto kolejny :)</description><content:encoded>&lt;p&gt;Jest sporo poradników na temat tego jak korzystać z chmury AWS. Oto kolejny :)&lt;/p&gt;&#10;&lt;h3 id="przygotowanie-środowiska"&gt;Przygotowanie środowiska&lt;/h3&gt;&#10;&lt;p&gt;Na początek zainstaluj potrzebne pakiety:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;apt update&#10;apt install ipython3 python3-boto3&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;i załóż konto na &lt;a href="https://aws.amazon.com"&gt;aws.amazon.com&lt;/a&gt;. Po zalogowaniu się do &lt;a href="https://console.aws.amazon.com"&gt;console.aws.amazon.com&lt;/a&gt; wpisz w polu do przeszukiwania serwisów AWS &lt;em&gt;IAM&lt;/em&gt;. IAM to narzędzie do zarządzania kluczami dostępu – Identity and Access Manager. Służą one do przydzielania dostępu do Twoich zasobów w AWS skryptom. Dzięki temu nie musisz zapisywać hasła na przykład na serwerze, lecz tylko klucz i ID klucza.&lt;br&gt;&#10;&lt;img src="https://nabozny.io/uploads/2019/02/Screenshot-2019-02-14-at-09.37.03.png" srcset="https://nabozny.io/uploads/2019/02/Screenshot-2019-02-14-at-09.37.03_hu_7f529e6617fb7ddd.png 600w, https://nabozny.io/uploads/2019/02/Screenshot-2019-02-14-at-09.37.03_hu_a025547644f5bbfd.png 1200w, https://nabozny.io/uploads/2019/02/Screenshot-2019-02-14-at-09.37.03.png 1366w" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" alt="" width="1366" height="732" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;/p&gt;&#10;&lt;p&gt;Następnie kliknij users i wybierz lub stwórz nowego użytkownika. W detalach, wybierz zakładkę &lt;em&gt;Security credentials&lt;/em&gt;. Powinna tam być sekcja &lt;em&gt;Access keys&lt;/em&gt;. Kliknij &lt;em&gt;Create access key&lt;/em&gt;. W okienku dialogowym pokaże się tabelka z ID klucza oraz sam klucz (widoczny po kliknięciu show).&lt;/p&gt;&#10;&lt;p&gt;Zapisz te wartości w pliku ~/.aws/credentials na Twoim komputerze. Boto3 oraz inne narzędzia AWS, z których możesz korzystać będą szukać właśnie tam domyślnych danych do logowania w AWS:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;[default]&#10;aws_access_key_id = ABCDEFGHIJKL&#10;aws_secret_access_key = joJ/fw4tgp98sjoif34faCSDSv459...&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="pierwsze-kroki"&gt;Pierwsze kroki&lt;/h3&gt;&#10;&lt;p&gt;Odpal konsolę ipython3, zaimportuj boto3 i połącz się z AWS:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;# ipython3&#10;import boto3&#10;&#10;ec2 = boto3.resource(&amp;#39;ec2&amp;#39;, &amp;#39;us-east-1&amp;#39;)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Za pomocą obiektu ec2 będziemy mogli odpytać API AWS o dostępne zasoby i uruchomić nowe instancje w danym regionie.&lt;/p&gt;&#10;&lt;h3 id="listowanie-zasobów"&gt;Listowanie zasobów&lt;/h3&gt;&#10;&lt;p&gt;Poniższy fragment wyświetli obrazy, których właścicielem jest Canonical (użytkownik o ID 099720109477):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;for i in ec2.images.filter(Owners=[&amp;#39;099720109477&amp;#39;]):&#10; print(i)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;ec2.images, podobnie jak instances, vpcs itd. posiada kilka metod służących do selektywnego listowania zasobów w AWS. Aby wyświetlić wszystkie zasoby, można wykorzystać ec2.images.all(), jednak nie polecam tego akurat na images – zbyt dużo wyników. Powyższa metoda filter pozwala przekazać kilka rodzajów kryteriów do wyświetlenia. Aby zobaczyć pełną dokumentację wpisz w konsoli ipython:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;In [11]: ec2.images.filter?&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;i naciśnij enter. Powinna pokazać się pełna dokumentacja funkcji:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;Signature: ec2.images.filter(**kwargs)&#10;Docstring:&#10;Creates an iterable of all Image resources in the collection filtered by kwargs passed to method.&#10;&#10;See also: `AWS API Documentation &amp;lt;https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeImages&amp;gt;`_&#10;&#10;&#10;**Request Syntax**&#10;::&#10;&#10; image_iterator = ec2.images.filter(&#10; ExecutableUsers=[&#10; &amp;#39;string&amp;#39;,&#10; ],&#10; Filters=[&#10; {&#10; &amp;#39;Name&amp;#39;: &amp;#39;string&amp;#39;,&#10; &amp;#39;Values&amp;#39;: [&#10; &amp;#39;string&amp;#39;,&#10; ]&#10; },&#10; ],&#10; ImageIds=[&#10; &amp;#39;string&amp;#39;,&#10; ],&#10; Owners=[&#10; &amp;#39;string&amp;#39;,&#10; ],&#10; DryRun=True|False&#10; )&#10;...&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Na kolejnych stronach dokumentacji&lt;/p&gt;&#10;&lt;h3 id="tworzenie-nowych-obrazów"&gt;Tworzenie nowych obrazów&lt;/h3&gt;&#10;&lt;p&gt;Użyj metody &lt;em&gt;ec2.create_volume&lt;/em&gt; aby stworzyć nowy obraz dysku. Sprawdź jej parametry wywołując ją najpierw z pytajnikiem. Na początek możesz dodać parametr DryRun=True aby nie zaalokować niepotrzebnie zasobów:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;v = ec2.create_volume(DryRun=True)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;API &lt;em&gt;boto3&lt;/em&gt; w odpowiedzi powinno zwrócić wyjątek z informacją jakich parametrów brakuje. Pododawaj je aż uzyskasz prawidłową odpowiedź z API.&lt;/p&gt;&#10;&lt;p&gt;Jednym z parametrów, jaki będziesz musiał podać jest AvailabilityZone, która jest czymś dokładniejszym niż określenie samego regionu. Aby zobaczyć jakie są AvailabilityZones w Twoim regionie, będziemy potrzebowali kolejnego obiektu API – ec2.client. Za jego pomocą można zobaczyć nieco inne parametry niż z tego, co zwróciło nam poprzednio ec2.resource:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;ec2client = boto3.client(&amp;#39;ec2&amp;#39;, &amp;#39;us-east-1&amp;#39;)&#10;ec2client.describe_availability_zones()&#10;Out[22]:&#10;{&amp;#39;AvailabilityZones&amp;#39;: [{&amp;#39;Messages&amp;#39;: [],&#10; &amp;#39;RegionName&amp;#39;: &amp;#39;us-east-1&amp;#39;,&#10; &amp;#39;State&amp;#39;: &amp;#39;available&amp;#39;,&#10; &amp;#39;ZoneName&amp;#39;: &amp;#39;us-east-1xyz&amp;#39;&#10;...&#10;In [23]:&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wybierz jedną strefę i wróć do tworzenia obrazu. Finalnie powinieneś dostać taki komunikat z DryRun=True:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;ClientError: An error occurred (DryRunOperation) when calling the CreateVolume operation: Request would have succeeded, but DryRun flag is set.&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Oznacza to dokładnie tyle, że wszystkie checki przeszyły i pozostało tylko usunąć DryRun aby poprawnie wysłać żądanie do AWS. Sprawdź jeszcze tylko rozmiar tworzonego volumenu aby nie zjeść całej swojej quoty :)&lt;/p&gt;&#10;&lt;p&gt;Jeśli zapisałeś zwrócony wynik do zmiennej, to powinieneś móc odczytać ID obrazu:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;print(v.id)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jeśli nie, to prawdopodobnie dostałeś podobny do poniższego string:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;Out[26]: ec2.Volume(id=&amp;#39;vol-0348fjqpif...&amp;#39;)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Możesz stworzyć nowy obiekt odpowiadający Twojemu obrazowi:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;v2 = ec2.Volume(id=&amp;#39;vol-0348fjqpif...&amp;#39;)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Mając &amp;ldquo;w ręce&amp;rdquo; taki obiekt można sprawdzić też jaki obraz ma rozmiar (v2.size), jego status (v2.state) i podobne rzeczy. Jeśli chcesz zobaczyć w konsoli ipython wszystkie opcje, wpisz v2 i wciśnij tabulator:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;In [31]: v2. [to wciśnij tabulator :) ]&#10; v.attach_to_instance v.create_time v.enable_io v.kms_key_id v.size v.volume_id&#10; v.attachments v.delete v.encrypted v.load v.snapshot_id v.volume_type&#10; v.availability_zone v.describe_attribute v.get_available_subresources v.meta v.snapshots&#10; v.create_snapshot v.describe_status v.id v.modify_attribute v.state&#10; v.create_tags v.detach_from_instance v.iops v.reload v.tags&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Strzałkami w górę/dół można wybrać sobie funkcję. Ot taki feature dla leniwych :) Na koniec, żeby nie dostać po kieszeni można skasować wolumen:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;v2.delete()&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="tworzenie-wirtualnej-maszyny--ec2-instance"&gt;Tworzenie wirtualnej maszyny – EC2 instance&lt;/h3&gt;&#10;&lt;p&gt;Przejdźmy przez dodanie wszystkich parametrów po kolei:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;i = ec2.create_instances(DryDun=True, MinCount=1, MaxCount=1)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Funckja pozwala stworzyć wiele instancji na raz. Pierwszymi parametrami o jakie będzie krzyczyała są powyższe MinCount i MaxCount. Tutaj chcemy zrobić dokładnie jedną instancje, więc oba są równe 1.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Wybór obrazu&lt;/em&gt;&lt;br&gt;&#10;Drugi brakujący parametr to ImageId. Zmodyfikujmy nieco wcześniejszą pętle aby sprawdzić jakie są nazwy obrazów i ich ID:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;for i in ec2.images.filter(Owners=[&amp;#39;099720109477&amp;#39;], Filters=[{&amp;#39;Name&amp;#39;: &amp;#39;name&amp;#39;, &amp;#39;Values&amp;#39;: [&amp;#39;*bionic*&amp;#39;]}]):&#10; print(str(i.id) + &amp;#39; - &amp;#39; + str(i.name))&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dodatkowy parametr metody filter to Filter. Przyjmuje on listę filtrów opisanych słownikiem. Wymaganymi polami są &lt;em&gt;Name&lt;/em&gt;, który określa nazwę pola, którego dotyczy dany filtr oraz &lt;em&gt;Values&lt;/em&gt; który określa dopuszczalne wartości tego pola w dowolnym pasującym wpisie. W powyższym przykładzie filtrujemy po polu name i wymagamy, aby w nazwie obrazu pojawiło się słowo &lt;em&gt;bionic&lt;/em&gt; w dowolnym otoczeniu (gwiazdki):&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;[&#10; {&#10; &amp;#39;Name&amp;#39;: &amp;#39;name&amp;#39;,&#10; &amp;#39;Values&amp;#39;: [&amp;#39;*bionic*&amp;#39;]&#10; }&#10;]&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Można też dołożyć kolejne kryteria aby na przykład wylistować tylko publiczne obrazy:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;[&#10; {&#10; &amp;#39;Name&amp;#39;: &amp;#39;name&amp;#39;,&#10; &amp;#39;Values&amp;#39;: [&amp;#39;*bionic*&amp;#39;]&#10; },&#10; {&#10; &amp;#39;Name&amp;#39;: &amp;#39;is-public&amp;#39;,&#10; &amp;#39;Values&amp;#39;: [&amp;#39;true&amp;#39;]&#10; }&#10;]&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;i tak dalej&amp;hellip; Wybierz swój ID obrazu z ubuntu i idźmy dalej. &lt;em&gt;ami-0a313d6098716f372&lt;/em&gt; to na przykład Ubuntu Bionic LTS w wersji server minimal.&lt;/p&gt;&#10;&lt;h3 id="dodanie-vpc-i-sieci"&gt;Dodanie VPC i sieci&lt;/h3&gt;&#10;&lt;p&gt;Kolejnym brakującym parametrem będzie VPC, o ile nie masz swojego. Stwórzmy nowe i dodajmy od razu do niego sieć. VPC to wirtualna prywatna sieć, w której można dodawać adresacje podsieci, routing i trochę innych fajnych rzeczy. To trochę tak jakbyś łączył kabelki do switcha, a później dodawał adresacje na serwerze dhcp jako subnet:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;vpc = ec2.create_vpc(CidrBlock=&amp;#39;10.0.0.0/16&amp;#39;)&#10;subnet = vpc.create_subnet(CidrBlock=&amp;#39;10.0.1.0/24&amp;#39;)&#10;iface = subnet.create_network_interface()&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Cały subnet powinien mieścić się w adresacji jaką podałeś w create_vpc. Obiekt &lt;em&gt;iface&lt;/em&gt; odpowiada podłączeniu do naszej nowej sieci. Coś w rodzaju ethernetowego kabelka z przypiętym adresem IP znajdującym się w danej sieci (subnet). Przypisany losowy adres możesz sprawdzić wpisując:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;print(iface.private_ip_address)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jeśli nie zapisałeś któregoś wyniku w zmiennej, w ipythonowej sesji, w każdej chwili możesz taki obiekt odtworzyć:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;vpc.create_subnet(CidrBlock=&amp;#39;10.0.1.0/24&amp;#39;)&#10;Out[32]: ec2.Subnet(id=&amp;#39;subnet-12345...&amp;#39;)&#10;&#10;s = ec2.Subnet(id=&amp;#39;subnet-12345...&amp;#39;)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="stworzenie-instancji--po-raz-ostateczny"&gt;Stworzenie instancji – po raz ostateczny&lt;/h3&gt;&#10;&lt;p&gt;Z tymi dwoma obiektami możemy wrócić do tworzenia instancji:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;i = ec2.create_instances(MinCount=1, MaxCount=1, ImageId=&amp;#39;ami-0a313d6098716f372&amp;#39;, InstanceType=&amp;#39;c4.xlarge&amp;#39;, SubnetId=s.id)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Create_instances zwraca listę wyników – można ustawić większą ilość maszyn w MaxCount. Jeśli nie AWS&amp;rsquo;owi nie uda się stworzyć takiej ilości jak &lt;em&gt;MaxCount&lt;/em&gt; będzie próbował aż do osiągnięcia &lt;em&gt;MinCount&lt;/em&gt;. Jeśli nie będzie mógł stworzyć &lt;em&gt;MinCount&lt;/em&gt; maszyn, to zwróci błąd, np. przez ograniczenia Twojego konta.&lt;/p&gt;&#10;&lt;p&gt;Jeśli zastanawiasz się po co nam był nowy interfejs – zgadza się, nie był potrzebny :) Można go skasować: &lt;em&gt;iface.delete()&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;Aby sprawdzić w jakim stanie jest nasza wirtualna maszyna, możesz wpisać:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;instance = i[0]&#10;print(instance.state)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Za każdym razem w odpowiedzi dostaniesz &amp;ldquo;pending&amp;rdquo;. To przez fakt, że boto3 pamięta ostatnią odpowiedź API AWS. Aby odświeżyć stan instancji (to samo tyczy się obrazów, VPC itd.) wywołaj &lt;em&gt;instance.reload()&lt;/em&gt;.&lt;/p&gt;&#10;&lt;h3 id="posprzątanie"&gt;Posprzątanie&lt;/h3&gt;&#10;&lt;p&gt;Na koniec pozostaje posprzątanie wszystkiego:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;inst.terminate()&#10;&#10;# Reload subnet information to be able to release it. If there are ifaces attached, first remove them&#10;subnet.reload()&#10;subnet.delete()&#10;&#10;vpc.delete()&#10;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>No masters, no slaves, blockchain inside – pisanie zdecentralizowanych aplikacji</title><link>https://nabozny.io/no-masters-no-slaves-blockchain-inside-pisanie-zdecentralizowanych-aplikacji/</link><dc:creator>maciej</dc:creator><pubDate>Fri, 14 Dec 2018 13:33:22 +0000</pubDate><category>Programowanie</category><category>Cloud</category><category>Linux</category><guid isPermaLink="true">https://nabozny.io/no-masters-no-slaves-blockchain-inside-pisanie-zdecentralizowanych-aplikacji/</guid><description>Blockchain idealnie wpisuje się w ostatni trend politycznej poprawności w świecie IT. Nie ma nodów master ani slave, wszyscy są równi niezależnie od płci miejsca w sieci. Framework Dinemic jest biblioteką, która pomaga tworzyć zdecentralizowane aplikacje w C++ oparte o technologię, która jest …</description><content:encoded>&lt;p&gt;Blockchain idealnie wpisuje się w ostatni trend politycznej poprawności w świecie IT. Nie ma nodów master ani slave, wszyscy są równi niezależnie od &lt;del&gt;płci&lt;/del&gt; miejsca w sieci. Framework Dinemic jest biblioteką, która pomaga tworzyć zdecentralizowane aplikacje w C++ oparte o technologię, która jest pochodną Blockchain, X509 oraz kilku innych technologi. Służy głównie do tworzenia programów, które mają działać na wielu maszynach równocześnie zapewniając przy tym wysoką wydajność i odporność na awarie.&lt;/p&gt;&#10;&lt;p&gt;Chociaż dinemic jest de facto ORM&amp;rsquo;em dla C++, to spróbujmy na początek inaczej. Wyobraź sobie repozytorium Git&amp;rsquo;a. Takie, w którym to nie developerzy robią commity, a poszczególne obiekty klas, a każdy commit jest informacją na temat zmiany stanu takiego obiektu (i bazy danych pod nim), podpisaną przez niego cyfrowo. Dodatkowo pomyśl, że nie mamy jednego centralnego serwera Git&amp;rsquo;a, ale każdy komputer w sieci jest za razem repozytorium lokalnym i remote&amp;rsquo;m dla innych repozytoriów. Jeszcze tylko dodać, że nieautoryzowane zmiany są odrzucane przez inne repa i mamy obraz framework&amp;rsquo;a dinemic. To tak w skrócie. Po nieco obszerniejszy opis zapraszam dalej.&lt;/p&gt;&#10;&lt;h3 id="jak-to-zwykle-działa"&gt;Jak to zwykle działa&lt;/h3&gt;&#10;&lt;p&gt;Zwykle aplikacje, nawet te obsługujące większe serwisy, mają następującą architekturę: program, na przykład w PHP, jest hostowany przez serwer www. Jego logika odpowiada on za przygotowanie i udostępnienie treści zapisanych w bazie danych. W ostatnim czasie, nawet jeśli jest to aplikacja desktopowa lub mobilna, to i tak jej backend jest obsługiwany przez serwis oparty o zapytania HTTP.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://nabozny.io/uploads/2018/12/dinemic-arch1.jpg" srcset="https://nabozny.io/uploads/2018/12/dinemic-arch1_hu_3b6d009c383398e2.jpg 600w, https://nabozny.io/uploads/2018/12/dinemic-arch1.jpg 1143w" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" alt="" width="1143" height="740" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;/p&gt;&#10;&lt;p&gt;Jak przy każdym serwisie, podczas budowania takiej aplikacji powstaje cała masa pytań. Jedno z najczęściej spotykanych to jak autoryzować ich użytkowników? W najprostszej formie można to zrobić na poziomie bazy danych, nie komplikując zbytnio logiki swoich aplikacji. Jednak w miarę rozwoju lub chcąc integrować się z zewnętrznymi serwisami pozostaje przeniesienie całej autoryzacji użytkowników do zewnętrznego lub wydzielonego wewnętrznego (mikro)serwisu.&lt;/p&gt;&#10;&lt;p&gt;Na koniec pozostaje do rozwiązania problem dostępności takiej usługi, jej bezpieczeństwa i całej masy innych problemów, na które prawdopodobnie się napotka. Na przykład granulacji uprawnień: czy użytkownik może lub nie mieć dostępu do serwisu, czy też administrator może zdecydować do jakich jego części może mieć dostęp? Jeśli to drugie, to czy chcemy dawać dostęp do wszystkich funkcjonalności, czy mieć możliwość ich wyboru? I tak dalej&amp;hellip;&lt;/p&gt;&#10;&lt;p&gt;Do tego może jeszcze dojść zewnętrzny system autoryzacji, np. przez google lub facebooka, i mamy gotowy projekt na długie miesiące służący jedynie do odpowiedniego &amp;ldquo;wpuszczenia&amp;rdquo; użytkownika do naszego systemu. Odpowiedniego, czyli takiego aby nie mógł za dużo nabroić i żeby nie był całkowicie sparaliżowany ograniczeniami. Do tego jeszcze należy pamiętać o zabezpieczeniu wszystkiego, odpowienio mocnych hashah haseł, sposobie hashowania, dedykowanym API&amp;hellip; ech&amp;hellip;&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://nabozny.io/uploads/2018/11/1.png" srcset="https://nabozny.io/uploads/2018/11/1_hu_3abbf7ffc10fd14a.png 600w, https://nabozny.io/uploads/2018/11/1_hu_e60fd6767eb992f8.png 1200w, https://nabozny.io/uploads/2018/11/1.png 1301w" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" alt="" width="1301" height="796" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;br&gt;&#10;Architektura OpenStack Havana :) (tak, stare)&lt;/p&gt;&#10;&lt;h3 id="jak-to-działa-z-dinemic"&gt;Jak to działa z dinemic&lt;/h3&gt;&#10;&lt;p&gt;Wyobraź sobie następujący scenariusz z Git&amp;rsquo;a (chociaż dinemic nie ma z nim nic wspólnego). Każdy developer tworzy swoje zmiany we własnych gałęziach. Czasami te zmiany zachodzą na siebie tworząc konflikty, a czasami po prostu konieczne jest zmergowanie dwóch gałęzi w jedną tak, aby połączyć nowe fragmenty kodu z gałęzią stable. Wszystkie takie zmiany są uporządkowane w logiczny ciąg lub nawet graf określający ich kolejność.&lt;br&gt;&#10;&lt;br&gt;&#10;Co się dzieje gdy dwóch developerów pisze w osobnych kopiach repozytorium? Zwykle nic. I jest to bardzo częste gdy wiele osób pracuje na własnych komputerach. Po połączeniu się z repozytorium ich zmiany są nakładane na siebie, a w przypadku konfliktów każdy z nich rozwiązuje je po swojemu.&lt;/p&gt;&#10;&lt;p&gt;Teraz wyobraź sobie bardzo podobny scenariusz, tylko zamiast programistów mamy obiekty klas w C++, których stan zmienia się co jakiś czas. Każda taka zmiana to nowa aktualizacja wpięta w łańcuch lub graf takich aktualizacji, podobnie jak commit w Git. Każda z tych zmian jest też na bieżąco rozsyłana po sieci klastra, w którym działają aplikacje oparte o framework dinemic. W Gicie programiści są identyfikowani kluczami SSH i mogą zmieniać dowolne gałęzie. W framerowku dinemic każdy obiekt klasy podczas jego tworzenia ma generowany zestaw kluczy kryptograficznych, którymi podpisuje się pod swoimi zmianami. Może to robić tylko sam w sobie lub w obiektach, które dodały jego publiczny klucz do listy autoryzowanych obiektów.&lt;br&gt;&#10;&lt;br&gt;&#10;Oprócz tego wszystkie te zmiany są porządkowane w formie łańcuchów umożliwiających odtworzenie stanu klastra z dowolnego momentu i ewentualne połączenie (merge) po awarii klastra.&lt;/p&gt;&#10;&lt;h3 id="bezpieczeństwo-i-uprawnienia"&gt;Bezpieczeństwo i uprawnienia&lt;/h3&gt;&#10;&lt;p&gt;Każdy obiekt, jaki jest tworzony przez klasy dziedziczące po &lt;em&gt;DModel&lt;/em&gt; automatycznie generuje i zapisuje parę kluczy kryptograficznych na hoście, na którym został utworzony. Za pomocą tych kluczy będą podpisane cyfrowo wszelkie zmiany w tym obiekcie, począwszy od informacji o jego stworzeniu, przez zmiany dowolnych pól aż po żądanie skasowania z wszystkich replik w klastrze. Sekretny, prywatny klucz jest tworzony tylko tam, gdzie został stworzony obiekt. Publiczna część klucza jest rozsyłana po całym klastrze wraz z informacją, że obiekt został utworzony w rozproszonej bazie. Na publicznej części klucza opierany też jest ID obiektu, tak więc nie ma możliwości (inaczej – jest to bardzo trudne) podrobienia obiektu o tym samym ID. Jeśli ktoś chciałby to zrobić musiałby złamać algorytmy, na których opiera się znaczna część komunikacji w Internecie.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;libdinemic/dmodel.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DModel&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;update_network&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;/// Create new object&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StoreInterface&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SyncInterface&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;authorized_object_ids&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;/// Restore existing object&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;db_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StoreInterface&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SyncInterface&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DModel&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DModel&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;caller&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;/// Create as child object&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DModel&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DField&lt;/span&gt; &lt;span class="n"&gt;important_property_a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DField&lt;/span&gt; &lt;span class="n"&gt;important_property_b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DList&lt;/span&gt; &lt;span class="n"&gt;list_of_something&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="text-center"&gt;&lt;small&gt;W dowolnym miejscu w klastrze, a dokładniej w dowolnej jego aplikacji korzystającej z libdinemic można odtworzyć obiekt, który znajduje się w bazie danych i korzystać z niego&lt;/small&gt;&lt;/p&gt;&#10;&lt;p&gt;Ok, ale co po tym, że obiekt można zmienić tylko na hoście, gdzie został stworzony? Dinemic służy do budowania aplikacji, które będą działały na wielu hostach. Koniecznym zatem jest, aby inne hosty w sieci też mogły mieć możliwość odtworzenia takich samych obiektów oraz ich modyfikacji. Pierwsza kwestia jest dość naturalna – tworząc obiekt klasy w jednej aplikacji, nie pojawi się on automatycznie w żadnym innym miejscu. Dopiero chcąc wykorzystać jego dane lub dokonać zmian w tym obiekcie musimy sami, w kodzie aplikacji stworzyć taki obiekt (czyli de facto zmienną tego typu). Dinemic zatroszczy się o to, aby jego stan był zgodny ze stanem na innych nodach klastra:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt; &lt;span class="nf"&gt;my_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;here_comes_long_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;my_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;important_property_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;abcd&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="text-center"&gt;&lt;small&gt;W dowolnym miejscu klastra możemy stworzyć zmienną, która zostanie odwzorowana przez ORM Dinemic w obiekt o podanym ID&lt;/small&gt;&lt;/p&gt;&#10;&lt;p&gt;Co do drugiej kwestii, czyli modyfikacji obiektów (również powyższy przykład), to Dinemic daje pełną elastyczność co do definiowania co kto może i jak to może. Po pierwsze przypomnijmy – wszystkie informacje na temat zmian obiektów są zawsze dokonywane przez &lt;em&gt;kogoś&lt;/em&gt;. Jeśli nie definiujemy tego, to są one anonimowe, czyli bez podpisu. W przeciwnym wypadku będą one podpisane kluczem prywatnym obiektu autoryzowanego do dokonania zmian lub podpisane kluczem prywatnym innego, nieautoryzowanego obiektu. Listę takich obiektów można zdefiniować już w trakcie tworzenia pierwszej instancji obiektu w klastrze, przez parametr &lt;em&gt;authorized_object_ids&lt;/em&gt;. Wszystkie zmiany dokonywane przez wymienione tam obiekty będą traktowane tak samo jakby były dokonywane przez ten sam obiekt.&lt;/p&gt;&#10;&lt;p&gt;Weźmy na przykład aplikację, która ma być rozproszonym kalendarzem. Każda osoba (obiekt klasy &lt;em&gt;Person&lt;/em&gt;) ma listę swoich wydarzeń (obiekty klasy &lt;em&gt;Event&lt;/em&gt;). Tworząc nowe wydarzenie możemy podać ID innych osób, które mają mieć możliwość zmian np. terminu. W ten sposób przypiszemy do naszego kalendarza nasze nowe wydarzenie. Nikt poza naszymi dwoma znajomymi nie będzie mógł zmienić tej daty:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DField&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DField&lt;/span&gt; &lt;span class="n"&gt;person_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DField&lt;/span&gt; &lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DField&lt;/span&gt; &lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Map object related to my calendar to new object in code&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="nf"&gt;me&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;my_id...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Map John to another object&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="nf"&gt;john&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;jobhn&amp;#39;s_id...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Create new event in database&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authorized_objects&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;authorized_objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_db_id&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;authorized_objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;john&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_db_id&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Event&lt;/span&gt; &lt;span class="n"&gt;new_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authorized_objects&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;W ten sposób John będzie mógł stworzyć taki sam obiekt wydarzenia i wszelkie jego zmiany w tym obiekcie będą traktowane na równi z naszymi:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Event&lt;/span&gt; &lt;span class="nf"&gt;received_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;event_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;received_event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Tomorrow!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Powyższa zmiana, wykonana już w aplikacji John&amp;rsquo;a jest automatycznie propagowana w całym klastrze i po chwili będzie również widoczna w naszym kalendarzu.&lt;/p&gt;&#10;&lt;h3 id="nieautoryzowane-i-anonimowe-zmiany"&gt;Nieautoryzowane i anonimowe zmiany&lt;/h3&gt;&#10;&lt;p&gt;To nie wszystko. Dinemic pozwala zaaplikować dowolny zestaw listenerów, które będą automatycznie odfiltrowywać niepodpisane lub nieautoryzowane zmiany. Dzięki listenerom możemy również nasłuchiwać zmian, jakie są wykonywane w klastrze przez innych uczestników.&lt;/p&gt;&#10;&lt;h3 id="poufne-dane"&gt;Poufne dane&lt;/h3&gt;&#10;&lt;p&gt;Kolejną rzeczą, którą dostaje się gratis jest możliwość szyfrowania danych. Ponieważ dinemic bazuje na otwartej architekturze i domyślnie pozwala akceptować wszystkie zmiany z sieci, programista tworzący w nim aplikacje ma możliwość wykorzystania istniejących kluczy kryptograficznych i zaszyfrowania poszczególnych pól w klasach. Dzięki temu tylko autoryzowane do odczytu obiekty będą miały możliwość odszyfrowania danych zapisanych w bazie i mapowanych przez framework na obiekty C++. Z punktu widzenia programisty wystarczy jedynie zmodyfikować klasę DField lub DList, ustawiając w jej konstruktorze parametr encrypted=true. Wszystkie pola z tą flagą są automatycznie szyfrowane przed wysłaniem do sieci za pomocą kluczy zapisanych w liście &lt;em&gt;read_authorized_keys&lt;/em&gt;.&lt;/p&gt;&#10;&lt;h3 id="jakie-są-największe-różnice-w-stosunku-do-centraljen-bazy-danych"&gt;Jakie są największe różnice w stosunku do centraljen bazy danych?&lt;/h3&gt;&#10;&lt;p&gt;Przede wszystkim Dinemic został stworzony aby zapewnić możliwie wysoką konsystencję danych i stanu klastra w przypadku awarii. Aplikacje w architekturach, które za pomocą logiki dokonują zmian w centralnej bazie danych nie są odporne na awarie sieci. Po dłuższym odcięciu od bazy realny stan klastra może się mocno różnić z zapisanymi w bazie danymi.&lt;/p&gt;&#10;&lt;p&gt;Dinemic postawił ten problem na pierwszym miejscu, przez co dowolne rozcięcie klastra, przez tzw. split brain nie powoduje rozjechania się stanu klastra i bazy danych. Po split brain&amp;rsquo;ie tworzą się de facto dwa oddzielne klastry Dinemic, które zaczynają żyć własnym życiem. Każdy z nich bazuje na ostatniej wersji bazy sprzed split-brain&amp;rsquo;a i modyfikuje tą bazę według własnego uznania. Po rejoin&amp;rsquo;ie, czyli po ponownym połączeniu dwóch części, wszystkie nody zaczynają się wymieniać brakującymi im aktualizacjami stanu.&lt;/p&gt;&#10;&lt;p&gt;W tym punkcje architektura Dinemic zaczyna się znacznie różnić od zwykłej, z centralją bazą danych lub od Blockchain&amp;rsquo;a. Po rejoin&amp;rsquo;ie może nie być możliwe doprowadzenie do konsystencji w takim klastrze za równo na warstwie bazy danych (co zostało przeniesione na barki programisty piszącego aplikacje korzystającą z tego frameworka) jak i na płaszczyźnie konfiguracji i stanu samego klastra. Zamiast tego otrzymujemy węzły klastra, które w miarę możliwości starają się doprowadzić do jednolitego stanu, jednak bez takiej gwarancji.&lt;/p&gt;&#10;&lt;p&gt;Dlaczego tak się dzieje? Popatrzmy na przykład na powyższą aplikację kalendarza. Co jeśli dwie osoby będące w danej chwili offline wprowadziły w swoich kalendarzach dwie sprzeczne zmiany dotyczące tego samego eventu? John przestawił godzinę na 11 rano, a my ustawiliśmy na 8 rano? Trudno podjąć decyzję, która wersja ma być prawidłowa i ostateczna. Co więcej, odrzucenie jednej, tak jak np. w blockchain&amp;rsquo;ie Bitcoin&amp;rsquo;a może doprowadzić do tego, że wydarzenie przesunięte przez Johna na 11 będzie kolidowało nam, ponieważ utworzyliśmy już o tej godzinie inne. Decyzja co w takim przypadku zrobić pozostaje już tylko nam (i John&amp;rsquo;owi). Z najbardziej oczywistych rozwiązań pozostaje nam odrzucenie obu zmian i zaproponowanie nowego czasu, np. przez wyświetlenie odpowiedniego komunikatu. Nie ma tu jednak sposobu na realne zsynchronizowanie planów dnia tych dwóch osób.&lt;/p&gt;&#10;&lt;p&gt;W przypadku tej samej sytuacji w aplikacji z centralną bazą danych, prawdopodobnie jedna z aktualizacji nie doszłaby do skutku, na przykład przez brak łączności lub zostałaby nadpisana przez drugą zmianę.&lt;/p&gt;&#10;&lt;p&gt;Z tego powodu framework Dinemic jedynie dąży do zsynchronizowania stanu obiektów w bazach poszczególnych węzłów klastra, jednak nie wymusza tego sztucznie i nie gwarantuje konsystencji. W zamian za to dostarcza pełen wgląd w historię zmian jakie odbywały się od początku istnienia takich obiektów i daje możliwość podejmowania dziań w zależności od kolejności zdarzeń.&lt;/p&gt;&#10;&lt;h3 id="gdzie-szukać-dalej"&gt;Gdzie szukać dalej?&lt;/h3&gt;&#10;&lt;p&gt;Dokładna instrukcja instalacji oraz dokumentacja frameworka jest dostępna pod adresem: &lt;a href="https://dinemic.io"&gt;https://dinemic.io&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Thank you AWS Free Tier!</title><link>https://nabozny.io/thank-you-aws-free-tier/</link><dc:creator>maciej</dc:creator><pubDate>Sun, 04 Nov 2018 12:21:16 +0000</pubDate><category>Cloud</category><category>Inne</category><guid isPermaLink="true">https://nabozny.io/thank-you-aws-free-tier/</guid><description>W tym poście kilka gorzkich żali na temat (prawie) darmowego okresu na chmurze Amazona, po korzystaniu z niego przez trzy miesiące. Polecam dla tych, którzy zastanawiają się czy warto oszczędzić na zasobach i skorzystać z darmowego okresu.</description><content:encoded>&lt;p&gt;W tym poście kilka gorzkich żali na temat (prawie) darmowego okresu na chmurze Amazona, po korzystaniu z niego przez trzy miesiące. Polecam dla tych, którzy zastanawiają się czy warto oszczędzić na zasobach i skorzystać z darmowego okresu.&lt;/p&gt;&#10;&lt;h3 id="super-oferta"&gt;Super oferta&lt;/h3&gt;&#10;&lt;p&gt;Amazon, podobnie jak Google Cloud ma w swojej ofercie całą masę usług. Od podstawowej wirtualnej maszyny, przez dodatkowe dyski, transfery, sieci aż po kontenery i sztuczną inteligencję. Jest w czym wybierać, zwłaszcza gdy zaczynamy. W Google wygląda to równie imponująco. Do tego dochodzi nam jeszcze gratis $300 na start do wykorzystania przez rok&amp;hellip; No jest w czym wybierać :)&lt;/p&gt;&#10;&lt;p&gt;Przeliczając na złotówki to ponad 1000zł lub ok. 100zł miesięcznie, w zależności od kursu PLN/USD. Co można za to kupić? Między innymi pojedynczą instancję AWS EC2 t1.micro, na której da się postawić prosty serwer www lub inną usługę. Nie ma ona zbyt wiele zasobów, ale zawsze to coś (1GB ram, 8GB dysku i 1vCPU współdzielone z innymi). Przez rok nie musimy się martwić o portfel, sporo kasy zostaje w kieszeni, ale&amp;hellip;&lt;/p&gt;&#10;&lt;p&gt;Co po roku? Korzystając z tej samej oferty będziesz musiał prawdopodobnie wyłożyć ten tysiąć zł aby utrzymać te same zasoby co wcześniej. Patrząc na inne oferty usługodawców, można odnieść wrażenie, że AWS w cale taki tani się nie okazuje. Nieco większa wirtualna maszyna (VPS) w OVH kosztuje całe&amp;hellip; 10zł netto miesięcznie. Za zaoszczędzone 1000zł trzeba liczyć się z koniecznością przemigrowania do tańszego usługodawcy po roku lub zwrócić z nawiązką to, co dostało się na początku.&lt;/p&gt;&#10;&lt;p&gt;Porównując koszty kolejnego roku zostania w chmurze lub przejścia do innego hostingu – 1000zł vs 120zł. Na jednym i na drugim mamy szanse wpaść na serwer upychany z takich tanich wirtualnych maszyn, gdzie nie ma też gwarancji osiągnięcia pełnej wydajności.&lt;/p&gt;&#10;&lt;h3 id="a-jeśli-o-wydajności-mowa"&gt;A jeśli o wydajności mowa&lt;/h3&gt;&#10;&lt;p&gt;Free tier w rejonie, z którego korzystałem oferował bardzo marną wydajność sieci. Docelowo używałem go tylko i wyłącznie jako reverse proxy do usługi, która stoi za VPN, ale samo przejście przez wirtualną maszynę w AWS spowalniało znacznie ruch. Natomiast same transfery z i do VM wahały się na poziomach ok. 2MB/s, ale czegóż chcieć od darmowej usługi. Przykładowo dzisiejsze niedzielne przedpołudnie, czyli okres, w którym raczej nie spodziewałbym się dużego ruchu w sieci, zwłaszcza gdy USA jeszcze śpi dawało transfer na poziomie 1,2MB/s:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://nabozny.io/uploads/2018/11/Zrzut-ekranu-2018-11-04-o-13.06.39.png" alt="" width="482" height="96" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;/p&gt;&#10;&lt;p&gt;Jeśli natomiast chciałbym przenieść swoje zasoby gdzieś indziej, w lepszy rejon, to prawdopodobnie wiązało by się to ze zwiększeniem kosztów, również przez postawienie lepszej instancji, o lepszych parametrach.&lt;/p&gt;&#10;&lt;h3 id="nie-takie-darmowe"&gt;Nie takie darmowe&lt;/h3&gt;&#10;&lt;p&gt;Ten, kto myśli, że koszt samej wirtualki to wszystko, ten się myli. W AWS bardzo łatwo wyklikać swoje zasoby, dokładając spore gigabajty, lepsze sieci itd. Free tier momentami ogranicza je i mówi, że nie można postawić np. lepszej instancji w ramach darmowego $300. Gdy jednak przekroczymy pewne granice, darmowy AWS staje się płatnym AWSem.&lt;/p&gt;&#10;&lt;p&gt;To właśnie skłoniło mnie do przeniesienia się na płatne (ale dużo mniej) OVH i porzucenia publicznych chmur na długi czas. Przez ostatni miesiąc transfer z serwisu, który hostuje duże pliki przekroczyło kilkaset GB. Nigdy nie było z tym problemu podczas korzystania z OVH, jednak w AWS kwalifikuje się to do obciążenia użytkownika dodatkowymi kosztami. Kosztami, które nie będą niestety pokryte z darmowego $300, a z jego karty. Tak oto za ponad 40zł Amazon dokupił mi dodatkowe 10TB transferu na ostatnie kilka dni października. Jedyny plus jaki mogę się tu doszukać, to że poinformowali mnie o tym (czego Google nie robiło dopinając dysk, który zjadł większość darmowego bonu).&lt;/p&gt;&#10;&lt;p&gt;Cóż, nie dość, że transfer w AWS jest marny, to jeszcze jest go mało i dość drogi. Z jednej strony warto przeczytać cenniki i niedopatrzenie jest tylko i wyłącznie moją winą, natomiast z drugiej – jeśli ktoś planuje hostować w Free Tier nieco więcej danych, to warto pomyśleć czy mu się to opłaci. Za jeden &amp;ldquo;darmowy&amp;rdquo; miesiąc można zapłacić tu więcej niż za kilka miesięcy płatnych u innych usługodawców.&lt;/p&gt;&#10;&lt;h3 id="jak-to-wygląda-gdzie-indziej"&gt;Jak to wygląda gdzie indziej?&lt;/h3&gt;&#10;&lt;p&gt;O Google Cloud już wspominałem – podobnie jak w AWS płaci się za wszystko, jednak darmowa kasa pokrywa wszystko na bieżąco, a nie jest rozłożona w 12 ratek i pokrywa tylko części usług.&lt;/p&gt;&#10;&lt;p&gt;Po drugiej stronie stoi OVH, z którego korzystam od dawna bez większych problemów. Można u nich kupić serwery dedykowane z oferty &lt;a href="https://kimsufi.com"&gt;Kimsufi&lt;/a&gt; lub VPS w chmurze. Jak na razie jedno i drugie wychodzi dużo taniej i wydajniej niż to, co za poprzednie miesiące zaoferował AWS w &amp;ldquo;darmowym&amp;rdquo; pakiecie.&lt;/p&gt;&#10;&lt;p&gt;To, o czym warto pamiętać to porządne zautomatyzowanie swoich usług, aby przejście z jednego usługodawcy do drugiego nie okazało się niewykonalnym krokiem.&lt;/p&gt;</content:encoded></item><item><title>Czego w dockerze nie zrobisz?</title><link>https://nabozny.io/czego-w-dockerze-nie-zrobisz/</link><dc:creator>maciej</dc:creator><pubDate>Tue, 02 Jan 2018 17:20:07 +0000</pubDate><category>Cloud</category><category>Linux</category><category>cloud</category><category>docker</category><guid isPermaLink="true">https://nabozny.io/czego-w-dockerze-nie-zrobisz/</guid><description>Lista jest pewnie dużo dłuższa, ale poniższe dały mi się we znaki:</description><content:encoded>&lt;p&gt;Lista jest pewnie dużo dłuższa, ale poniższe dały mi się we znaki:&lt;/p&gt;&#10;&lt;h5 id="nie-uruchomisz-openvpna"&gt;Nie uruchomisz openvpn&amp;rsquo;a&lt;/h5&gt;&#10;&lt;p&gt;Kontenery domyślnie nie pozwalają na ingerencje w sieci systemu operacyjnego. Przez to nie da się w łatwy sposób uruchomić np. OpenVPN, który potrzebuje stworzyć własny interfejs sieciowy tun/tap. Czasami uruchamiając kontenery w różnych miejscach świata i w różnych serwerowniach postawienie VPN&amp;rsquo;a spinającego je wszystkie ze sobą jest dość wygodnym rozwiązaniem, dodatkowo dającym bezpieczeństwo.&lt;/p&gt;&#10;&lt;p&gt;Aby podłączyć taką sieć do kontenera trzeba się albo pogimnastykować z routingiem w systemie gospodarza albo poczarować z uprawnieniami kontenera. Aby to zrobić trzeba uruchomić nowy obraz z parametrem &lt;em&gt;--cap-add=NET_ADMIN&lt;/em&gt;, który zwiększa możliwości ingerencji w macierzysty system, a w szczególności w konfigurację jego sieci.&lt;/p&gt;&#10;&lt;h5 id="nie-poda-piwa"&gt;Nie poda piwa&lt;/h5&gt;&#10;&lt;p&gt;z drugiej strony zwykły desktop też piwa nie poda. Ale chociaż potrzyma na tacce cd, a docker nie. Idźmy dalej.&lt;/p&gt;&#10;&lt;h5 id="debugowanie-programów"&gt;Debugowanie programów&lt;/h5&gt;&#10;&lt;p&gt;Kolejną rzeczą jest debugowanie kodu. Debugger (np. gdb) wymaga dość głębokiego integrowania się z inną aplikacją, które jest zwykle wykonywane przez funkcję &lt;em&gt;ptrace&lt;/em&gt;. Funkcja ta pozwala pisać i czytać po pamięci innych programów, które chcemy badać. Dzięki ptrace debuger może zatrzymać wykonywanie programu w wybranym przez siebie miejscu, wykonywać go krok po kroku lub przechwytywać (i zmieniać) przerwania systemowe. Z systemowej funkcji &lt;em&gt;ptrace&lt;/em&gt; korzysta też programik strace, pokazujący co program aktualnie robi, przechwytując jego przerwania oraz User Mode Linux, który podmienia przerwania systemowe na własne.&lt;/p&gt;&#10;&lt;p&gt;Aby umożliwić korzystanie z ptrace w kontenerze, musimy dodać mu flagę &lt;em&gt;--cap-add SYS_PTRACE&lt;/em&gt;, która przez linux capabilities pozwoli na dostęp do tej funkcji.&lt;/p&gt;&#10;&lt;h5 id="nici-z-network-namespaces"&gt;Nici z network namespaces&lt;/h5&gt;&#10;&lt;p&gt;Czasami potrzeba wykonać coś więcej, niż sam VPN i poczarować np. z przestrzeniami nazw aby uruchomić niektóre narzędzia (np. podczas pisania i testów &lt;a href="https://cloudover.org/"&gt;CoreCluster&lt;/a&gt;). Podobnie jak przy konfiguracji VPN, tutaj też Docker ograniczy nam taką możliwość. Można to obejść podobnie, przez capabilities.&lt;/p&gt;&#10;&lt;h3 id="czyli-jak-się-da-obejść-to-jednak-nie-ogranicza"&gt;Czyli jak się da obejść to jednak nie ogranicza?&lt;/h3&gt;&#10;&lt;p&gt;W sumie tak. Ale należy pamiętać, że dodając kontenerowi każdą taką flagę ryzykujemy, że kod uruchomiony w kontenerze będzie mógł w jakiś sposób przejść do systemu gospodarza. Idąc tą drogą możemy pozdejmować wszystkie ograniczenia Dockera (lub innego silnika kontenerów) i cieszyć się nieskrępowaną pracą w łatwo zarządzalnym &lt;em&gt;chroot&lt;/em&gt;. W ten sposób dostajemy wtedy sam chroot, z którego prawdopodobnie będziemy mogli łatwo wyjść, podobnie jak potencjalny intruz, który mógłby być nieco lepiej powstrzymany przez ograniczonego dockera (w domyślnej konfiguracji). Z drugiej strony wszyscy więksi dostawcy usług, oferujący silnik kontenerów nigdy nie dają dockera bezpośrednio na swoim sprzęcie, a za pośrednictwem wirtualnych maszyn, które nie są współdzielone pomiędzy użytkownikami.&lt;/p&gt;&#10;&lt;p&gt;Podsumowując, Docker mimo swoich fajnych narzędzi pozostawia trochę ograniczeń na to, co możemy wykonać w kontenerze. Tyczy się to prawdopodobnie również innych silników. W takich przypadkach (których zapewne znajdzie się znacznie więcej) możemy posiłkować się starą, dobrą wirtualizacją, która daje w pełni funkcjonalny system. Wykorzystując obrazy Copy On Write i &lt;a href="https://en.wikipedia.org/wiki/Kernel_same-page_merging"&gt;Kernel Same Page Mapping&lt;/a&gt; możemy znacznie ograniczyć użycie pamięci i przestrzeni dyskowej potrzebnej na wirtualne maszyny, zachowując pełną funkcjonalność.&lt;/p&gt;</content:encoded></item><item><title>Wirtualka w 7 sekund – jak skonfigurować macierz z SheepDog</title><link>https://nabozny.io/wirtualka-w-7-sekund-jak-skonfigurowac-macierz-z-sheepdog/</link><dc:creator>maciej</dc:creator><pubDate>Fri, 03 Nov 2017 08:15:17 +0000</pubDate><category>Cloud</category><guid isPermaLink="true">https://nabozny.io/wirtualka-w-7-sekund-jak-skonfigurowac-macierz-z-sheepdog/</guid><description>Podczas sprawdzania działania CoreCluster z Debianem Stretch przetestowałem też macierz software’ową, opartą o Sheepdog, zamiast tradycyjnego udziału opartego o NFS + lokalne dyski. Wynik – niecałe 7 sekund od kliknięcia w interfejsie do otrzymania wirtualnej maszyny w stanie stopped.</description><content:encoded>&lt;p&gt;Podczas sprawdzania działania &lt;a href="https://cloudover.org"&gt;CoreCluster&lt;/a&gt; z Debianem Stretch przetestowałem też macierz software&amp;rsquo;ową, opartą o Sheepdog, zamiast tradycyjnego udziału opartego o NFS + lokalne dyski. Wynik – niecałe 7 sekund od kliknięcia w interfejsie do otrzymania wirtualnej maszyny w stanie &lt;em&gt;stopped&lt;/em&gt;.&lt;/p&gt;&#10;&lt;h3 id="jak-to-działało-do-tej-pory"&gt;Jak to działało do tej pory?&lt;/h3&gt;&#10;&lt;p&gt;Domyślnie CoreCluster konfiguruje się w najprostszej konfiguracji do używania zasobu NFS. W panelu administratora podajemy IP i katalog z macierzy (lub jakiegokolwiek serwera) i wszystko się montuje automatycznie. Dyski bazowe dla wirtualek są kopiowane podczas tworzenia ich na lokalne dyski nodów (do lokalnego &lt;em&gt;libvirt pool images&lt;/em&gt;). Wydajność poszczególnych wirtualek zależy wtedy głównie od wydajności lokalnego dysku i jego obciążenia przez inne wirtualki.&lt;/p&gt;&#10;&lt;p&gt;Czas dostarczenia zatrzymanej wirtualnej maszyny jest więc zależny głównie od tego, co startujemy. Kilku gigabajtowy dysk kopiuje się do kilku minut, w zależności od naszego sprzętu i sieci.&lt;/p&gt;&#10;&lt;h3 id="jak-to-działa-z-sheepdog"&gt;Jak to działa z Sheepdog?&lt;/h3&gt;&#10;&lt;p&gt;Zanim niektórzy zaczną krzyczeć dlaczego nie Ceph albo Gluster – sheepdog odpala się prawie bezboleśnie dla administratora (o czym później) i nie ma absolutnie żadnego centralnego punktu klastra, co czasem może być plusem.&lt;/p&gt;&#10;&lt;p&gt;Jak więc to działa? Sheepdog po doinstalowaniu korzysta z Corosync zintegrowanego z CoreCluster do znalezienia innych węzłów w sieci. Od strony użytkownika pozwala on tworzyć dyski (odpowiednik block storage), które możemy wykorzystać m.in. w wirtualnych maszynach. Dzięki jego architekturze, każdy węzeł obsługujący Sheepdog&amp;rsquo;a ma taki sam dostęp do obiektów zapisanych na macierzy, niezależnie od położenia w sieci.&lt;/p&gt;&#10;&lt;p&gt;Podczas tworzenia klastra możemy podać w ilu miejscach na raz będą trzymane dane. Tak – w RAID możesz to zrobić wybierając odpowiednią konfigurację (RAID1, 5 itd.). W sheepdog podajesz po prostu ile kopii każdego dysku ma mieć Twój klaster:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dog cluster format --copies &lt;span class="m"&gt;3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Niezależnie od Twoich dysków o sprzętu, jeśli tylko ilość węzłów sheepdog&amp;rsquo;a pozwoli, będziesz on starał się tak rozłożyć dane, aby każdy fragment każdego dysku był zapisany w trzech różnych lokalizacjach. Oczywiście ma to wpływ na pojemność klastra.&lt;/p&gt;&#10;&lt;p&gt;CoreCluster, po dodaniu macierzy z transportem &lt;em&gt;sheepdog&lt;/em&gt; wykorzystuje ją jako pojemnik na wszystkie swoje dyski. Obrazy użytkowników chmury są zapisywane oczywiście przez sheepdog, natomiast w odróżniniu od NFS, lokalne pule obrazów na nodach (te, w których są trzymane wirtualne maszyny, w &lt;em&gt;/images&lt;/em&gt;) nie są wykorzystane. Zamiast tego CoreCluster zapisuje te obrazy również jako obrazy w sheepdog, pod trochę inną nazwą.&lt;/p&gt;&#10;&lt;p&gt;Plusem takiego rozwiązania jest to, że każdy dysk wirtualnej maszyny jest klonem swojego bazowego obrazu. Dzięki temu tworzenie wirtualnej maszyny trwa sekundy zamiast minut. Nie jest konieczne kopiowanie obrazów pomiędzy libvirt&amp;rsquo;owymi pulami podczas bootu. Dodatkowo Sheepdog nieco przyspiesza sam dostęp do danych – jeśli obraz ma więcej niż jedną kopię w sheepdog, to sam odczyt jest wtedy nieco szybszy w porównaniu do NFS i lokalnych obrazów.&lt;/p&gt;&#10;&lt;h3 id="instalacja-i-konfiguracja-macierzy"&gt;Instalacja i konfiguracja macierzy&lt;/h3&gt;&#10;&lt;p&gt;Management CoreCluster potrzebuje jednego dodatkowego pakietu – &lt;em&gt;corecluster-storage-sheepdog&lt;/em&gt;. Zainstaluj go poleceniem:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt-get install corecluster-storage-sheepdog&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Doinstaluje on wszystkie zależności. Następnie na każdym Compute Node doinstaluj sam pakiet sheepdog:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt-get install sheepdog&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Następnie sprawdź, czy w plikach &lt;em&gt;/etc/default/corosync&lt;/em&gt; oraz &lt;em&gt;/etc/default/sheepdog&lt;/em&gt; oba demony są uruchamiane (parametr &lt;em&gt;START=yes&lt;/em&gt;). Możesz też sprawdzić, czy są w uruchomionych procesach:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ps uax &lt;span class="p"&gt;|&lt;/span&gt; grep corosync&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ps uax &lt;span class="p"&gt;|&lt;/span&gt; grep sheep&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sprawdź też, czy corosync jest poprawnie zainstalowany i skonfigurowany. Powinieneś widzieć wszystkie Compute Nody oraz Management:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;corosync-cmapctl &lt;span class="p"&gt;|&lt;/span&gt; grep member&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772417.config_version &lt;span class="o"&gt;(&lt;/span&gt;u64&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772417.ip &lt;span class="o"&gt;(&lt;/span&gt;str&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; r&lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt; ip&lt;span class="o"&gt;(&lt;/span&gt;10.0.1.1&lt;span class="o"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772417.join_count &lt;span class="o"&gt;(&lt;/span&gt;u32&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772417.status &lt;span class="o"&gt;(&lt;/span&gt;str&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; joined&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772418.config_version &lt;span class="o"&gt;(&lt;/span&gt;u64&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772418.ip &lt;span class="o"&gt;(&lt;/span&gt;str&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; r&lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt; ip&lt;span class="o"&gt;(&lt;/span&gt;10.0.1.2&lt;span class="o"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772418.join_count &lt;span class="o"&gt;(&lt;/span&gt;u32&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772418.status &lt;span class="o"&gt;(&lt;/span&gt;str&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; joined&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772419.config_version &lt;span class="o"&gt;(&lt;/span&gt;u64&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772419.ip &lt;span class="o"&gt;(&lt;/span&gt;str&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; r&lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt; ip&lt;span class="o"&gt;(&lt;/span&gt;10.0.1.3&lt;span class="o"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772419.join_count &lt;span class="o"&gt;(&lt;/span&gt;u32&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772419.status &lt;span class="o"&gt;(&lt;/span&gt;str&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; joined&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772670.config_version &lt;span class="o"&gt;(&lt;/span&gt;u64&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772670.ip &lt;span class="o"&gt;(&lt;/span&gt;str&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; r&lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt; ip&lt;span class="o"&gt;(&lt;/span&gt;10.0.1.254&lt;span class="o"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772670.join_count &lt;span class="o"&gt;(&lt;/span&gt;u32&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runtime.totem.pg.mrp.srp.members.167772670.status &lt;span class="o"&gt;(&lt;/span&gt;str&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; joined&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Jeśli wszystko działa, to Twój klaster sheepdog powinien być gotowy do użycia. Sformatuj go poleceniem:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dog cluster format --copies &lt;span class="m"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Możesz oczywiście zmienić ilość kopii jeśli potrzebujesz. Następnie dodaj taką macierz do CoreCluster, w panelu administratora z następującymi parametrami:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;em&gt;directory&lt;/em&gt; /var/lib/sheepdog (jest pomijane) przez sterownik&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;address&lt;/em&gt; 127.0.0.1 (każdy node będzie miał swój własny punkt dostępu)&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;transport&lt;/em&gt; sheepdog (najważniejszy parametr)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Oraz &lt;em&gt;capacity&lt;/em&gt; według Twojego uznania.&lt;/p&gt;&#10;&lt;p&gt;To, czy sheepdog działa możesz sprawdzić kilkoma poleceniami:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dog node list&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Id Host:Port V-Nodes Zone&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;0&lt;/span&gt; 10.0.1.1:7000 &#9;&lt;span class="m"&gt;129&lt;/span&gt; &lt;span class="m"&gt;16842762&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;1&lt;/span&gt; 10.0.1.2:7000 &#9;&lt;span class="m"&gt;129&lt;/span&gt; &lt;span class="m"&gt;33619978&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;2&lt;/span&gt; 10.0.1.3:7000 &#9;&lt;span class="m"&gt;129&lt;/span&gt; &lt;span class="m"&gt;50397194&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="m"&gt;3&lt;/span&gt; 10.0.1.254:7000 &#9;&lt;span class="m"&gt;126&lt;/span&gt; &lt;span class="m"&gt;4261478410&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Powinien wyświetlić listę wszystkich nodów oraz management&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dog vdi create &lt;span class="nb"&gt;test&lt;/span&gt; 10M&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dog vdi list&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Name Id Size Used Shared Creation &lt;span class="nb"&gt;time&lt;/span&gt; VDI id Copies Tag&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt; MB 0.0 MB 0.0 MB 2017-11-03 09:17 7c2b26 &lt;span class="m"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Powinno utworzyć nowy dysk o rozmiarze 10M. Dysk ten powinien być widoczny z wszystkich nodów w klastrze oraz managementu.&lt;/p&gt;&#10;&lt;p&gt;Pozostało jeszcze ustawić sterownik w konfiguracji CoreCluster management i zrestartować wszystko. Znajdź listę &lt;em&gt;APPS&lt;/em&gt; w pliku &lt;em&gt;/etc/corecluster/config.py&lt;/em&gt; i zamień wpis:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;corecluster-storage-libvirt.app&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;na:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;corecluster-storage-sheepdog.app&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finalnie lista APPS powinna wyglądać mniej więcej tak:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;corecluster.app&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;corecluster-auth-db.app&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;corecluster-algorithm-storage-default.app&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;corecluster-algorithm-node-fillup.app&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;corecluster-algorithm-id-uuid.app&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;corecluster-storage-sheepdog.app&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;corenetwork.app&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;coretalk.app&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Na koniec restartujemy procesy agentów corecluster:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;service corecluster restart&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Przy pomyślnym układzie gwiazd i sprzyjającej koniunkcji Jowisza z Neptunem po restarcie macierz zamontuje się i będziesz mógł cieszyć się jej wydajnością.&lt;/p&gt;&#10;&lt;h3 id="problemy"&gt;Problemy?&lt;/h3&gt;&#10;&lt;p&gt;Większość problemów jest związana z konfiguracją sieci na nodach lub management (plik pakietu CoreNetwork – &lt;em&gt;/etc/corenetwork/config.py&lt;/em&gt;), która ma wpływ na konfigurację Corosync. Jeśli to działa poprawnie i corosync-cmapctl poprawnie listuje wszystkie węzły, to powinieneś bez problemu uruchomić całość.&lt;/p&gt;&#10;&lt;h3 id="ps-co-się-składa-na-wspomniane-7-sekund"&gt;ps. co się składa na wspomniane 7 sekund?&lt;/h3&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Task – ładowanie dysku&lt;/li&gt;&#10;&lt;li&gt;Task – zdefiniowanie wirtualnej maszyny&lt;/li&gt;&#10;&lt;li&gt;Task – podłączenie karty sieciowej&lt;/li&gt;&#10;&lt;li&gt;Odświeżanie listy wirtualnych maszyn co sekundę – max. 1s&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Plus mały, pomijalny lag przeglądarki i komunikacji z nodami.&lt;/p&gt;</content:encoded></item></channel></rss>