Bitrix24: "Szybko podniesione nie jest uważane za upadłe"

Obecnie usługa „Bitrix24” nie dysponuje setkami gigabitów ruchu ani ogromnym parkiem serwerów (choć oczywiście istnieje ich sporo). Mimo to dla wielu klientów jest to główne narzędzie pracy w firmie, prawdziwa aplikacja krytyczna dla biznesu. Dlatego awaria — w żadnym wypadku nie może mieć miejsca. A co jeśli awaria jednak się zdarzy, ale usługa „odrodziła się” tak szybko, że nikt tego nie zauważył? I jak udaje się zrealizować failover bez utraty jakości pracy i liczby klientów? Aleksander Demidow, dyrektor ds. usług chmurowych „Bitrix24”, opowiedział w naszym blogu o tym, jak przez 7 lat istnienia produktu ewoluował system rezerwacji.

Bitrix24: "Szybko podniesione nie jest uważane za upadłe"

„W formacie SaaS uruchomiliśmy „Bitrix24” 7 lat temu. Główną trudnością była prawdopodobnie następująca kwestia: przed uruchomieniem publicznym w formacie SaaS, ten produkt istniał po prostu w formie rozwiązania pudełkowego. Klienci kupowali go od nas, instalowali na swoich serwerach, zakładali portal korporacyjny — wspólne rozwiązanie do komunikacji między pracownikami, przechowywania plików, zarządzania zadaniami, CRM, i tak dalej. Do 2012 roku zdecydowaliśmy, że chcemy uruchomić to jako SaaS, samodzielnie administrując, zapewniając odporność na awarie i niezawodność. Zbieraliśmy doświadczenie w tym procesie, ponieważ do tego momentu po prostu go nie mieliśmy — byliśmy tylko producentami oprogramowania, a nie dostawcami usług.

Uruchamiając usługę, rozumieliśmy, że najważniejsze to zapewnić odporność na awarie, niezawodność i stałą dostępność usługi, ponieważ jeśli masz zwykłą stronę internetową, sklep na przykład, i ona przestaje działać przez godzinę — cierpisz tylko ty, tracisz zamówienia, tracisz klientów, ale dla samego twojego klienta — to nie jest zbyt krytyczne. Oczywiście jest zawiedziony, ale poszedł i kupił na innej stronie. A jeśli to aplikacja, na której opiera się cała praca wewnątrz firmy, komunikacja, podejmowanie decyzji, to najważniejsze jest zdobycie zaufania użytkowników, czyli nie zawodzenie ich i nie padać. Dlatego że cała praca może stanąć, jeśli coś wewnątrz nie będzie działać.

Bitrix24 jako SaaS

Pierwszy prototyp zbudowaliśmy rok przed publicznym uruchomieniem, w 2011 roku. Zajęło nam to około tygodnia, przetestowaliśmy go — był nawet działający. To znaczy, można było wejść do formularza, wpisać nazwę portalu, tworzył się nowy portal, zakładana była baza użytkowników. Przyjrzeliśmy się temu, oceniliśmy produkt i przez cały rok dalej go rozwijaliśmy. Mieliśmy duże zadanie: nie chcieliśmy tworzyć dwóch oddzielnych baz kodu, nie chcieliśmy wspierać osobno produktu pudełkowego i osobno rozwiązań chmurowych — chcieliśmy wszystko robić w ramach jednego kodu.

Bitrix24: "Szybko podniesione nie jest uważane za upadłe"

Typowa aplikacja internetowa w tamtym czasie to jeden serwer, na którym działał jakiś kod PHP, baza MySQL, pliki były przesyłane, dokumenty, zdjęcia umieszczane w folderze upload — i to wszystko działało. Niestety, uruchomienie krytycznie niezawodnej usługi internetowej na tym jest niemożliwe. Nie obsługiwano tam rozproszonego cache'a ani replikacji baz danych.

