이전에 작성했던 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를 통해 이를 배포한다.
'Kubernetes' 카테고리의 다른 글
Kind와 OrbStack를 사용해서 쉽게 Local에서 Kubernetes 사용하기 (2) | 2024.09.05 |
---|---|
Container 격리 기술 이해하기 (3) | 2024.08.31 |
[스터디] Kubernetes Container Networking 정리 (0) | 2023.03.18 |
Kubernetes cluster에 Locust를 올려서 분산 부하 테스트한 후기 (4) | 2023.01.08 |
[스터디] DOIK - Kafka & Strimzi Operator 배포 및 테스트 (0) | 2022.06.25 |