DevOps + GitOps przy użyciu GitLab i ArgoCD – część pierwsza (CI)

Jest to pierwszy artykuł z dwóch o tematyce DevOps + GitOps, wyjaśniających jak zautomatyzować ścieżkę zmian w projekcie od wypchania kodu do repozytorium do publikacji usługi na k8s.

Zagadnienia, jakie będą poruszane to:

  • Co to jest DevOps i GitOps
  • Co to jest ArgoCD
  • Konfiguracja procesu GitLab CI
  • Instalacja i konfiguracja ArgoCD
  • Dobre praktyki GitOps


DevOps – co to jest?

Zacznijmy od najbardziej ogólnego pojęcia, jakim jest DevOps. Pojęcie te łączy dwa obszary: developmentu (Dev) oraz operacji (Ops). Działy, zespoły, osoby zajmujące się DevOps pracują nad tym, żeby tak naprawdę projekty były realizowane łatwiej, szybciej, bezpieczniej. Takie cele są głównie realizowane przez automatyzację developmentu i operacji na projektach.

DevOps a GitOps

Jest to bardzo ogólne pojęcie, ale automatyzacja budowania i publikacji API opisana w dalszej części tej serii artykułów podchodzi pod tą tematykę.


GitOps – co to jest?

GitOps jest to stosunkowo nowy sposób na podejście ciągłego dostarczania (Continuous Delivery). Sprowadza się do przechowywania wszelkiego rodzaju konfiguracji klastrów, serwerów, czy to usług w gicie oraz do nasłuchiwania zmian w takich konfiguracjach. Odpowiednie narzędzia w przypadku takich zmian na podstawie stanu z repozytorium zmieniają stan serwerów, czy usług itd. Sprowadza się do tego, że commit do gita tworzy, modyfikuje, usuwa dany byt. 

Co nam to daje? 

Główną zaletą takiego rozwiązania jest budowanie historii zmian w konfiguracjach, tak jak jest budowana historia zmian kodu poprzez repozytorium gitowe i co za tym idzie łatwe cofanie zmian. Inną zaletą jest centralizacja wszelkiego rodzaju konfiguracji oraz łatwy sposób na wdrażanie usług. Po zastosowaniu odpowiedniego narzędzia jedynym miejscem, gdzie się coś modyfikuje, jest repozytorium gitowe.


ArgoCD – narzędzie do GitOps

ArgoCD jest jednym z najpopularniejszych narzędzi do ciągłego dostarczania przy użyciu technik GitOps. Jest to narzędzie, które na podstawie repozytorium potrafi konfigurować klastry Kubernetes. Istotne jest to, że z poziomu jednej instancji ArgoCD możemy konfigurować wiele klastrów. Narzędzie to wymusza dodatkowo deklaratywne konfigurowanie Kubernetes, bo przecież nie przechowujemy poleceń konfigurujących klaster w repozytorium, tylko pliki, które to robią.

Narzędzie ArgoCD jest jednym z kilku narzędziach projektu Argo do pracy z Kubernetes. Innymi są:

  • Argo Workflows – narzędzie do budowania procesów opartych o kontenery,
  • Argo Rollouts – narzędzie do budowania wdrożeń strategiami Canary i Blue-Green,
  • Argo Events – narzędzie pozwalające na budowanie zależności oparte o przeróżne zdarzenia powiązane z Kubernetes.

Istnieje też narzędzie do CI/CD, które nazywa się Codefresh integrujące wszystkie narzędzia Argo. Adres projektu: https://codefresh.io. Jest to kompleksowa platforma do integracji i wdrożeń aplikacji.

ArgoCD - Declarative GitOps CD for Kubernetes
Źródło obrazka: https://codefresh.io/events/

Do nauki ArgoCD można wykorzystać szkolenie zakończone certyfikatem na platformie Codefresh:
https://codefresh.learnworlds.com/


DevOps + GitOps – ogólnie o procesie docelowym

W artykule opisano kluczowe pojęcia i narzędzia, które będą użyte do zbudowania pełnego procesu od napisania kodu do wdrożenia. Podążając kulturą DevOps zautomatyzujemy cały proces. Część CI związana z integracją kodu będzie zrobiona przy użyciu Gitlab CI, a część CD związaną ze wdrożeniem będzie częściowo zrobiona w Gitlab CI. Będą też użyte założenia GitOps przy użyciu narzędzia ArgoCD.

