Archiwa tagu: CI

Ciągła Integracja: jak skonfigurować proces budowania na serwerze Bamboo?

Ten wpis jest uzupełnieniem artykułu Ciągła Integracja: anioł stróż dobrego programisty, gdzie obiecałam pokazać konfigurację procesu budowania na konkretnym przykładzie serwera Atlassian Bamboo.

Co jest potrzebne? Parę słów na temat konfiguracji serwera i agentów

Żeby zdefiniować proces budowania przy użyciu Bamboo musimy mieć dostęp do:

  1. Serwera Bamboo, który będzie zarządzał naszymi procesami budowania (inaczej buildami lub planami budowania).
  2. Agentów Bamboo, czyli serwerów roboczych, które będą realizowały zadania zlecone przez Serwer Bamboo.

Kod serwera i agentów jest napisany w Javie. Serwery robocze, które chcą pełnić rolę agentów Bamboo i przyjmować zadania związane z procesem budowania, muszą zarejestrować się na serwerze.

Bamboo można, po wykupieniu licencji, zainstalować na własnym serwerze – z taką instalacją mam do czynienia na co dzień w pracy. Druga możliwość to skorzystanie z chmury firmy Atlassian. Wszystkie użyte w tym wpisie zrzuty ekranu pochodzą z wersji demonstracyjnej w chmurze (Bamboo Cloud). Główny serwer Bamboo działa wówczas w chmurze firmy Atlassian, natomiast agentów (serwery robocze) musisz uruchomić w chmurze Amazon EC2 w oparciu o obraz systemu dostarczony przez Atlassiana. Architekturę tego rozwiązania przedstawia poniższy rysunek.

