GitHub Actions와 Argo CD 기반 CI/CD 도전기

Introduction

  • CI/CD
    • CI (Continuous Integration)
      • 개발자들이 자주 (일반적으로 하루에 여러 번) 자신의 코드 변경 사항을 메인 브랜치에 통합하는 프로세스
      • 이는 코드의 통합 문제를 조기에 견하고 해결하는 데 도움이 됩니다.
    • CD (Continuous Deployment/Delivery)
      • 자동화된 테스트를 통과한 모든 코드 변경 사항이 자동으로 프로덕션 환경에 배포되는 프로세스 (Continuous Deployment) 또는 프로덕션 환경에 배포될 준비가 되면 수동으로 배포될 수 있게 하는 프로세스 (Continuous Delivery).
  • GitHub Actions: GitHub에서 제공하는 CI/CD 플랫폼
    • Workflow를 .yml 또는 .yaml 파일로 정의하여 코드의 빌드, 테스트, 배포 등 다양한 자동화 작업 수행
  • Argo CD: Kubernetes를 위한 선언적, GitOps 연속 배포 도구
    • Git 저장소에 정의된 Kubernetes 명세를 cluster와 동기화하는 데 사용
    • Application의 배포 상태, 구성 차이 및 동기화 상태 시각화 대시보드 제공

이러한 GitHub Actions와 Argo CD를 통해 아래와 같은 CI/CD를 도전해본다.

schematic


GitHub Actions

Hands-on

GitHub Actions를 통한 CI를 테스트하기 위해 아래 파일들을 준비했다.

1
2
3
4
5
6
.
├── Dockerfile
├── .github
│ └── workflows
│ └── ci.yaml
└── main.py
Dockerfile
1
2
3
4
5
6
FROM python:3.8

WORKDIR /app
COPY main.py .

CMD ["python", "main.py"]
main.py
1
print("Hello, World!")

GitHub Actions는 .github/workflows/*.yaml에서 어떤 작업들을 언제 진행할지 정의할 수 있다.
위에서 정의한 Dockerfile을 build하고 push하기 위해 Docker Hub에 login한다.
이를 위해 아래와 같이 GitHub Actions에서 Docker Hub에 login하기 위해 사용할 변수들을 정의한다.

secrets

.github/workflows/ci.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
name: Build and Push Docker Image

on:
push:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3

- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: zerohertzkr/build-test:latest

이 파일을 master branch에 push하면 build 후 Docker Hub에 잘 push되는 것을 확인할 수 있다.

build-test-latest

1
2
3
4
$ docker run --name build-test zerohertzkr/build-test
...
Status: Downloaded newer image for zerohertzkr/build-test:latest
Hello, World!

아래와 같이 변경 후 master branch에 push하고 다시 docker run을 하게되면 아래와 같이 새로 build된 image임을 확인할 수 있다.

main.py
1
print("Hello, World! => CI!")
1
2
3
4
5
6
$ git push origin master
...
$ docker run --name build-test zerohertzkr/build-test
...
Status: Downloaded newer image for zerohertzkr/build-test:latest
Hello, World! => CI!

Portal

server-portal

Home server portalrepository에 push하면 Docker Hub에 build된 이미지를 push하도록 설정했다.
또한 Argo CD에 Docker image 버전 변경을 알리기 위해 k8s/main.yaml에서 사용하는 버전을 바꾸고 push하도록 설정했다.

.github/workflows/ci.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
name: CI

on:
push:
branches:
- main

jobs:
build:
name: Build and Push Docker Image
runs-on: ubuntu-latest
if: contains(github.event.head_commit.message, 'Build')
outputs:
TAG: ${{ steps.extract_tag.outputs.TAG }}
steps:
- name: Check out code
uses: actions/checkout@v3

- name: Extract tag from commit message
id: extract_tag
run: |
TAG=$(echo "${{ github.event.head_commit.message }}" | grep -oP 'Build: \s*\K[\w\.]+')
echo "Extracted tag is $TAG"
echo "TAG=$TAG" >> $GITHUB_ENV
echo "TAG=$TAG" >> $GITHUB_OUTPUT

- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: zerohertzkr/server-portal:${{ env.TAG }}

push:
name: Push Manifest
needs: build
runs-on: ubuntu-latest
env:
TAG: ${{ needs.build.outputs.TAG }}
steps:
- name: Check out code
uses: actions/checkout@v3
with:
token: ${{ secrets.GH_TOKEN }}

- name: Change manifest
run: sed -i "s|zerohertzkr/server-portal:[^ ]*|zerohertzkr/server-portal:${{ env.TAG }}|" k8s/main.yaml

- name: git push
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config credential.helper store
git add k8s/main.yaml
git commit -m ":tada: Update: Image [${{ env.TAG }}]"
git push

Argo CD

Home server portal를 Argo CD로 배포하기 위해 아래와 같이 설정했다.

argo-cd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: portal
spec:
destination:
name: ''
namespace: portal
server: 'https://kubernetes.default.svc'
source:
path: ./k8s
repoURL: 'https://github.com/Zerohertz/server-portal'
targetRevision: HEAD
sources: []
project: default
syncPolicy:
automated:
prune: true
selfHeal: true

그리고 테스트를 위해 아래 사항들을 변경 후 배포해봤다.

src/common/constants/app.ts
1
2
3
4
5
6
7
8
9
10
import type { AppInterface } from "@/common/types/app";

export const APP_LIST: AppInterface[] = [
...
{
name: "TEST",
href: "https://zerohertz.xyz",
imageSrc: "/favicon.png",
},
];
1
2
3
$ git add src/common/constants/app.ts
$ git commit -m ":memo: Build: test"
$ git push origin main

아주 잘 배포되는 것을 확인할 수 있다.

results

github-actions
argo-cd