Sformułowaliśmy wymagania: musi mieć możliwość umiejscowienia w różnych lokalizacjach, wspierać replikację, idealnie, aby mieścił się w różnych geograficznie rozdzielonych centrach danych. Podzielić logikę produktu i właściwe przechowywanie danych. Musi mieć możliwość dynamicznego skalowania w zależności od obciążenia, a statykę w ogóle zewnętrznie wydzielić. Z tych powodów powstały wymagania do produktu, który rozwijaliśmy przez cały rok. W tym czasie w platformie, która okazała się jednolita — dla rozwiązań pudełkowych oraz dla naszej własnej usługi — wprowadziliśmy wsparcie dla rzeczy, które były nam potrzebne. Wsparcie dla replikacji MySQL na poziomie samego produktu: to jest, programista, który pisze kod — nie musi się martwić, jak będą rozdzielane jego zapytania, korzysta z naszego API, a my umiejętnie rozdzielamy zapytania do zapisu i odczytu między masterami i slave'ami.

Wprowadziliśmy wsparcie na poziomie produktu dla różnych obiektowych magazynów chmurowych: Google Storage, Amazon S3 — plus wsparcie Open Stack Swift. Dlatego było to wygodne zarówno dla nas jako usługi, jak i dla programistów, którzy pracują z rozwiązaniem pudełkowym: jeśli korzystają po prostu z naszego API do pracy, nie muszą się martwić, gdzie ostatecznie plik zostanie zapisany, lokalnie w systemie plików czy trafi do obiektowego magazynu plików.

Ostatecznie od razu zdecydowaliśmy, że będziemy rezerwować na poziomie całego centrum danych. W 2012 roku w pełni wystartowaliśmy w Amazon AWS, ponieważ mieliśmy już doświadczenie w pracy z tą platformą — nasza własna strona była tam hostowana. Przyciągało nas to, że w każdym regionie Amazon ma kilka stref dostępności — w ich terminologii, kilka centrów danych, które są w miarę niezależne i pozwalają nam rezerwować na poziomie całego centrum danych: jeśli jedno z nich nagle zawiedzie, bazy są replikowane w trybie master-master, serwery aplikacji internetowych są rezerwowane, a statyka trafia do obiektowego magazynu S3. Obciążenie jest równoważone — w tamtym czasie przez elb Amazon, ale nieco później przeszliśmy do własnych równoważników obciążenia, ponieważ potrzebowaliśmy bardziej skomplikowanej logiki.

Co chcieliśmy — to i otrzymaliśmy...

Wszystkie podstawowe rzeczy, które chcieliśmy zapewnić — niezawodność samych serwerów, aplikacji internetowych, baz danych — działały dobrze. Najprostszy scenariusz: jeśli któreś z naszych aplikacji internetowych zawiedzie, to sprawa jest prosta — są wyłączane z równoważenia obciążenia.

Bitrix24: "Szybko podniesione nie jest uważane za upadłe"

Uszkodzone maszyny równoważnik obciążenia (wtedy był to elb Amazon) sam oznaczał jako unhealthy, wyłączał rozkład obciążenia na nie. Działał automatyczny skalowanie Amazon: gdy obciążenie rosło, do grupy auto-skalowania dodawano nowe maszyny, obciążenie rozdzielano na nowe maszyny — wszystko było dobrze. Z naszymi równoważnikami obciążenia logika jest podobna: jeśli coś się stanie z serwerem aplikacji, usuwamy z niego zapytania, eliminujemy te maszyny, uruchamiamy nowe i kontynuujemy pracę. Schemat przez te wszystkie lata trochę się zmieniał, ale wciąż działa: jest prosty, zrozumiały i nie ma z tym żadnych trudności.

Działamy na całym świecie, szczyty obciążenia u klientów są absolutnie różne i, szczerze mówiąc, powinniśmy mieć możliwość przeprowadzania różnych prac serwisowych z dowolnymi komponentami naszego systemu w dowolnym czasie – niewidocznie dla klientów. Dlatego mamy możliwość wyłączenia bazy danych, przenosząc obciążenie na drugie centrum danych.

