Sytuacja kobiet w IT w 2024 roku
12.06.20199 min
Erik Englund

Erik EnglundJava ArchitectOptum

Jenkins się starzeje

Jenkins cieszy się popularnością od lat, ale coraz bardziej odczuwalne są jego braki na różnych przestrzeniach.

Jenkins się starzeje

Przez długi czas Jenkins był moim zdaniem jedynym kozakiem w mieście, jeśli chodzi o ciągłą integrację (CI), a nawet do pewnego stopnia ciągłe wdrażanie (CD). Jednak po kilku latach używania i walki z Jenkinsem, myślę, że nadszedł czas, aby się z nim pożegnać. Podobnie jak wiele innych programów - Jenkins stał się ofiarą własnego sukcesu.

Jenkins rozpoczął działalność jako Hudson w 2005 roku. W 2008 roku zyskał rozgłos jako zwycięzca nagrody Duke's Choice na konferencji Java One. Gdy Oracle wykupiło Sun, społeczność Open Source zdecydowała się na własny nowy projekt - Jenkins, w celu utrzymania prawdziwie otwartego programu i bezpłatnego przedsięwzięcia. Jenkins pozostał takim do dziś.

Jenkins towarzyszył mi przez większość mojej kariery. Najpierw byłem konsumentem jego artefaktów, gdy był jeszcze Hudsonem, a teraz jako administrator DevOps, z Jenkinsem tworzę i konfiguruje tysiące jobów w ogromnym zespole.

To narzędzie bardzo urosło i dojrzało przez te lata. Ale nie wszystko z nim dobrze. Codziennie walczę z różnymi jego aspektami. Chociaż były poczynione pewne kroki, by rozwiązać wiele obaw takich jak moje, pewne problemy są nierozwiązywalne i wiążą się z samą istotą Jenkinsa.

Konfiguracja kompilacji

Podstawowymi komponentami Jenkinsa są joby. Są to konfiguracje budowania, które mówią narzędziu, co i jak ma zrobić.

Interfejs sieciowy

Początkowo wszystko było wykonywane za pośrednictwem interfejsu. Wszystkie projekty były konfigurowane za pomocą bardzo łatwych w użyciu formularzy internetowych. Na początku było super. Wkrótce jednak okazało się, że trudno jest wykonać kopię zapasową interfejsu, lub go utrzymywać. Nie pasowało to również do rosnącego pragnienia konfiguracji kodem. Konfiguracja dużej liczby zadań była żmudna i podatna na błędy. A utrzymanie takich konfiguracji było jeszcze trudniejsze.

Job DSL

Następnie pojawił się Jenkins Job DSL. Był to mile widziany krok w kierunku rozwoju opartego na konfiguracji. Jenkins Job DSL pozwalało używać języka Groovy do kodowania jobów. Można było w nim tworzyć klasy i metody wielokrotnego użytku do generowania 10, 100 i 1000 jobów z małą ilością wysiłku administratorów. Dodatkową korzyścią była kontrola nad źródłem konfiguracji jobów.

def gitUrl = 'git://github.com/jenkinsci/job-dsl-plugin.git'
job('PROJ-unit-tests') {
    scm {
        git(gitUrl)
    }
    triggers {
        scm('*/15 * * * *')
    }
    steps {
        maven('-e clean test')
        maven('-B release:prepare release:perform')
        shell('cleanup.sh')
    }
}


Ale znowu, nie wszystko działało jak należy. Praca DSL miała (ma) kilka kluczowych wad.

Nie zawsze łatwo było zacząć od zera. Trudno było rozkręcić wzór pracy z seedami.

Testowanie skryptów budowy było prawie niemożliwe. Doprowadzało to sys adminów do testowania konfiguracji podczas produkcji. Również eksperymentowanie z nowymi funkcjami było bardzo trudne. Co, jeśli chciałbyś, żeby jedna gałąź budowała się inaczej niż inne gałęzie? Mogłeś to zrobić, ale tylko kosztem czystego kodu.

Podczas gdy Job DSL był utrzymywany centralnie, co kochali SysAdmini, deweloperzy czuli się jak obywatele drugiej kategorii, jeśli chodzi o konfigurowanie zadań. Nie wszystkie joby przebiegały dokładnie tak samo. To często prowadziło do bardzo złożonych skryptów DSL.

Jenkins Pipeline