1
Architektura Bamboo w wersji pochmurnej (http://devopscloud.net/2011/03/25/setting-up-a-windows-ami-for-use-with-elastic-bamboo/)

Przy okazji drobna uwaga z kategorii troubleshooting: spędziłam parę godzin na próbach zrozumienia, dlaczego, mimo że moja instancja w chmurze EC2 bez problemu uruchamia się na żądanie serwera, nie realizuje żadnych wyznaczonych przez serwer zadań. Wreszcie okazało się, że odpowiedni obraz systemu z zainstalowanym agentem Bamboo jest dostępny tylko, jeśli jako lokalizację wybiorę region „US East (N. Virginia)”. W przeciwnym razie uruchamia się czysty system, niezdolny do współpracy z serwerem Bamboo. Być może od czasu moich prób problem został już rozwiązany.

Struktura Planu Bamboo

Proces budowania (ang. build) w świecie Bamboo jest określany mianem Planu Budowania (ang. Build Plan).

Tabela poniżej przedstawia elementy tworzące strukturę takiego planu.

Projekt
  • Może zawierać wiele planów.
  • Wyświetla zbiorczy raport dla wszystkich swoich planów.
  • Łączy się z innymi aplikacjami (np. JIRA do zarządzania zadaniami, Crucible do przeglądów kodu).
Plan
  • Dzieli się na Etapy, które wykonywane są sekwencyjnie.
  • Definiuje domyślne repozytorium kodu.
  • Określa, kiedy uruchomić proces budowania (wyzwalacze planu).
  • Wysyła powiadomienia o wynikach kolejnych przebiegów.
  • Pozwala definiować zmienne planu.
Etap (Stage)
  • Dzieli się na Porcje Pracy, wykonywane równolegle na wielu agentach jednocześnie (jeśli są dostępne).
  • Etap (tj. wszystkie Porcje Pracy) musi się zakończyć powodzeniem, żeby wykonany został kolejny etap.
Porcja Pracy(Job )
  • Steruje wykonaniem listy Zadań, sekwencyjnie na tym samym agencie.
  • Zbiera wymagania poszczególnych zadań, by wybrać agenta z odpowiednim zestawem umiejętności (ang. capabilities).
  • Definiuje artefakty.
Zadanie (Task)
  • Zadanie to atomowa jednostka pracy, np. pobranie kodu z repozytorium, wywołanie polecenia Maven, uruchomienie skryptu.

Kompletna dokumentacja w języku angielskim jest dostępna na stronie https://confluence.atlassian.com/display/BAMBOO/Bamboo+Documentation+Home.

Utworzenie planu

Po pierwsze, w nowszych wersjach firma Atlassian ukryła przed użytkownikami link do Bamboo. Jeśli masz już konto w Jira/Bamboo Cloud lub dostęp do firmowej instalacji, oto jak przejść do panelu Bamboo:

Link do konfiguracji Bamboo
Link do konfiguracji Bamboo

Tworzenie planu:

Tworzenie nowego planu (procesu budowania)
Tworzenie nowego planu (procesu budowania)

Po utworzeniu planu możemy przejść do jego konfiguracji (na rysunku widać plan, który został już kilka razy uruchomiony – kolorowe ptaszki i wykrzykniki to historia jego kolejnych przebiegów):

5
Historia wyników i przejście do konfiguracji planu

Repozytorium kodu źródłowego

Plan ma na celu testowanie, budowanie i być może wdrażanie kodu źródłowego. Kod powinien być przechowywany w dobrze zorganizowanym repozytorium. Bamboo pozwala połączyć się ze wszystkimi najpopularniejszymi repozytoriami (w przypadku rozwiązań niestandardowych można jeszcze uciec się do pomocy pluginów). Repozytoria kodu źródłowego (w widocznym na rysunku przykładzie jest do publiczne repozytorium GitHub) konfiguruje się w zakładce Repositories:

Dodanie definicji repozytorium kodu źródłowego
Dodanie definicji repozytorium kodu źródłowego

Wyzwalacze

Wspomniałam w poprzednim wpisie, że każdy proces budowania ma swoje wyzwalacze. Możesz chcieć uruchamiać go ręcznie, automatycznie o określonej porze, albo po zmianie w wybranym repozytorium kodu. Jeśli zakończyłeś już konfigurację repozytoriów, możesz przejść do zakładki Triggers i tam zdefiniować wyzwalacze:

Kiedy uruchomić plan?
Kiedy uruchomić plan?

Ręczne uruchomienie planu przez wyznaczone osoby (zakładka Permissions pozwala na definicję uprawnień) zawsze jest możliwe, dlatego tej opcji nie ma na rozwijanej liście. Do dyspozycji masz: odpytywanie repozytorium o zmiany (sam wyznaczasz interwał dla tych zapytań), reakcję na sygnał z repozytorium, planowanie w oparciu o składnię cron oraz możliwość prostszego wskazania konkretnej godziny, o której plan ma zostać wykonany.

Powiadomienia

Skoro wiesz już, kiedy będzie uruchamiany plan, możesz podjąć decyzję kto, kiedy i jakim kanałem ma być powiadamiany o jego wynikach. Podstawowe źródło informacji to oczywiście raport widoczny poprzez interfejs www serwera,  gdzie wyniki odpowiednich planów „palą się” na czerwono bądź zielono. Warto wprowadzić wśród programistów dyscyplinę zerkania tam przynajmniej rano, po przyjściu do pracy. Oprócz tego możesz jednak stworzyć proste reguły powiadamiania konkretnych osób – np. mailem – o powodzeniu bądź niepowodzeniu procesu. Służy do tego zakładka Notifications.

Dobra rada – nie przesadzaj z powiadomieniami, ponieważ w końcu programiści przekierują je do katalogu spam. Informuj tylko te osoby, które naprawdę powinny wiedzieć o awarii lub samym fakcie uruchomienia danego planu.

Kto powinien wiedzieć o wynikach?
Kto powinien wiedzieć o wynikach?

Tworzenie etapu (Stage)

Domyślnie plan składa się z jednego etapu (Default Stage). Jeśli etapów będzie więcej, zostaną wykonane sekwencyjnie, przy czym niepowodzenie jednego z etapów zatrzyma wykonanie kolejnych. Dodatkowe etapy możesz tworzyć, korzystając z przycisku Create stage w zakładce Stages.

Tworzenie etapu (stage) w procesie budowania
Tworzenie etapu (stage) w procesie budowania

Tworzenie porcji pracy (Job)

Etap składa się z jednej lub więcej porcji pracy. Na powyższym rysunku widać etap zawierający dwie porcje pracy (Job1 i Job2) oraz możliwość dodania kolejnych (Add Job).

Uwaga! – porcje pracy są wykonywane równolegle; ich kolejność na liście nie ma najmniejszego znaczenia (Bamboo sortuje je alfabetycznie). Jeśli masz dyspozycji tylko jeden serwer roboczy, to oczywiście zostaną wykonane jedna po drugiej – w kolejności, na którą nie masz wpływu.

Każdej porcji pracy przypisane jest kilka właściwości:

  1. Zadania (patrz niżej)
  2. Artefakty (również niżej)
  3. Wymagania

Wymagania to oczekiwania wobec serwera roboczego, który może podjąć się wykonania danej porcji pracy (i zdefiniowanych w niej zadań). Możemy zażądać dostępności określonych bibliotek, nałożyć wymagania na nazwę serwera itp. Wymaga to pewnej konfiguracji po stronie agenta Bamboo. Serwer Bamboo sprawdza wymagania w czasie rzeczywistym i od razu informuje, ile agentów potrafi wykonać daną porcję pracy.

W poniższym przykładzie żaden agent nie spełnia wymagań (zerowych), ponieważ nie została uruchomiona żadna instancja w chmurze 🙂

Definicja wymagań porcji pracy wobec serwera roboczego (agenta Bamboo)
Definicja wymagań porcji pracy wobec serwera roboczego (agenta Bamboo)

Tworzenie zadania (Task)

Zadanie to najmniejsza, atomowa jednostka pracy w planie Bamboo. Zadania są definiowane w ramach jednostek pracy i są wykonywane sekwencyjnie. Uwaga – niepowodzenia są raportowane na poziomie jednostek pracy: nie dostaniesz czytelnej informacji, które z zadań spowodowało problem (chyba że w dobrze zorganizowanych logach lub na postawie informacji o tym, który test nie przeszedł).

Bamboo predefiniuje wiele zadań. Pobranie danych z repozytorium, wykonanie budowania przy użyciu narzędzia takiego jak Maven – w takich sytuacjach musisz tylko wypełnić odpowiedni formularz, np. wskazując odpowiednie ze zdefiniowanych repozytoriów.

Poniższy zrzut ekranu przedstawia widok jednego z dwóch zadań w ramach jednostki pracy. Jest to predefiniowane zadanie pobrania kodu z repozytorium kodu źródłowego.

Zadania w ramach porcji pracy
Zadania w ramach porcji pracy

Jeżeli musisz zrobić coś niestandardowego – jak niezalecane specjalnie wysłanie przeprowadzonych przez Twój plan zmian w kodzie do repozytorium – możesz poszukać odpowiedniego pluginu (lub samodzielnie go napisać), albo użyć zadania Script, w którym samodzielnie napiszesz kod dla odpowiedniej powłoki w systemie operacyjnym agenta:

Niestandardowe zadanie: zawsze można napisać skrypt!
Niestandardowe zadanie: zawsze można napisać skrypt!

Artefakty

Artefakty, czyli dodatkowe produkty planu budowania, jak wspomniałam już wcześniej, są dostępne na etapie jednostek pracy (jobs), ale wygenerować je trzeba przy użyciu zadań. Kod przedstawiony na powyższym rysunku zapisuje do pliku wartość jednej ze zmiennych Bamboo (ich lista oraz zasady definiowania własnych są dostępne tutaj). Możemy oznaczyć ten plik jako artefakt planu. Artefakty można pobierać (także przez przeglądarkę) oraz współdzielić pomiędzy etapami (a w nowszych wersjach serwera również pomiędzy planami).

Oto, jak wygląda gotowy do pobrania artefakt:

Artefakt gotowy do pobrania po udanym przebiegu planu
Artefakt gotowy do pobrania po udanym przebiegu planu

Uwaga! – przy definiowaniu artefaktów należy pamiętać o tym, że ścieżkę do nich podaje się względem katalogu roboczego danej jednostki pracy. Jeśli korzystasz ze ścieżek bezwzględnych na serwerze, możesz sprawdzić wartość zmiennej bamboo.build.working.directory.

Testy

Testy mają kluczowe znaczenie dla całego procesu ciągłej integracji. Technicznie sprowadzają się jednak do zwykłego, predefiniowanego zadania Bamboo: odczytu wskazanego przez Ciebie (w katalogu roboczym) pliku JUnit/XML.

Troubleshooting (na podstawie własnych, bolesnych doświadczeń):

  1. Jeśli w planie były nieprzechodzące testy, a w kolejnym przebiegu testów brak, Bamboo uzna to za błąd kompilacji.
  2. Kilka wersji Bamboo przejawiało następujący błąd: katalog z wynikami testów nie był odświeżany, jeśli plan został wykonany „zbyt szybko”.
  3. Jeśli chcesz dopuścić nieprzechodzące testy (np. na potrzeby TDD), musisz skorzystać z opcji dodania ich do kwarantanny.

Podsumowanie

Rysunek przedstawia podsumowanie kilku ostatnich przebiegów w planie budowania Bamboo, wraz z informacją o przyczynie uruchomienia planu i o liczbie testów:

Podsumowanie ostatnich przebiegów planu budowania wraz z informacją o liczbie testów i przyczynie uruchomienia procesu
Podsumowanie ostatnich przebiegów planu budowania wraz z informacją o liczbie testów i wyzwalaczu

Dla każdego z przebiegów możesz bardzo intuicyjnie doklikać się do informacji o testach, commitach do repozytorium i odpowiedzialnych za nie osobach. Wspominałam już o możliwości synchronizacji Bamboo z innymi produktami firmy Atlassian. Możesz, na przykład, utworzyć zadanie w Jirze w oparciu o nieudany przebieg:

Możliwość utworzenia zadania w Jirze w oparciu o nieudany przebieg procesu budowania
Możliwość utworzenia zadania w Jirze w oparciu o nieudany przebieg procesu budowania

Miłej zabawy!

Ciągła Integracja: anioł stróż dobrego programisty

Gdybym miała wskazać jedną ważną umiejętność, której brakuje większości absolwentów informatyki (i pokrewnych kierunków) aplikujących do pracy u nas w biurze, z pewnością byłaby to Ciągła Integracja. Wygląda na to, że programy studiów nie obejmują tego tematu, a prowadzący zajęcia nie przykładają do niego specjalnej wagi. Tymczasem w moim doświadczeniu niewiele rozwiązań skutkuje tak zauważalnym wzrostem produktywności pracy, a także codziennego zadowolenia programistów, jak ta właśnie praktyka. Na szczęście, luki w wiedzy nie tak trudno uzupełnić, choć diabeł, jak zwykle, tkwi w szczegółach.

Ciągła Integracja (CI) – co to?

Definicją służy, jak zawsze niezawodny, Martin Fowler:

Ciągła Integracja (ang. Continuous Integration) to praktyka programistyczna, w której członkowie zespołu często scalają wyniki swojej pracy – z reguły każdy robi to przynajmniej raz dziennie. W tej sposób każdego dnia powstaje kilka zintegrowanych wersji kodu, które są sprawdzane przez automatyczny proces budowania (i testowania).

Jest to również jedna z 12 praktyk Programowania Ekstremalnego.

Zasadę dobrze przedstawia poniższa ilustracja, pochodząca ze strony firmy Atlassian.

Ciągła Integracja: architektura na przykładzie narzędzia Atlassian Bamboo

W wielkim skrócie: programiści pracują nad bazą kodu, na bieżąco aktualizując zawartość wspólnego repozytorium. Niezależnie od niego działa serwer Ciągłej Integracji, który co jakiś czas (np. po każdym wysłaniu porcji kodu do repozytorium, albo zawsze o 2 w nocy) pobiera kod z repozytorium, który następnie testuje, kompiluje i, jeśli wszystko poszło jak trzeba, przygotowuje do wdrożenia. W przypadku wykrycia błędów niezwłocznie informuje osoby zaangażowane w projekt o problemie.

Po co to?

  1. Ciągła Integracja zmniejsza ryzyko związane z integracją na samym końcu projektu – błędy, niekompatybilność interfejsów, trudny do oszacowania czas na poskładanie całości. Z własnego doświadczenia znam przypadek, w którym sprzeczka o to, po której stronie interfejsu jest problem i kto ma przyjść do pracy w sobotę, żeby naprawić go przed poniedziałkową prezentacją, doprowadziła do odejścia „pokrzywdzonej” osoby z pracy.
  2. CI ułatwia naprawę błędów: ich szybkie wykrywanie sprawia, że łatwiej zlokalizować przyczynę  – wiadomo, co było ostatnio modyfikowane i jaka wersja działała poprawnie.
  3. CI chroni przed niespodziankami wynikającymi z różnic pomiędzy środowiskiem deweloperskim a produkcyjnym (np. inne środowisko uruchomieniowe danego języka, niestandardowe biblioteki).beavis
  4. CI umożliwia demonstrowanie aplikacji i konsultację z klientem w dowolnym momencie dzięki stałej dostępności ostatniej działającej wersji.
  5. CI ułatwia refaktoryzację (po każdej „kosmetycznej”  zmianie możemy szybko sprawdzić, czy wszystko gra).
  6. Ciągła integracja zdejmuje z programistów obowiązek wykonywania wielu powtarzalnych, nierozwijających (a jednak trudnych!) czynności.

(Niezgrabne) terminy

Terminy związane z Ciągłą Integracją trudno zwięźle przełożyć na języki polski. Najwięcej kłopotów sprawia najważniejszy z terminów, czyli build.

Proces budowania (build) to dużo więcej niż sama kompilacja. Często obejmuje:

  • kompilację
  • testy
  • inspekcję kodu
  • wdrożenie

Jeśli wykorzystujesz serwer CI tylko do kompilacji, nie powinieneś nazywać swojego procesu Ciągłą Integracją.

Narzędzie do budowania (build script lub build automation tool) pozwala na zdefiniowanie procesu budowania uruchamianego „jednym kliknięciem”. Nie musi wiązać się z serwerem Ciągłej Integracji: może działać w ramach IDE lub konsolowo. Przykłady: Maven, Rake, make.

Integracyjny proces budowania (integration build) to proces budowania uruchomiony na osobnym, specjalnie do tego przeznaczonym serwerze.

Prywatny proces budowania (private build) to proces budowania uruchomiony przez programistę lokalnie, przed wysłaniem kodu do repozytorium (lub w prywatnej, należącej do danego programisty gałęzi kodu w repozytorium).

Serwer Ciągłej Integracji (schematycznie przedstawiony na pierwszej ilustracji w tym wpisie) usprawnia definiowanie integracyjnych procesów budowania i  odpowiada za ich uruchamianie w określonych okolicznościach (zwróć uwagę na akapit o wyzwalaczach).

Dobre praktyki

Cechy, którymi powinno charakteryzować się środowisko Ciągłej Integracji:

  1. Jedno wspólne repozytorium kodu, zawierające wszystko, co potrzebne do zbudowania i uruchomienia systemu na czystej maszynie. Oczywiście zdarza się, że nad projektem pracuje kilka rozproszonych zespołów odpowiadających za oddzielne komponenty i preferujących różne systemy kontroli wersji. Ciągła Integracja jest wtedy jeszcze bardziej potrzebna!
  2. Automatyczne budowanie – żadnych wyskakujących okienek i ręcznego kopiowania plików!
  3. Automatyczne testy podczas budowania – to, że program się skompilował i da się go uruchomić, nie znaczy jeszcze, że wszystko z nim w porządku. Warto sprawdzać nie tylko wyniki testu, ale także procent pokrycia kodu testami.
  4. Codzienne wysyłanie i scalanie (działającego!) kodu przez programistów. Integracja to komunikacja – programiści widzą, nad czym pracują inni i jak im idzie.
  5. Inspekcje kodu. Przeglądy kodu, przeprowadzane przez innych programistów, mogą nawet doprowadzić do niewpuszczenia kodu na serwer. Ich podstawową wartością jest jednak wymiana wiedzy.
  6. Uruchomienie procesu budowania po każdej zmianie kodu w repozytorium i natychmiastowa reakcja na awarie
  7. Szybkość procesu budowania – natychmiastowa informacja zwrotna. Automatyczne testy nie powinny ciągnąć się przez godzinę. Jeżeli nie ma możliwości optymalizacji działania systemu ani dołożenia większej liczby maszyn, można stosować techniki takie jak randomizacja testów lub staged tests, w których najistotniejsze, szybkie testy przesuwa się na początek, żeby autor kodu wiedział, czy może (wstępnie) kontynuować pracę.
  8. Testy w środowisku jak najlepiej udającym produkcję. Drobiazgi, takie jak inne numery portów, potrafią doprowadzić do prawdziwej katastrofy.
  9. Ciągła dostępność wyników testów oraz najnowszej zbudowanej wersji aplikacji. Wszyscy programiści powinni wiedzieć, co dzieje się w głównej gałęzi repozytorium. Informację tę można dystrybuować na różne sposoby: powiadomienia (np. mailowe), ekrany wyświetlające status (zielony = wszystko gra, czerwony = awaria), lava lamps… Wyobraźnia nie zna granic 🙂

220px-Series_of_build_lights

Ciągłe Wdrażanie, czyli jeden krok dalej

Oprócz terminu Continuous Integration możesz natknąć się jeszcze na Continuous Deployment, Continuous Release, Continuous Delivery (dosłownie to: ciągłe wdrażanie, wydawanie, dostarczanie).

Ciągłe Wdrażanie to to Ciągła Integracja rozszerzona o wdrażanie z częstotliwością odpowiadającą integrowaniu – udostępniamy klientom każdą wersję, która przeszła testy automatyczne.

Omawianie szczegółowych różnic pomiędzy przytoczonymi tu terminami zdecydowanie wykracza poza ambicje tego artykułu.

Wyzwalacze, czyli kiedy budować?

Większość serwerów Ciągłej Integracji pozwala na zdefiniowanie następujących „wyzwalaczy” procesu budowania:

  • ręczny – na żądanie programisty,
  • planowy – o określonej porze (np. tzw. „nightly build”),
  • odpytanie repozytorium – w reakcji na zmianę kodu źródłowego,
  • zdarzeniowy – np. w reakcji na zmianę kodu źródłowego (ale to repozytorium informuje) lub zakończenie innego procesu budowania.

Który serwer wybrać?

Serwerów Ciągłej Integracji jest na rynku bardzo, bardzo dużo. Najpopularniejszy jest w tej chwili otwarty serwer Jenkins (fork projektu Hudson, to historia na osobny artykuł). Inna popularna opcja to Bamboo – narzędzie komercyjne, którego siła tkwi w pełnej integracji (sic!) z innymi ułatwiającymi życie rozwiązaniami firmy Atlassian. Bardziej szczegółowe porównanie można znaleźć nawet w Wikipedii.

Co z tego zapamiętać? tl;dr

Ciągła Integracja pozwala programiście być programistą. Programista zajmuje się tworzeniem kodu, a nie powtarzalnymi operacjami niezbędnymi do scalenia kodu i przygotowania wydania.

Dzięki CI natychmiast (ewentualnie następnego ranka po beztroskiej nocy) otrzymujemy informację o tym, że coś poszło nie tak, dzięki czemu możemy reagować na bieżąco.

W następnym odcinku

W kolejnym odcinku pokażę, jak zdefiniować proces budowania na serwerze Bamboo.

Gdzie znaleźć więcej informacji?