Jak to wszystko działa? — Przekierowujemy ruch do działającego centrum danych — jeśli to awaria centrum danych, to całkowicie, jeśli to nasze planowe prace z jakąś jedną bazą, to część ruchu obsługującego tych klientów przekierowujemy do drugiego centrum danych, wstrzymujemy replikację. Jeśli potrzebne są nowe maszyny do aplikacji webowych, ponieważ obciążenie wzrosło w drugim centrum danych, automatycznie się uruchamiają. Kończymy prace, replikacja się odnawia i przywracamy całe obciążenie z powrotem. Jeśli musimy równolegle przeprowadzić jakieś prace w drugim DC, na przykład zainstalować aktualizacje systemowe lub zmienić ustawienia w drugiej bazie danych, to generalnie powtarzamy to samo, tylko w drugą stronę. A jeśli to awaria, to robimy wszystko banalnie: w systemie monitorowania używamy mechanizmu event-handlers. Jeśli aktywuje się kilka kontroli i status przechodzi w krytyczny, uruchamiany jest ten handler, procesor, który może wykonać tę czy inną logikę. Dla każdej bazy mamy zapisane, który serwer jest dla niej serwerem zapasowym, i gdzie należy przekierować ruch w przypadku jej niedostępności. W historii korzystamy w różnym stopniu z nagios lub jego forków. Generalnie podobne mechanizmy są praktycznie w każdym systemie monitorowania, coś bardziej skomplikowanego na razie nie używamy, ale być może kiedyś będziemy. Teraz monitorowanie działa na niedostępność i ma możliwość coś przekierować.

Czy wszystko zarezerwowaliśmy?

Mamy wielu klientów z USA, wielu klientów z Europy, wielu klientów z Bliskiego Wschodu — Japonii, Singapuru itd. Oczywiście znaczną część naszych klientów stanowią osoby w Rosji. Pracujemy, więc, w wielu regionach. Użytkownicy oczekują szybkiej reakcji, istnieją wymagania dotyczące przestrzegania różnych lokalnych przepisów, a w każdym regionie rezerwujemy i tak dwa centra danych. Dodatkowo mamy jeszcze inne usługi, które również wygodnie jest lokować w obrębie jednego regionu — dla klientów tam działających. Serwery aplikacji REST, serwery autoryzacji są mniej krytyczne dla ogólnej pracy klienta, można na nie przełączać się z akceptowalnym opóźnieniem, ale nie chcemy wymyślać nowego sposobu monitorowania ich i co z nimi robić. Dlatego w maksymalnym stopniu staramy się korzystać z już istniejących rozwiązań, a nie rozwijać u siebie jakąś kompetencję w zakresie dodatkowych produktów. Czasem zwyczajnie korzystamy z przełączania na poziomie DNS, a żywotność usługi określamy tym samym DNS. W Amazonie istnieje usługa Route 53, ale to nie jest tylko DNS, w którym można dodać rekordy i na tym koniec — jest znacznie bardziej elastyczne i wygodne. Dzięki niemu można zbudować usługi geo-rozproszone z geolokalizacją, definiując, skąd pochodzi klient, i przydzielając mu różne rekordy — umożliwia to także budowę architektur failover. Te same health-checki ustawia się w samej usłudze Route 53, definiujemy endpointy, które są monitorowane, ustalamy metryki oraz protokoły, na podstawie których określamy „żywotność” usługi — TCP, HTTP, HTTPS; ustalamy częstotliwość sprawdzania, które określają, czy usługa działa czy nie. W samym DNS wpisujemy, co będzie primary, co będzie secondary, na co przełączyć, jeśli działa health-check w Route 53. Wszystko to można zrobić za pomocą innych narzędzi, ale co jest wygodne — ustawia się to raz, a potem w ogóle nie myślimy o tym, jak wykonujemy kontrole, jak odbywa się przełączanie: wszystko działa samo.

Pierwsze „bo”: jak i czym rezerwować sam route 53? Na wszelki wypadek, co jeśli coś się z nim stanie? Na szczęście nigdy nie mieliśmy z tym problemu, ale znowu, przede mną opowieść, dlaczego warto to jednak zarezerwować. Tutaj stawiamy sobie poduszkę bezpieczeństwa z wyprzedzeniem. Kilka razy dziennie wykonujemy pełny eksport wszystkich stref, które mamy w route 53. API Amazona pozwala na ich łatwe generowanie w formacie JSON, a my mamy kilka serwerów zapasowych, na które to konwertujemy, wyprowadzamy w postaci konfiguracji i mamy, mówiąc wprost, zapasową konfigurację. W razie czego możemy ją szybko wdrożyć ręcznie, nie tracąc danych z ustawień dns.

