Tekton으로 CI/CD 구축하기 - 2

Tekton으로 CI/CD 구축하기

  • 깃허브에 특정 태그를 푸시하면 jib로 이미지를 빌드, 도커 허브로 푸시, 로컬 클러스터로 배포를 진행하는 파이프라인을 Tekton을 통해 만들어보겠습니다.
  • Tekton으로 CI/CD 구축하기 - 1 에서 이어집니다.

Task 생성

  • Tekton Hub에서 사람들이 만든 Task를 볼 수 있으며 자기가 Task를 정의할 수 도 있습니다. 깃허브에서 소스코드를 다운받는 Taks, Image Build Task, Kubectl Deploy Task를 바탕으로 파이프라인을 만들어보겠습니다.

Git-clone

Jib-Build

  • Jib를 통해 Spring 프로젝트를 이미지로 빌드 후 push 하는 Task
  • https://hub.tekton.dev/tekton/task/jib-gradle
  • 프로젝트가 사용하는 gradle은 7.3.2 버전이지만 해당 Task는 5 버전을 사용하고 있기 때문에 그대로 사용할 수 없고 openjdk11 이미지에서 gradlew를 사용해 gradlew jib 명령을 실행하는 새로운 Task를 정의했습니다.
kind: Task
metadata:
  name: jib-gradle
  labels:
    app.kubernetes.io/version: "0.3"
  annotations:
    tekton.dev/pipelines.minVersion: "0.12.1"
    tekton.dev/categories: Image Build
    tekton.dev/tags: image-build
    tekton.dev/displayName: "jib gradle"
    tekton.dev/platforms: "linux/amd64,linux/s390x,linux/ppc64le"
spec:
  description: >-
    This Task builds Java/Kotlin/Groovy/Scala source into a container image using Google’s Jib tool.

    Jib works with Gradle and Maven projects, and this template is for Gradle projects.

  params:
  - name: BUILDER_IMAGE
    description: The location of the gradle builder image
    default: openjdk:11-jdk
  - name: IMAGE
    description: Reference of the image gradle will produce
  - name: DIRECTORY
    description: The directory containing the app, relative to the source repository root
    default: .
  - name: EXTRA_ARGS
    description: Extra arguments to add to the gradle jib build
    default: ""

  workspaces:
  - name: source

  results:
  - name: IMAGE_DIGEST
    description: Digest of the image just built.

  steps:
  - name: build-and-push
    image: $(params.BUILDER_IMAGE)
    workingDir: $(workspaces.source.path)/$(params.DIRECTORY)
    script: |
			chmod +x gradlew
      ./gradlew jib \
          -Djib.to.image="$(params.IMAGE)" \
          $(params.EXTRA_ARGS)

    env:
    - name: HOME
      value: /workspace
    - name: "DOCKER_CONFIG"
      value: $(credentials.path)/.docker/
    volumeMounts:
    securityContext:
      runAsUser: 0
  - name: digest-to-results
    image: $(params.BUILDER_IMAGE)
    script: cat $(workspaces.source.path)/$(params.DIRECTORY)/image-digest | tee /tekton/results/IMAGE_DIGEST
  volumes:
  - name: empty-dir-volume
    emptyDir: {}

Kubectl-Deploy

  • Tekton Hub에 존재하는 Kubectl Task는 kubectl apply 명령을 적용하기 위해 사용
  • https://hub.tekton.dev/tekton/task/kubernetes-actions
  • kubectl apply를 적용하기전에 sed 명령어를 통해 yaml 파일을 수정해주는 새로운 Task를 정의했습니다.
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |

    tekton.dev/categories: Kubernetes
    tekton.dev/displayName: kubernetes actions
    tekton.dev/pipelines.minVersion: 0.17.0
    tekton.dev/platforms: linux/amd64
    tekton.dev/tags: 'CLI, kubectl'
  creationTimestamp: '2022-01-25T01:00:34Z'
  generation: 1
  labels:
    app.kubernetes.io/version: '0.2'
  managedFields:
    - apiVersion: tekton.dev/v1beta1
      fieldsType: FieldsV1
      fieldsV1:
        'f:metadata':
          'f:annotations':
            .: {}
            'f:kubectl.kubernetes.io/last-applied-configuration': {}
            'f:tekton.dev/categories': {}
            'f:tekton.dev/displayName': {}
            'f:tekton.dev/pipelines.minVersion': {}
            'f:tekton.dev/platforms': {}
            'f:tekton.dev/tags': {}
          'f:labels':
            .: {}
            'f:app.kubernetes.io/version': {}
        'f:spec':
          .: {}
          'f:description': {}
          'f:params': {}
          'f:results': {}
          'f:steps': {}
          'f:workspaces': {}
      manager: kubectl-client-side-apply
      operation: Update
      time: '2022-01-25T01:00:34Z'
  name: kubectl-deploy
  resourceVersion: '3052774'
  uid: 44000dca-ba62-4494-bb29-2dc4aa3af86b
spec:
  description: This task is the generic kubectl CLI task which can be used to run all kinds of k8s commands
  params:
    - default: 'gcr.io/cloud-builders/kubectl@sha256:8ab94be8b2b4f3d117f02d868b39540fddd225447abf4014f7ba4765cb39f753'
      name: IMAGE
      type: string
    - name: TAG
      type: string
    - name: YAMLFILE
      type: string
    - name: NAMESPACE
      type: string
  results:
    - description: some result can be emitted if someone wants to.
      name: output-result
  steps:
    - args:
        - '-i'
        - s;latest;$(params.TAG);g
        - $(workspaces.manifest-dir.path)/$(params.YAMLFILE)
      command:
        - sed
      image: alpine
      name: update-yaml
      resources: {}
    - image: $(params.IMAGE)
      name: kubectl
      resources: {}
      script: |
        #!/usr/bin/env bash
        [ "$(workspaces.manifest-dir.bound)" == "true" ] && \
        cd $(workspaces.manifest-dir.path)

        [ "$(workspaces.kubeconfig-dir.bound)" == "true" ] && \
        [ -f $(workspaces.kubeconfig-dir.path)/kubeconfig ] && \
        export KUBECONFIG=$(workspaces.kubeconfig-dir.path)/kubeconfig

        kubectl apply -f $(workspaces.manifest-dir.path)/$(params.YAMLFILE) --namespace $(params.NAMESPACE)

  workspaces:
    - name: manifest-dir
      optional: true
    - name: kubeconfig-dir
      optional: true

  • Tekton DashBoard에서 만든 Task들 확인 가능

Pipeline, PipelineRun 생성

  • Task들을 묶어서 하나의 파이프라인을 만들 수 있습니다.
  • Pipeline을 만들 때 Task에서 정의한 Parameter 값을 새롭게 넣어줄 수 있습니다.

Pipeline

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: tekton-pipeline
spec:
  workspaces:
    - name: pipeline-shared-data
    - name: kubeconfig-dir
  tasks:
    - name: clone-repository
      taskRef:
          kind: Task
          name: git-clone
      params:
        - name: url
          value: "https://github.com/sgwon96/devopsTest"
        - name: revision
          value: "main"
        - name: deleteExisting
          value: "true"
      workspaces:
        - name: output
          workspace: pipeline-shared-data

    - name: build-image
      taskRef:
          kind: Task
          name: jib-gradle
      runAfter:
        - clone-repository
      params:
        - name: IMAGE
          value: "zxcvb5434/devopstest:$(tasks.clone-repository.results.commit)"
      workspaces:
        - name: source
          workspace: pipeline-shared-data

    - name: kubectl-deploy
      taskRef:
          kind: Task
          name: kubectl-deploy
      runAfter:
        - build-image
      params:
        - name: TAG
          value: "$(tasks.clone-repository.results.commit)"
        - name: YAMLFILE
          value: "./k8s/deployment.yaml"
        - name: NAMESPACE
          value: "default"
      workspaces:
        - name: kubeconfig-dir
          workspace: kubeconfig-dir
        - name: manifest-dir
          workspace: pipeline-shared-data
  • params : Task에서 정의한 Parameter에 새로운 값을 넣을 수 있습니다. Pipeline을 생성할 때 마다 값을 변경할 수 있습니다.
  • params를 정의하지 않으면 task에서 정의한 defaultvalue 값이 들어갑니다.
  • runAfters : Task간의 순서를 정의해 줍니다. 해당 값에 들어간 Task가 완료되면 다음 Task가 실행됩니다.
  • Pipleine에서 정의한 Workspace는 PipelineRun에서 입력 받습니다.
  • workspace를 통해 Task들이 공유하는 Persistent Volume Claim을 설정하거나 Kubeconfig 값을 설정 할 수 있습니다.
  • Task들은 Task가 종료될 때 Result를 만들어 다른 Task와 공유할 수 있는데 Git-Clone Task는 커밋의 sha 값을 Result로 생성합니다.
  • 컨테이너 이미지 태그를 commit의 sha값으로 설정했습니다.

PipelineRun

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: tekton-pipelinerun
spec:
  pipelineRef:
      name: tekton-pipeline
  serviceAccountName: tekton-sa
  workspaces:
    - name: pipeline-shared-data
      persistentvolumeclaim:
        claimName: task-pv-claim
    - name: kubeconfig-dir
      configMap:
        name: kubeconfig
  • 깃허브, Docker에 접근하기위한 Sercert 정보를 가지고 있는 Service Account를 PipelineRun에 할당

  • pipeline에서 정의해준 workspace에 persistent volume claim, configmap 할당

결과 확인

  • Tekton DashBoard에서 파이프라인 실행 결과 확인 가능

Trigger를 통해 PipelineRun 자동 생성하기

  • 이전까지는 수동으로 PipelineRun을 생성시켜 Pipeline을 실행 시켰습니다.
  • Tekton은 Trigger를 통해 HTTP 요청으로 event가 발생할 때마다 자동으로 PipelineRun을 생성 할 수 있습니다.
  • GitHub Webhook과 Tekton을 통해 깃허브에 코드가 푸시되면 자동으로 파이프라인을 실행하도록 만들어보겠습니다.

구성요소

  • Trigger : 전달된 이벤트에 대한 검증, 파싱 로직을 실행시킨 뒤 Trigger Template 와 Trigger Binding을 연결
  • EvnetListener : JSON payload를 사용하여 HTTP 기반 이벤트를 처리하는 Kubernetes custom Resource
  • Trigger Bindings : Event와 Trigger를 연결 합니다. EventListener로 들어온 정보를 parsing 해 TriggerTemplate로 전달합니다.
  • Trigger Template : Resource를 Template화 시킴. 파라미터를 통해 기존에 Pipeline에 정의된 Parameter에 새로운 값을 넣을 수 있습니다.

설치

kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/previous/v0.14.2/release.yaml
kubectl get pods -n tekton-pipelines

NAME                                           READY   STATUS    RESTARTS   AGE
tekton-dashboard-68b95c8fd5-mdl7q              1/1     Running   0          7h2m
tekton-pipelines-controller-8695d55cc6-mrg4f   1/1     Running   0          7h3m
tekton-pipelines-webhook-77bd94976b-4ghqj      1/1     Running   0          7h3m
tekton-triggers-controller-5878b4dcdb-q4xjv    1/1     Running   0          2m53s
tekton-triggers-webhook-5d5c4d948d-w4dzv       1/1     Running   0          2m53s
  • tekton에서 제공해주는 yaml 파일을 바탕으로 apply 명령을 사용하면 tekton-trigger-controller, tekton-triggers-webhook이 생성 됩니다.

Trigger Binding

apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
  name: triggerbinding
spec:
  params:
    - name: tag
      value: $(body.ref)
  • https://docs.github.com/en/developers/webhooks-and-events/webhooks/about-webhooks
  • 깃허브 웹훅으로 깃허브에 특정이벤트가 발생할 때마다 POST 요청을 보낼 수 있습니다.
  • POST 요청의 body안에는 github event에 대한 정보가 들어 있는데 ref에는 태그 정보가 들어가 있습니다.
  • Trigger Binding을 사용해 해당 정보를 파이프라인에 params 값으로 넣어주겠습니다.

Trigger Template

apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
  name: triggertemplate
spec:
  params:
    - name: tag
      description: git tag
      default: latest
  resourcetemplates:
    - apiVersion: tekton.dev/v1beta1
      kind: PipelineRun
      metadata:
        generateName: tekton-pipeline-run-
      spec:
        serviceAccountName: tekton-sa
        pipelineRef:
          name: tekton-pipeline
        params:
          - name: tag
            value: $(tt.params.tag)
        workspaces:
          - name: pipeline-shared-data
            persistentvolumeclaim:
              claimName: task-pv-claim
					- name: kubeconfig-dir
						configMap:
							name: k8s-kubeconfig
  • TriggerBinding에서 Parameter를 받아서 PipelineRun 실행합니다.

