Rura wielogałęziowa Jenkinsa w GKE

1. Przegląd

Jenkins to jedno z najpopularniejszych dostępnych rozwiązań do ciągłej integracji. Służy do automatyzacji najważniejszych elementów procesu tworzenia oprogramowania, które nie wymagają udziału człowieka. Dzięki wdrożeniu Jenkinsa w Kubernetes w Google Cloud i wykorzystaniu wtyczki GKE możemy szybko i automatycznie skalować wykonawców kompilacji w miarę potrzeb. W połączeniu z Cloud Storage możemy tworzyć i testować aplikacje przy minimalnym wysiłku.

Co musisz zrobić

  • Wdrażanie usługi Jenkins w klastrze Kubernetes
  • Wdrażanie i konfigurowanie wtyczki Jenkins GKE, aby umożliwić Jenkinsowi tworzenie i niszczenie podów jako węzłów wykonawczych
  • Kompilowanie i testowanie przykładowej aplikacji SpringBoot
  • Tworzenie i publikowanie kontenera w Google Container Registry
  • Wdrażanie przykładowej aplikacji w środowiskach GKE typu staging i produkcyjnym

Czego potrzebujesz

  • projekt Google Cloud ze skonfigurowanymi rozliczeniami; Jeśli nie masz konta, musisz je utworzyć.

2. Przygotowania

Ten przewodnik można w całości uruchomić na Google Cloud Platform bez konieczności instalowania ani konfigurowania czegokolwiek lokalnie.

Cloud Shell

W tym module praktycznym będziemy udostępniać różne zasoby i usługi w chmurze oraz nimi zarządzać za pomocą wiersza poleceń w Cloud Shell.

Włączanie interfejsów API

Oto interfejsy API, które musimy włączyć w naszym projekcie:

  • Compute Engine API – tworzy i uruchamia maszyny wirtualne.
  • Kubernetes Engine API – tworzy aplikacje oparte na kontenerach i zarządza nimi.
  • Cloud Build API – platforma Google Cloud do ciągłej integracji i ciągłego dostarczania.
  • Service Management API – umożliwia producentom usług publikowanie usług na platformie Google Cloud Platform.
  • Cloud Resource Manager API – tworzy, odczytuje i aktualizuje metadane kontenerów zasobów Google Cloud.

Włącz wymagane interfejsy API za pomocą tego polecenia gcloud:

gcloud services enable compute.googleapis.com \
container.googleapis.com \
cloudbuild.googleapis.com \
servicemanagement.googleapis.com \
cloudresourcemanager.googleapis.com \
--project ${GOOGLE_CLOUD_PROJECT}

Utwórz zasobnik GCS

Aby przesłać testową pracę, potrzebujemy zasobnika GCS. Utwórzmy zasobnik, używając w nazwie identyfikatora projektu, aby zapewnić jej unikalność:

gsutil mb gs://${GOOGLE_CLOUD_PROJECT}-jenkins-test-bucket/ 

3. Tworzenie klastrów Kubernetes

Tworzenie klastra

Następnie utworzymy klaster GKE, który będzie hostować nasz system Jenkins, w tym pody, które będą wysyłane jako węzły robocze. Dodatkowy zakres wskazany przez flagę --scopes umożliwi Jenkinsowi dostęp do Cloud Source Repositories i Container Registry. W Cloud Console wykonaj te czynności:

gcloud container clusters create jenkins-cd \
--machine-type n1-standard-2 --num-nodes 1 \
--zone us-east1-d \
--scopes "https://www.googleapis.com/auth/source.read_write,cloud-platform" \
--cluster-version latest

Wdrożymy też 2 klastry, w których będziemy hostować wersje testowe i produkcyjne naszej przykładowej aplikacji:

gcloud container clusters create staging \
--machine-type n1-standard-2 --num-nodes 1 \
--zone us-east1-d \
--cluster-version latest
gcloud container clusters create prod \
--machine-type n1-standard-2 --num-nodes 2 \
--zone us-east1-d \
--cluster-version latest