Drugie „ale”: co w tym obrazie jeszcze nie jest zarezerwowane? Sam balancer! Mamy rozłożenie klientów według regionów zrobione bardzo prosto. Mamy domeny bitrix24.ru, bitrix24.com, .de — w tej chwili jest ich około 13 różnych, które działają w najróżniejszych strefach. Doszliśmy do następującego wniosku: w każdym regionie — swoje balancery. Tak łatwiej rozdzielać na regiony w zależności od tego, gdzie jest największe obciążenie w sieci. Jeśli dochodzi do awarii na poziomie jednego balancera, po prostu wyciągamy go z eksploatacji i usuwamy z dns. Jeśli występuje jakiś problem z grupą balancerów, są one rezerwowane w innych lokalizacjach, a przełączanie między nimi odbywa się za pomocą właśnie route53, ponieważ dzięki krótkiemu ttl przełączenie następuje w maksimum w ciągu 2, 3, 5 minut.

Trzecie „ale”: co jeszcze nie jest zarezerwowane? S3, to prawda. Umieszczając pliki, które przechowujemy u użytkowników w S3, szczerze wierzyliśmy, że jest on niezawodny i nic tam nie trzeba rezerwować. Ale historia pokazuje, że rzeczywistość jest inna. W ogóle, Amazon opisuje S3 jako podstawową usługę, ponieważ sam Amazon używa S3 do przechowywania obrazów maszyn, konfiguracji, obrazów AMI, zrzutów... I jeśli S3 pada, jak to miało miejsce raz przez te 7 lat, kiedy eksploatujemy bitrix24, ciągnie ze sobą mnóstwo innych problemów — niedostępność uruchamiania wirtualek, awarie w działaniu api itd.

S3 może również zawieść — tak zdarzyło się kiedyś. Dlatego doszliśmy do następującego wniosku: jeszcze kilka lat temu w Rosji nie było poważnych publicznych obiektowych magazynów, a my rozważaliśmy możliwość stworzenia czegoś własnego... Na szczęście nie zaczęliśmy tego robić, ponieważ pogrążylibyśmy się w ekspertyzie, której nie posiadamy, i z pewnością popełnilibyśmy błędy. Obecnie magazyny zgodne z s3 dostępne są w Mail.ru, Yandexie oraz u kilku innych dostawców. Ostatecznie doszliśmy do wniosku, że chcemy mieć, po pierwsze, replikację, a po drugie, możliwość pracy z lokalnymi kopiami. Dla konkretnego regionu rosyjskiego korzystamy z usługi Mail.ru Hotbox, która jest zgodna z s3 przez API. Nie potrzebowaliśmy żadnych poważnych poprawek w kodzie aplikacji, więc stworzyliśmy następujący mechanizm: w s3 są wyzwalacze, które uruchamiają się przy tworzeniu/usunięciu obiektów, a Amazon ma taki serwis jak Lambda — to bezserwerowe uruchamianie kodu, które wykona się właśnie przy aktywacji tych lub innych wyzwalaczy.

Bitrix24: "Szybko podniesione nie jest uważane za upadłe"

Zrobiliśmy to bardzo prosto: gdy wyzwalacz zostaje uruchomiony, wykonujemy kod, który skopiuje obiekt do magazynu Mail.ru. Aby w pełni uruchomić pracę z lokalnymi kopiami danych, potrzebujemy jeszcze synchronizacji zwrotnej, aby klienci z rosyjskiego segmentu mogli pracować z magazynem, który jest im bliżej. Mail ma wkrótce dokończyć wyzwalacze w swoim magazynie — będzie można już na poziomie infrastruktury realizować synchronizację zwrotną, ale na razie robimy to na poziomie naszego własnego kodu. Jeśli widzimy, że klient umieścił jakiś plik, to na poziomie naszego kodu umieszczamy zdarzenie w kolejce, przetwarzamy je i wykonujemy replikację zwrotną. Jest w tym jeden problem: jeśli zachodzi jakaś operacja z naszymi obiektami poza naszym produktem, czyli za pomocą jakichś narzędzi zewnętrznych, nie weźmiemy tego pod uwagę. Dlatego czekamy na wyzwalacze na poziomie magazynu, aby niezależnie od tego, skąd uruchomiliśmy kod, obiekt, który do nas trafił, był kopiowany w drugą stronę.