Opis procesu:

  1. Zmiana w kodzie,
  2. Wypchnięcie kodu do repozytorium,
  3. Zbudowanie kodu przez Gitlab CI,
  4. Zbudowanie obrazu dockerowego,
  5. Wypchnięcie obrazu do rejestru obrazów,
  6. Modyfikacja tagu obrazu w pliku yaml z deploymentem,
  7. Wypchnięcie zmienionego pliku do repozytorium,
  8. Modyfikacja klastra Kubernetes na podstawie zmiany tagu przez ArgoCD.

A więc zaczynamy…


DevOps + GitOps – przygowanie testowego API

W ramach testu do zmian w kodzie przygotujemy proste API napisane w node.js z jednym endpointem z metodą GET. Do tego celu przygotowujemy sobie folder, w którym będzie projekt. W ramach tego folderu inicjalizujemy plik package.json poleceniem:

npm init -y

Będzie on potrzebny do pobrania jedynej zależności do uruchomienia API, czyli pobranie modułu express poleceniem:

npm install express

W folderze tworzymy plik index.js. z treścią:

const express = require('express');
const app = express();

app.get('/', function (req, res) {
   res.send({
       body: "API v1.0." + process.env.BUILD_VERSION
   });
})

const server = app.listen(3000, () => {
   console.log("Przykład działa pod adresem http://localhost:3000");
})

Jest to bardzo proste API zwracające swoją wersję ze zmiennej środowiskowej BUILD_VERSION. Uruchamiamy teraz API, przekazując od razu numer wersji poleceniem:

export BUILD_VERSION=1 && node index.js

Teraz kiedy mamy już zbudowane API, możemy zweryfikować jego działanie np. w Postmanie:

http://localhost:3000/

Nie będziemy chcieli wysyłać folderu node_modules do repozytorium, więc tworzymy plik .gitignore ze wpisem:
/node_modules/


DevOps + GitOps – przygotowanie obrazu dockerowego

Artykuł głównie skupia się na zbudowaniu procesu od zmian w kodzie po wdrożenie, więc zawartość całego pliku nie będzie tutaj szczegółowo opisana. Poniżej jego cała treść:

FROM node:16

ENV NODE_ENV=production
WORKDIR /home/node/app

COPY ./package* ./
RUN npm install --prefer-offline --no-audit && \
   npm cache clean --force

USER node

COPY index.js .

CMD ["node", "index.js"]

Budowanie pliku w NodeJS na przykładzie frameworka NestJS jest dokładniej opisane w artykule API NestJS w Dockerze.


DevOps + GitOps – przygotowanie procesu CI 

Teraz przejdziemy do przygotowania kroku procesu CI do budowania i publikowania API w Gitlab CI. W ramach artykułu nie opisano instalacji i konfiguracji Gitlab, oraz założono, że instancja tej platformy stoi na Kubernetes. W Gitlabie opisujemy cały proces przy użyciu pliku konfiguracyjnego. Tworzymy go w głównym folderze projektu z nazwą .gitlab-ci.yml. Na samym początku tego pliku określamy wszystkie stany naszego procesu:

stages:
 - push
 - deploy

Stany w Gitlab CI grupują zadania tego samego typu. W ramach stanu push wypchniemy nasze API w obrazie do repozytorium, a stan deploy zmodyfikuje plik yaml odpowiedzialny za deployment w Kubernetes. W warunkach produkcyjnych tych stanów jest więcej. Są nimi np.: budowanie, testowanie, weryfikacja bezpieczeństwa.


DevOps + GitOps – automatyczne przygotowywanie obrazu (stan push)

Teraz przejdziemy do zadania, które zbuduje obraz i wypchnie go do repozytorium. Z racji, iż zakładamy, że obraz będzie przygotowywany w środowisku Kubernetes, należy użyć specjalnego narzędzia od Google. W przypadku jakby Gitlab CI było skonfigurowane on promise wystarczy użyć poleceń docker build/docker push.  Narzędzie to jest w postaci obrazu dockerowego, więc zadanie skorzysta z poniższego polecenia:

