CI/CD – jest częścią metodyki DevOps. Podstawowa jej cecha to umiejętne, bezpieczne i częstsze dostarczanie sprawdzonych zmian w kodzie aplikacji.
Definicja Continuous Integration (CI) oraz Continuous Delivery (CD)
Continuous integration (CI) i Continuous Delivery (CD) to zbiór zasad, wytycznych, kultura pracy i kolekcja dobrych praktyk dotyczących pracy nad projektami informatycznymi. Dzięki nim zespół developerów ma możliwość częstszego dostarczania pewnych, przetestowanych i sprawdzonych zmian w kodzie. Implementacja tych praktyk często nazywana jest CI/CD pipeline i jest uważana za jeden z najlepszych i najefektywniejszych sposobów pracy nad projektami informatycznymi i ich rozwojem.
Continous integration – ciągła integracja
Jest to filozofia wprowadzania zmian w kodzie aplikacji, która polega na tym, że zespół developerów wprowadza szereg drobnych zmian w kodzie, które następnie okresowo sprawdzane są w repozytorium wersji kodu. Ponieważ większość nowoczesnych aplikacji wymaga tworzenia kodu z wykorzystaniem różnych platform i narzędzi zespół programistów wymagać więc będzie mechanizmów do „łączenia” (integracji), a co za tym idzie sprawdzania poprawności wprowadzanych przez poszczególne osoby zmian w kodzie.
Celem stosowania CI jest osiągnięcie spójnego i zautomatyzowanego sposobu budowania i testowania aplikacji. Dzięki konsekwentnemu procesowi integracji zespoły częściej wprowadzają zmiany w kodzie częściej, co prowadzi do lepszej współpracy i lepszej jakości oprogramowania.
Continous delivery – ciągłe dostarczanie
Continuous delivery rozpoczyna się tam, gdzie continuous integration się kończy. CD automatyzuje proces wdrażania aplikacji i wprowadzanych zmian w kodzie do przygotowanej infrastruktury serwerowej. Większość zespołów developerskich tworzy oprogramowanie przy wykorzystaniu środowisk odpowiednich dla konkretnych etapów pracy – są to przykładowo środowisko developerskie, testowe czy tzw. „produkcja”. Kluczową cechą CD jest zadbanie o automatyzacje procesu dostarczania zmian dla wszystkich wykorzystywanych środowisk i wykonanie niezbędnych dodatkowych mechanizmów takich jak np. wysłanie żądania do serwera WWW, wykonanie zapytań SQL, wysłanie powiadomienia czy nawet restart / przeładowanie ustawień lub uruchomionych usług.
Continous Testing
CI oraz CD wymagają ciągłego przeprowadzania testów (Continous Testing), ponieważ celem jest dostarczenie wysokiej jakości aplikacji i kodu do użytkownika końcowego. CT jest zazwyczaj implementowane jako zbiór automatycznie przeprowadzanych testów np. wydajnościowych czy testów regresji. Proces ten zazwyczaj jest częścią procesu CI/CD (pipeline).
Pomimo iż praktyki CI/CD przynoszą wiele dobrego – wprowadzają bardzo uporządkowany, schematyczny, a więc i w miarę przewidywalny sposób pracy nad projektami informatycznymi, to jednak nie zawsze są rozwiązaniem, które należy wdrażać we wszystkich przypadkach.
Jakie korzyści przynosi Continous Integration dla współpracy i jakości kodu?
Praktyka CI nakazuje, aby developerzy wysyłali (commit) swój kod do repozytorium wersji (np. GIT) okresowo i możliwie często. Większość zespołów developerskich stosuje standardy wysyłania zmian w kodzie przynajmniej raz dziennie. Głównym powodem do stosowania takiej praktyki jest to, że łatwiej jest identyfikować błędy i innego rodzaju problemy jakościowe kodu gdy jest go po prostu mniej do analizy, niż w przypadku gdy wysyłana jest cała, duża paczka zmian wprowadzanych przez dłuższy czas. Dodatkowo praca w krótszych cyklach zmniejsza prawdopodobieństwo wprowadzania zmian w tych samych plikach przez różnych developerów, co wymusza konieczność porównywania i łączenia (merge) zmian w kodzie.
Zespoły, które zaimplementowały rozwiązania CI wdrażają także mechanizmy okresowego testowanie kodu i wdrażanie poprawek zarówno od strony samej wprowadzanej zmiany (branch) jak i od strony finalnej wersji (release).
Istnieją różne techniki kontrolowania tego, jakie zmiany (features) – a więc i kod, trafia na tzw. produkcje – czyli na serwer końcowy. Jedna z nich dotyczy tzw. „version-control branching”, gdzie wybrana jest strategia (np. Gitflow), która wprowadza uporządkowany sposób wprowadzania i łączenia zmian do poszczególnych etapów pracy nad projektem (development, test, produkcja). Dodatkowe feature-branch’e są tworzone dla tych zadań, które mogą trwać nawet kilka cykli produkcyjnych. Kiedy wprowadzana zmiana (feature) jest ukończona programista wysyła i łączy zmiany ze swojej gałęzi z główną gałęzią developerską. Taka praktyka zarządzania zmianami działa wyśmienicie – jednak wykorzystywanie jej może być problematyczne w przypadku gdzie wielu developerów pracuje równolegle nad wieloma rzeczami.
Pojawiły się także inne techniki zarządzania zmianami (features). Niektóre zespoły używają tzw. „feature flag” – jest to po prostu przełącznik za pomocą, którego dana funkcjonalność znajdująca się już w kodzie głównym jest po prostu włączana / wyłączana. W tym przypadku funkcjonalność, która ciągle znajduje się w fazie rozwoju może być po prostu wyłączona i niedostępna dla końcowego użytkownika, czekając na swój moment – ale i tak znajdować się w kodzie aplikacji.
Większość narzędzi CI/CD pozwala na uruchomienie procesu tworzenia „paczki” (build) na kilka sposobów, np:
- ręcznie – uruchamiane przez użytkownika
- automatycznie – po commicie kolejnego fragmentu kodu
- automatycznie – w zdefiniowanym czasie
Wybór odpowiedniego sposobu zależy głównie od projektu i zespołu, jaki przy nim pracuje. Najlepszą praktyką jest wysyłanie częstych commitów – to pozwoli o osiągnięcie krótkiego czasu tworzenia paczki.
Continuous testing – to coś więcej niż automatyzacja testowania
Do utrzymania wysokiej jakości produktu zespoły programistyczne często wykorzystują specjalistyczne frameworki dotyczące projektowania, tworzenia i wykonywania testów automatycznych – w tym testów regresji. Testy te pozwalają na sprawdzenie, czy tworzona aplikacja lub jej fragment działa poprawnie, tj. zgodnie z założeniami. Zawierają w sobie testy funkcjonalne tworzone zazwyczaj pod koniec każdego sprintu (Scrum), które następnie są integrowane z testami regresji całej aplikacji. Testy regresji, z kolei, pozwalają na sprawdzenie całej aplikacji w kontekście wprowadzanych zmian.
Najlepszą praktyką jest umożliwienie przeprowadzenie testów regresji przez developerów w lokalnym środowisku, jeszcze przed commitem zmian do zewnętrznego repozytorium. Dzięki takiemu rozwiązaniu developer jest w stanie szybko wychwycić i poprawić błędy w tworzonym fragmencie kodu bez dodatkowych poprawek.
Testy regresji, to tylko początek. Testy wydajności, testowanie API, analiza statycznego kodu, testy bezpieczeństwa i wiele pozostałych również mogą zostać zautomatyzowane. Kluczową rzeczą jest umożliwienie uruchomienie testu za pośrednictwem CLI (Command Line Interface), webhook (akcja przez WWW), albo usługa sieciowa, która jest w stanie zwrócić rezultat testu z odpowiednim statusem błędu w przypadku negatywnego wyniku.
Automatyzacja wysyłania zmian, jako część CD
Większość projektów tworzonych i rozwijanych jest w oparciu o dwa środowiska – produkcyjne i testowe. Zdarza się jednak, że duże projekty , opierające się o kilka stopni weryfikacji poprawności zmian, opierają się o wiele odpowiadającym im środowisk. Continous Delivery wprowadza automatyzację procesu wysyłania zmian na te środowiska.
Typowy CD pipeline zawiera kroki takie jak:
- Pobranie kodu z repozytorium wersji i uruchomienie build’a
- Automatyczne wykonanie wszystkich niezbędnych kroków dotyczących infrastruktury
- Przeniesienie / transfer kodu na wybrane środowisko
- Zarządzanie zmiennymi środowiskowymi i konfiguracja ich dla konkretnego przypadku
- Przeniesienie komponentów aplikacji do odpowiadających im usług (np. usługi WEB’owe, usługi API, usługi bazodanowe)
- Wykonanie wszystkich niezbędnych restartów usług
- Wykonanie testów (Continous Tests) i cofnięcie zmian w przypadku błędów
- Zwrócenie wynikow, udostępnienie logów zdarzeń
- Providing log data and alerts on the state of the delivery.
To jednak nie są wszystkie możliwe kroki – często w pipeline włączane są takie procesy jak synchronizacja danych, archiwizacja, kopie zapasowe, wdrożenie zmian w aplikacji itp.
Wiele zespołów zajmujących się tworzeniem aplikacji implementuje rozwiązania CI/CD w oparciu o rozwiązania w chmurze przy wykorzystaniu konteneryzacji np. Docker lub Kubernetes. Jednak po wybraniu narzędzia należy upewnić się, że wszystkie zmienne środowiskowe są skonfigurowane poza aplikacją. Dobre narzędzia tego typu powinny pozwolić na ustawienie zmiennych, maskowanie haseł i kluczy dostępowych wykorzystywanych w tworzonym pipeline, a także bezpośrednią integrację z repozytorium kodu czy zadaniami. Ważny jest także dostęp do raportów i przyjazny widok zbiorczy (dashbord) – taki widok pozwala na łatwą kontrolę procesu wysyłania zmian i reakcji na ewentualne problemy.
CI/CD zwiększa częstotliwość wysyłania zmian
CI/CD jest najlepszą praktyką devops, ponieważ rozwiązuje problem występujący między programistami, którzy chcą często wprowadzać zmiany, a administratorami serwerów, których głównym celem jest stabilna praca systemów / aplikacji. Jest to idealne rozwiązanie dla firm, które chcą potrzebują często poprawiać lub zmieniać aplikację, gdzie głównym celem jest stabilność jej działania. Dzięki automatyzacji poszczególnych etapów dostarczania zmian w kodzie, oraz automatyczne przeprowadzanie testów znacząco ułatwia i przyspiesza się proces wysyłania zmian na środowisko produkcyjne. Zespoły operacyjne widzą większą stabilność, ponieważ środowiska mają standardowe konfiguracje, w procesie dostarczania są testy, zmienne środowiskowe / systemowe są oddzielane i niezależne od aplikacji, a procedury cofania zmian są zautomatyzowane.
Rozpoczęcie wdrażania procesu CI/CD wymaga od zespołów programistycznych i administracyjnych współpracy przy wyborze odpowiednich schematów pracy, narzędzi, technologii itp. Jednak nakład pracy włożonej we wdrożenie tych rozwiązań szybko się zwraca. Dzięki standaryzacji pracę, znaczącej minimalizacji błędów, zwiększenie szybkości reakcji itp. niewspółmiernie zwiększa się jakość tworzonego kodu, a co za tym idzie bezpieczeństwo całej aplikacji.