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

.·2022년 3월 29일
0
post-thumbnail

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

profile
지금부터 공부하고 개발한것들을 꾸준하게 기록하자.

0개의 댓글

관련 채용 정보