Czy CI/CD może być fascynujące? Część 2

Terraform

Czy CI/CD może być fascynujące? Część 2

07/05/2021
Podziel się

Wstęp

Niniejszy artykuł jest kontynuacją pierwszej części publikacji, w której skupiłem się na stworzeniu aplikacji w Go, IaC z wykorzystaniem Terraform, a także obiektów Kubernetesowych z wykorzystaniem Kustomiza.

W ramach przypomnienia – stan środowiska na chwilę obecną prezentuje się następująco:

  • skonteneryzowana aplikacja;
  • dwa klastry Kubernetesa w chmurze publicznej Google;
  • deployment tworzony za pomocą Kustomiza.

O czym dokładnie będzie artykuł?

Postaram się dziś przedstawić prosty proces CI/CD, który będzie łatwy do modyfikacji i utrzymania, a jego stworzenie nie będzie czasochłonne.

Użyte technologie
  • GitHub Action
  • Bash
  • Istniejące komponenty
Diagram infrastruktury

Implementacja rozwiązania

Manifesty GitHub Action

Czym w ogóle jest GitHub Action? Jest to system CI/CD hostowany po stronie GitHuba, darmowy do pewnej puli wywołań, oparty na Azure DevOps. Rozwiązanie jest dopracowane i bardzo przyjemne w użyciu. Świetnie się sprawdza w przypadku typowych aplikacji webowych oraz klasycznych przepływów. Minusem jest konieczność używania GitHuba jako repozytorium.

Struktura

Całość implementacji opiera się na stworzeniu trzech plików w folderze ’.github/workflows’ i umieszczeniu ich w poszczególnych repozytoriach.

Repozytorium aplikacyjne

Tutaj dodany został jeden plik, wyglądający następująco:

---
name: Build app and manifests

on: [push]

env:
  APP_NAME: "go-hello-world"

jobs:

  build-image:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: google-github-actions/setup-gcloud@master
        with:
          project_id: ${{ secrets.PROJECT_ID }}
          service_account_key: ${{ secrets.GCP_SA_KEY }}
          export_default_credentials: true
      - run: gcloud info
      - name: auth to CR
        run: gcloud auth configure-docker
      - name: build app
        run: docker build -f $(pwd)/Dockerfile $(pwd) -t $APP_NAME
      - name: tag app
        run: docker tag $APP_NAME gcr.io/${{ secrets.PROJECT_ID }}/$APP_NAME:${{ github.sha }}
      - name: push image
        run: docker push gcr.io/${{ secrets.PROJECT_ID }}/$APP_NAME:${{ github.sha }}

  deploy-k8s-manifests:
    needs: build-image
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: imranismail/setup-kustomize@v1
        with:
          kustomize-version: "4.0.4"
      - run: git clone https://${REPO_TOKEN}@github.com/linuxpolska/k8s-infra-and-objects.git
        env:
          REPO_TOKEN: ${{secrets.REPO_TOKEN}}
      - run: cd k8s-infra-and-objects && git checkout dev
      - run: cd k8s-infra-and-objects/kustomize/dev && kustomize edit set image hello=gcr.io/${{ secrets.PROJECT_ID }}/$APP_NAME:${GITHUB_SHA}
      - run: cd k8s-infra-and-objects/kustomize/prod && kustomize edit set image hello=gcr.io/${{ secrets.PROJECT_ID }}/$APP_NAME:${GITHUB_SHA}
      - run: git config --global user.email "3sky@protonmail.com"
      - run: git config --global user.name "3sky"
      - run: cd k8s-infra-and-objects && git add . && git commit -m "Set hello image tag to ${GITHUB_SHA}" && git push origin dev

Przedstawiony proces jest dość prosty. Akcja uruchamia się przy pushu do repozytorium, niezależnie od gałęzi. W kroku `build-image` pobieramy kod aplikacji, logujemy się do naszego rejestru w GCP, budujemy i tagujemy kontener, po czym umieszczamy go we wspomnianym zasobie. Drugi krok jest bardziej zawiły. Pobieramy kod, instalujemy kustomize w wybranej wersji, pobieramy repozytorium manifestów za pomocą tokena. Pobieramy branch dev, następnie modyfikujemy manifesty o nową wersję aplikacji, po czym umieszczamy zmodyfikowane pliki w repozytorium, także w gałęzi dev.

Repozytorium manifestów

Tutaj akcje zostały podzielone na dwa pliki, aby zwiększyć czytelność podziału przepływów.

  • dev.yaml
---
name: Deploy app to K8S pipeline - DEV

on:
  push:
    branches:
      - dev

env:
  APP_NAME: "go-hello-world"
  CLUSTER_NAME: "gke-dev"
  CLUSTER_ZONE: "europe-west3-a"

jobs:

  deploy-to-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          ref: dev
      - uses: imranismail/setup-kustomize@v1
        with:
          kustomize-version: "4.0.4"
      - uses: google-github-actions/setup-gcloud@master
        with:
          project_id: ${{ secrets.PROJECT_ID }}
          service_account_key: ${{ secrets.GCP_SA_KEY }}
          export_default_credentials: true
      - name: install kubectl
        uses: azure/setup-kubectl@v1
      - name: config cluster
        run: gcloud container clusters get-credentials $CLUSTER_NAME --zone $CLUSTER_ZONE
      - name: Update the cluster
        run: cd kustomize && kustomize build dev | kubectl apply -f -

Powyższa akcja uruchamia się tylko w przypadku wypchnięcia kodu do gałęzi dev. Pobiera ona kod ze wskazanej referencji, instaluje kustomize oraz kubectl, loguje się GKE, po czym aplikuje zmiany na działający klaster. Jest to proste wdrożenie zmiany na wybrane środowisko.

  • main.yaml
---
name: Deploy app to K8S pipeline - PROD

on:
  push:
    branches:
      - main

env:
  APP_NAME: "go-hello-world"
  CLUSTER_NAME: "gke-prod"
  CLUSTER_ZONE: "europe-west3-a"

jobs:

  deploy-to-prod:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: imranismail/setup-kustomize@v1
        with:
          kustomize-version: "4.0.4"
      - uses: google-github-actions/setup-gcloud@master
        with:
          project_id: ${{ secrets.PROJECT_ID }}
          service_account_key: ${{ secrets.GCP_SA_KEY }}
          export_default_credentials: true
      - name: install kubectl
        uses: azure/setup-kubectl@v1
      - name: config cluster
        run: gcloud container clusters get-credentials $CLUSTER_NAME --zone $CLUSTER_ZONE
      - name: Update the cluster
        run: cd kustomize && kustomize build prod | kubectl apply -f-

Jak widać proces tutaj jest dokładnie taki sam, jak w przypadku niższych środowisk. Z jedną małą różnicą. Akcja uruchamia się tylko w przypadku pusha do gałęzi main. I to właśnie tutaj w nieskomplikowany sposób zaimplementowana została metodyka GitOps. Po wdrożeniu na dev wystawiamy pull request. Osoba z odpowiednimi uprawnieniami weryfikuje wdrożenia i w przypadku spełnienia kryteriów jakości akceptuje zmianę. W tym momencie uruchamia się powyższa akcja, wdrażając aplikacje na środowisko produkcyjne.

Podsumowanie

Jak widać omawiany przepły jest dość prosty. Nic jednak nie stoi na przeszkodzie, aby dodać do niego kolejne elementy w postaci testów jednostkowych, kontroli bezpieczeństwa, pełnej automatyzacji RP’ów, łącznie z przypisaniem konkretnej osoby. Co do wykorzystanego narzędzia, GitHub Action bardzo dobrze integruje się z kodem źródłowym i zdarzeniami, które zachodzą nad repozytorium. Dużym plusem jest szeroki zakres akcji, które można wykorzystać. Dzięki nim, nie musimy sami implementować poszczególnych działań, a możemy wykorzystać zamknięty twór zachowujący się jak funkcja. W związku z tym zachęcam do eksperymentów, szczególnie w przypadku projektów, które można potraktować jako naukę własną.

Zobacz również

Dodaj komentarz

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

    Skontaktuj się z nami