Ostatnio ruch został skierowany do Jenkins Pipeline lub, jak wielu woli go nazywać, Jenkinsfile, gdyż kompilacja jest odpalana ze specjalnego pliku w repozytorium o nazwie Jenkinsfile. Jenkinsfile przywraca kontrolę w ręce programistów.

Konfiguracja kompilacji jest przechowywana wraz z kodem. Pozwala to różnym gałęziom zachowywać się inaczej w zależności od konfiguracji pliku Jenkinsfile w tej gałęzi. SysAdmini mogą nadal utrzymywać zestaw podstawowych funkcji, które programiści mogą importować do swoich skryptów i dostosowywać do swoich potrzeb.

pipeline {
    agent any 
    stages {
        stage('Build') { 
            steps {
                // 
            }
        }
        stage('Test') { 
            steps {
                // 
            }
        }
        stage('Deploy') { 
            steps {
                // 
            }
        }
    }
}


Chociaż zapewnia możliwość eksperymentowania w bardziej odizolowanym środowisku, nadal ma kilka problemów. Chociaż Jenkinsfile jest technicznie groovy, nie możesz w nim zrobić wielu groovy rzeczy, które wydawałyby się oczywiste, ze względu na silne nastawienie na bezpieczeństwo. Zazwyczaj objawiałoby się to w postaci dziwnych błędów, takie jak brak możliwości wykonania for each na liście.

Nie wspomnę, ile razy kodowałem w Jenkinsfile tylko po to, żeby coś pozornie łagodnego wybuchło z powodu uprawnień do skryptów bezpieczeństwa. Wymaga to wtedy logowania się do Jenkinsa, zatwierdzenia połączenia i ponownego uruchomienia zadania. Powtarzasz do skutku, aż przy odrobinie szczęścia, job się wykona.

Oprócz tego, whitelsity nie można konfigurować i przechowywać zewnętrznie. Dlatego, kiedy zaczniesz nową instancję Jenkinsa w swojej bibliotece, musisz ponownie zatwierdzić wszystkie te same skrypty. I nie możesz zatwierdzić skryptu, dopóki ten się nie powiedzie. Tak więc nie zazdroszczę Ci całego upewniania się, że wszystkie komponenty są zdatne do pracy.

Plik Jenkinsfile jest podzielony na etapy. Pozwala to na ładną izolację pomysłów na budowanie, takich jak budowanie, testowanie, uwalnianie itp. Poprzednie wersje Jenkinsa nie pozwalały na wznowienie pipelineu. Z uśmiechem melduję, że zostało to rozwiązanie w wydaniu z kwietnia 2018 roku.

Wtyczki

W swojej podstawowej wersji Jenkins jest całkiem mały. Ale nikt nie używa go w ten sposób. Wtyczki to coś, czego chcesz i potrzebujesz. Są niesamowite! Pozwalają stronom trzecim na pisanie nowych funkcji w Jenkinsie. Dostajemy strony z wszelkimi rodzajami treści, połączone w całość.

Np. mogę mieć w jednym miejscu kompilację wyświetlającą pokrycie kodu, historię testów jednostek, trendy analizy kodu statycznego i wiele innych przydatnych narzędzi. Cudowne, prawda? Mogłoby się tak wydawać.

Ale wtyczki mają też mroczną stronę. Przez ich wersje, utrzymanie Jenkinsa jest koszmarem. Wtyczki są instalowane WEWNĄTRZ Jenkinsa. Jako część pojedynczego pojemnika internetowego Jenkins Master. Oznacza to, że wszystkie mają wspólną ścieżkę klasy. Skąd mam wiedzieć, czy czegoś nie zepsuje?

Wiele razy upgradowanie Jenkinsa zmienia również podstawowe pliki konfiguracyjne. Jeśli używasz Jenkins Job DSL, musisz upewnić się, że jest zsynchronizowany z Twoją wersją Jenkinsa i wszystkimi zainstalowanymi wtyczkami. Zmusza nas to do posiadania wielu różnych instancji Jenkinsa. Ale z Jenkinsem nie można łatwo wykonać tradycyjnych wzorów canary deployment.

To kończy się stagnacją nowych funkcji i opóźnia aktualizacje.

Dane plików

Wszystkie dane generowane i używane przez Jenkinsa do renderowania tych wszystkich pięknych stron wynikowych są przechowywane jako pliki. Duużo plików. To sprawia, że ​​wydajność Jenkinsa jest niesamowicie ograniczona. Nie jest rzadkością, że proces renderowania dużych pipelinów zajmuje kilka sekund.

Te duże pliki powodują również, że Jenkins zjada dużo pamięci. Często analizuje te pliki jako całe modele DOM, ładując całą zawartość pliku do pamięci. Nie ma nic niezwykłego w tym, że Jenkins używa zazwyczaj kilku gigabajtów dla wielkości sterty. Np. nasza produkcyjna instancja Jenkins ma stertę 32 GB, tylko po to, by upewnić się, że wszystko będzie działało.

Pamięć

Apropos pamięci, ponieważ wszystkie wtyczki są w rzeczywistości kodem, który jest ładowany do Jenkinsa, dość często widać przecieki pamięci. To nie jest wina podstawowego Jenkinsa, ale nadal wywali nam serwer. Musieliśmy zaplanować sobie regularne resetowanie instancji Jenkinsa, by zapobiec wyciekom pamięci.

System Groovy Scripts

Jeżeli kiedykolwiek poczujesz pokusę użycia System Groovy Scripts, NIE RÓB TEGO! Możesz łatwo zepsuć Jenkinsa. Dzieje się tak, ponieważ skrypt nie jest procesem rozwidlonym, a zamiast tego uruchamia się WEWNĄTRZ Jenkinsa.

Jest to celowe, ponieważ odpalając systemowy skrypt, chcesz uzyskać dostęp do samego Jenkinsa i rzeczywistych klas uruchomionych. Ale to oznacza, że ​​masz również dostęp bezpośrednio do JVM i procesu, w którym uruchomiony jest Jenkins. W związku z tym, możesz zepsuć ścieżkę klasy, stertę, a nawet zburzyć całość (System.exit?).

Konfiguracja Jenkinsa

Podczas gdy Job DSL i Jenkinsfile robią wiele dla konfiguracji Jobów, nie są zbyt pomocne w konfiguracji samego Jenkinsa. Będziesz ZMUSZONY skonfigurować Jenkinsa, aby łączył się ze statycznymi slave’ami lub klastrami Mesos, lub konfigurować zmienne środowiskowe bezpieczeństwa, narzędzia do budowania itp. Job DSL ma kilka bibliotek, których możesz do tego użyć, ale są one trudne do zrozumienia i niemożliwe do przetestowania.

Niemożliwość testowania

Mówiłem to już raz, dwa razy, tysiące razy... Jenkins jest NIEMOŻLIWY DO TESTOWANIA. Kto używałby języka programowania, którego nie można przetestować, z wyjątkiem testowania produkcji? Nikt! Niektórzy z Was mogą oczywiście się upierać, że mogą testować w Jenkinsie. Uwierz mi, próbowałem.

Jak naprawdę to testujesz? Próbowałeś uruchomić lokalną instancję Jenkinsa, ale ta się szybko psuje, gdy chcemy uruchomić kompilację. A co z rzeczywistą konfiguracją? Musisz skonfigurować dostęp do klastra, mockować infrastrukturę krytyczną, taką jak artifactory i DTR. To naprawdę trudne. A na dodatek chcesz, aby Twoje testy nie miały wpływu na rzeczywisty system. Wielokrotnie nasze „testy” powodowały problemy produkcyjne z powodu braku izolacji.

Uaktualnienia

Wspomniałem o tym krótko przy wtyczkach, ale powiem to jeszcze raz, że aktualizacje są prawdziwym problemem. Jeżeli chcesz uaktualnić plugin, czy samego Jenkinsa, musisz uaktualnić WSZYSTKO albo nie uaktualniać wcale. Jeśli masz jednego joba, który wymaga starszej wersji wtyczki i innego, która wymaga nowszej wersji, utkniesz w miejscu. Będziesz potrzebować wielu instancji Jenkinsa. Migracja do nowych wersji nie zawsze jest możliwa, ponieważ aktualizacja wtyczki może spowodować zmianę podstawowego XML. Dlatego też cofnięcie się do poprzedniej wersji może być prawie niemożliwe.

Jenkins Web UI

Rzeczywisty interfejs sieciowy jest uruchamiany na pojedynczym kontenerze internetowym, zwanym pieszczotliwie Jenkins Master. Możesz mieć naraz tylko JEDNEGO. Nie obsługuje żadnego rodzaju klastrowania ani przełączania awaryjnego. Jeśli masz bardzo duży zespół programistów, wszyscy uderzają Jenkinsa w jedną instancję, która musi być bardzo solidna i stale monitorowana.

