Cześć wszystkim, nazywam się Sergey Emelyanchik. Jestem kierownikiem firmy Audit-Telecom, głównym programistą i autorem systemu Veliam. Postanowiłem napisać artykuł o tym, jak z przyjacielem stworzyliśmy firmę outsourcingową, napisaliśmy oprogramowanie dla siebie, a następnie zaczęliśmy je rozpowszechniać w systemie SaaS. O tym, jak kategorycznie nie wierzyłem, że to możliwe. W artykule będzie nie tylko opowieść, ale również techniczne szczegóły dotyczące powstania produktu Veliam, włącznie z niektórymi fragmentami kodu źródłowego. Opowiem o błędach, które popełniliśmy i jak je później naprawialiśmy. Miałem wątpliwości, czy publikować taki artykuł. Ale pomyślałem, że lepiej to zrobić, uzyskać feedback i poprawić się, niż nie publikować artykułu i zastanawiać się, co by było, gdyby...
Tło
Pracowałem w jednej firmie jako pracownik IT. Firma była dość duża z rozbudowaną strukturą sieciową. Nie będę się zatrzymywał na moich obowiązkach, powiem tylko, że na pewno nie obejmowały one tworzenia czegokolwiek.
Mieliśmy monitoring, ale z czysto akademickiego zainteresowania chciałem spróbować napisać swój najprostszy. Pomysł był taki: chciałem, aby to było w sieci, aby można było łatwo, bez instalowania jakichkolwiek klientów, wejść i zobaczyć, co się dzieje w sieci z dowolnego urządzenia, w tym mobilnego przez Wi-Fi, a także bardzo chciałem szybko zrozumieć, w którym pomieszczeniu znajduje się sprzęt, który "zawiódł", ponieważ mieliśmy bardzo rygorystyczne wymagania dotyczące czasu reakcji na takie problemy. W końcu w mojej głowie zrodził się plan, by napisać prostą stronę internetową, która miałaby w tle jpeg ze schematem sieci, wyciąć na tym obrazku same urządzenia z ich adresami IP, a na obrazku w odpowiednich współrzędnych pokazywać już dynamiczną treść w postaci zielonego lub migającego czerwonego adresu IP. Zadanie ustalone, przystępujemy do działania.
Wcześniej zajmowałem się programowaniem w Delphi, PHP, JS i bardzo powierzchownie w C++. Dość dobrze znam działanie sieci. VLAN, routing (OSPF, EIGRP, BGP), NAT. To było wystarczające, aby samodzielnie napisać prototyp prymitywnego monitoringu.
Napisałem to, co zamierzałem, w PHP. Serwer Apache i PHP działał na Windows, ponieważ Linux w tym momencie wydawał mi się czymś nieznanym i bardzo skomplikowanym. Jak się później okazało, bardzo się myliłem i w wielu miejscach Linux jest dużo prostszy od Windows, ale to osobny temat, a wszyscy wiemy, ile jest kontrowersji na ten temat. Harmonogram zadań w Windows uruchamiał co jakiś czas (dokładnie nie pamiętam, ale coś w stylu raz na trzy sekundy) skrypt PHP, który sprawdzał wszystkie obiekty zwykłym pingiem i zapisywał stan do pliku.
system("ping -n 3 -w 100 {$ip_address}");
Tak, tak, w tamtym czasie praca z bazami danych również była dla mnie nowością. Nie wiedziałem, że można równolegle przetwarzać procesy, więc przechodzenie przez wszystkie węzły w sieci zajmowało dużo czasu, ponieważ działało to w jednym wątku. Szczególne problemy pojawiały się, gdy kilka węzłów było niedostępnych, ponieważ każdy z nich opóźniał skrypt o 300 ms. Po stronie klienta była prosta funkcja, która w nieskończoność co kilka sekund pobierała zaktualizowane informacje z serwera za pomocą zapytań Ajax i aktualizowała interfejs. A po trzech nieudanych pingi z rzędu, jeśli na komputerze była otwarta strona z monitoringiem, grała wesoła melodia.
Kiedy wszystko się udało, byłem bardzo zainspirowany rezultatem i myślałem, że można dodać jeszcze więcej (w miarę moich umiejętności i możliwości). Jednak zawsze nie podobały mi się systemy z milionem wykresów, które, jak wtedy uważałem, a i do dziś tak sądzę, w większości przypadków są zbędne. Chciałem dodać tylko to, co naprawdę pomagałoby mi w pracy. Ta zasada pozostaje do dziś fundamentem w rozwoju Veliam. Potem zrozumiałem, że byłoby naprawdę świetnie, gdyby nie trzeba było utrzymywać otwartego monitoringu i wiedzieć o problemach, a gdy już się pojawią, wtedy otworzyć stronę i zobaczyć, gdzie znajduje się problematyczny węzeł sieci i co dalej z nim zrobić. W tamtym czasie jakoś nie czytałem e-maili, po prostu z nich nie korzystałem. Natknąłem się w internecie na to, że istnieją bramy SMS, na które można wysłać zapytanie GET lub POST, a one prześlą mi na telefon SMS z tekstem, który napiszę. Od razu zrozumiałem, że bardzo tego chcę. I zacząłem studiować dokumentację. Po pewnym czasie udało mi się to, i teraz otrzymywałem SMS-y o problemach w sieci na telefon komórkowy z nazwą „awaryjnego obiektu”. Chociaż system był prymitywny, został stworzony przez mnie, a co najważniejsze, co motywowało mnie do jego rozwoju, to fakt, że to aplikacja, która naprawdę pomaga mi w pracy.
I nadeszła chwila, kiedy w pracy jeden z kanałów internetowych przestał działać, a mój monitoring nie dał mi o tym znać. Ponieważ DNS Google nadal świetnie odpowiadały. Nadszedł czas, aby zastanowić się, jak można monitorować, czy kanał jest aktywny. Było wiele pomysłów na to, jak to zrobić. Nie miałem dostępu do całego sprzętu. Musiałem wymyślić, jak zrozumieć, który z kanałów jest aktywny, nie mając możliwości zajrzenia na sam sprzęt sieciowy. Wtedy kolega podsunął mi pomysł, że możliwe, że ślad trasy do publicznych serwerów może się różnić w zależności od tego, przez jaki kanał teraz się łączę z internetem. Sprawdziłem, i rzeczywiście tak było. Były różne trasy podczas śledzenia.
system('tracert -d -w 500 8.8.8.8');
W ten sposób powstał jeszcze jeden skrypt, a dokładniej śledzenie zostało dodane na końcu tego samego skryptu, który pingował wszystkie urządzenia w sieci. To był kolejny długi proces, który wykonywał się w tym samym wątku i spowalniał działanie całego skryptu. Wtedy jednak nie było to tak oczywiste. Tak czy inaczej, robił swoje, w kodzie było sztywno zapisane, jaka miała być trasa dla każdego z kanałów. Tak zaczęła działać system, który już monitorował (głośno powiedziane, ponieważ nie zbierano żadnych metryk, a po prostu pingowano) urządzenia sieciowe (routery, przełączniki, Wi-Fi itp.) oraz kanały komunikacyjne ze światem zewnętrznym. SMS-y przychodziły regularnie, a na schemacie zawsze było dobrze widać, gdzie leżał problem.
Następnie, w codziennej pracy musiałem zajmować się cross-linkowaniem. I za każdym razem wchodziłem na przełączniki Cisco, aby sprawdzić, który interfejs należy użyć, co stało się męczące. Jakby to było świetnie, kliknąć na monitorowaniu obiekt i zobaczyć listę jego interfejsów z opisami. To zaoszczędziłoby mi czas. Ponadto w tym schemacie nie musiałbym uruchamiać Putty ani SecureCRT, wprowadzać poświadczeń i komend. Po prostu kliknąłem w monitorowaniu, zobaczyłem, co trzeba i wróciłem do pracy. Zacząłem szukać, jak można współpracować z przełącznikami. Od razu przyszły mi do głowy dwa rozwiązania: SNMP lub wejść na przełącznik przez SSH, wprowadzić potrzebne komendy i sparsować wynik. Odmówiłem SNMP z powodu trudności w realizacji, nie mogłem się doczekać, aby uzyskać wyniki. Z SNMP spędziłbym dużo czasu, przeszukując MIB, aby na podstawie tych danych sformułować dane o interfejsach. Istnieje wspaniała komenda w CISCO.
show interface statusPokazuje dokładnie to, czego potrzebuję do przeprowadzania cross-checków. Po co męczyć się z SNMP, skoro po prostu chcę widzieć wynik tej komendy, pomyślałem. Po pewnym czasie zrealizowałem taką możliwość. Klikając na obiekcie na stronie internetowej, uruchamiałem zdarzenie, które za pomocą AJAX-a łączyło się z serwerem, a ten z kolei łączył się przez SSH z odpowiednim przełącznikiem (dane uwierzytelniające były zaszyte w kodzie, nie miałem ochoty na ulepszanie, tworzenie jakichkolwiek menu, w których można by zmieniać dane logowania z interfejsu, potrzebny był wynik i jak najszybciej) wprowadzałem tam wyżej wymienioną komendę i wysyłałem z powrotem do przeglądarki. Dzięki temu mogłem jednym kliknięciem myszki zobaczyć informacje o interfejsach. To było niezwykle wygodne, zwłaszcza gdy trzeba było sprawdzać te informacje na różnych przełącznikach jednocześnie.
Monitorowanie kanałów na podstawie trasowania okazało się ostatecznie niezbyt dobrym pomysłem, ponieważ czasami wykonywano prace w sieci, a trasowanie mogło się zmieniać, przez co monitorowanie zaczynało mi krzyczeć, że są problemy z kanałem. Po spędzeniu mnóstwa czasu na analizie zdawałem sobie sprawę, że wszystkie kanały działają, a moje monitorowanie mnie oszukuje. W końcu poprosiłem kolegów, którzy zarządzali przełącznikami tworzącymi kanały, aby po prostu przesyłali mi syslog, kiedy zmieniało się stan widoczności sąsiadów. Oczywiście było to znacznie prostsze, szybsze i prawdziwsze niż trasowanie. Przyjście zdarzenia typu neighbor lost, i od razu robiłem powiadomienie o awarii kanału.
Następnie pojawiły się wyniki po kliknięciu na obiekt, jeszcze kilka komend oraz dodano SNMP do zbierania niektórych metryk, i w zasadzie na tym się skończyło. System w żadnym innym aspekcie się nie rozwijał. Robił wszystko, czego potrzebowałem, był to dobry narzędzie. Wielu czytelników pewnie mi powie, że do rozwiązania tych problemów dostępna jest w internecie masa oprogramowania. Ale tak naprawdę, wtedy nie znalazłem żadnych darmowych takich produktów i bardzo chciałem rozwijać swoje umiejętności programistyczne, a co może lepiej skłonić do tego niż rzeczywiste zadanie praktyczne. Tak pierwsza wersja monitorowania została zakończona i więcej nie była modyfikowana.
Tworzenie firmy Audyt-Telekom
Z biegiem czasu zacząłem dorabiać w innych firmach, na szczęście mój grafik pracy na to pozwalał. Pracując w różnych miejscach, bardzo szybko rozwijasz swoje umiejętności w różnych dziedzinach i wzbogacasz swoje horyzonty. Są firmy, w których, jak się powszechnie mówi, jesteś zarówno krawcem, jak i żniwiarzem, a nawet grajkiem. Z jednej strony to trudne, z drugiej, jeśli się nie lenisz, stajesz się specjalistą o szerokim profilu, co pozwala szybciej i skuteczniej rozwiązywać problemy, ponieważ wiesz, jak działa pokrewna dziedzina.
Mój przyjaciel Pawel (też informatyk) nieustannie namawiał mnie do założenia własnego biznesu. Było mnóstwo pomysłów na różne wersje własnej działalności. Dyskutowaliśmy o tym przez wiele lat. I ostatecznie nic z tego nie wyszło, ponieważ jestem sceptykiem, a Pawel marzycielem. Za każdym razem, gdy proponował jakiś pomysł, zawsze w niego nie wierzyłem i odmawiałem uczestnictwa. Ale bardzo chcieliśmy założyć własny biznes.
W końcu udało nam się znaleźć rozwiązanie, które odpowiadało obu stronom i zająć się tym, co potrafimy. W 2016 roku postanowiliśmy stworzyć firmę IT, która będzie wspierać biznes w rozwiązywaniu zadań informatycznych. Obejmuje to wdrażanie systemów IT (np. 1C, serwer terminali, serwer pocztowy itp.), ich wsparcie, klasyczny HelpDesk dla użytkowników oraz administrację sieci.
Szczerze mówiąc, w momencie zakupu firmy nie wierzyłem w nią w około 99,9%. Ale jakoś Paweł udało się mnie przekonać, żebym spróbował, i w przyszłości okazał się mieć rację. Złożyliśmy się z Pawłem po 300 000 rubli, zarejestrowaliśmy nową spółkę „Audyt-Telekom”, wynajęliśmy maleńkie biuro, zrobiliśmy świetne wizytówki, no cóż, jak to zazwyczaj robią początkujący biznesmeni, i zaczęliśmy szukać klientów. Szukanie klientów to zupełnie inna historia. Może napiszemy osobny artykuł w ramach firmowego bloga, jeśli kogoś to zainteresuje. Zimne telefony, ulotki i tym podobne. Nie dawało to żadnych rezultatów. Jak już teraz widzę w wiele historii o biznesie, dużo zależy od szczęścia. Mieliśmy szczęście. I dosłownie po kilku tygodniach od założenia firmy, zwrócił się do nas mój brat Włodzimierz, który doprowadził do nas pierwszego klienta. Nie będę męczyć szczegółami współpracy z klientami, artykuł nie jest o tym, tylko powiem, że pojechaliśmy na audyt, zidentyfikowaliśmy krytyczne miejsca, a te miejsca zepsuły się, gdy podejmowano decyzję, czy współpracować z nami na stałe jako outsourcerami. Po tym została podjęta pozytywna decyzja.
Następnie, głównie poprzez networking, zaczęli pojawiać się inni klienci. Helpdesk był w jednym systemie. Połączenia z urządzeniami sieciowymi i serwerami w innym, a właściwie jak kto miał. Niektórzy przechowywali skróty, inni używali książek adresowych RDP. Monitoring to kolejny osobny system. Praca zespołu w rozproszonych systemach jest bardzo niewygodna. Ważne informacje mogą umknąć. Na przykład, jeśli serwer terminalowy u klienta staje się niedostępny, od razu dostajemy zgłoszenia od użytkowników tego klienta. Specjalista wsparcia technicznego tworzy zgłoszenie (wpłynęło telefonicznie). Gdyby incydenty i zgłoszenia były rejestrowane w jednym systemie, specjalista wsparcia od razu widziałby, w czym leży problem użytkownika i mógłby mu to przekazać, równocześnie łącząc się z odpowiednim obiektem w celu rozwiązania sytuacji. Wszyscy są na bieżąco z sytuacją i pracują sprawnie. Nie znaleźliśmy takiego systemu, w którym wszystko to byłoby zintegrowane. Stało się jasne, że pora robić swój produkt.
Kontynuacja pracy nad własnym systemem monitorowania
Było jasne, że system napisany wcześniej w ogóle nie pasował do obecnych zadań. Ani pod względem funkcjonalności, ani jakości. Postanowiono zbudować system od nowa. Graficznie powinien wyglądać zupełnie inaczej. Powinna to być hierarchiczna struktura, aby można było szybko i wygodnie otworzyć potrzebny obiekt u odpowiedniego klienta. Schemat z pierwszej wersji w tym przypadku był całkowicie nieuzasadniony, ponieważ klienci są różni i nie miało znaczenia, w jakich pomieszczeniach znajduje się sprzęt. To już zostało przeniesione do dokumentacji.
A więc zadania:
- Struktura hierarchiczna;
- Jakakolwiek część serwerowa, którą można umieścić u klienta w formie wirtualnej maszyny do zbierania potrzebnych nam metryk i przesyłania ich na centralny serwer, który wszystkie te dane podsumuje i wyświetli dla nas;
- Powiadomienia. Takie, których nie można przegapić, ponieważ w tamtym czasie nie było możliwości, aby ktoś siedział i tylko obserwował monitor;
- System zgłoszeń. Zaczęli pojawiać się klienci, dla których obsługiwaliśmy nie tylko sprzęt serwerowy i sieciowy, ale również stacje robocze;
- Możliwość szybkiego łączenia się z serwerami i sprzętem z systemu;
Zadania wytyczone, zaczynamy pisać. Równocześnie przetwarzając zgłoszenia od klientów. W tym momencie było nas już cztery osoby. Zaczęliśmy pisać od razu obie części, zarówno centralny serwer, jak i serwer do instalacji u klientów. W tym momencie Linux nie był nam obcy i podjęto decyzję, że wirtualne maszyny, które będą stały u klientów, będą na Debianie. Nie będzie żadnych instalatorów, po prostu zrealizujemy projekt części serwerowej na jednej konkretnej wirtualnej maszynie, a następnie po prostu sklonujemy ją do potrzebnego klienta. To była kolejna pomyłka. Później stało się jasne, że w takim schemacie mechanizm aktualizacji nie był w ogóle przemyślany. To znaczy dodawaliśmy jakąś nową funkcję, a potem pojawiał się poważny problem z jej rozpowszechnieniem na wszystkich serwerach klientów, ale do tego wrócimy później, wszystko w swoim czasie.
Stworzyliśmy pierwszy prototyp. Potrafił pingować niezbędne nam urządzenia sieciowe klientów i serwery, a także wysyłać te dane na nasz centralny serwer. A ten z kolei aktualizował te dane w zbiorze na centralnym serwerze. Będę tutaj pisać nie tylko historię o tym, co się udało, ale także o tym, jakie amatorskie błędy zostały popełnione i jak później trzeba było za to płacić czasem. Tak więc, całe drzewo obiektów było przechowywane w jednym pliku w postaci zserializowanego obiektu. Dopóki podłączyliśmy do systemu kilku klientów, wszystko było jakoś w porządku, chociaż czasami pojawiały się jakieś artefakty, które były absolutnie niezrozumiałe. Ale kiedy podłączyliśmy do systemu dziesięć serwerów, zaczęły dziać się cuda. Czasami, z nieznanego powodu, wszystkie obiekty w systemie po prostu znikały. Ważne jest, aby zauważyć, że serwery, które miały klienci, wysyłały dane na centralny serwer co kilka sekund za pomocą żądania POST. Uważny czytelnik i doświadczony programista już domyślił się, że pojawił się problem z wieloma dostępami do tego samego pliku, w którym przechowywano zserializowany obiekt z różnych wątków jednocześnie. I właśnie wtedy, gdy to się działo, pojawiały się cuda z znikającymi obiektami. Plik po prostu stawał się pusty. Ale to wszystko zostało odkryte nie od razu, a dopiero w trakcie eksploatacji z kilkoma serwerami. W międzyczasie dodano funkcjonalność skanowania portów (serwery wysyłały do centralnego nie tylko informacje o dostępności urządzeń, ale także o otwartych na nich portach). Zrobiono to poprzez wywołanie polecenia:
$connection = @fsockopen($ip, $port, $errno, $errstr, 0.5);
wyniki często były nieprawidłowe, a skanowanie trwało bardzo długo. Całkowicie zapomniałem o pingowaniu, które wykonywane było za pomocą fping:
system("fping -r 3 -t 100 {$this->ip}");
To wszystko też nie było zrównoleglone, więc proces był bardzo długi. Później w fping przekazywano od razu całą listę niezbędnych do sprawdzenia adresów IP i z powrotem otrzymywaliśmy gotową listę tych, którzy odpowiadają. W przeciwieństwie do nas, fping potrafił równolegle przetwarzać procesy.
Inna częstą rutynową pracą była konfiguracja różnych usług przez WEB. Na przykład ECP od MS Exchange. W zasadzie to tylko link. Postanowiliśmy, że musimy umożliwić dodawanie takich linków bezpośrednio w systemie, aby nie szukać w dokumentacji czy gdzie indziej jak zalogować się na ECP konkretnego klienta. Tak powstała koncepcja linków zasobów w systemie, ich funkcjonalność jest dostępna do dziś i nie uległa zmianom, prawie.
Działanie linków zasobów w Veliam

