Jak bezpiecznie wdrażać aplikację na produkcję 

“Jedyny słuszny język programowania”, “jedyny słuszny framework”, “jedyne słuszne narzędzie do wdrażania aplikacji”. W mojej opinii, nie ma czegoś takiego. Bez zamknięcia tego w kontekście wyzwania, z którym mamy się zmierzyć, taka generalizacja nie ma sensu. Dlatego przedstawię swoje doświadczenia związane z wdrażaniem kodu, które było na dany moment oparte o (moim zdaniem) najlepsze dostępne rozwiązania. Były to rozwiązania, które spełniały swoje założenia, czyli dzięki nim dostarczaliśmy kod na produkcję. Czy były bez wad? Nie. Czy widzieliśmy możliwość usprawnień? Zdecydowanie tak.

Tomasz Tomczyk
Latest posts by Tomasz Tomczyk (see all)

W pracy związanej z programowaniem piękne jest to, że nie ma jednej słusznej drogi prowadzącej do celu. I nie mówię wyłącznie o pisaniu kodu – mam na myśli cały proces wytwarzania oprogramowania: od fazy koncepcyjnej, poprzez prototypowanie, implementację, testowanie, wdrażanie, klepanie się po plecach po odniesieniu sukcesu lub szukanie kozła ofiarnego po porażce. 

Wiele z tych dróg może prowadzić na manowce, wiele jest też skrótów, które kuszą wizją szybszego osiągnięcia celu (czy na pewno?) oraz wiele takich, które faktycznie doprowadzą nas do realizacji naszego celu. Specjalnie kładę nacisk na “wiele” ponieważ dużo zależy od nas samych, naszej kreatywności, Twojej i zespołu, w którym pracujesz.

Początki bywają trudne (proste?)

Z komercyjnym programowaniem mam doświadczenie od 2005 roku. Cały ten okres nauczył mnie tego, że poczucie braku wiedzy jest u mnie permanentne. Przez ten czas jednak obserwowałem, jak zmienia się podejście do wdrażania kodu na świecie, jak i w firmach, w których pracowałem. Sam miałem ten przywilej wgrywania kodu na produkcję za pomocą ftp i pamiętam swoje modlitwy, aby w tym czasie nikt inny nie nadpisał moich plików. Jednak to było lata temu, i “tak to się robiło” w tamtym czasie. 

Ftp, sftp, svn/git, phing, ansible, postępująca automatyka, testy, Bamboo, Jenkins… to tylko niektóre narzędzia, które przysłużyły mi się w wydawaniu kodu. Jednak to jest tylko narzędziówka, która zmienia się z czasem. 

Dziś mogłoby się wydawać, że użycie ftp do wdrażania kodu to archaizm. Jednak z rozmów w “społeczności” wynika, że tak do końca nie jest. Nawet patrząc z perspektywy mojej pracy w Displate.com to nie taka odległa przeszłość, ponieważ jeszcze 3-4 lata temu używane były skrypty, które wygrywały kod na serwery produkcyjne. Nie wstydzę się o tym pisać, ponieważ taka sytuacja jest wpisana w naturę startupów. 

Niektóre mają to szczęście, że projektują je ludzie, którzy znają się na robocie i robią to dobrze, zachowując konsensus pomiędzy elastycznością biznesową, a jakością technologiczną. Inne z kolei są projektowane przez ludzi bez dużego zaplecza programistycznego jednak mających wizję połączoną z pasją realizacji. 

Z czasem niektóre projekty upadają, inne jednak nabierają wiatru w żagle i są rozwijane na podstawie tego, co już zostało zrobione. Z upływem czasu świadomość pewnych barier zaczyna się coraz bardziej uwidaczniać. Displate był właśnie w takim miejscu. Świadomość tego, że wraz z rozwojem serwisu od strony biznesowej musi też iść rozwój od strony technicznej, coraz bardziej była zauważalna. Dzięki temu mogliśmy usprawnić wiele procesów w dziale IT. Teraz sytuacja wygląda zupełnie inaczej – czarne wieki FTP odeszły w niepamięć nastała nowa era CI/CD

Czytając ponownie to, co napisałem powyżej można odnieść wrażenie, że to było takie “tadaaaa” i nagle mamy CI/CD. To nieprawda. Musisz wiedzieć, że to nie jest prosty proces w szczególności jeśli pracujemy na istniejących już aplikacjach, które wymagają również refaktoringu. Mamy jeszcze wiele pracy przed sobą, wiele rzeczy do ogarnięcia i wdrożenia. Jednak cały czas mamy świadomość, co było wcześniej, gdzie jesteśmy teraz i to w którym kierunku chcemy podążać. 