image:
 name: gcr.io/kaniko-project/executor:debug
 entrypoint: [""]

Ustawienie wartości dla entrypoint z pustą komendą pozwala czytelnie w definicji skryptów na obrazie wykonać całą pracę podczas tego zadania. Przygotujemy trzy skrypty. Pierwszy utworzy w ramach kontenera folder potrzebny do konfiguracji Dockera:

- mkdir -p /kaniko/.docker

Drugi ustawi w konfiguracji Dockera autoryzacje w rejestrze obrazów Gitlaba:

- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json

Zmienne CI_REGISTRY, CI_REGISTRY_USER i CIREGISTRY_PASSWORD są domyślnie przechowywane w procesie CI bez dodatkowej konfiguracji.

Ostatnim krokiem jest wywołanie przygotowanego w obrazie polecenia budującego i wypychającego obraz:

- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY/emil.juchnikowski/argocd-gitlab-example:$CI_COMMIT_BRANCH.$CI_PIPELINE_ID --cache=true

Przekazujemy tutaj parametry w większości te, które byśmy użyli podczas wywołania poceń docker build/docker push:

  • context – kontekst budowania obrazu,
  • dockerfile – ścieżka do pliku Dockerfile,
  • destination – to nazwa obrazu,
  • build-arg – dodatkowy argument przekazywany podczas budowania obrazu,
  • cache – to jest dodatkowy parametr, który zoptymalizuje kolejne wersje obrazu przez cachowanie w rejestrze warstw obrazu.

Do budowania tagów obrazów użyto w tym przypadku kombinacji [nazwa brancha].[numer porządkowy wykonania pipelinu CI].

Całe zdanie wygląda tak:

push:docker:
 stage: push
 image:
   name: gcr.io/kaniko-project/executor:debug
   entrypoint: [""]
 script:
   - mkdir -p /kaniko/.docker
   - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
   - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY/emil.juchnikowski/argocd-gitlab-example:$CI_COMMIT_BRANCH.$CI_PIPELINE_ID --cache=true --build-arg "BUILD_VERSION=$CI_PIPELINE_ID"

Mamy już przygotowany proces przygotowujący obraz, więc możemy go przetestować, wypychając zmiany do repozytorium gita. Po wywołaniu git push, Hook nasłuchujący w GitLabie rozpocznie wykonywanie zadania. Stan realizacji możemy podejrzeć na stronie GitLaba przechodząc w menu do strony CI/CD -> Pipelines. Po poprawnym wykonaniu procesu powinniśmy zobaczyć taki widok:

CI/CD Pipeline

Możemy też zweryfikować czy obraz przygotowany znajduje się w repozytorium przechodząc w menu do strony Packages & Registries -> Container Registry:

Container Registry

Jak widać stworzył się obraz z cachem oraz obraz z API. Interfejs GitLaba pozwala na usuwanie wszystkich obrazów i tych z konkretnym tagiem. Możliwe jest też ustawianie reguł tak, żeby po pewnym czasie automatycznie pewne obrazy były usuwane.


Podsumowanie

Artykuł przedstawił ogólnie pojęcia i narzędzia użyte w procesie, a także uproszczony proces CI. W kolejnym artykule, który będzie kontynuacją tematu, będzie o przygotowaniu procesu CD w podejściu GitOps z użyciem ArgoCD, oraz o najlepszych praktykach związanych z podejściem GitOps.


Autor

Emil Juchnikowski - autor artykułu DevOps + GitOps przy użyciu Gitlab i ArgoCD - część I (CI)

Emil Juchnikowski — pracuje jako Architekt Oprogramowania. Swoją karierę rozpoczął jako .NET Developer ponad 10 lat temu. Teraz już z technologią .NET ma bardzo mało wspólnego. Rozwija się w technologiach JS, w różnych kierunkach: frontend Angular i Ionic, backend NodeJS i NestJS, bazy danych MongoDB. Poza tym zajmuje się też zagadnieniami DevOps, GitOps opartymi o konteneryzację w Kubernetes. Poza pracą lubi biegać na długie dystanse oraz trenuje boks. Jego motto to: Jedynym ograniczeniem jest nasza wyobraźnia… a niektórzy twierdzą, że jeszcze czas i pieniądze.







.

Leave a Comment

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *