Usługi osierocone: wada architektury (mikro)usług

Na ubiegłorocznej konferencji przemawiał dyrektor operacyjny portalu Banki.ru Andrey Nikolsky DevOpsDays w Moskwie o usługach dla sierot: jak rozpoznać sierotę w infrastrukturze, dlaczego usługi dla sierot są złe, co z nimi zrobić i co zrobić, jeśli nic nie pomaga.

Poniżej wycięcia znajduje się tekstowa wersja raportu.


Cześć koledzy! Nazywam się Andrey i kieruję operacjami w Banki.ru.

Mamy duże usługi, są to takie usługi monolityczne, są usługi w bardziej klasycznym rozumieniu i są bardzo małe. W mojej robotniczo-chłopskiej terminologii mówię, że jeśli usługa jest prosta i niewielka, to jest mikro, a jeśli nie jest bardzo prosta i mała, to jest po prostu usługą.

Plusy usług

Szybko omówię zalety usług.

Usługi osierocone: wada architektury (mikro)usług

Pierwszym z nich jest skalowanie. Można szybko zrobić coś w serwisie i rozpocząć produkcję. Otrzymałeś ruch, sklonowałeś usługę. Masz większy ruch, sklonowałeś go i żyjesz z nim. To dobry bonus i w zasadzie, kiedy zaczynaliśmy, za najważniejsze uznano dla nas to, dlaczego to wszystko robimy.

Usługi osierocone: wada architektury (mikro)usług

Po drugie, izolowany rozwój, gdy masz kilka zespołów programistycznych, kilku różnych programistów w każdym zespole i każdy zespół opracowuje własną usługę.

W przypadku zespołów istnieje pewien niuans. Deweloperzy są różni. A są np. ludzie-płatki śniegu. Po raz pierwszy zobaczyłem to u Maxima Dorofeeva. Czasami ludzie-płatki śniegu są w niektórych zespołach, a w innych nie. To sprawia, że ​​różne usługi używane w całej firmie są nieco nierówne.

Usługi osierocone: wada architektury (mikro)usług

Spójrz na zdjęcie: to dobry programista, ma duże ręce, może wiele. Głównym problemem jest to, skąd pochodzą te ręce.

Usługi osierocone: wada architektury (mikro)usług

Usługi umożliwiają korzystanie z różnych języków programowania, które są bardziej odpowiednie do różnych zadań. Część usług jest w Go, część w Erlangu, część w Ruby, coś w PHP, coś w Pythonie. Ogólnie rzecz biorąc, możesz rozszerzyć się bardzo szeroko. Tutaj też są niuanse.

Usługi osierocone: wada architektury (mikro)usług

Architektura zorientowana na usługi opiera się przede wszystkim na devopsach. Oznacza to, że jeśli nie masz automatyzacji, nie ma procesu wdrażania, jeśli skonfigurujesz go ręcznie, konfiguracje mogą zmieniać się z instancji usługi na instancję i musisz się tam udać, aby coś zrobić, to jesteś w piekle.

Na przykład masz 20 usług i musisz je wdrożyć ręcznie, masz 20 konsol i jednocześnie naciskasz „Enter” jak ninja. To nie jest zbyt dobre.

Jeśli masz usługę po testach (oczywiście jeśli są testy), a jeszcze musisz ją dokończyć plikiem, żeby działała na produkcji, to też mam dla Ciebie złą wiadomość.

Jeśli korzystasz z konkretnych usług Amazona i pracujesz w Rosji, to dwa miesiące temu również miałeś „Wszystko wokół się pali, u mnie wszystko w porządku, wszystko jest w porządku”.

Usługi osierocone: wada architektury (mikro)usług

Używamy Ansible do automatyzacji wdrażania, Puppet do konwergencji, Bamboo do automatyzacji wdrażania i Confluence, aby w jakiś sposób to wszystko opisać.

Nie będę się nad tym szczegółowo rozwodzić, ponieważ raport dotyczy bardziej praktyk interakcji, a nie technicznego wdrożenia.

Usługi osierocone: wada architektury (mikro)usług

Na przykład mieliśmy problemy, gdy Puppet na serwerze współpracował z Ruby 2, ale niektóre aplikacje zostały napisane dla Ruby 1.8 i nie współpracowały ze sobą. Coś tam idzie nie tak. A kiedy musisz uruchomić wiele wersji Ruby na jednym komputerze, zwykle zaczynają się problemy.

Na przykład dajemy każdemu programiście platformę, na której znajduje się w przybliżeniu wszystko, co mamy, wszystkie usługi, które można rozwinąć, aby miał izolowane środowisko, mógł je rozbić i zbudować tak, jak chce.

Zdarza się, że potrzebujesz jakiegoś specjalnie skompilowanego pakietu z obsługą czegoś tam. To dość trudne. Wysłuchałem raportu, w którym obraz Dockera waży 45 GB. W Linuksie jest oczywiście łatwiej, wszystko jest tam mniejsze, ale nadal nie będzie wystarczająco dużo miejsca.

Cóż, istnieją sprzeczne zależności, gdy jedna część projektu zależy od biblioteki jednej wersji, inna część projektu zależy od innej wersji, a biblioteki w ogóle nie są instalowane razem.

Usługi osierocone: wada architektury (mikro)usług

Mamy strony i usługi w PHP 5.6, wstydzimy się ich, ale co możemy zrobić? To jest nasza jedyna witryna. Na PHP 7 są strony i usługi, jest ich więcej, nie wstydzimy się ich. I każdy programista ma swoją bazę, w której chętnie widzi.

Jeśli piszesz w firmie w jednym języku, to trzy maszyny wirtualne na programistę brzmią normalnie. Jeśli masz różne języki programowania, sytuacja się pogarsza.

Usługi osierocone: wada architektury (mikro)usług

Masz witryny i usługi na tym, na tym, potem kolejna witryna dla Go, jedna witryna dla Ruby i kilka innych Redis z boku. W rezultacie wszystko to zamienia się w duże pole do wsparcia i cały czas część z niego może się zepsuć.

Usługi osierocone: wada architektury (mikro)usług

Dlatego zastąpiliśmy zalety języka programowania wykorzystaniem różnych frameworków, ponieważ frameworki PHP są zupełnie inne, mają inne możliwości, inne społeczności i inne wsparcie. A usługę można napisać tak, żeby mieć już na nią coś gotowego.

Każdy serwis ma swój własny zespół

Usługi osierocone: wada architektury (mikro)usług

Naszą główną zaletą, która krystalizowała się przez kilka lat, jest to, że każdy serwis ma swój własny zespół. Jest to wygodne w przypadku dużego projektu, można zaoszczędzić czas na dokumentacji, menedżerowie dobrze znają swój projekt.

Możesz łatwo przesyłać zadania z pomocy technicznej. Na przykład zepsuła się usługa ubezpieczeniowa. I od razu ekipa zajmująca się ubezpieczeniami rusza to naprawić.

Nowe funkcje powstają szybko, bo mając jedną usługę atomową, można szybko coś w nią wkręcić.

A kiedy zepsujesz usługę, a to nieuchronnie się stanie, nie wpłyniesz na usługi innych osób, a programiści z innych zespołów nie przybiegną do ciebie z bitami i nie powiedzą: „Aj, nie rób tego”.

Usługi osierocone: wada architektury (mikro)usług

Jak zawsze są niuanse. Mamy stabilne zespoły, menedżerowie są przywiązani do zespołu. Istnieją jasne dokumenty, menedżerowie ściśle monitorują wszystko. Każdy zespół z menadżerem ma kilka usług i istnieje określony punkt kompetencyjny.

Jeśli zespoły pływają (czasem też tego używamy), istnieje dobra metoda zwana „mapą gwiazd”.

Usługi osierocone: wada architektury (mikro)usług

Masz listę usług i osób. Gwiazdka oznacza, że ​​dana osoba jest ekspertem w tej usłudze, książka oznacza, że ​​dana osoba studiuje tę usługę. Zadaniem osoby jest zamiana książeczki na gwiazdkę. A jeśli nic nie zostanie napisane przed usługą, zaczną się problemy, o których powiem dalej.

Jak wyglądają usługi sieroce?

Usługi osierocone: wada architektury (mikro)usług

Pierwszym problemem, pierwszym sposobem na wprowadzenie usługi dla sierot w swojej infrastrukturze, jest zwalnianie ludzi. Czy kiedykolwiek zdarzyło się, że firma dotrzymywała terminów przed oceną zadań? Czasami zdarza się, że terminy są napięte i po prostu nie ma czasu na dokumentację. „Musimy przekazać usługę produkcji, a potem ją dodamy”.

Jeśli zespół jest mały, zdarza się, że jest jeden programista, który pisze wszystko, reszta jest w przygotowaniu. „Napisałem podstawową architekturę, dodajmy interfejsy.” Potem w pewnym momencie np. menadżer odchodzi. I w tym okresie, kiedy menadżer odszedł, a nowy nie został jeszcze powołany, sami deweloperzy decydują, dokąd zmierza usługa i co się w niej będzie działo. A jak wiemy (cofnijmy się kilka slajdów wstecz), w niektórych zespołach są ludzie-płatki śniegu, czasem lider zespołu-płatek śniegu. Potem odchodzi i otrzymujemy opiekę dla sierot.

Usługi osierocone: wada architektury (mikro)usług

Jednocześnie zadania ze wsparcia i z biznesu nie znikają, lądują w backlogu. Jeśli podczas opracowywania usługi wystąpiły jakieś błędy architektoniczne, one również lądują w zaległościach. Usługa powoli się pogarsza.

Jak rozpoznać sierotę?

Ta lista dobrze opisuje sytuację. Kto dowiedział się czegoś o ich infrastrukturze?

Usługi osierocone: wada architektury (mikro)usług

O udokumentowanych obejściach: istnieje usługa i w ogóle działa, ma dwustronicową instrukcję, jak z nią pracować, ale nikt nie wie, jak to działa w środku.

Lub na przykład istnieje jakiś rodzaj skracacza linków. Na przykład obecnie mamy trzy narzędzia do skracania linków używane do różnych celów w różnych usługach. To są po prostu konsekwencje.

Usługi osierocone: wada architektury (mikro)usług

Teraz ja będę kapitanem oczywistości. Co powinno być zrobione? Najpierw musimy przekazać usługę innemu menadżerowi, innemu zespołowi. Jeśli lider Twojego zespołu jeszcze nie odszedł, to w tym drugim zespole, kiedy zrozumiesz, że usługa jest jak sierota, musisz włączyć kogoś, kto przynajmniej coś o tym rozumie.

Najważniejsze: musisz mieć procedury przeniesienia spisane krwią. W naszym przypadku zwykle to monitoruję, ponieważ potrzebuję, aby to wszystko działało. Menedżerom zależy na tym, aby zostało to szybko dostarczone, a to, co stanie się z tym później, nie jest już dla nich tak ważne.

Usługi osierocone: wada architektury (mikro)usług

Następny sposób na sierotę to: „Zrobimy to w outsourcingu, będzie szybciej, a potem przekażemy to zespołowi”. Wiadomo, że każdy ma w zespole jakieś plany, taka kolej. Często klient biznesowy uważa, że ​​outsourcer zrobi to samo, co dział techniczny, którym dysponuje firma. Chociaż ich motywacje są różne. W outsourcingu są dziwne rozwiązania technologiczne i dziwne rozwiązania algorytmiczne.

Usługi osierocone: wada architektury (mikro)usług

Na przykład mieliśmy serwis, który miał Sfinksa w różnych nieoczekiwanych miejscach. Powiem ci później, co musiałem zrobić.

Outsourcingowcy mają samodzielnie napisane ramy. To jest po prostu czysty PHP z kopią i wklejeniem z poprzedniego projektu, w którym można znaleźć najróżniejsze rzeczy. Skrypty wdrożeniowe stanowią dużą wadę, gdy trzeba użyć skomplikowanych skryptów Bash do zmiany kilku wierszy w jakimś pliku, a te skrypty wdrożeniowe są wywoływane przez jakiś trzeci skrypt. W rezultacie zmieniasz system wdrażania, wybierasz coś innego, przeskakujesz, ale Twoja usługa nie działa. Ponieważ tam trzeba było umieścić jeszcze 8 linków pomiędzy różnymi folderami. Albo zdarza się, że tysiąc rekordów działa, ale sto tysięcy już nie działa.

Nadal będę kapitanem. Akceptacja usługi zleconej jest procedurą obowiązkową. Czy zdarzyło się komuś, że zlecona usługa dotarła i nigdzie nie została przyjęta? Nie jest to oczywiście tak popularne jak usługa dla sierot, ale jednak.

Usługi osierocone: wada architektury (mikro)usług

Trzeba sprawdzić usługę, sprawdzić usługę, zmienić hasła. Mieliśmy przypadek, gdy dali nam usługę, był panel administracyjny „if login == 'admin' && hasło == 'admin'...”, jest to zapisane bezpośrednio w kodzie. Siedzimy i myślimy, a ludzie to piszą w 2018 roku?

Testowanie pojemności pamięci jest również niezbędną rzeczą. Trzeba przyjrzeć się temu, co stanie się ze stu tysiącami rekordów, jeszcze zanim wprowadzisz gdzieś tę usługę do produkcji.

Usługi osierocone: wada architektury (mikro)usług

Wysyłanie usługi w celu poprawy nie powinno być wstydem. Kiedy mówisz: „Nie przyjmiemy tej usługi, mamy 20 zadań, wykonaj je, a potem przyjmiemy”, jest to normalne. Twoje sumienie nie powinno ranić faktem, że powołujesz menedżera lub że firma marnuje pieniądze. Firma wyda wtedy więcej.

Mieliśmy przypadek, gdy zdecydowaliśmy się na outsourcing projektu pilotażowego.

Usługi osierocone: wada architektury (mikro)usług

Dostarczono terminowo i to było jedyne kryterium jakości. Dlatego wykonaliśmy kolejny projekt pilotażowy, który już nawet nie był pilotażem. Usługi te zostały zaakceptowane i za pomocą środków administracyjnych powiedzieli: oto twój kod, oto zespół, oto twój menedżer. Usługi faktycznie zaczęły już przynosić zyski. Jednocześnie tak naprawdę nadal są sierotami, nikt nie rozumie, jak działają, a menedżerowie robią wszystko, aby wyprzeć się swoich zadań.

Usługi osierocone: wada architektury (mikro)usług

Jest jeszcze jedna świetna koncepcja – rozwój partyzancki. Kiedy jakiś dział, zwykle dział marketingu, chce przetestować hipotezę i zleca outsourcing całej usługi. Zaczyna do niego lać się ruch, zamykają dokumenty, podpisują dokumenty z wykonawcą, wchodzą do eksploatacji i mówią: „Kochani, mamy tu serwis, już jest ruch, przynosi nam pieniądze, przyjmijmy to”. Pomyśleliśmy: „Oppa, jak to możliwe”.

Usługi osierocone: wada architektury (mikro)usług

I jeszcze jeden sposób na uzyskanie usługi sierocej: gdy jakiś zespół nagle czuje się obciążony, kierownictwo mówi: „Przenieśmy obsługę tego zespołu do innego zespołu, ma on mniejsze obciążenie”. A potem przeniesiemy go do trzeciego zespołu i zmienimy menadżera. I w końcu znowu mamy sierotę.

Jaki jest problem z sierotami?

Usługi osierocone: wada architektury (mikro)usług

Kto nie wie, to pancernik Wasa wyhodowany w Szwecji, słynący z tego, że zatonął 5 minut po wodowaniu. Nawiasem mówiąc, król Szwecji nikogo za to nie stracił. Został zbudowany przez dwa pokolenia inżynierów, którzy nie wiedzieli, jak budować takie statki. Naturalny efekt.

Swoją drogą statek mógł zatonąć w dużo gorszy sposób, np. gdy król już na nim jechał gdzieś w czasie sztormu. I tak od razu utonął, według Agile dobrze jest wcześnie ponieść porażkę.

Jeśli wcześniej poniesiemy porażkę, zazwyczaj nie ma żadnych problemów. Przykładowo podczas akceptacji wysłano go do sprawdzenia. Ale jeśli poniesiemy porażkę w produkcji, gdy zainwestujemy pieniądze, mogą pojawić się problemy. Konsekwencje, jak się je nazywa w biznesie.

Dlaczego usługi dla sierot są niebezpieczne:

  • Usługa może nagle przestać działać.
  • Naprawa usługi zajmuje dużo czasu lub nie jest naprawiana w ogóle.
  • Problemy bezpieczeństwa.
  • Problemy z ulepszeniami i aktualizacjami.
  • Jeśli ważna usługa ulegnie awarii, ucierpi na tym reputacja firmy.

Co zrobić z usługami dla sierot?

Usługi osierocone: wada architektury (mikro)usług