Zdalne połączenia
Tak to wygląda w praktyce w obecnej wersji Veliam

Jednym z zadań było szybkie i wygodne łączenie z serwerami, których już jest wiele (niejedna setka), a przeszukiwanie milionów wcześniej zapisanych skrótów RDP było niezwykle niewygodne. Potrzebne było narzędzie. Istnieje w internecie oprogramowanie, które pełni funkcję swoistego rodzaju książki adresowej dla takich połączeń RDP, ale nie jest zintegrowane z systemem monitorowania, i nie można zapisać danych logowania. Za każdym razem wprowadzanie danych logowania dla różnych klientów to istny koszmar, kiedy w ciągu dnia łączysz się dziesiątki razy z różnymi serwerami. W przypadku SSH sytuacja jest nieco lepsza, istnieje wiele dobrego oprogramowania, które umożliwia organizację takich połączeń w foldery oraz zapamiętywanie danych logowania. Ale są dwie problemy. Po pierwsze — dla połączeń RDP i SSH nie znaleźliśmy jednego programu. Po drugie — jeśli w danym momencie nie jestem przy swoim komputerze i muszę szybko się połączyć, lub po prostu zainstalowałem system na nowo, muszę przeszukiwać dokumentację, aby znaleźć dane logowania tego klienta. To niewygodne i strata czasu.
Potrzebna nam hierarchiczna struktura dla serwerów klientów już istniała w naszym wewnętrznym produkcie. Tylko trzeba było wymyślić, jak dodać szybkie połączenia do potrzebnego sprzętu. Na początek, chociażby w ramach własnej sieci.
Biorąc pod uwagę, że klientem w naszym systemie był przeglądarka, która nie miała dostępu do lokalnych zasobów komputera, aby po prostu wziąć i jakąś komendą uruchomić potrzebną nam aplikację, wymyślono, by wszystko zrealizować za pomocą "Windows custom url scheme". Tak powstał pewien "plugin" do naszego systemu, który po prostu zawierał w sobie Putty i Remote Desktop Plus, a podczas instalacji rejestrował schemat URI w systemie Windows. Teraz, gdy chcieliśmy połączyć się z obiektem przez RDP lub SSH, klikaliśmy tę akcję w naszym systemie i uruchamiała się praca Custom URI. Uruchamiał się standardowy mstsc.exe wbudowany w Windows lub putty, który znajdował się w składzie "pluginu". Używam słowa plugin w cudzysłowie, ponieważ nie jest to typowy plugin przeglądarki.
To było już coś. Wygodna książka adresowa. A jeśli chodzi o Putty, wszystko było naprawdę dobrze, ponieważ jako parametry wejściowe można było podać zarówno IP połączenia, jak i loginy oraz hasła. Tzn. do serwerów Linux w naszej sieci podłączaliśmy się jednym kliknięciem bez wprowadzania haseł. Jednak w przypadku RDP nie było to takie proste. W standardowym mstsc nie można podać danych logowania jako parametrów. Na pomoc przyszedł Remote Desktop Plus. Umożliwiał to. Teraz już się bez niego obejdziemy, ale przez długi czas był naszym wiernym pomocnikiem w systemie. Z witrynami HTTP(S) wszystko było proste, takie obiekty po prostu otwierały się w przeglądarce i wszystko. Wygodne i praktyczne. Ale to szczęście istniało tylko w sieci wewnętrznej.
Ponieważ zdecydowana większość problemów była rozwiązywana zdalnie z biura, najprostszym rozwiązaniem była konfiguracja VPN do klientów. I wtedy z naszego systemu można było się z nimi łączyć. Ale wciąż było to dość niewygodne. Dla każdego klienta trzeba było trzymać na każdym komputerze masę zapamiętanych VPN połączeń i przed połączeniem z jakimkolwiek, trzeba było włączyć odpowiedni VPN. Z tego rozwiązania korzystaliśmy przez dość długi czas. Ale liczba klientów rosła, liczba VPN-ów również, co zaczęło być uciążliwe i trzeba było coś zrobić z tym. Szczególnie pojawiały się łzy w oczach po reinstalacji systemu, gdy trzeba było na nowo wprowadzać dziesiątki połączeń VPN w nowym profilu Windows. Dość tego znoszenia, pomyślałem i zacząłem zastanawiać się, co można z tym zrobić.
Powszechnie przyjęło się, że wszyscy klienci używają routerów znanej marki Mikrotik. Są one bardzo funkcjonalne i wygodne do wykonywania praktycznie wszelkich zadań. Do minusów należy to, że są „kradzione”. Rozwiązaliśmy ten problem w prosty sposób, zamykając wszelkie dostępne połączenia z zewnątrz. Jednak trzeba było jakoś uzyskać do nich dostęp, nie odwiedzając klienta na miejscu, ponieważ to trwa zbyt długo. Po prostu zbudowaliśmy tunel do każdego Mikrotika i wydzieliliśmy je w osobny pul, bez jakiejkolwiek routingu, aby nie doszło do połączenia naszej sieci z sieciami klientów oraz ich sieci między sobą.
Zrodził się pomysł, aby przy naciśnięciu na odpowiedni obiekt w systemie centralny serwer monitoringu, znając dane logowania SSH wszystkich klientowskich Mikrotików, łączył się z wybranym z nich, tworząc regułę przekierowania do odpowiedniego hosta z odpowiednim portem. Tutaj pojawia się kilka kwestii. Rozwiązanie nie jest uniwersalne — będzie działać tylko dla Mikrotika, ponieważ składnia poleceń różni się w przypadku każdego routera. Ponadto te przekierowania musiały być jakoś usuwane, a serwerowa część naszego systemu właściwie nie miała możliwości monitorowania, czy zakończyłem swoją sesję pracy przez RDP. Przekierowanie to również stwarzało lukę w bezpieczeństwie dla klienta. Nikogo nie interesowało uniwersalność, ponieważ produkt był używany tylko wewnątrz naszej firmy i nie mieliśmy zamiaru wypuszczać go do sieci publicznej.
Każdy problem został rozwiązany na swój sposób. Kiedy tworzono regułę, przekierowanie to było dostępne tylko dla jednego konkretnego zewnętrznego adresu IP (z którego zainicjowano połączenie). Dzięki temu udało się uniknąć luk w bezpieczeństwie. Jednak przy każdym takim połączeniu dodawana była reguła na Mikrotiku na stronę NAT, która nie była usuwana. Jak wszyscy wiedzą, im więcej tam reguł, tym bardziej obciążony jest procesor routera. Ogólnie rzecz biorąc, nie mogłem zaakceptować, że pewnego dnia wejdę na jakiś Mikrotik, a tam setki martwych, nikomu niepotrzebnych reguł.
Ponieważ nasz serwer nie może śledzić stanu połączenia, niech Mikrotik śledzi je sam. Napisałem skrypt, który ciągle monitorował wszystkie reguły przekierowania z określonym opisem (description) i sprawdzał, czy istnieje odpowiednie połączenie TCP dla reguły. Jeśli nie było go przez jakiś czas, to prawdopodobnie połączenie już zostało zakończone i można usunąć to przekierowanie. Wszystko działało dobrze, skrypt sprawnie funkcjonował.
A oto on:
global atmonrulecounter {"dontDelete"="dontDelete"}
:foreach i in=[/ip firewall nat find comment~"atmon_script_main"] do={
local dstport [/ip firewall nat get value-name="dst-port" $i]
local dstaddress [/ip firewall nat get value-name="dst-address" $i]
local dstaddrport "$dstaddress:$dstport"
#log warning message=$dstaddrport
local thereIsCon [/ip firewall connection find dst-address~"$dstaddrport"]
if ($thereIsCon = "") do={
set ($atmonrulecounter->dstport) ($atmonrulecounter->dstport + 1)
#:log warning message=($atmonrulecounter->dstport)
if (($atmonrulecounter->dstport) > 5) do={
#log warning message="Usuwanie reguł nat dodanych automatycznie przez atmon_script"
/ip firewall nat remove [/ip firewall nat find comment~"atmon_script_main_$dstport"]
/ip firewall nat remove [/ip firewall nat find comment~"atmon_script_sub_$dstport"]
set ($atmonrulecounter->dstport) 0
}
} else {
set ($atmonrulecounter->dstport) 0
}
}
Z pewnością można było to zrobić ładniej, szybciej itd., ale działało, nie obciążało Mikrotików i doskonale się sprawdzało. W końcu mogliśmy łączyć się z serwerami i sprzętem sieciowym klientów jednym kliknięciem myszki. Bez podnoszenia VPN i wpisywania haseł. Praca z systemem stała się naprawdę bardzo wygodna. Czas na konserwację się skrócił, a wszyscy poświęcaliśmy czas na pracę, a nie na łączenie się z odpowiednimi obiektami.
Kopia zapasowa Mikrotik
Mieliśmy skonfigurowane kopie zapasowe wszystkich Mikrotików na FTP. Ogólnie wszystko było w porządku. Ale gdy trzeba było wyciągnąć kopię zapasową, musieliśmy otworzyć ten FTP i szukać tam. Mamy system, w którym zarejestrowane są wszystkie routery, umiemy komunikować się z urządzeniami przez SSH. Czemu by nie zrobić, aby system codziennie samodzielnie pobierał kopie zapasowe ze wszystkich Mikrotików, pomyślałem. I wziąłem się za realizację. Połączyliśmy się, zrobiliśmy kopię zapasową i zabraliśmy ją do przechowalni.
Kod skryptu w PHP do pobierania kopii zapasowej z Mikrotika:
<?php
$IP = '0.0.0.0';
$LOGIN = 'admin';
$PASSWORD = '';
$BACKUP_NAME = 'test';
$connection = ssh2_connect($IP, 22);
if (!ssh2_auth_password($connection, $LOGIN, $PASSWORD)) exit;
ssh2_exec($connection, '\/system backup save name="atmon" password="atmon"');
stream_get_contents($connection);
ssh2_exec($connection, '\/export file="atmon.rsc"');
stream_get_contents($connection);
sleep(40);
$sftp = ssh2_sftp($connection);
\/\/ Download backup file
$size = filesize("ssh2.sftp:\/$sftp\/atmon.backup");
$stream = fopen("ssh2.sftp:\/$sftp\/atmon.backup", 'r');
$contents = '';
$read = 0;
$len = $size;
while ($read < $len && ($buf = fread($stream, $len - $read))) {
$read += strlen($buf);
$contents .= $buf;
}
file_put_contents ($BACKUP_NAME . ‘.backup’,$contents);
@fclose($stream);
sleep(3);
\/\/ Download RSC file
$size = filesize("ssh2.sftp:\/$sftp\/atmon.rsc");
$stream = fopen("ssh2.sftp:\/$sftp\/atmon.rsc", 'r');
$contents = '';
$read = 0;
$len = $size;
while ($read
Kopia zapasowa jest tworzona w dwóch formatach — binarnym i tekstowym. Format binarny ułatwia szybkie przywracanie potrzebnej konfiguracji, podczas gdy format tekstowy pozwala zrozumieć, co należy zrobić w przypadku wymuszonej wymiany sprzętu, kiedy nie można użyć wersji binarnej. Ostatecznie uzyskaliśmy jeszcze jedną wygodną funkcjonalność w systemie. Co więcej, przy dodawaniu nowych mikrotików nie trzeba było nic konfigurować, wystarczyło dodać obiekt do systemu i ustawić dla niego konto SSH. System samodzielnie zajmował się tworzeniem kopii zapasowych. W bieżącej wersji SaaS Veliam ta funkcjonalność nie jest jeszcze dostępna, ale wkrótce ją przeniesiemy.
Zrzuty ekranu, jak to wyglądało w wewnętrznym systemie

Przejście na normalne przechowywanie w bazie danych
Wcześniej pisałem o pojawiających się artefaktach. Czasami znikała cała lista obiektów w systemie, czasami podczas edytowania obiektu informacje nie były zapisywane i trzeba było trzykrotnie zmieniać nazwę obiektu. To było strasznie irytujące dla wszystkich. Znikanie obiektów zdarzało się rzadko i łatwo je było przywrócić przez odtworzenie tego samego pliku, ale awarie podczas edytowania obiektów zdarzały się znacznie częściej. Prawdopodobnie początkowo nie zrobiłem tego przez bazę danych, ponieważ nie mogłem sobie wyobrazić, jak można trzymać drzewo ze wszystkimi powiązaniami w płaskiej tabeli. Tabela jest płaska, a drzewo hierarchiczne. Ale dobre rozwiązanie dla dostępu równoczesnego, a później (przy komplikowaniu systemu) także dla transakcyjnego — to system zarządzania bazą danych. Na pewno nie jestem pierwszym, kto natknął się na ten problem. Zaczęło mnie to interesować i odkryłem, że wszystko zostało już wymyślone przede mną, a istnieje kilka algorytmów, które budują drzewo z płaskiej tabeli. Po przyjrzeniu się każdemu z nich, zrealizowałem jeden z nich. Ale to była już nowa wersja systemu, ponieważ w zasadzie musiałem przepisać stosunkowo dużo. Wynik był oczywisty, problemy z losowym zachowaniem systemu zniknęły. Ktoś może powiedzieć, że błędy były dość amatorskie (jednopoziomowe skrypty, przechowywanie informacji, do których istniał równoczesny dostęp z różnych wątków w pliku itd.) w dziedzinie rozwoju oprogramowania. Może tak i jest, ale moją główną pracą było administrowanie, a programowanie było dodatkiem do mojej pasji, i po prostu nie miałem doświadczenia w pracy w zespole programistycznym, gdzie takie elementarne rzeczy byłyby mi od razu sugerowane przez starszych kolegów. Dlatego wszystkie te błędy musiałem popełniać samodzielnie, ale bardzo dobrze przyswoiłem materiał. A zresztą, moja praca to także spotkania z klientami, działania mające na celu promowanie firmy, mnóstwo spraw administracyjnych wewnątrz firmy i wiele, wiele innych. Tak czy inaczej, to co było, było potrzebne. Chłopaki i ja sami używaliśmy produktu w codziennej pracy. Były i zupełnie nieudane pomysły oraz rozwiązania, na które poświęcono czas, a w końcu okazało się, że to nie działa i nikt tego nie używał oraz nie trafiło to do Veliam.
Wsparcie techniczne — HelpDesk
Nie zaszkodzi wspomnieć, jak powstawał HelpDesk. To zresztą zupełnie osobna historia, ponieważ w Veliam mamy już trzecią, całkowicie nową wersję, która różni się od wszystkich poprzednich. Obecnie to prosta system, intuicyjny, bez zbędnych ozdobników i bajerów, umożliwiający integrację z domeną, a także dostęp do tego samego profilu użytkownika z dowolnego miejsca za pomocą linku w wiadomości e-mail. I co najważniejsze, istnieje możliwość połączenia się z wnioskodawcą za pomocą VNC bezpośrednio z zgłoszenia, bez VPN lub przekazywania portów. Opowiem, jak do tego doszliśmy, co było wcześniej i jakie straszne decyzje były podejmowane.
Podłączaliśmy się do użytkowników za pomocą dobrze znanego TeamViewer. Na wszystkich komputerach użytkowników, którymi się zajmujemy, zainstalowany był TV. Pierwszą rzeczą, którą zrobiliśmy źle, i którą później usunęliśmy, było powiązanie każdego klienta z HWID sprzętu. Jak użytkownik wchodził do systemu HW, aby złożyć zgłoszenie? Na komputerach, oprócz TV, zainstalowane było specjalne narzędzie napisane w Lazarusie (można tu podnieść brwi, a może nawet zacząć googlować, co to jest, ale z wszystkich kompilowalnych języków znałem Delphi, a Lazarus to prawie to samo, tylko darmowe). Generalnie użytkownik uruchamiał u siebie specjalny skrypt, który uruchamiał to narzędzie, a ono odczytywało HWID systemu, po czym uruchamiała się przeglądarka i następowało logowanie. Po co to wszystko? W niektórych firmach liczba obsługiwanych użytkowników jest liczona indywidualnie, a cena za miesięczną obsługę jest ustalana na podstawie liczby osób. To zrozumiałe, powiesz, ale po co powiązanie z hardwarem. To proste, niektórzy użytkownicy wracali do domu i składali zgłoszenie z domowego laptopa w stylu "zróbcie mi tutaj wszystko ładnie". Oprócz odczytywania HWID systemu, narzędzie pobierało z rejestru aktualny ID TeamViewer i również przekazywało go do nas. TeamViewer ma API do integracji. I zrobiliśmy tę integrację. Ale był jeden haczyk. Przez to API nie można połączyć się z komputerem użytkownika, gdy on sam nie inicjuje tej sesji, a po próbie połączenia musi jeszcze nacisnąć "potwierdź". W tamtym czasie wydało nam się logiczne, że bez zgody użytkownika nikt nie powinien się łączyć, a skoro człowiek jest przy komputerze, to on inicjuje sesję i pozytywnie odpowie na prośbę o połączenie zdalne. Okazało się, że nie jest to takie proste. Wnioskodawcy zapominali nacisnąć inicjację sesji, i trzeba było im o tym mówić w rozmowie telefonicznej. To zajmowało czas i denerwowało obie strony procesu. Co więcej, nie jest rzadkością, że osoba zostawia zgłoszenie, ale zezwala na połączenie tylko wtedy, gdy wychodzi na lunch. Ponieważ problem nie jest krytyczny i nie chce, aby jego praca była przerywana. W związku z tym nie nacisną żadnych przycisków, aby zezwolić na połączenie. I tak pojawiła się dodatkowa funkcjonalność przy logowaniu do HelpDesk — odczyt ID TeamViewer'a. Znaliśmy stałe hasło, które było używane przy instalacji TeamViewer'a. Właściwie, znał je tylko system, ponieważ było osadzone w instalatorze oraz w naszym systemie. W związku z tym istniał przycisk łączenia z prośby, po naciśnięciu którego nie trzeba było nic czekać, a od razu otwierał się TeamViewer i dochodziło do połączenia. Ostatecznie powstały dwa rodzaje możliwych połączeń. Przez oficjalne API TeamViewer oraz nasz własny. Ku mojemu zdziwieniu, z pierwszego prawie natychmiast przestano korzystać, chociaż istniały zalecenia, aby korzystać z niego tylko w szczególnych przypadkach i gdy użytkownik sam na to się zgadza. W końcu bezpieczeństwo jest obecnie priorytetem. Ale okazało się, że wnioskodawcom to nie jest potrzebne. Wszyscy zdecydowanie nie mają nic przeciwko, że łączy się z nimi bez potwierdzenia. A skoro tak, to dalsza funkcjonalność łączenia przez API została zlikwidowana z braku potrzeby.
Przejście na wielowątkowość w Linuxie
Od dłuższego czasu nasuwa się pytanie o przyspieszenie skanowania sieci pod kątem otwartości wcześniej określonej listy portów oraz prosty ping obiektów sieciowych. Tutaj nasuwa się pierwsze rozwiązanie, które przychodzi na myśl — wielowątkowość. Ponieważ główny czas, który poświęcamy na pingowanie, to oczekiwanie na zwrot pakietu, a następny ping nie może się rozpocząć, dopóki nie wróci poprzedni pakiet, w firmach, które posiadają nawet 20+ serwerów oraz urządzeń sieciowych, działało to już dość wolno. Istota sprawy polega na tym, że jeden pakiet może zniknąć, a nie ma sensu od razu informować o tym administratora systemu. Po prostu w końcu przestanie on traktować taki spam na poważnie. Oznacza to, że każdy obiekt należy pingować nie raz, zanim wyciągniemy wnioski o niedostępności. Jeśli nie będziemy zagłębiać się w szczegóły, trzeba to zrównoleglić, ponieważ jeśli tego nie zrobimy, istnieje duże prawdopodobieństwo, że administrator systemu usłyszy o problemie od klienta, a nie od systemu monitorowania.
PHP sam w sobie nie obsługuje wielowątkowości. Obsługuje wieloprocesowość, można używać forków. Ale już miałem napisany mechanizm ankiety i chciałem, aby wszystkie potrzebne mi węzły z bazy danych były odczytywane za jednym razem, aby móc je wszystkie pingować jednocześnie, czekać na odpowiedź od każdego z nich i dopiero wtedy zapisywać dane. To oszczędza na liczbie żądań odczytu. W ten pomysł świetnie wpisywała się wielowątkowość. Dla PHP istnieje moduł PThreads, który pozwala na prawdziwą wielowątkowość, chociaż musiałem mocno się napracować, aby to skonfigurować na PHP 7.2, ale udało się. Skanowanie portów i ping stały się szybkie. I zamiast przykładowo 15 sekund na jedno przejście, ten proces zajmował teraz 2 sekundy. To był dobry wynik.
Szybki audyt nowych firm
Jak powstała funkcjonalność zbierania różnych metryk i charakterystyk sprzętu? To proste. Czasami zamawiają u nas po prostu audyt obecnej infrastruktury IT. To samo jest potrzebne, aby przyspieszyć audyt nowego klienta. Potrzebna była opcja, która umożliwiłaby nam przyjście do średniej lub dużej firmy i szybkie zorientowanie się, co w ogóle mają. Ping w sieci wewnętrznej blokują, moim zdaniem, tylko ci, którzy sami chcą sobie utrudnić życie, a takich zgodnie z naszym doświadczeniem jest niewielu. Ale tacy się zdarzają. Dlatego możemy szybko przeskanować sieci w poszukiwaniu urządzeń za pomocą prostego pinga. Następnie można je dodać i przeskanować pod kątem otwartych portów, które nas interesują. W zasadzie ta funkcjonalność już istniała, trzeba było tylko dodać komendę z serwera centralnego do podrzędnego, aby ten przeskanował określone sieci i dodał do listy wszystko, co znajdzie. Zapomniałem wspomnieć, zakładano, że mamy już gotowy obraz z skonfigurowanym systemem (podrzędny serwer monitorowania), który mogliśmy po prostu zainstalować u klienta podczas audytu i połączyć go z naszym chmurą.
Jednak wynik audytu zwykle zawiera wiele różnych informacji, a jedna z nich to to, jakie urządzenia znajdują się w sieci. W pierwszej kolejności interesowały nas serwery Windows i stacje robocze Windows w obrębie domeny. W średnich i dużych firmach brak domeny to chyba wyjątek od reguły. Żeby mówić jednym głosem, średnia w moim rozumieniu to 100+ osób. Musieliśmy znaleźć sposób na zebranie danych ze wszystkich maszyn z systemem Windows i serwerów, znając ich adresy IP oraz konto administratora domeny, ale nie instalując na każdej z nich żądnego oprogramowania. Na pomoc przyszło nam WMI. Windows Management Instrumentation (WMI) w dosłownym tłumaczeniu to narzędzie zarządzania Windows. WMI to jedna z podstawowych technologii do centralnego zarządzania i monitorowania różnych części infrastruktury komputerowej działającej na platformie Windows. Zostało to wzięte z Wikipedii. Następnie musiałem zająć się zebranie wmic (to klient WMI) dla Debiana. Po tym, jak wszystko było gotowe, wystarczyło tylko zapytać przez wmic odpowiednie węzły o potrzebne informacje. Dzięki WMI można uzyskać prawie każdą informację z komputera z systemem Windows, a co więcej, za jego pomocą można również zarządzać komputerem, na przykład wysyłając go do ponownego uruchomienia. Tak powstało zbieranie informacji o stacjach roboczych i serwerach Windows w naszym systemie. Dodatkowo zbieraliśmy bieżące informacje o aktualnych wskaźnikach obciążenia systemu. Te pytaliśmy częściej, a informacje o sprzęcie rzadziej. Dzięki temu przeprowadzanie audytu stało się trochę przyjemniejsze.
Decyzja o dystrybucji oprogramowania
Sami codziennie korzystamy z systemu, a on zawsze jest dostępny dla każdego pracownika technicznego. Pomyśleliśmy, że możemy podzielić się tym, co już mamy, z innymi. System był jeszcze zupełnie niegotowy do dystrybucji. Należało przerobić bardzo wiele, aby lokalna wersja przekształciła się w SaaS. Dotyczyło to zmian w zakresie różnych aspektów technicznych działania systemu (połączenia zdalne, obsługa klienta), analizy modułów pod kątem licencjonowania, shardingu baz danych klientów, skalowania każdego z serwisów oraz opracowania systemów automatycznych aktualizacji dla wszystkich części. Ale o tym będzie w drugiej części artykułu.
Update
Źródło: habr.com