Na poziomie kodu dla każdego klienta definiujemy oba magazyny: jeden uważany jest za podstawowy, a drugi za zapasowy. Jeśli wszystko działa dobrze, korzystamy z magazynu, który jest nam bliżej: czyli nasi klienci w Amazonie korzystają z S3, a ci, którzy pracują w Rosji, korzystają z Hotbox. Gdy aktywuje się sygnał, musimy połączyć failover i przełączamy klientów na inny magazyn. Sygnał ten możemy ustawiać niezależnie w regionach i przełączać ich w obie strony. W praktyce jeszcze z tego nie korzystaliśmy, ale mechanizm ten został przewidziany i myślimy, że kiedyś ten proces przełączenia będzie nam potrzebny i przydatny. Już jeden raz się to zdarzyło.

Ojej, a twój Amazon uciekł...

W tym kwietniu mija rocznica rozpoczęcia blokad Telegramu w Rosji. Najbardziej poszkodowanym dostawcą, który ucierpiał z tego powodu, jest Amazon. I, niestety, bardziej ucierpiały rosyjskie firmy, które działały na cały świat.

Jeśli firma jest globalna, a Rosja dla niej to zaledwie mały segment, 3-5% — to w ten czy inny sposób można się nimi poświęcić.

Jeśli to firma wyłącznie rosyjska — jestem pewien, że trzeba lokalizować się — po prostu użytkownikom będzie wygodniej, komfortowo, będzie mniej ryzyk.

A jeśli to firma działająca globalnie, i ma mniej więcej równą liczbę klientów z Rosji i z innych części świata? Łączność segmentów jest ważna i powinny one w ten czy inny sposób współpracować ze sobą.

Na koniec marca 2018 roku Roskomnadzor wysłał do największych operatorów pismo, że planują zablokować kilka milionów adresów IP Amazon, aby zablokować... komunikator Zello. Dziękujemy tym dostawcom — skutecznie rozesłali pismo wszystkim, i pojawiło się zrozumienie, że łączność z Amazonem może się rozpaść. To był piątek, w panice przybiegliśmy do kolegów z servers.ru, mówiąc: „Przyjaciele, potrzebujemy kilku serwerów, które nie będą w Rosji, nie w Amazonie, lecz, na przykład, gdzieś w Amsterdamie”, aby móc w jakiś sposób umieścić tam nasze. vpn a proxy for certain endpoints that we cannot influence in any way, for example, the endpoints of S3 — we cannot try to launch a new service and get a different IP, we still need to reach them. In just a few days, we set up these servers, brought them online, and by the time the blocks started, we were prepared. Interestingly, the RKN, seeing the uproar and panic that had arisen, said: "No, we are not going to block anything right now." (But that was exactly until they started blocking Telegram.) Having configured the bypass options and realizing that the block had not been implemented, we still did not disassemble everything. Just in case.

Bitrix24: "Szybko podniesione nie jest uważane za upadłe"

And so, in 2019, we are indeed living under blocking conditions. Last night, I was watching: about a million IPs continue to be blocked. However, nearly all of Amazon has been unblocked, at the peak there were up to 20 million addresses… In general, the reality is that connectivity, good connectivity — may suddenly not be there. It may not be there for technical reasons — fires, excavators, all that. Or, as we have seen, not purely technical reasons. Therefore, someone big and large, with their own ASs, can probably manage this in other ways — direct connect and other things at the L2 level. But in the simple scenario, like us or even smaller, it's wise to have redundancy at the server level, set up somewhere else in advance, configured with VPNs, proxies, with the ability to quickly switch configurations in those segments that are critical for you in terms of connectivity. This has come in handy for us repeatedly when the Amazon blocks started; we used it in the worst case to send S3 traffic through them, but gradually it all unraveled.

And how to back up… an entire provider?

Obecnie nie mamy scenariusza na wypadek awarii całego Amazonu. Mamy podobny scenariusz dla Rosji. W Rosji współpracowaliśmy z jednym dostawcą, u którego staraliśmy się zapewnić kilka lokalizacji. Rok temu napotkaliśmy problem: nawet mimo że to były dwa centra danych, już na poziomie konfiguracji sieci dostawcy mogą wystąpić problemy, które dotkną oba centra. Możemy zatem doświadczyć niedostępności w obu lokalizacjach. Oczywiście, tak się stało. W efekcie przemyśleliśmy architekturę wewnętrzną. Nie zmieniła się ona znacząco, ale w Rosji mamy teraz dwa lokalizacje, które są u dwóch różnych dostawców. Jeśli u jednego coś zawiedzie, możemy przełączyć się na drugiego.