Strategie wdrażania kodu

Nie wiem jak bardzo doświadczonym programistą jesteś, nie wiem czym się zajmujesz, jednak ważna jest świadomość sytuacji w jakiej znajduje się Twój projekt, ewentualnie projekt w firmie, do której chcesz dołączyć. Nie należy demonizować tego, że w danej chwili firma nie używa tego, co jest “top of the top”. Ważne jest podejście firmy do zmian. Jeśli jest zgoda i chęć stopniowego ulepszanie procesu, to już jesteśmy na dobrej drodze. 

To, o czym wcześniej pisałem to było głównie podkreślenie podejścia firmy do zmian w tym do zmian przy wdrażaniu kodu oraz pobieżnie wymieniona narzędziówka, która jedynie dotykała tematu. Jednak sama narzędziówka nie zapewni nam bezpiecznego wdrożenia. To jest trochę jak z pieniędzmi (podobno) szczęścia nie dają, ale jednak szczęściu pomagają.

Same narzędzia jednak to nie wszystko. Ważna jest również strategia, za pomocą której wdrażamy kod. Poniższe strategie nie są wszystkimi możliwymi podejściami, jednak na pewno stanowią pewną bazę, od której możesz zacząć wdrażać się głębiej w temat.

Oto one:

  • Recreate
  • Ramped (rolling update)
  • Blue/Green
  • Canary
  • A/B testing
  • Shadow

Każda z tych strategii zasługuje na osobny artykuł. Wspomnę jedynie w kilku zdaniach o każdej z nich i odrobinę rozwinę temat jednej z nich a mianowicie “Shadow”. Dlaczego na niej? To nie jest technika, która jest przez nas najczęściej wykorzystywana, ale jest na tyle interesująca i niosąca ze sobą bardzo ciekawe możliwości, że warto ją poznać odrobinę lepiej.

Trochę teorii

Recreate – można powiedzieć, że to strategia odchodząca w niebyt, ze względu na czasową niedostępność serwisu, na który wdrażamy kod.

Proces:

  • zatrzymanie wszystkich instancji produkcyjnych,
  • wdrożenie nowej wersji kodu,
  • uruchomienie serwisu na instancjach działających z nową wersją kodu.

Niewątpliwym plusem jest prostota takiego podejścia i w niektórych przypadkach użycie tej strategii może być nawet uzasadnione, jednak czasowa niedostępność serwisu zdecydowanie przekreśla to podejście jako domyślną strategię dostarczania nowych funkcjonalności.

Ramped – stopniowe zastępowanie (rolling) istniejących instancji na nowy kod.

Proces:

  • zatrzymanie jednej (bądź n) instancji,
  • wdrożenie nowej wersji kodu na zatrzymane instancje,
  • uruchomienie nowych instancji,
  • przepięcie na nie ruchu produkcyjnego,
  • zatrzymanie pozostałych instancji ze starym kodem (nie obsługujących już ruchu),
  • wdrożenie nowej wersji kodu na zatrzymane instancje,
  • uruchomienie zatrzymanych instancji,
  • wpuszczenie na nie ruchu.

Proces może wydawać się skomplikowany, jednak wbrew pozorom taki nie jest. W tej chwili jest to najczęściej wybierany (to nie znaczy, że zawsze) sposób wdrażania kodu.

Blue/Green

Proces:

  • wdrażamy nowy kod na n instancji (przeważnie n jest równa liczbie instancji obsługujących ruch),
  • przeprowadzamy/uruchamiamy testy na nowych instancjach w konfiguracji produkcyjnej,
  • po pozytywnie przeprowadzonych testach przepinamy ruch na instancje z nową wersją kodu.

Dużym plusem tego podejścia jest bardzo szybki rollback wprowadzonych zmian gdyby wyniknął jakiś problem w trakcie testów. Należy też pamiętać, że etap testowania nie jest czymś, co zastępuje dotychczasowe testy (unit, funkcjonalne, kontraktowe) tylko stanowi ich uzupełnienie.

Canary

Proces:

  • wdrażamy nowy kod na n instancji,
  • ruch jest proporcjonalnie kierowany na instancje z nowym i starym kodem (np 90% stare, 10% nowe).

Dzięki takiemu podejściu jesteśmy w stanie produkcyjnie uruchomić jednocześnie starą i nową wersie kodu, co daje nam możliwość monitoringu i obserwacji jak nowa wersja zachowuje się w realnym środowisku pracy (stabilność, performance itp.). W końcu nikt nie jest lepszym testerem od użytkownika końcowego. 

