pl  |  en

Umarł agent, niech żyje agent

Przez pięć lat praktycznie całym środowiskiem hostingowym MegiTeam zarządzał niepozorny skrypt w Perlu, zwany agentem. Zakładał konta, uruchamiał aplikacje, pilnował przydzielonej pamięci itp. Radził sobie nienajgorzej, ale z biegiem czasu wychodziły na wierzch różne braki, a stara architektura nie pozwalała nam na szybkie rozwijanie jego kodu. Dlatego od pewnego czasu pracowaliśmy nad nowym narzędziem, które będzie się lepiej dostosowywać do zmieniających się Waszych i naszych potrzeb. Tada, oto nowy agent.

mtagent-architektura

Zmiany w architekturze systemu obejmują wszystkie warstwy, od interfejsu WWW, aż po najgłębsze zakamarki konfiguracji systemu operacyjnego. Jednym z podstawowych problemów starego agenta było to, że większość operacji wykonywała się asynchronicznie, a panel nie mógł łatwo poznać ich wyniku. Nowy agent korzysta w pełni z MTRPC i wszystkie wywołania są synchroniczne i od razu zwracają informację o wyniku i ewentualnych błędach. Ma to też swoje minusy — niektóre operacje wymagają nieco więcej czasu, a nie chcemy Was zmuszać do patrzenia na kręcące się kółko w przeglądarce. Dlatego komunikacją z agentem zajmuje się serwer celery. I tu pojawia się kolejny problem: musimy zachować spójność bazy danych z rzeczywistą konfiguracją w systemie. Braliśmy pod uwagę różne rozwiązania, ale najpewniejsze okazało się użycie względnie mało znanej możliwości postgresa — dwufazowych transakcji. W skrócie to interfejs WWW wykonuje zmiany w bazie, ale dopiero zadanie celery je ostatecznie zatwierdza (jeżeli wywołanie MTRPC się udało) lub odrzuca (w razie błędu). Początkowo wydawało się, że integracja dwufazowych transakcji z Django będzie większym wyzwaniem (sądząc po wynikach z Google’a, przecieraliśmy nowe szlaki), ale natknęliśmy się tylko na dwie pułapki. Po pierwsze, dla zapytań COMMIT PREPARED/ROLLBACK PREPARED musieliśmy otworzyć dedykowane połączenie do bazy, bo nie można ich wywołać z otwartej transakcji. Po drugie, trochę pracy zajęło nam zintegrowanie ich z johnny-cache tak, żeby jego cache był spójny z bazą danych niezależnie od losów transakcji. Po takim przygotowaniu gruntu, wywołanie funkcji przez celery z zachowaniem spójnej bazy danych jest już bardzo proste.

Drugą kluczową zmianą w architekturze jest rozdzielenie menedżera procesów, który pilnuje, żeby wszystkie aplikacje działały, od zarządzania konfiguracją (serwera WWW, pocztowego itp.). Dzięki temu możemy dużo szybciej dodawać nowe funkcje i bezproblemowo je wdrażać. Wcześniej wymagało to restartu aplikacji, czego unikaliśmy jak ognia. Do pilnowania procesów planowaliśmy użyć supervisora, ale nie sprawdził się w testach ekstremalnych (pojawiały się wycieki pamięci). Mimo że nie pracowałby normalnie pod takim obciążeniem (kilkadziesiąt jednoczesnych startów, zatrzymań, restartów i awarii aplikacji, a to wszystko podczas ciągłego przeładowywania konfiguracji), zdecydowaliśmy się napisać własne narzędzie — mtproc. Jest ono w całości napisane w C i zużywa minimalne ilości zasobów, a w miarę możliwości korzysta z gotowych rozwiązań — opiera się na libev, ZeroMQ i XDR. Resztę zadań dotychczasowego agenta przejmuje nowy, napisany od zera w Pythonie. Dzięki temu, że nie nadzoruje on działających aplikacji, możemy go bez problemów aktualizować i dużo szybciej rozwijać i sprawdzać w boju nowe pomysły.

appctl

Razem z nowym agentem ma premierę nowy skrypt do obsługi aplikacji. Nazywa się appctl i od starego restart-app różni się paroma istotnymi szczegółami.

Po pierwsze: jest kolorowy 🙂

appctl list

