Devops

Tekton 사용해보기 (2) Tekton으로 인프라를 자동 배포하는 Terraform Pipeline을 만들어보자

Seungwoo Lee 2022. 11. 6. 18:13

본 포스팅은 "Tekton 사용해보기" 시리즈의 두 번째 글입니다.

 

해당 시리즈의 첫 번째 글은 Tekton 사용해보기 (1) Tekton으로 쿠버네티스 CI/CD 파이프라인을 구성해보자 에서 볼 수 있습니다.

 

 

이번 포스팅에서는 Tekton을 이용해서 Terraform으로 인프라를 자동 배포하는 파이프라인을 구성해보겠습니다.

 

저번 포스팅에서 소개했듯이 Tekton은 사용자가 자유롭게 취사선택할 수 있는 빌딩 블록을 제공하기 때문에 원하는 작업을 수행하는 파이프라인을 유연하게 구성할 수 있다는 장점이 있는데요.

 

Terraform 파이프라인도 이러한 Tekton의 유연한 빌딩 블록의 장점을 활용해서 구성한 것이라 할 수 있겠습니다.

 

이번 Tekton 사용해보기의 두 번째 글을 통해서는 Tekton으로 Terraform 배포 파이프라인을 어떻게 구성할 수 있는지에 대해서 알아보겠습니다.

 

 

1. Tekton 파이프라인 개요

 

 

이번 포스팅에서 소개할 Tekton 파이프라인의 아키텍쳐 다이어그램은 위와 같습니다.

 


파이프라인이 실행할 작업들을 순서대로 나열하면 아래와 같습니다.

 

1. Terraform 코드가 존재하는 Git repository에 Push 작업을 제출합니다.

 

2. 동시에 Git repository는 Kubernetes Ingress로 노출되어 있는 Tekton Eventlistener에게 Webhook을 호출합니다.

 

3. Tekton Eventlistener는 이벤트를 감지해 지정된 Tekton Pipline을 Trigger합니다.

 

4. Tekton Pipeline은 git-clone Task를 실행해 Webhook을 호출한 Git repository를 Clone해 저장장치에 저장합니다.

 

5. 이후 terraform-cli Task를 실행해 Terraform init 및 지정한 Terraform 명령어를 실행합니다.

 


 

위 Terraform 파이프라인의 실제 사용 결과는 아래와 같습니다.

 

1. Terraform 코드가 존재하는 Git repository를 준비합니다. 

 

2. 해당 Repository에 Webhook을 Trigger하기 위해 Push 작업을 제출합니다.

 

3. Webhook을 감지한 Tekton EventListener가 Tekton Pipeline을 실행합니다. Pipeline은 실행되며 git-clone 및 terraform 커맨드를 실행합니다.

 

4. Tekton Pipeline이 실행되며 실제 인프라가 배포된 것을 확인합니다.

 

 

본 포스팅에서는 Git repository로 Bitbucket을, Cloud Provider는 Google Cloud Platform를, Tekton 환경은 GKE를 사용했습니다.

 

하지만 파이프라인 구성은 유연하게 가져갈 수 있기 때문에 위 컴포넌트들은 Github, AWS, Azure, EKS 등의 다른 구성요소로 대체 가능합니다.

 

 

2. Tekton Pipeline 구성요소

 

 

이번 파이프라인에서 사용되는 Tekton 구성 요소들은 위와 같은 관계도를 가지게 됩니다.

 

EventListener, TriggerBinding, Pipeine, Task 등의 Tekton 구성요소들에 대한 설명이 필요하다면 이전 글인 

Tekton 사용해보기 (1) Tekton으로 쿠버네티스 CI/CD 파이프라인을 구성해보자 를 참고하시기 바랍니다.

 

이제 파이프라인을 구성하는 Tekton 빌딩 블록들을 분석해보며 어떤 코드로 이루어져 있는지 살펴보겠습니다.


2-1. Task "git-clone"

첫번째로 살펴볼 구성요소는 파이프라인에서 가장 먼저 실행되는 Task인 "git-clone" Task입니다.

 

 

"git-clone" Task는 Tekton의 레포지토리와도 같은 Tekton Hub에서 제공해주는 커뮤니티 제공 Task이기 때문에 tkn 명령어 등으로 간단하게 내려받을 수 있습니다.

 

1
tkn hub install task git-clone
cs

 

"git-clone" Task는 "url" 변수로 지정한 Repository를 Clone해 내부 저장장치에 저장하도록 구성되어 있습니다. 

 

그 외에 자세한 동작 및 Parameter에 대한 설명은 아래 Tekton hub 페이지에서 확인할 수 있습니다.

 

https://hub.tekton.dev/tekton/task/git-clone 

 

Tekton Hub

 

hub.tekton.dev


2-2. Task "terraform-cli"

다음으로 살펴볼 구성요소는 테라폼 명령어를 실행하는 "terraform-cli" Task입니다.

 

이 Task는 이전 "git-clone"에서 실행되어 저장된 Terraform 코드를 기반으로 테라폼 명령어를 실행해 인프라를 배포하는 역할을 합니다.

 

"terraform-cli" Task 또한 "git-clone"과 마찬가지로 tekton hub에서 제공되는 커뮤니티 제공 Task지만, 이번 파이프라인에서 사용하기 위해서는 약간의 수정이 필요하기 때문에 아래 yaml 매니페스트를 사용합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  annotations:
    tekton.dev/categories: CLI
    tekton.dev/displayName: terraform cli
    tekton.dev/pipelines.minVersion: 0.12.1
    tekton.dev/platforms: linux/amd64
    tekton.dev/tags: cli
  labels:
    app.kubernetes.io/version: "0.2"
    hub.tekton.dev/catalog: tekton
  name: terraform-cli
spec:
  description: |-
    Terraform is an open-source infrastructure as code software tool created by HashiCorp.
    It enables users to define and provision a datacenter infrastructure using a high-level configuration language known as Hashicorp Configuration Language (HCL), or optionally JSON
    This Task will do a terraform init before it executes the actual configured ARGS from parameter.
  params:
  - default:
    - --help
    description: The terraform cli commands to tun
    name: ARGS
    type: array
  - default: terraform-creds
    description: The terraform secret with credentials
    name: terraform-secret
    type: string
  - default: docker.io/hashicorp/terraform:light
    description: the terraform image to use
    name: image
    type: string
  - default: ""
    description: HTTP proxy server for non-SSL requests
    name: httpProxy
    type: string
  - default: ""
    description: HTTPS proxy server for SSL requests
    name: httpsProxy
    type: string
  - default: ""
    description: no proxy - opt out of proxying HTTP/HTTPS requests
    name: noProxy
    type: string
  steps:
  - args:
    - init
    command:
    - terraform
    env:
    - name: HTTP_PROXY
      value: $(params.httpProxy)
    - name: HTTPS_PROXY
      value: $(params.httpsProxy)
    - name: HTTP_PROXY
      value: $(params.httpProxy)
    image: $(params.image)
    name: init
    resources: {}
    volumeMounts:
    - mountPath: $(workspaces.source.path)/key
      name: gcp-sa
    workingDir: $(workspaces.source.path)
  - args:
    - $(params.ARGS)
    command:
    - terraform
    env:
    - name: HTTP_PROXY
      value: $(params.httpProxy)
    - name: HTTPS_PROXY
      value: $(params.httpsProxy)
    - name: HTTP_PROXY
      value: $(params.httpProxy)
    envFrom:
    - secretRef:
        name: $(params.terraform-secret)
    image: $(params.image)
    name: terraform-cli
    resources: {}
    volumeMounts:
    - mountPath: $(workspaces.source.path)/key
      name: gcp-sa
    workingDir: /workspace/source
  volumes:
  - name: gcp-sa
    secret:
      secretName: gcp-sa-key
  workspaces:
  - name: source
cs

 

코드를 살펴보면 이 Task는 terraform init 명령어 실행, terraform $ARGS 명령어 실행2개 Step으로 이루어져 있는 것을 알 수 있습니다.

 

즉 우선 terraform init을 실행한 뒤, ARGS 파라미터에 받은 변수를 기반으로 한 terraform 명령어를 실행하는 구조로 되어있는 것입니다.

 

ARGS 파라미터에는 테라폼 코드 검증을 위한 validate, 배포 확인을 위한 plan, 실제 배포를 위한 apply 등 다양한 명령어를 사용할 수 있으므로 상황에 맞는 변수를 할당하면 되겠습니다. 이번 포스팅에서는 실제 배포를 위한 apply 명령어를 넣겠습니다.

 

중요한 점은 이 Task에 2개의 Kubernetes Secret이 사용된다는 점인데요. 2개의 Secret 모두 GCP의 Service account key를 필요로 합니다.

 

위 Secret에 사용되는 Service account Key를 생성하는 방법은 아래 Document를 참고하시기 바랍니다.

 

https://cloud.google.com/iam/docs/creating-managing-service-account-keys

 

서비스 계정 키 생성 및 관리  |  IAM 문서  |  Google Cloud

의견 보내기 서비스 계정 키 생성 및 관리 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이 페이지에서는 Google Cloud Console, Google Cloud CLI, Identity and Access Manag

cloud.google.com

 

하나는 Terraform backend 인증에 사용할 Secret으로 GCS 접근에 필요한 Service account key JSON 파일을 담고 있습니다.

 

이 Secret은 "gcp-sa"라는 volume으로 추가되어 각 Step에 VolumeMount된 것을 볼 수 있습니다.

 

나머지 하나는 Terraform Provider 인증에 사용할 Secret으로 GCP 인프라 배포에 필요한 Service account key JSON 파일을 담고 있습니다.

 

이 Secret은 Task의 "terraform-secret" parameter에 할당되어 환경 변수로 사용되는 것을 볼 수 있습니다.


2-3.  Pipeline "clone-terraform-cli"

 

다음은 위에서 알아본 Task들을 일련의 실행 가능한 구성으로 묶어주는 매개체인 Pipeline에 대해서 알아보겠습니다.

 

Pipeline 리소스에서는 실행할 Task의 순서를 지정할 수 있는데, Git repo에서 가져온 코드를 기반으로 Terraform 커맨드를 실행해야 하기 때문에 "git-clone" -> "terraform-cli" 순으로 실행하도록 지정합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: clone-terraform-cli
spec:
  description: |
    This pipeline clones a git repo, run "terraform plan" command based on .tf config file 
  params:
  - name: repo-url
    type: string
  workspaces:
  - name: shared-data
  tasks:
  - name: fetch-source
    taskRef:
      name: git-clone
    workspaces:
    - name: output
      workspace: shared-data
    params:
    - name: url
      value: $(params.repo-url)
  - name: terraform-command
    runAfter: ["fetch-source"]
    taskRef:
      name: terraform-cli
    workspaces:
    - name: source
      workspace: shared-data
    params:
    - name: terraform-secret
      value: "terraform-secret"
    - name: ARGS
      value: 
        - apply
        - "-auto-approve"
cs

 

코드를 보면 spec.tasks.runAfter 애트리뷰트를 통해 "terraform-cli" Task를 "git-clone" 이후에 실행하도록 지정한 것을 볼 수 있습니다.

 

"terraform-cli" Task를 실행하는 부분에서는 "ARGS" params를 통해 실행하고자 하는 Terraform 명령어를 지정할 수 있습니다.

 

실제 환경에 인프라를 배포하고자 한다면 여기에 apply -auto-approve를 넣습니다.


2-4. TriggerTemplate "terraform-triggertemplate"

 

TriggerTemplate 리소스는 앞서 생성한 Pipeline에 EventListner에서 받은 Parameter를 부여함으로써 Pipelinerun 리소스를 생성해주는 역할을 합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
  name: terraform-triggertemplate
spec:
  params:
    - name: gitrepositoryurl
      description: The git repository url
  resourcetemplates:
    - apiVersion: tekton.dev/v1beta1
      kind: PipelineRun
      metadata:
        generateName: clone-terraform-plan-run-
      spec:
        pipelineRef:
          name: clone-terraform-plan
        podTemplate:
          securityContext:
            fsGroup: 65532
        workspaces:
        - name: shared-data
          volumeClaimTemplate:
            spec:
              accessModes:
              - ReadWriteOnce
              resources:
                requests:
                  storage: 1Gi
        params:
        - name: repo-url
          value: $(tt.params.gitrepositoryurl)

cs

 

spec.resourcetemplates 어트리뷰트를 통해 이전에 생성한 Pipeline을 참조해서 PipelineRun 리소스를 생성하는 것을 확인할 수 있습니다.

 

spec.params 값은 TriggerBinding에서 미리 정의한 파라미터를 넣어야 합니다. 이 어트리뷰트를 통해 EventListener에서 받아온 정보를 기반으로 Pipeline 작업을 수행할 수 있습니다.

 

이 값은 sepc.resourcetemplates.params 어트리뷰트의 $(tt.params.gitrepositoryurl)와 같이 참조할 수 있습니다. 

 


2-5. TriggerBinding "terraform-triggerbinding"

 

다음은 EventListner에서 어떤 정보를 받아올지 정의할 수 있는 TriggerBinding 리소스입니다.

 

1
2
3
4
5
6
7
8
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
  name: terraform-triggerbinding
spec:
  params:
    - name: gitrepositoryurl
      value: $(body.repository.url)
cs

 

여기서는 EventListener가 받는 정보가 Github Webhook이 보내는 payload이므로, 여기서 git clone을 위해 필요한 git repository url 정보만 가져오도록 정의하겠습니다.

 

필요한 정보를 어떤 경로로 가져올 수 있는지는 Github webhook Payload 구성을 참조하세요.

 

https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#push

 

Webhook events and payloads - GitHub Docs

When configuring a webhook, you can use the UI or API to choose which events will send you payloads. Only subscribing to the specific events you plan on handling limits the number of HTTP requests to your server. You can also subscribe to all current and f

docs.github.com


2-6. EventListener "terraform-eventlistener" 

 

마지막으로 Tekton Trigger의 맨 앞단에서 Event의 감지를 담당하는 EventListner입니다.

 

본 파이프라인에서는 Github webhook을 감지해 Tekton Pipeline을 Trigger하는 역할을 하게 됩니다.

 

1
2
3
4
5
6
7
8
9
10
11
apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener
metadata:
  name: terraform-eventlistener
spec:
  serviceAccountName: tekton-trigger-sa # clusterrolebindinag과 rolebinding 둘 다 존재해야 권한문제가 발생하지 않음
  triggers:
    - bindings:
      - ref: terraform-triggerbinding
      template:
        ref: terraform-triggertemplate
cs

 

EventListener는 실제 Pod가 실행되는 리소스이기 때문에 Pod를 실행할 Service account를 지정해야 합니다.

 

이 Service account는 Tekton 리소스에 대한 적절한 권한이 부여되어 있어야 권한 이슈 없이 Pipeline을 실행할 수 있습니다.

 

마지막으로 Binding할 TriggerTemplate과 TriggerBinding을 지정해 위에서 생성한 리소스들을 EventListener 아래로 묶어줍니다.

 

 

 

3. TF State 관리 

 

Terraform을 사용하고자 할때 고려해야 할 점 중 한가지는 TF State 파일의 관리입니다.

 

특히 이번처럼 Pipeline을 태우며 Terraform 배포를 자동화하고자 할때 State 파일만 따로 관리할 수는 없을 텐데요.

 

이를 해결하기 위해 Remote Backend에 State 파일을 두어서 저장하는 것을 권장합니다.

 

특히 Public Cloud Provider 환경에 인프라를 배포하고자 하는 경우에는 그 Provider가 제공하는 오브젝트 스토리지 서비스를 Backend로 사용하는 것을 권장합니다.

 

이에 대한 이유는 다음과 같습니다.

 

1. 여러 사용자가 동시에 Pipeline을 실행할 경우에도 일관성이 보장된다.

대부분의 Public Cloud 제공 오브젝트 스토리지는 어느정도의 일관성 수준을 제공하고 있습니다. 때문에 여러 사용자가 동시에 Pipeline을 실행하는 경우에도 배포되는 TF State에 대한 일관성을 유지할 수 있습니다.

 

2. State파일과 Configration파일의 관리 포인트가 일원화되어 Burden이 줄어든다.
Local Backend를 사용하거나 각기 다른 Provider의 서비스를 이용한다면 관리 포인트가 많아져 관리에 대한 부담이 가중될 수 밖에 없습니다. 하지만 같은 Provider의 서비스를 이용한다면 이와 같은 Burden을 덜 수 있습니다.

 

3. 인증에 필요한 키, 암호 등의 Secret의 관리가 용이해진다.

Backend 인증에 필요한 Secret을 관리하는 것도 부담이 되는 관리 포인트가 될 것입니다. 이때 Provider의 Secret Management 서비스를 이용하거나 Provider를 통일한다면 관리를 용이하게 만들 수 있습니다.


본 포스팅의 DEMO에 사용한 환경은 아래와 같이 Backend 설정이 구성되어 있습니다.

 

 

Terraform을 통해 배포하고자 하는 환경이 "Google Cloud Platform" Provider이므로 같은 Provider에서 제공하는 오브젝트 스토리지인 GCS를 Backend로 사용했습니다.

 

GCS를 Backend로 사용했을 시 인증 방법에는 Service account Key를 참조하게끔 하는 방법이 있습니다. 

 

"terraform-cli" Task 구성시 해당 Key를 Mount해 참조할 수 있도록 구성했습니다.

 

 

 

GCS는 Eventual Consistency를 보장하기 때문에 여러 사용자의 Pipeline 이용시에도 어느 정도의 일관성을 보장할 수 있습니다.

 

게다가 Object Versioning 기능을 활성화하면 삭제된 오브젝트에 대해서 복구할 수 있기 때문에 이전 Terraform State로 복구할 수 있는 선택지가 생긴다는 장점도 존재합니다.

 

추가적으로 Lifecycle 규칙을 사용하면 Object Versioning으로 인해 쌓인 버전들을 무제한 쌓이지 않도록 관리할 수도 있습니다.

 

 

 

4. 마치며 

본 포스팅은 "Tekton 사용해보기" 시리즈의 두 번째 글로 Tekton을 사용해 Terraform 파이프라인을 구성해봤습니다.

 

이를 통해 Tekton Pipeline, Tekton Trigger와 Tekton Dashboard 컴포넌트를 사용하면 Terraform 코드를 배포할 수 있는 자동화된 파이프라인을 생성할 수 있다는 것을 함께 알아볼 수 있었습니다.

 

게다가 Terraform 파이프라인의 Backend로 배포할 환경에서 제공하는 오브젝트 스토리지 서비스를 사용하는 것의 이점도 알아봤습니다.

 

이 글을 읽으시는 분들이 Tekton으로 파이프라인을 구성하는데 많은 도움이 되었으면 합니다.

 

다음 시리즈에서는 Tekton Dashboard에 Oauth2.0을 활용한 RBAC 적용해보기를 포스팅할 예정입니다.