Pół żartem, pół serio dzięki tego typu wdrożenia angażujemy użytkownika niejako do testowania nowych wersji kodu. Pozostaje nam obserwować i reagować. Muszę podkreślić, że stosowanie blue/green deployment nie upoważnia nas do przykładania mniejszej wagi do procesu testowania w trakcie implementacji. Tutaj otrzymujemy możliwość zbadania takich czynników, które ciężko by było nam wytworzyć na środowiskach testowych (np. jak aplikacja zachowa się podczas obciążenia realnym ruchem).

ZOBACZ TEŻ:  Od programisty do testera. Jak doświadczenie programistyczne pomaga w byciu lepszym QA

A/B Testing

Proces:

  • wdrażamy nowy kod na n instancji,
  • ruch na podstawie “określonych kryteriów” (np. kraj z którego pochodzi użytkownik) jest dzielony pomiędzy stare i nowe instancje.

Jak pewnie zauważyłeś, to podejście jest bardzo zbliżone do poprzedniego (cannary), ale tutaj jest pewna różnica. Celem wdrożenia nie jest dostarczenie nowej wersji oprogramowania samo w sobie, ale prowadzenie eksperymentów. Eksperymentów, które dadzą odpowiedź na zadane pytania biznesowe, np. czy nowy sposób prezentacji produktu przyczyni się do wzrostu sprzedaży. Konfrontujemy ze sobą starą i nową wersję aplikacji. Zapinamy odpowiedni monitoring metryk i przeprowadzamy test. 

Użytkownicy na podstawie pewnych kryteriów np. oznaczeń w ciastku czy lokalizacji, kierowani są na odpowiednią wersję oprogramowania, a nam nie pozostaje nic innego, jak uzbroić się w cierpliwość i czekać na sygnał, czy nasz “super pomysł na ficzer” jest tak super jak nam się wydawało.

Shadow 

Proces:

  • wdrażamy nowy kod na n instancji (przeważnie na taką samą ile jest instancji produkcyjnych),
  • ruch jest klonowany obsługiwany zarówno przez starą i nową wersję kodu.

“Ruch jest klonowany” – o co chodzi?

Nowy wersja aplikacji obsługuje kopię ruchu produkcyjnego, jednak nie ma to wpływu na samego użytkownika, działa niejako w cieniu (shadow). Możemy ją potraktować jak kopię środowiska wraz z warstwą utrwalania stanu (zapisu). Różnica jest jedynie w nowych funkcjonalnościach dostarczonych z daną wersją kodu. Użytkownik nawet nie jest świadomy tego, że jego żądanie jest obsługiwane de facto przez dwie aplikacje.

Rezultat, który zobaczy w wyniku swoich działań będzie pochodził z aplikacji zbudowanej na podstawie starej wersji kodu. Co w takim razie daje nam ten sposób wdrażania aplikacji? Niewątpliwie jest to bezpieczeństwo. Jesteśmy w stanie w środowisku produkcyjnym poprzez monitorowania nowych instancji stwierdzić czy nowa wersja aplikacji działa poprawnie. Co ważne sprawdzamy jej działanie na rzeczywistym (pamiętaj: kopii) ruchu, dzięki temu nie tylko testujemy poprawność biznesową, ale też samą wydajność przy pełnym obciążeniu.

W przypadku jakichkolwiek problemów, czy to związanych z procesami biznesowymi, czy wydolnościowymi, jesteśmy w stanie bardzo prosto usunąć wdrożoną aplikację, usuwając instancje, na których działa. Użytkownik pozostanie w błogiej nieświadomości, że gdzieś w cyfrowym świecie wydarzyła się “tragedia” i dalej będzie mógł bez przeszkód używać swojego ulubionego serwisu.

Czy to podejście ma wady? Owszem. Jedną z nich jest koszt samego wdrożenia. Musimy powołać do życia kopię środowiska produkcyjnego i utrzymywać ją przez pewien czas, a to wiąże się oczywiście z $$$. Trzeba też pamiętać, że w pewnych specyficznych przypadkach, np. płatnościach, musimy odpowiednio obsłużyć ten proces. Nie możemy obciążyć użytkownika podwójnymi kosztami pochodzącymi ze starej i nowej wersji aplikacji, podobnie nie powinniśmy wysyłać dwóch maili dotyczących tego samego zakupu. Jak widzisz tych pułapek trochę jest, zatem do tematu trzeba podejść z głową.

Więcej na drugiej stronie artykułu.

Zapraszamy do dyskusji

Patronujemy

 
 
More Stories
Dropbox wydał swój password manager. Śniadanie z Programowaniem #40