28b45298e1e82748.png Zweryfikuj

Po utworzeniu klastrów możemy potwierdzić, że działają one z gcloud container clusters list.

Dane wyjściowe powinny zawierać RUNNING w kolumnie STATUS:

NAME        LOCATION    MASTER_VERSION  MASTER_IP     MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
jenkins-cd  us-east1-d  1.15.9-gke.9    34.74.77.124  n1-standard-2  1.15.9-gke.9  2          RUNNING
prod        us-east1-d  1.15.9-gke.9    35.229.98.12  n1-standard-2  1.15.9-gke.9  2          RUNNING
staging     us-east1-d  1.15.9-gke.9    34.73.92.228  n1-standard-2  1.15.9-gke.9  2          RUNNING

4. Wdrażanie Jenkinsa za pomocą narzędzia Helm

Instalowanie Helma

Do zainstalowania Jenkinsa w klastrze użyjemy Helma, czyli menedżera pakietów aplikacji dla Kubernetes. Na początek pobierz projekt zawierający pliki manifestu Kubernetes, których użyjemy do wdrożenia Jenkinsa:

git clone https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes.git ~/continuous-deployment-on-kubernetes

Zmień aktualny katalog roboczy na katalog projektu:

cd ~/continuous-deployment-on-kubernetes/

Utwórz powiązanie roli klastra, aby przyznać sobie uprawnienia roli administratora klastra:

kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud config get-value account)

Połącz się z klastrem Jenkins, uzyskując jego dane logowania:

gcloud container clusters get-credentials jenkins-cd --zone us-east1-d --project ${GOOGLE_CLOUD_PROJECT}

Pobierz plik binarny Helm do konsoli Cloud:

wget https://storage.googleapis.com/kubernetes-helm/helm-v2.14.1-linux-amd64.tar.gz

Rozpakuj plik i skopiuj zawarty w nim plik Helm do bieżącego katalogu roboczego:

tar zxfv helm-v2.14.1-linux-amd64.tar.gz && \
cp linux-amd64/helm .

Tiller to serwerowa część Helm, która działa w klastrze Kubernetes. Utwórz konto usługi o nazwie tiller:

kubectl create serviceaccount tiller \
--namespace kube-system

Powiąż go z rolą klastra cluster-admin, aby mógł wprowadzać zmiany:

kubectl create clusterrolebinding tiller-admin-binding \
--clusterrole=cluster-admin \
--serviceaccount=kube-system:tiller

Teraz możemy zainicjować Helm i zaktualizować repozytorium:

./helm init --service-account=tiller && \
./helm repo update

28b45298e1e82748.png Zweryfikuj

Sprawdź, czy Helm jest gotowy do użycia – w tym celu wpisz ./helm version. Powinny się wyświetlić numery wersji klienta i serwera:

Client: &version.Version{SemVer:"v2.14.1", GitCommit:"5270352a09c7e8b6e8c9593002a73535276507c0", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.14.1", GitCommit:"5270352a09c7e8b6e8c9593002a73535276507c0", GitTreeState:"clean"}

Instalowanie Jenkinsa

Po zainstalowaniu Helm w klastrze możemy zainstalować Jenkins:

./helm install stable/jenkins -n cd \
-f jenkins/values.yaml \
--version 1.2.2 --wait

28b45298e1e82748.png Zweryfikuj

Sprawdźmy pody:

kubectl get pods

Dane wyjściowe powinny zawierać nasz pod Jenkinsa ze stanem RUNNING:

NAME                          READY     STATUS    RESTARTS   AGE
cd-jenkins-7c786475dd-vbhg4   1/1       Running   0          1m

Sprawdź, czy usługa Jenkins została utworzona prawidłowo:

kubectl get svc

Dane wyjściowe powinny wyglądać mniej więcej tak:

NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
cd-jenkins         ClusterIP   10.35.241.170   <none>        8080/TCP    2m27s
cd-jenkins-agent   ClusterIP   10.35.250.57    <none>        50000/TCP   2m27s
kubernetes         ClusterIP   10.35.240.1     <none>        443/TCP     75m

Instalacja Jenkinsa będzie używać wtyczki Kubernetes do tworzenia agentów kompilacji. W razie potrzeby będą one automatycznie uruchamiane przez serwer główny Jenkinsa. Po zakończeniu pracy są one automatycznie zamykane, a ich zasoby są ponownie dodawane do puli zasobów klastra.

Łączenie z Jenkinsem

Jenkins działa w naszym klastrze, ale aby uzyskać dostęp do interfejsu, skonfigurujmy przekierowanie portów z Cloud Shell:

export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/component=jenkins-master" -l "app.kubernetes.io/instance=cd" -o jsonpath="{.items[0].metadata.name}") &&
kubectl port-forward $POD_NAME 8080:8080 >> /dev/null &

Podczas instalacji wygenerowano hasło administratora. Odzyskajmy go:

printf $(kubectl get secret cd-jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo

U góry Cloud Shell kliknij ikonę podglądu w przeglądarce 7ddf5a65fd556dd6.png i wybierz „Podejrzyj na porcie 8080”.

1d614c831a621cff.png

Powinien pojawić się ekran logowania do Jenkinsa, na którym możesz wpisać admin jako nazwę użytkownika i hasło zwrócone w poprzednim kroku:

9cba23e856cbc84f.png

Po kliknięciu Zaloguj się powinniśmy zostać przekierowani na stronę główną Jenkinsa.

9261f3e914829137.png

5. Instalowanie i konfigurowanie wtyczki GKE

Wtyczka Google Kubernetes Engine umożliwia publikowanie wdrożeń utworzonych w Jenkinsie w klastrach Kubernetes działających w GKE. W projekcie trzeba skonfigurować uprawnienia. Wdrożymy tę konfigurację za pomocą Terraform.

Najpierw pobierz projekt wtyczki GKE:

git clone https://github.com/jenkinsci/google-kubernetes-engine-plugin.git ~/google-kubernetes-engine-plugin

Automatyczna konfiguracja uprawnień

Zmień bieżący katalog roboczy na katalog rbac projektu GKE, który został wcześniej sklonowany:

cd ~/google-kubernetes-engine-plugin/docs/rbac/

gcp-sa-setup.tf to plik konfiguracji Terraform, który utworzy niestandardową rolę uprawnień GCP z ograniczonymi uprawnieniami oraz konto usługi GCP, do którego zostanie przypisana ta rola. Plik wymaga wartości zmiennych projektu, regionu i nazwy konta usługi. Podajemy te wartości, deklarując najpierw te zmienne środowiskowe:

export TF_VAR_project=${GOOGLE_CLOUD_PROJECT}
export TF_VAR_region=us-east1-d
export TF_VAR_sa_name=kaniko-role

Zainicjuj Terraform, wygeneruj plan i zastosuj go:

terraform init
terraform plan -out /tmp/tf.plan
terraform apply /tmp/tf.plan && rm /tmp/tf.plan

Konto usługi będzie potrzebować uprawnień administratora miejsca na dane, aby zapisywać dane w naszym zasobniku Cloud Storage:

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
--member serviceAccount:kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com \
--role 'roles/storage.admin'

Będzie też potrzebować uprawnień do kontenera na etapach wdrażania potoku:

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} --member \
serviceAccount:kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com --role 'roles/container.developer'

Teraz możemy użyć Helm, aby skonfigurować uprawnienia klastra dla wtyczki GKE za pomocą narzędzia do wdrażania robotów GKE. Zmień katalog roboczy na katalog helm projektu GKE:

cd ~/google-kubernetes-engine-plugin/docs/helm/

Zainstaluj za pomocą podanej karty Helm:

export TARGET_NAMESPACE=kube-system && \
envsubst < gke-robot-deployer/values.yaml | helm install ./gke-robot-deployer --name gke-robot-deployer -f -

6. Konfigurowanie Jenkinsa

Klucze kont usługi

Aby konto usługi działało prawidłowo, musimy wygenerować plik klucza prywatnego i dodać go jako wpis tajny Kubernetes. Najpierw wygeneruj plik za pomocą tego polecenia gcloud:

gcloud iam service-accounts keys create /tmp/kaniko-secret.json --iam-account kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com

Utworzymy klucz tajny w magazynie obiektów tajnych Kubernetes za pomocą tego pliku:

kubectl create secret generic jenkins-int-samples-kaniko-secret --from-file=/tmp/kaniko-secret.json 

Pobierz plik JSON na dysk lokalny, klikając opcję Pobierz plik w menu z 3 kropkami w Cloud Shell:

c40378e72013b843.png

Wpisz ścieżkę do pliku /tmp/kaniko-secret.json i kliknij Pobierz.

Wróć na stronę Jenkinsa i w panelu po lewej stronie kliknij kolejno Dane logowania i System.

6c140f7e6bb82f8.png

3b874912cdc8019b.png

W sekcji strony System kliknij po lewej stronie kolejno Globalne dane logowania i Dodaj dane logowania:

4350c0e68561119b.png

3d3526551cdae8b.png

W menu Rodzaj wybierz Konto usługi Google z klucza prywatnego. Wpisz „kaniko-role” jako nazwę, a następnie prześlij klucz JSON utworzony w poprzednich krokach i kliknij OK.

b0502213408e730e.png

Zmienne środowiskowe

Zanim utworzymy potok wielogałęziowy, musimy zdefiniować w Jenkinsie kilka zmiennych środowiskowych. Są to:

  • JENK_INT_IT_ZONE – strefa klastra Kubernetes. W naszym przypadku us-east1-d
  • JENK_INT_IT_PROJECT_ID – identyfikator projektu GCP, w którym znajduje się ta instancja Jenkinsa.
  • JENK_INT_IT_STAGING – nazwa naszego klastra „staging”, na potrzeby demonstracji jest to staging
  • JENK_INT_IT_PROD – nazwa naszego klastra „prod”. W celach demonstracyjnych jest to prod
  • JENK_INT_IT_BUCKET – zasobnik Cloud Storage utworzony w poprzednim kroku.
  • JENK_INT_IT_CRED_ID – odnosi się do danych logowania utworzonych przy użyciu pliku JSON w poprzednim kroku. Wartość powinna być zgodna z nadaną przez nas nazwą, kaniko-role

Aby je dodać, otwórz Zarządzaj Jenkinsem:

d54f279190a07878.png

Następnie kliknij Skonfiguruj system:

ce79d218b2799640.png

Znajduje się tam sekcja Właściwości globalne. Gdy zaznaczymy pole Zmienne środowiskowe, pojawi się przycisk Dodaj, który klikniemy, aby dodać powyższe zmienne jako pary klucz-wartość:

81aa222a2b17b2cc.png

Aby zastosować zmiany, kliknij przycisk Zapisz u dołu strony.

7. Konfigurowanie potoku

W Jenkinsie kliknij „New Item” (Nowy element):

8d1270ce4d7b6a8a.png

Wpisz „jenkins-integration-sample” jako nazwę i wybierz „Multibranch Pipeline” jako typ projektu, a potem kliknij OK:

eb071ecfbb4d775b.png

Nastąpi przekierowanie na stronę konfiguracji potoku. W sekcji Źródła gałęzi wpisz https://github.com/GoogleCloudPlatform/jenkins-integration-samples.git jako Repozytorium projektu. W sekcji Konfiguracja kompilacji wpisz „gke/Jenkinsfile” jako Ścieżkę skryptu.

5135bd6b0374508c.png

Kliknij Zapisz, aby zastosować te ustawienia. Po zapisaniu Jenkins rozpocznie skanowanie repozytorium i kolejną kompilację dla każdej gałęzi. W miarę postępu procesu na stronie Zadania Kubernetes będą tworzone, uruchamiane i usuwane pody.

Po zakończeniu kompilacji na stronie Zadania Kubernetes znajdziesz 2 elementy o nazwie jenkins-integration-samples-gke, z których każdy odpowiada klastrowi produkcyjnemu lub testowemu. Stan będzie OK:

bdec6b1753d1ba07.png

Za pomocą tego polecenia gcloud sprawdzimy, czy obraz kontenera odpowiadający naszemu potokowi został przesłany do Google Container Registry:

gcloud container images list

Aby wyświetlić zadanie w przeglądarce, uzyskaj dane logowania do klastra produkcyjnego:

gcloud container clusters get-credentials prod --zone us-east1-d --project ${GOOGLE_CLOUD_PROJECT}

Uruchom to polecenie, aby skonfigurować przekierowanie portu 8081 z powłoki do portu 8080 obciążenia:

export POD_NAME=$(kubectl get pods -o jsonpath="{.items[0].metadata.name}") &&
kubectl port-forward $POD_NAME 8081:8080 >> /dev/null &

U góry Cloud Shell kliknij ikonę podglądu w przeglądarce i wybierz „Podejrzyj na porcie 8081”.

1b19b5b56f1bae7.png

e80e995e71763bb2.png

8. Czyszczenie

Omówiliśmy już, jak wdrożyć Jenkinsa i przykładowy potok wielogałęziowy w Kubernetes. Teraz musimy usunąć z projektu wszystkie utworzone zasoby.

Usuwanie projektu

Jeśli wolisz, możesz usunąć cały projekt. W konsoli GCP otwórz stronę Cloud Resource Manager:

Na liście projektów wybierz projekt, nad którym pracowaliśmy, i kliknij Usuń. Pojawi się prośba o wpisanie identyfikatora projektu. Wpisz go i kliknij Wyłącz.

Możesz też usunąć cały projekt bezpośrednio z Cloud Shell za pomocą gcloud:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Jeśli wolisz usuwać poszczególne komponenty podlegające opłacie, przejdź do następnej sekcji.

Klaster Kubernetes

Usuń cały klaster Kubernetes za pomocą gcloud:

gcloud container clusters delete jenkins-cd --zone=us-east1-d

Zasobniki Cloud Storage

Usuń wszystkie przesłane pliki i usuń nasz zasobnik za pomocą narzędzia gsutil:

gsutil rm -r gs://${GOOGLE_CLOUD_PROJECT}-jenkins-test-bucket

Obrazy w Google Container Registry

Usuniemy obrazy Google Container Registry za pomocą skrótów obrazów. Najpierw pobierz skróty za pomocą tego polecenia:

gcloud container images list-tags gcr.io/${GOOGLE_CLOUD_PROJECT}/jenkins-integration-samples-gke --format="value(digest)"

Następnie w przypadku każdego zwróconego podsumowania:

gcloud container images delete gcr.io/${GOOGLE_CLOUD_PROJECT}/jenkins-integration-samples-gke@sha256:<DIGEST>

9. Gratulacje!

Super! Udało się. Wiesz już, jak wdrożyć Jenkinsa w GKE i wysyłać zadania do klastrów Kubernetes.

Omówione zagadnienia

  • Wdrożyliśmy klaster Kubernetes i użyliśmy Helma do zainstalowania Jenkinsa.
  • Zainstalowaliśmy i skonfigurowaliśmy wtyczkę GKE, aby umożliwić Jenkinsowi wdrażanie artefaktów kompilacji w klastrach Kubernetes.
  • Skonfigurowaliśmy Jenkinsa tak, aby skonfigurować potok wielogałęziowy, który rozdziela pracę między klastry GKE.