Kubernetes

GitOps : Tekton, ArgoCD 로 CI/CD 하기

mokpolar 2023. 3. 24. 15:25
반응형

이전에 작성했던 Tekton 글에 이어 마침 이번 주 CloudNeta 스터디 주제가 GitOps여서 

Tekton, ArgoCD 를 결합한 CI/CD, GitOps 구현 예제를 작성해보려고 한다. 

 

간단한 FastAPI 를 배포하는 예제이다. 

 

구조

예제를 위한 평범한 GitOps 구조를 그려봤다.

  • FastAPI app에 추가 변경사항을 반영해서 Repo를 업데이트하고나서
  • Tekton PipelineRun 으로 Trigger를 한다. 
  • 그러면 Tekton Pipeline에 정의된 순서대로 Task 3개가 동작을 시작한다. 
    • App repo를 clone 해와서 build 하고 DockerHub에 push한다. 
    • 이 과정에서 나온 image tag를 GitOps repo에 업데이트 한다. 
    • Slack으로 완료 메시지를 보낸다.
    • ArgoCD가 에 있던 GitOps 대상 파일의 변경을 감지한다. 
  • 수동으로 deploy를 누르면 대상 클러스터에 새 FastAPI app을 배포한다. 

 

 

App Repository

 

간단한 FastAPI 코드가 들어있는 repository를 생성했다. 

Tekton Pipeline은 이 repository 로부터 clone을 해간다.

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

 

 

Tekton

Tekton이 어떻게 동작하는 지는 이전 Tekton 글 에서 설명했다. 

Pipeline과 Task들, 그리고 이를 trigger하는 PipelineRun을 작성했다. 

 

  • Pipeline

Pipeline manifest는 아래와 같다. 

git clone, build-container-and-push, yaml-update라는 task를 순차적으로 실행하는 모습이다. 

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: test-pipeline
  namespace: tekton
spec:
  workspaces:
    - name: shared-directory
  tasks:
    - name: git-clone
      taskRef:
        name: git-clone
      workspaces:
        - name: shared-directory
          workspace: shared-directory
    - name: build-container-and-push
      taskRef:
        name: build-container-and-push
      runAfter: 
        - "git-clone"
      params:
        - name: commitshashort
          value: "$(tasks.git-clone.results.commitshashort)"
      workspaces:
        - name: shared-directory
          workspace: shared-directory
    - name: yaml-update
      taskRef:
        name: yaml-update
      runAfter: 
        - "build-container-and-push"
      params:
        - name: imageurl
          value: mokpolar/tekton-pipeline-test:$(tasks.git-clone.results.commitshashort)
      workspaces:
        - name: shared-directory
          workspace: shared-directory

 

  • Task1

App repo를 clone해오는 task는 대략 아래와 같이 작성하면 된다. 

이 예제에서는 Tekton workspace로서 block storage로 되어있는 pvc를 사용하고 있는데, 

그 볼륨에서 task간에 모든 데이터가 서로 전달된다. 

 

그리고 image tag 업데이트를 위해 commitshashort를 result로 넘겼다. 

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: git-clone
  namespace: tekton
spec:
  results:
  - name: commitshashort
  workspaces:
  - name: shared-directory
  params:
    - name: repositoryurl
      description: git url to clone
      type: string
      default: https://github.com/mokpolar/gitops-tekton-argocd
  steps:
    - name: generate-tag
      image: mokpolar/test-cloner
      script: |
        #!/bin/bash

        CHECKOUT_DIR="$(workspaces.shared-directory.path)/"

        cd $CHECKOUT_DIR

        git clone $(params.repositoryurl)
        
        cd gitops-tekton-argocd

        COMMIT_SHA_SHORT="$(git rev-parse --short HEAD | tr -d '\n')"
        echo -n "$COMMIT_SHA_SHORT" > $(results.commitshashort.path)
        echo "$COMMIT_SHA_SHORT"

 

 

  • Task2

2번째 Task는 Kaniko로 컨테이너 이미지를 빌드, 푸시하는 과정이다. 

 

Dockerfile는 미리 해당 위치에 준비해둔 것을 쓴다. 

아래 처럼 생겼다. 

FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8

COPY app /app

# Install any additional dependencies here (if required)

# Expose port 80
EXPOSE 80

# Start the server
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]

아래는 Task2 manifest이다. 

kaniko 이미지를 사용하는 모습을 볼 수 있다. 

volume에는 dockerhub 에 접근할 수 있게 미리 생성해둔 secret을 붙여주었다. 

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-container-and-push
  namespace: tekton
spec:
  workspaces:
  - name: shared-directory
  params:
    - name: commitshashort
      type: string
  steps:
    - name: kaniko
      image: gcr.io/kaniko-project/executor:v1.9.0
      workingDir: $(workspaces.shared-directory.path)
      args:
        - --dockerfile=$(workspaces.shared-directory.path)/gitops-tekton-argocd/Dockerfile
        - --destination=mokpolar/tekton-pipeline-test:$(params.commitshashort)
        - --context=$(workspaces.shared-directory.path)/gitops-tekton-argocd/
      volumeMounts:
        - name: my-secret
          mountPath: /kaniko/.docker/
  volumes:
    - name: my-secret
      secret:
        secretName: my-secret
        items:
        - key: .dockerconfigjson
          path: config.json

 

  • Task3

3번째 Task에서는 GitOps 대상 repo에 있는 대상 manifest를 업데이트 해주는 과정이다.

1번째 Task에서 넘어온 commitshashort가 반영된 image url로

yq를 이용해서 pod manifest의 image 부분을 변경해주었다. 

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: yaml-update
  namespace: tekton
spec:
  workspaces:
  - name: shared-directory
  params:
  - name: repositoryurl
    type: string
    default: https://github.com/mokpolar/gitops-tekton-argocd
  - name: imageurl
    type: string
  steps:
    - name: deploy-to-argocd
      image: mokpolar/test-cloner
      script: |
        #!/bin/bash        
        
        CHECKOUT_DIR="$(workspaces.shared-directory.path)/"

        cd gitops-tekton-argocd

        yq e --inplace '.spec.containers.[0].image = "$(params.imageurl)"' -i ./deploy/pod.yaml

    
        git add ./deploy/pod.yaml
        git commit -m "tekton : deploy image version $(params.imageurl)"
        git push

 

Slack bot을 만들고 아래와 같은 코드를 추가하면 이 과정 후에 간단하게 Slack 채널로 완료가 되었다는 알림을 보낼 수도 있다.

curl -X POST -H 'Content-type: application/json' --data '{"text":"'"$(params.imageurl)"'"}' $Slack-url

 

 

  • PipelineRun

PipelineRun은 Pipeline에서 정의된 value들을 전달해주면서 trigger하는 역할을 한다. 

아래와 같이 작성해서 실행해준다.

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: test-pipelinerun
  namespace: tekton
spec:
  workspaces:
  - name: shared-directory
    persistentvolumeclaim:
      claimName: shared-directory
  serviceAccountName: my-sa
  pipelineRef:
    name: test-pipeline

 

 

모든 과정이 정상적으로 이루어졌다면 Tekton dashboard에서 아래와 같은 화면을 볼 수 있다. 

Pipeline이 실행되어서 각 Task가 수행된다. 

 

완료가 되어서 각 Task 왼쪽에 초록색 아이콘으로 체크가 되어있다. 

Task가 이루어지는 동안에 새로운 container image가 빌드되고 DockerHub에 push 되었을 것이다.

 

DockerHub에 들어가보면 이미지가 정상적으로 들어와있는 모습을 볼 수 있다.

 

GitOps Repository

ArgoCD가 모니터링 하고 있는 Repository와 대상 파일이 있어야 한다.

대상 파일은 간단하게 위 DockerHub에 등록한 이미지 주소를 보게 만들고, 

뒤의 image tag만 변경하도록 한 상태이다.

 

이 Tekton에서의 3번째 task가 이 파일의 image tag를 변경하면서 repository에 push 가 일어나면 

ArgoCD가 이를 감지할 것이다.

 

apiVersion: v1
kind: Pod
metadata:
  name: my-fastapi-server-pod
spec:
  containers:
  - name: my-fastapi-server-container
    image: mokpolar/tekton-pipeline-test:06b424a
    ports:
    - containerPort: 80

 

ArgoCD

ArgoCD에 gitops repo와 연결된 새 app을 생성하는 과정이다. 

아래와 같이 정보를 기입해준다. 

app 이름과 target cluster, 그리고 위에서의 gitops 대상 파일이 들어있는 path를 입력해준다. 

생성되면 아직 배포가 되어있지 않은 상태이고 정보만 연결되어 있다. 

 

좌측 상단에서 아래 처럼 생긴 Sync 버튼을 클릭하면

아래 yaml 파일 명세대로 아래와 같이 Pod가 배포되기 시작하는 모습을 볼 수 있다. 

 

 

FastAPI

 

Pod가 정상적으로 배포되어서 동작 되었는지 아래와 같이 확인할 수 있다. 

kubectl get pod | grep my
my-fastapi-server-pod                                             1/1     Running   0          5m33s

 

이제 해당 Pod에 외부에서 접근해보도록 하자. 

여러가지 방법으로 서비스를 생성해서 크롬에서 해당 주소로 접속하면 아래와 같은 화면을 볼 수 있다. 

 

이후에는 또 같은 과정이 반복될 것이다. 

App이 들어있는 repository에서 업데이트를 하고, 

Tekton을 trigger하고, 

ArgoCD를 통해 이를 배포한다. 

반응형