Modularyzacja

Podstawowym problemem Jenkinsa jest jego monolityczność. Wszystko żyje razem, wtyczki, konfiguracja, web ui, rdzeń Jenkinsa, wszystko w jednej dużej aplikacji internetowej. Pora już, abyśmy byli w stanie wykorzystywać w Jenkinsie to, czego się nauczyliśmy na produkcji. Jenkins musi stać się systemem modularnym w chmurze.

Jenkins X

Jenkins X to próba sprowadzenia Jenkinsa do chmurowego środowiska macierzystego Kubernetes. Chociaż moje doświadczenie z Jenkins X jest ograniczone, nadal uważam, że nie rozwiązuje ono wielu moich potrzeb modułowych odłączonych komponentów i łatwych testów.

Moje marzenie

Mój idealny system CI / CD musi:

  • Wspierać jakiekolwiek testy jednostkowe jobów
  • Być całkowicie odłączony
  • Być poziomo skalowalny
  • Być modułowy
  • Być  rozszerzalny


Na szczęście wydaje się, że jest coś, co może pasować do tej listy.

Drone.io

Drone.io to system CI / CD oparty na kontenerach dokera. Czym różni się to od Jenkinsa w dokerze? Każdy krok to inny kontener, skupiony na jednej rzeczy. Plik konfiguracji zadania łączy szereg tych kontenerów.

kind: pipeline
name: default
steps:
- name: test
  image: maven:3-jdk-10
  commands:
  - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
  - mvn test -B


Dlaczego to takie ważne?

Teraz mogę uruchomić joba na moim lokalnym komputerze w idealnym środowisku, z którego będzie korzystał system kompilacji. Każdy krok jest izolowany w małych kontenerach, które są wersjonowane. Dzięki temu mogę aktualizować i dodawać nowe funkcje, bez wpływu na istniejące joby.

To samo można osiągnąć w Jenkinsfile. Jednak nie ma łatwego lokalnego interpretera do konfiguracji Jenkinsfile. Również Jenkinsfile działa w Jenkinsie. Dlatego nawet gdybyś mógł uruchomić skrypt, nie powieliłbyś dokładnie tego, co zrobiłby Jenkins. Z Drone mogłem bardzo łatwo zinterpretować konfigurację i uruchomić wszystkie kroki lokalnie, ponieważ cała praca przebiega w kontenerach.

Metadane budowania wyników

Co z artefaktami i meta-danymi budowy? I tymi wszystkimi ładnymi stronami internetowymi?

Obowiązkiem kontenerów jest przesyłanie wszelkich artefaktów do innych instancji pamięci. Coś jak S3, Artifactory. Zostały zbudowane w celu obsługi dużej liczby żądań i łatwej dystrybucji obciążenia. Metadane budowania powinny również być przesyłane do innych usług metryk, takich jak Casandra.

Strony internetowe buildów?

Cóż, to stanowi problem. Na razie nie mamy prawdziwych rozwiązań. Możesz stworzyć strony Maven dla każdej kompilacji. Może to wyświetlić wszystkie wyniki kompilacji w tak ładnych formatach do wykorzystania przez programistów. Ale pokazywałyby tylko wyniki pojedynczych przebiegów. Tworzenie wskaźników i pulpitów nadal wymaga opracowania zewnętrznego.

Co teraz?

Widzę pustkę w tworzeniu przestrzeni CI/CD. Dostępnych jest kilka różnych frameworków, ale większość z nich wymaga wybrania platformy, na której będą działać, np. Google Cloud Build i AWS Pipelines. Jenkins, w stanie dzisiejszym, po prostu nie działa dobrze. Mogę tylko mieć nadzieję, że Jenkins X wyciągnie wnioski z przeszłości i stanie się prawdziwie nowoczesnym środowiskiem CI/CD, którego wszyscy zapragniemy.

Tak dla jasności, lubię Jenkinsa. Nadal go używam. Chcę używać go w przyszłości i chcę, aby odniósł sukces. Po prostu uważam, że obecna architektura musi zostać zaktualizowana dla świata chmur. Mam nadzieję, że stanie się to w Jenkins X.


Oryginał tekstu w języku angielskim przeczytasz tutaj.

<p>Loading...</p>