Jeszcze raz powtórzę, co należy zrobić. Po pierwsze, musi być dokumentacja. 7 lat pracy w Banki.ru nauczyło mnie, że testerzy nie powinni wierzyć na słowo programistom, a operacje nie powinny wierzyć każdemu na słowo. Musimy sprawdzić.

Usługi osierocone: wada architektury (mikro)usług

Po drugie, konieczne jest pisanie diagramów interakcji, ponieważ zdarza się, że usługi, które nie są zbyt dobrze odbierane, zawierają zależności, o których nikt nie mówił. Na przykład programiści zainstalowali usługę na swoim kluczu do niektórych Yandex.Maps lub Dadata. Skończył Ci się darmowy limit, wszystko się zepsuło i w ogóle nie wiesz, co się stało. Wszystkie takie grabie muszą być opisane: usługa korzysta z Dadata, SMS-ów, czegoś innego.

Usługi osierocone: wada architektury (mikro)usług

Po trzecie, praca z długiem technicznym. Kiedy podpierasz się o kulach lub przyjmujesz usługę i mówisz, że coś trzeba zrobić, musisz upewnić się, że zostało to zrobione. Bo wtedy może się okazać, że ta mała dziura wcale nie jest taka mała i wpadniesz przez nią.

Przy zadaniach architektonicznych mieliśmy opowieść o Sfinksie. Jeden z serwisów wykorzystywał Sphinxa do wprowadzania list. To tylko lista podzielona na strony, ale co noc była ponownie indeksowana. Składał się z dwóch indeksów: jednego dużego, indeksowanego co noc i przykręcanego do niego małego indeksu. Każdego dnia, przy 50% prawdopodobieństwie bombardowania lub nie, indeks zawieszał się podczas obliczeń, a nasze wiadomości przestawały być aktualizowane na stronie głównej. Początkowo ponowna indeksacja indeksu trwała 5 minut, następnie indeks rósł, aż w pewnym momencie ponowna indeksacja zaczęła zająć 40 minut. Kiedy to wycięliśmy, odetchnęliśmy z ulgą, bo było jasne, że minie jeszcze trochę czasu i nasz indeks zostanie ponownie zaindeksowany w pełnym wymiarze czasu. To będzie porażka naszego portalu, przez osiem godzin nie będzie żadnych wiadomości - to wszystko, biznes się zatrzymał.

Zaplanuj pracę w ośrodku dla sierot

Usługi osierocone: wada architektury (mikro)usług

W rzeczywistości jest to bardzo trudne, ponieważ w devops chodzi o komunikację. Chcesz być w dobrych stosunkach ze swoimi współpracownikami, a kiedy uderzysz ich po głowie przepisami, mogą mieć sprzeczne uczucia w stosunku do osób, które to robią.

Oprócz tych wszystkich punktów jest jeszcze jedna ważna rzecz: za każdą konkretną usługę, za każdą konkretną sekcję procedury wdrażania muszą odpowiadać określone osoby. Kiedy nie ma ludzi i trzeba przyciągnąć inne osoby, aby przestudiowały całą tę kwestię, staje się to trudne.

Usługi osierocone: wada architektury (mikro)usług

Jeśli to wszystko nie pomogło, a Twój sierociniec nadal jest sierotą, nikt nie chce się tego podjąć, dokumentacja nie jest spisana, ekipa, która została wezwana do tej usługi, nie chce nic zrobić, jest prosty sposób - przerobić wszystko.

Czyli bierzecie wymagania na usługę od nowa i piszecie nową usługę, lepszą, na lepszej platformie, bez dziwnych rozwiązań technologicznych. I migrujesz do niego w bitwie.

Usługi osierocone: wada architektury (mikro)usług

Mieliśmy sytuację, kiedy wzięliśmy usługę na Yii 1 i zdaliśmy sobie sprawę, że nie możemy jej dalej rozwijać, ponieważ zabrakło nam programistów, którzy potrafiliby dobrze pisać na Yii 1. Wszyscy programiści dobrze piszą na Symfony XNUMX. Co robić? Przydzieliliśmy czas, przydzieliliśmy zespół, przydzieliliśmy menadżera, przepisaliśmy projekt i płynnie przerzuciliśmy na niego ruch.

Następnie starą usługę można usunąć. To moja ulubiona procedura, kiedy trzeba usunąć jakąś usługę z systemu zarządzania konfiguracją, a następnie sprawdzić, czy wszystkie produkowane samochody zostały wyłączone, aby programiści nie pozostawili żadnych śladów. Repozytorium pozostaje w Git.

Tylko o tym chciałem porozmawiać, jestem gotowy do dyskusji, tematem jest holivar, wielu w nim pływało.

Na slajdach było napisane, że ujednoliciliście języki. Przykładem była zmiana rozmiaru zdjęć. Czy naprawdę konieczne jest ścisłe ograniczenie tego do jednego języka? Ponieważ zmianę rozmiaru obrazu w PHP, cóż, można faktycznie wykonać w Golangu.

W rzeczywistości jest to opcjonalne, jak wszystkie praktyki. Być może w niektórych przypadkach jest to nawet niepożądane. Ale trzeba zrozumieć, że jeśli masz dział techniczny w firmie liczącej 50 osób, 45 z nich to specjaliści PHP, kolejnych 3 to devopsi, którzy znają Pythona, Ansible, Puppet i coś w tym stylu, a tylko jeden z nich pisze w jakimś rodzaj języka. Niektóre usługi zmiany rozmiaru obrazu Go, a kiedy opuszczą, towarzyszy im wiedza specjalistyczna. Jednocześnie będziesz musiał poszukać programisty specyficznego dla rynku, który zna ten język, zwłaszcza jeśli jest on rzadki. Oznacza to, że z organizacyjnego punktu widzenia jest to problematyczne. Z punktu widzenia devopsa nie będziesz musiał po prostu sklonować gotowego zestawu podręczników, których używasz do wdrażania usług, ale będziesz musiał napisać je od nowa.

Obecnie budujemy usługę na Node.js i będzie to po prostu platforma w pobliżu dla każdego programisty z osobnym językiem. Ale siedzieliśmy i myśleliśmy, że gra jest warta świeczki. Oznacza to, że jest to pytanie, nad którym możesz usiąść i pomyśleć.

Jak monitorujesz swoje usługi? Jak gromadzisz i monitorujesz logi?

Logi zbieramy w Elasticsearch i umieszczamy je w Kibanie i w zależności czy jest to środowisko produkcyjne czy testowe, wykorzystywane są tam różne kolektory. Gdzieś Drwal, gdzieś indziej coś innego, nie pamiętam. I nadal są miejsca w niektórych usługach, w których instalujemy Telegraf i kręcimy gdzie indziej osobno.

Jak żyć z Puppet i Ansible w tym samym środowisku?

Tak naprawdę mamy teraz dwa środowiska, jedno to Puppet, drugie to Ansible. Pracujemy nad ich hybrydyzacją. Ansible to dobry framework do wstępnej konfiguracji, Puppet to zły framework do początkowej konfiguracji, ponieważ wymaga praktycznej pracy bezpośrednio na platformie, a Puppet zapewnia zbieżność konfiguracji. Oznacza to, że platforma sama utrzymuje się w aktualnym stanie i aby ansibilizowana maszyna była na bieżąco, trzeba cały czas i z pewną częstotliwością uruchamiać na niej playbooki. To jest różnica.

Jak zachować kompatybilność? Czy masz konfiguracje zarówno w Ansible, jak i Puppet?

To jest nasz wielki ból, utrzymujemy zgodność z rękami i zastanawiamy się, jak gdzieś teraz z tego wszystkiego wyjść. Okazuje się, że Puppet wdraża tam pakiety i utrzymuje niektóre łącza, a Ansible na przykład wdraża tam kod i dostosowuje najnowsze konfiguracje aplikacji.

Prezentacja dotyczyła różnych wersji Ruby. Jakie rozwiązanie?

Spotkaliśmy się z tym w jednym miejscu i cały czas musimy mieć to w głowach. Po prostu wyłączyliśmy część działającą na Ruby, która była niezgodna z aplikacjami i trzymaliśmy ją oddzielnie.

Tegoroczna konferencja DevOpsDays w Moskwie odbędzie się 7 grudnia w Technopolis. Zgłoszenia na raporty przyjmujemy do 11 listopada. pisać z nami, jeśli chcesz porozmawiać.

Ruszyły zapisy dla uczestników, dołącz do nas!

Źródło: www.habr.com

Dodaj komentarz