Pipeline 수정하기

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: tekton-pipeline
spec:
  workspaces:
    - name: pipeline-shared-data
    - name: kubeconfig-dir
  params:
    - name: tag
      type: string
      description: Docker image tag
  tasks:
    - name: clone-repository
      taskRef:
          kind: Task
          name: git-clone
      params:
        - name: url
          value: "https://github.com/sgwon96/devopsTest"
        - name: revision
          value: "main"
        - name: deleteExisting
          value: "true"
      workspaces:
        - name: output
          workspace: pipeline-shared-data

    - name: build-image
      taskRef:
          kind: Task
          name: jib-gradle
      runAfter:
        - clone-repository
      params:
        - name: IMAGE
          value: "zxcvb5434/devopstest:$(params.tag)"
      workspaces:
        - name: source
          workspace: pipeline-shared-data

    - name: kubectl-deploy
      taskRef:
          kind: Task
          name: kubectl-deploy
      runAfter:
        - build-image
      params:
        - name: TAG
          value: "$(params.tag)"
        - name: YAMLFILE
          value: "./k8s/deployment.yaml"
        - name: NAMESPACE
          value: "default"
      workspaces:
        - name: kubeconfig-dir
          workspace: kubeconfig-dir
        - name: manifest-dir
          workspace: pipeline-shared-data
  • 기존에는 Pipeline에 github commit sha 값을 이미지 태그 값으로 설정했지만 Parmeter를 정의해서 Trigger Template에서 깃허브에 푸시된 태그 값을 받아오도록 설정합니다.

Event Listener

apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener
metadata:
  name: trigger-eventlistner
spec:
  serviceAccountName: tekton-triggers-sa
  triggers:
    - bindings:
        - ref: triggerbinding
      template:
        ref: triggertemplate
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tekton-triggers-sa
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: tekton-triggers-role
rules:
# EventListeners need to be able to fetch all namespaced resources
- apiGroups: ["triggers.tekton.dev"]
  resources: ["eventlisteners", "triggerbindings", "triggertemplates", "triggers"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
 # secrets are only needed for GitHub/GitLab interceptors
 # configmaps is needed for updating logging config
  resources: ["configmaps", "secrets"]
  verbs: ["get", "list", "watch"]
 # Permissions to create resources in associated TriggerTemplates
- apiGroups: ["tekton.dev"]
  resources: ["pipelineruns", "pipelineresources", "taskruns"]
  verbs: ["create"]
- apiGroups: [""]
  resources: ["serviceaccounts"]
  verbs: ["impersonate"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: tekton-triggers-rolebinding
subjects:
- kind: ServiceAccount
  name: tekton-triggers-sa
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: tekton-triggers-role
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tekton-triggers-clusterrole
rules:
  # EventListeners need to be able to fetch any clustertriggerbindings
- apiGroups: ["triggers.tekton.dev"]
  resources: ["clustertriggerbindings","clusterinterceptors"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: tekton-triggers-clusterbinding
subjects:
- kind: ServiceAccount
  name: tekton-triggers-sa
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: tekton-triggers-clusterrole
  • Github Webhok Event를 받을 Event Listener와 해당 Event Listener이 PipelineRun을 생성할 수 있게 만들어주는 Service Account와 권한 설정을 해줍니다.
k get pods
NAME                                                  READY   STATUS      RESTARTS   AGE
devops-spring-deployment-7b4c96f45c-fhzzt             1/1     Running     0          20m
el-trigger-eventlistner-7fdcbfcb9-l9mqc               1/1     Running     0          52s
  • 해당 pod을 원래는 Ingress를 통해 외부로 노출시켜줘야 하지만 파이프라인 실행 시연을 위해 port-forward를 사용해 외부로 노출시키겠습니다.
  • kubectl port-forward pod/el-trigger-eventlistner-7fdcbfcb9-l9mqc 9098
  • 공유기에도 port forward 설정을 진행하는 과정은 Kubernetes에 Spring 어플리케이션 배포하기 글을 참고 부탁드립니다.

GitHub Webhook 등록

  • 저장소의 설정 → Webhooks에서 등록

  • 포트포워딩이 설정 된 공인 아이피 주소로 웹훅 설정
  • 브랜치 생성 또는 태그가 생성됬을 때 웹훅 실행

  • final3 Tag를 달아 깃허브로 push하면 웹훅 실행
  • Event Listener로 POST 요청을 보냄

  • Event Listener가 GitHub Webhook으로 POST 요청을 받고 PipelineRun 생성

  • 최종 버전으로 이미지 교체 성공

정리

  • Kubernets 내부에 CI/CD를 구축하기 위해 Tekton을 사용할 수 있습니다.
  • Git-clone,jib-build,kubectl-deploy 3가지 Task를 실행하는 파이프라인을 만들었습니다.
  • GitHub Webhook 과 Event Listener Pod을 통해 깃허브 이벤트를 받아와 파이프라인을 실행시켰습니다.
  • GitHub에 태그나 브랜치를 생성하면 github에서 소스코드를 다운받아 jib로 이미지를 빌드, Docker Hub에 업로드, kubectl apply를 통한 이미지 교체 작업이 실행 됩니다.

Reference

좋은 웹페이지 즐겨찾기