0부터 시작하는 TEKTON 공부 - CI/CD Pipeline 구축 - Trigger를 통한 Pipeline 자동 실행

Jaehong Lee·2023년 5월 2일
2
post-thumbnail

Deploy 구축에서 이어서 진행된다

https://velog.io/@lijahong/0%EB%B6%80%ED%84%B0-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-TEKTON-%EA%B3%B5%EB%B6%80-CI-CD-Pipeline-%EA%B5%AC%EC%B6%95-Deploy-%ED%95%98%EA%B8%B0

완성된 Pipeline & Trigger 파일은 GitHub를 참조하자

https://github.com/lijahong/Tekton-Pipeline-GitHub


1. Tekton Trigger

Trigger 란

  • Tekton Trigger는 특정 Event를 WebHook을 통해 받아 등록된 Pipeline을 자동으로 실행시켜주는 컴포넌트이다
  • WebHook이란 WebPage or WebApp에서 발생하는 Event( 특정 행동 ) 데이터를 다른 애플리케이션에게 실시간으로 제공하기 위한 방법이다

Tekton Trigger를 이용하여 Repo에 Push Event가 발생하면, 자동으로 Pipeline이 실행되게 할 수 있다


Trigger 구축 구조

  • 전체 구조는 위와 같다

  • EventListener는 Pod로 동작한다. EventListener를 배포하면 Deploy와 SVC가 배포된다

  • EventListener는 외부로부터 Event를 받아야 하므로, Ingress를 통해 EventListener Pod를 외부에 노출시켜야 한다

실제 동작을 하는 것은 EventListener Pod이다. Event를 받아서 내부 Trigger의 TriggerBinding과 TriggerTemplate을 참고하여, PipelineRun을 생성해서 Pipeline을 실행시켜준다

  1. GitHub에서 Event가 발생하면, EventListener Pod는 WebHook을 통해 Json payload를 Event로 받는다. 받은 Json payload를 내부의 Trigger에게 전달한다
  2. Trigger는 받은 Event를 검증 및 파싱하고, Trigger Binding에게 전달한다. Trigger는 Trigger Binding과 Trigger Template을 연결해준다
  3. Trigger Binding은 Trigger로부터 받은 Event 데이터를 연결된 Trigger Template의 파라미터와 매핑해준다
  4. EventListener Pod는 Trigger Template을 참고하여 PipelineRun을 생성한다. 이를 통해 Pipeline이 실행된다
  5. Pipeline이 실행되면, Pipeline의 Task들을 실행시키는 TaskRun Pod가 배포된다

2. SA For Trigger

EventListener는 실제 Pod로 동작한다. 동작하면서 TriggerBindings, TriggerTemplate, Interceptor와 같은 Trigger 오브젝트 / PipelineRun, TaskRun과 같은 Tekton Pipeline 오브젝트 / configMape, Secret, ServiceAccount와 같은 Kubernetes 오브젝트를 사용해야 하므로, 적절한 권한이 부여된 ServiceAccount가 필요하다

참조 : https://github.com/tektoncd/triggers/tree/v0.8.1/examples/role-resources


ServiceAccount

apiVersion: v1
kind: ServiceAccount
metadata:
  name: trigger-sa
  namespace: hongspace
  • EventListener가 동작하면서 Tekton 오브젝트 및 Kubernetes 오브젝트를 사용하기 위한 ServiceAccount를 생성하자

Role

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: tekton-triggers-role
  namespace: hongspace
rules:
# EventListeners need to be able to fetch all namespaced resources
- apiGroups: ["triggers.tekton.dev"]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: [""]
 # secrets are only needed for GitHub/GitLab interceptors
 # configmaps is needed for updating logging config
  resources: ["configmaps", "secrets"]
  verbs: ["get", "list"]
 # Permissions to create resources in associated TriggerTemplates
- apiGroups: ["tekton.dev"]
  resources: ["pipelineruns", "taskruns"]
  verbs: ["create"]
- apiGroups: [""]
  resources: ["serviceaccounts"]
  verbs: ["impersonate"]

---

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"]
  • Role과 Cluster Role이 필요하다

Role의 경우 아래와 같다

  • Trigger 리소스에 대해 모든 작업이 가능해야 한다
  • Interceptors 사용 시, Repo 인증 작업을 위해 ConfigMap과 Secrets에 대해 조회가 가능해야 한다
  • Pipeline을 실행해야 하므로, pipelineruns와 taskruns를 생성할 수 있어야 한다
  • Impersonate는 다른 보안주체 ( ServiceAccount, User, Group )을 이용하여 Api 요청을 보낼 수 있게 하는 조작이다. 다른 ServiceAccount를 이용할 수 있게 해줘야 한다

Cluster Role의 경우 아래와 같다

  • clustertriggerbindings과 clusterinterceptors에 대해 조회가 가능해야 한다

RoleBinding

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: tekton-triggers-clusterbinding
subjects:
- kind: ServiceAccount
  name: trigger-sa
  namespace: hongspace
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: tekton-triggers-clusterrole
  • ServiceAccount와 Role & ClusterRole을 Binding 해주자

3. Trigger 구축

참조 : https://tekton.dev/docs/triggers/
참조 : https://github.com/tektoncd/triggers/tree/release-v0.23.x/examples/v1beta1

TriggerBinding

TriggerBinding은 Trigger로부터 받은 Event 데이터를 연결된 TriggerTemplate 파라미터와 매핑한다

  • 쉽게 말해, EventListener에 들어온 Json Payload의 데이터를 TriggerTemplate의 파라미터로 사용할 수 있도록 매핑해주는 것이다
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding
metadata:
  name: hong-pb
  namespace: hongspace
spec:
  params:
  - name: imagetag
    value: $(body.head_commit.id)
  - name: giturl
    value: $(body.repository.url)
  • TriggerBinding을 정의하자
  • image tag는 Json Payload에 들어있는 Commit 번호를 이용하여, Build할 Image의 Tag를 지정하기 위한 파라미터이다
  • giturl은 Json Payload에 들어있는 Repo url을 이용하여, Clone할 Repo 주소를 지정하기 위한 파라미터이다

TriggerTemplate

TriggerTemplate은 TriggerBinding으로부터 받은 파라미터를 이용하여 어떤 PipelineRun을 생성할지 정의한다

  • 실제 PipelineRun을 생성해주는 것은 EventListener Pod이다. TriggerTemplate과 TriggerBinding을 참고하여 PipelineRun을 생성한다
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerTemplate
metadata:
  name: hong-tt
  namespace: hongspace
spec:
  params:
    - name: giturl # git repo url
    - name: imagetag # image tag in commit message
  resourcetemplates: # automate create pipelinerun
    - apiVersion: tekton.dev/v1beta1
      kind: PipelineRun
      metadata:
        namespace: hongspace
        generateName: hong-cicd-run-
      spec:
        serviceAccountName: build-bot # sa for git & docker credientials -> pipeline will use for clone private repo
        pipelineRef: # select pipeline
          name: hong-cicd
        podTemplate: # setting taskpod
          securityContext:  # pod security
            fsGroup: 65532 # set volume owner gid = 65532
        workspaces: # volume for clone code
        - name: shared-data
          volumeClaimTemplate: # automatically create pvc -> create pv
            spec:
              accessModes:
              - ReadWriteOnce
              resources:
                requests:
                  storage: 1Gi

        params: # git address
        - name: repo-url
          value: $(tt.params.giturl)
        - name: image-reference
          value: lijahong/tektontest:$(tt.params.imagetag)
        - name: kubectl-args
          value:
           - "apply"
           - "-f"
           - "test.yaml"
  • TriggerTemplate을 정의하자
  • spec에는 TriggerBinding으로부터 받을 파라미터 정의와 생성할 PipelineRun 정의를 한다
  • resouretemplates를 통해 정의한 PipelineRun을 자동으로 생성할 수 있다. PipelineRun 정의는 이전 편에서 정의한 내용과 동일하다
params: # git address
        - name: repo-url
          value: $(tt.params.giturl)
        - name: image-reference
          value: lijahong/tektontest:$(tt.params.imagetag)
  • TriggerBinding으로부터 받아온 파라미터는 $(tt.params.파라미터이름) 형식으로 사용해야 한다

이전에 생성한 PipelineRun 매니페스트 파일은 이제 사용하지 않는다


EventListener

EventListener는 실제 Pod로 동작하며, 외부에서 Event를 전달 받으면 내부 Trigger의 TriggerBinding과 TriggerTemplate을 참고하여 PipelineRun을 생성한다

Trigger는 따로 정의하는 방법과 EventListener 안에 정의하는 방법이 있다. 이번에는 EventListener 안에 정의하는 방법을 사용하겠다

apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: hong-eventlistner
  namespace: hongspace
spec:
  serviceAccountName: trigger-sa
  triggers:
    - name: hong-trigger
      bindings:
       - ref: hong-pb
      template:
         ref: hong-tt
  • EventListener를 정의하자. EventListener Pod를 실행할 ServiceAccount를 지정하고, Trigger를 정의하면 된다
  • Trigger에는 연결할 TriggerBindings와 TriggerTemplate을 정의하면 된다

EventListener를 생성하면 Deployment와 ClusterIp Service가 배포된다


Ingress-Trigger

생성한 EventListner는 Github Repo로부터 WebHook을 통해 Event를 받아야 한다. 따라서 EventListener Pod를 외부에 노출시켜야 하는데, 현재 EKS 환경이므로 Ingress를 이용하여 외부에 노출시키겠다

[ec2-user@ip-100-0-1-19 trigger]$ k get svc -n hongspace | grep el
el-hong-eventlistner   ClusterIP      10.100.26.104   <none>                                                                         8080/TCP,9000/TCP   3h54m
  • EventListener Service Name을 확인하자
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tekton-trigger-ingress
  namespace: hongspace
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
    - http:
        paths:
          - path: /tektontrigger
            pathType: Prefix
            backend:
              service:
                name: el-hong-eventlistner
                port:
                  number: 8080
  • Ingress를 정의하자
  • Nginx Ingress Controller 앞 단에 위치한 Load Balancer를 이용하여, EventListener Pod가 외부에서 Event를 받게 할 것이다

이전에 Tekton DashBoard를 외부에 노출시킬 때는 Ingress에 host 주소를 지정하여 사용하였다. 이 경우는 로컬 컴퓨터 hosts에 해당 Dns 주소에 대한 Ip 주소를 설정하였기에 가능한 방법이다. 지금은 GitHub에서 접근할 주소에 대해 설정하는 것이므로 host 주소를 사용할 수 없다


WebHook 등록

  • Clone할 Repo의 설정에 들어가자

-Webhooks에 들어가자

[ec2-user@ip-100-0-1-19 trigger]$ k get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP                                                                    PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.100.215.111   ********************.ap-northeast-2.elb.amazonaws.com   80:31676/TCP,443:31231/TCP   25d
  • ingress-nginx-controller LB의 DNS 주소를 확인하자

  • add Webhook을 눌러서 Webhook을 추가하자. Payload를 보낼 URL과 Payload Type을 지정하면 된다
  • ingress에서 Path를 /tektontrigger로 설정했으므로, PayLoad URL에는 http://'LB DNS 주소'/tektontrigger를 입력하면 된다
  • Content type은 json으로 지정하자

  • 추가했을 때, 위와 같이 체크 모양이 나오면 잘 등록된 것이다

Pipeline & Task 수정

우리는 Repo에 저장된 매니페스트 파일을 이용하여 배포한다. Push가 일어날 때마다 Commit ID를 이용하여 새로운 Image를 Build해서 배포하는데, 매니페스트 파일은 Repo에 담겨 있으므로 Image Tag 내용이 적용되지 않는다

  • 따라서 Pipeline과 Task를 수정하여, Clone한 Deploy 매니페스트 파일 내용에 Build한 Image Tag 내용을 적용하자

Pipeline.yaml

- name: deploy
    runAfter: ["build-push"]
    taskRef:
      name: kubernetes-actions
    workspaces:
      - name: manifest-dir
        workspace: shared-data
    params:
      - name: args
        value: $(params.kubectl-args[*])
      - name: deployimage
        value: $(params.image-reference)
  • Pipeline 매니페스트 파일에 들어가서 내용을 추가하자
  • 넘겨줄 파라미터 정의를 추가하자. 해당 파라미터는 Deploy 매니페스트 파일 수정에 필요한 Image 이름 & Tag 정보를 인자 값으로 갖는다

Kubecommand.yaml

params:
    - name: script  # cli command, you setting
      description: The Kubernetes CLI script to run
      type: string
      default: "kubectl $@" # $@ means use all parameter and all parameter is individual. parameter is args
    - name: args
      description: The Kubernetes CLI arguments to run # args for kubectl command. if you not describe script. then default script " kubectl $@ " activate and use this args like "get" "pod"
      type: array # multiple args can come
      default:
        - "help"
    - name: image # image for use kubectl to our cluster or other cluster ( if you set kubeconfig )
      default: gcr.io/cloud-builders/kubectl@sha256:8ab94be8b2b4f3d117f02d868b39540fddd225447abf4014f7ba4765cb39f753 #image is huge
      description: Kubectl wrapper image
    - name: deployimage # deploy image, which kaniko build
      description: Name (reference) of the image to build.
  steps:
    - name: kubectl
      image: $(params.image)
      script: |
        #!/usr/bin/env bash

        # if workspace which yaml file saved has bound, go to that directory
        [[ "$(workspaces.manifest-dir.bound)" == "true" ]] && \
        cd $(workspaces.manifest-dir.path)

        # update yaml file image name
        sed -i "s@changetagname@$(params.deployimage)@" test.yaml

        # if kubeconfig workspace has bound and kubeconfig file exist, set env for kubeconfig path
        [[ "$(workspaces.kubeconfig-dir.bound)" == "true" ]] && \
        [[ -f $(workspaces.kubeconfig-dir.path)/kubeconfig ]] && \
        export KUBECONFIG=$(workspaces.kubeconfig-dir.path)/kubeconfig

        # do kubectl command
        $(params.script)

      args: # args for kubectl command
        - "$(params.args)"
  • Deploy를 담당하는 Task의 매니페스트 파일 내용을 수정하자
  • Params에 넘겨 받을 파라미터를 정의하자
  • steps에 sed 명령어를 추가하면 된다. Clone한 매니페스트 파일의 배포할 Image 정의 부분을 Build한 Image 이름 & Tag로 바꿔주는 것이다

  • Repo에 저장된 매니페스트 파일 내용도 수정해주자. 배포할 Image 정의 부분을 sed가 인식하기 위해서, 지정한 키워드로 수정해주면 된다. 우리는 changetagname이라는 키워드를 사용할 것이다

5. 결과 확인

오브젝트 생성 확인

[ec2-user@ip-100-0-1-19 trigger]$ k get triggerbindings -n hongspace
NAME      AGE
hong-pb   4h
[ec2-user@ip-100-0-1-19 trigger]$ k get triggertemplates -n hongspace
NAME      AGE
hong-tt   4h
[ec2-user@ip-100-0-1-19 trigger]$ k get eventlistener -n hongspace
NAME                ADDRESS                                                        AVAILABLE   REASON                     READY   REASON
hong-eventlistner   http://el-hong-eventlistner.hongspace.svc.cluster.local:8080   True        MinimumReplicasAvailable   True
[ec2-user@ip-100-0-1-19 trigger]$ k get ingress -n hongspace
NAME                     CLASS    HOSTS   ADDRESS                                                                        PORTS   AGE
tekton-trigger-ingress   <none>   *       *******************************.elb.amazonaws.com   80      4h
  • 생성한 오브젝트들을 확인하자
[ec2-user@ip-100-0-1-19 trigger]$ k get deploy -n hongspace | grep el
el-hong-eventlistner   1/1     1            1           4h1m
[ec2-user@ip-100-0-1-19 trigger]$ k get pod -n hongspace | grep el
el-hong-eventlistner-5dc7b955bb-98xdc   1/1     Running     0          4h1m
[ec2-user@ip-100-0-1-19 trigger]$ k get svc -n hongspace | grep el
el-hong-eventlistner   ClusterIP      10.100.26.104   <none>                                                                         8080/TCP,9000/TCP   4h2m
  • EventListener가 실제로 Pod로 배포되었으며, ClusterIP Service가 배포된 것을 확인할 수 있다

동작 확인

  • index.html 내용을 수정해서 Push 해주자

  • Commit ID를 확인하자

  • Push를 하면, Tekton Trigger를 통해 PipelineRun이 생성되어 Pipeline이 실행되었다

  • 새로 Build한 Image가 Docker Hub Repo에 Push 되었다
[ec2-user@ip-100-0-1-19 task]$ k get deploy -n hongspace
NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
tektontest             5/5     5            5           4h21m
[ec2-user@ip-100-0-1-19 task]$ k get pod -n hongspace
NAME                                    READY   STATUS      RESTARTS   AGE
tektontest-57f568cfd6-9gckz             1/1     Running     0          4h21m
tektontest-57f568cfd6-b5wxh             1/1     Running     0          4h21m
tektontest-57f568cfd6-dtsgc             1/1     Running     0          4h21m
tektontest-57f568cfd6-hz456             1/1     Running     0          4h21m
tektontest-57f568cfd6-p9vlw             1/1     Running     0          4h21m
[ec2-user@ip-100-0-1-19 task]$ k get svc -n hongspace
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP                                                                    PORT(S)             AGE
svc-lb-hong            LoadBalancer   10.100.28.234   *******************.ap-northeast-2.elb.amazonaws.com   80:30347/TCP        4h21m
  • Pod와 Load Balancer Service가 잘 배포되었다

  • Deployment의 상세 정보를 확인하면, Pod 배포에 Commit ID를 이용하여 Build한 Image를 사용한 것을 확인할 수 있다

  • Load Balancer를 통해 Pod에 잘 접속되며, 수정한 index.html 내용이 Image에 잘 적용되었다
profile
멋진 엔지니어가 될 때까지

0개의 댓글