Po drugie, wie o wiele więcej o stanie aplikacji: pokazuje, czy aplikacje działają i jak długo, co widać na powyższym screenie (z aplikacjami newapp i newapp2 jest jakiś problem, pozostałe działają poprawnie). Dodatkowo wie, gdzie aplikacje (i serwer WWW) zapisują logi. Na zwykłej liście pokazuje tylko ścieżkę do pliku z logami aplikacji, ale można poprosić o bardziej szczegółową listę:

appctl logs

Do każdego z tych plików można się dostać wywołując appctl z parametrem podanym na liście. Przykładowo, listę żądań do serwera WWW dla tej aplikacji można obejrzeć poleceniem: appctl log:access kota. Listę logów wszystkich aplikacji można zobaczyć po wywołaniu appctl logs bez dodatkowych parametrów.

Restart aplikacji również jest odrobinę mądrzejszy:

appctl restart

Po pierwsze, jeżeli aplikacja ma kilka procesów, to są one restartowane kolejno, nie jednocześnie. Po drugie, appctl restart od razu pokazuje stan nowo uruchomionej aplikacji. Trzeba mieć tylko na uwadze, że domyślne 3 sekundy to zbyt mało, żeby uznać, że aplikacja napisana w Rails uruchomiła się poprawnie, więc w ich przypadku najlepiej sprawdzić stan przez appctl list.

Żeby zobaczyć stan wybranej aplikacji lub ją zrestartować, trzeba ją jakoś nazwać. Dopuszczamy użycie dowolnego fragmentu nazwy (bez rozróżniania dużych/małych liter), co ogólnie jest wygodne, ale może prowadzić do niejednoznacznych wyników. Np. jak zrestartować aplikację newapp bez restartu newapp2? Dla takich przypadków każda aplikacja ma swój unikalny identyfikator, który się nie zmieni, niezależnie od zmiany konfiguracji, przypisanych domen itp. i jest wyświetlany przez appctl list. W tym przypadku potrzebujemy wywołania appctl restart 0000319dc12a3a5b. Takie wywołanie można spokojnie umieścić np. w skryptach wdrażających nową wersję aplikacji.

Autor: Grzegorz Nosek

  • Robert

    to się spisałeś na urodziny 🙂

  • Czy to już działa na hostingach współdzielonych?

    • magdazarych

      Tylko na części – aktualizujemy po trochę, żeby jeszcze ewentualne błędy powyłapywać.

  • magdazarych

    Odnośnie logów aplikacji to w końcu app.log ma daty 🙂

  • Addam

    super! 🙂

  • Fprct

    Niedługo zacznę brzmieć jak zacięta płyta, ale zapytam jeszcze raz o postępy przy pracach nad podpinaniem kilku aplikacji pod tę samą domenę (tj. podpinanie pod ścieżki) i api/cli do zakładania baz danych. Wiem już, że planujecie te rzeczy, ale może dacie radę zdradzić jakieś przewidywane terminy? 🙂

    Osobiście strasznie tego wyczekuję, obecnie zwłaszcza podpinania pod katalog. Bez tego nie postawię sobie WordPressa pod „/blog” z Django pod „/”. 🙂

    A bazy to z kolei dużo wygodniejsza automatyzacja zakładania całych środowisk z aplikacją. Teraz trzeba dodawać bazy z palca lub „reużywać” stare i pilnować ich odpowiedniego wyczyszczenia.

    Takie moje żale. 🙂

    • magdazarych

      Przekierowanie na URI być może będziemy w stanie zrobić każdorazowo ręcznie. Nie będzie to tak wygodne jak samodzielna konfiguracja w panelu ale może wystarczy na razie? Właśnie kończymy aktualizacje na serwerach i możemy zacząć planować dalsze kroki. To co nam się marzy to w ogóle uruchamianie szkieletu aplikacji Django, Rails z poziomu panelu (łącznie z zakładaniem baz). Pracujemy teraz intensywnie nad nową platformą, którą będzie mocno zautomatyzowana i uruchamianie aplikacji będzie jej częścią.

      Odezwij się mailem w sprawie Django i WordPressa to przetestujemy taką konfigurację, oki?

      • Fprct

        Okej, bardzo chętnie. Jednorazowe ustawienie WordPressa pod ścieżkę mi wystarczy i chętnie to przetestuję. Odezwę się meilowo w ciągu kilku tygodni w tej sprawie. 🙂 Dzięki wielkie.

  • tomekG

    super zmiany!