Hipotetycznie rozważamy dla Amazon możliwość rezerwacji na poziomie innego dostawcy; może to być Google, może jeszcze ktoś inny... Ale dotychczas obserwowaliśmy w praktyce, że gdy występują awarie w ramach jednej strefy dostępności Amazon, awarie na poziomie całego regionu są dosyć rzadkim zjawiskiem. Dlatego teoretycznie mamy wyobrażenie o tym, że być może zrobimy rezerwację 'Amazon — nie Amazon', ale na razie na praktyce takiego nie ma.

Kilka słów o automatyzacji

Czy automatyzacja jest zawsze potrzebna? Warto tu przypomnieć efekt Dunninga-Krugera. Na osi 'x' mamy nasze wiedzę i doświadczenie, a na osi 'y' — pewność naszych działań. Najpierw nic nie wiemy i zupełnie nie jesteśmy pewni. Następnie wiemy trochę i stajemy się mega-pewni — to tzw. 'szczyt głupoty', dobrze ilustrowany obrazkiem 'głupota i odwaga'. Potem już trochę się nauczyliśmy i jesteśmy gotowi do działania. Następnie natrafiamy na poważne problemy, wpadając w dolinę rozpaczy, kiedy wydaje się, że coś wiemy, a w rzeczywistości nie wiemy zbyt wiele. Następnie, wraz z nabieraniem doświadczenia, stajemy się coraz bardziej pewni siebie.

Bitrix24: "Szybko podniesione nie jest uważane za upadłe"

Nasza logika dotycząca różnych przełączeń automatycznie w przypadku awarii dobrze opisuje ten wykres. Zaczęliśmy, nie umiejąc nic, praktycznie wszystkie prace były wykonywane ręcznie. Potem zrozumieliśmy, że można wszystko zautomatyzować i, jakby, spać spokojnie. I nagle stajemy na mega-minie: mamy fałszywy alarm i przełączamy ruch tam i z powrotem, kiedy w rzeczywistości nie powinno się tego robić. W związku z tym psuje się replikacja lub coś innego – oto ta dolina rozpaczy. A potem przychodzimy do zrozumienia, że do wszystkiego trzeba podchodzić z rozwagą. To znaczy, warto polegać na automatyce, przewidując możliwość fałszywego alarmu. Ale! jeśli konsekwencje mogą być katastrofalne, lepiej pozostawić to do oceny zespołu dyżurnego, inżynierom dyżurnym, którzy upewnią się, sprawdzą, że faktycznie jest awaria i podejmą niezbędne działania ręcznie…

Podsumowanie

W ciągu 7 lat przeszliśmy drogę od tego, że kiedy coś się psuło, panikowaliśmy, do zrozumienia, że problemy nie istnieją – są tylko zadania, które należy rozwiązywać. Kiedy budujesz jakiś serwis, spójrz na niego z góry, oceń wszystkie ryzyka, które mogą się wydarzyć. Jeśli od razu je dostrzegasz – to przewiduj rezerwację i możliwość zbudowania infrastruktury odpornej na awarie, ponieważ każdy punkt, który może ulec awarii i doprowadzić do niesprawności serwisu – z pewnością to zrobi. I nawet jeśli wydaje ci się, że niektóre elementy infrastruktury na pewno nie zawiodą – jak na przykład s3, to i tak weź pod uwagę, że mogą. I przynajmniej teoretycznie miej plan działania w przypadku, gdyby coś się stało. Miej plan zarządzania ryzykiem. Gdy myślisz o tym, żeby wszystko robić automatycznie lub ręcznie – oceń ryzyko: co się stanie, jeśli automatyka zacznie wszystko przełączać – czy nie doprowadzi to do gorszej sytuacji w porównaniu do awarii? Może gdzieś warto znaleźć rozsądny kompromis między automatyzacją a reakcją inżyniera dyżurnego, który oceni rzeczywistą sytuację i zrozumie, czy coś od razu należy przełączyć, czy „tak, ale nie teraz”.

Rozsądny kompromis między perfekcjonizmem a rzeczywistymi zasobami, czasem, pieniędzmi, które możesz wydać na schemat, który w końcu otrzymasz.

Niniejszy tekst jest uzupełnioną i rozszerzoną wersją wystąpienia Aleksandra Demidowa na konferencji Uptime dzien 4.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster