GitHub Actions는 GitHub의 자동화 워크플로우 서비스로, CI/CD 파이프라인 구축에 자주 사용된다.

필자도 GitHub Actions를 통해 매주 일요일에는 제텔카스텐 메모 정리를, 월요일에는 기술 아티클 토픽 조사를 도와주는 AI 자동화 워크플로우를 만들어서 사용 중이다. (혹시 이 주제에 대해 궁금한 독자가 있다면 추후 아티클로 소개해보겠다.)

이렇게 활용도가 좋은 GitHub Actions으로 Kubernetes 테스트도 가능하다는 사실을 알고 있는가?

개발 중인 소프트웨어가 Kubernetes 클러스터에서 잘 동작하는지 GitHub Actions의 파이프라인으로 테스트할 수 있는 것이다.

그래서 이번 아티클에서는 Kubernetes 테스트가 필요한 이유를 알아보고, GitHub Actions로 Kubernetes 테스트를 진행하는 실습을 진행해보겠다.


1. CI에 Kubernetes 테스트가 필요한 경우

CI 파이프라인을 구성하는 가장 일반적인 스텝은 소스 코드 빌드와 단위 테스트일 것이다.

하지만 빌드와 단위 테스트만으로는 개발 중인 소프트웨어가 Kubernetes 클러스터에서 잘 동작하는지 보장할 수 없다. 특히 Kubernetes 테스트가 필수인 경우를 몇 가지 살펴보겠다.

Helm 차트 및 매니페스트 검증

Helm 차트나 Kubernetes 매니페스트를 개발한다면 Kubernetes 테스트가 필수다. Yaml 문법은 맞더라도 실제 배포 시 리소스 간 충돌이나 이미지 오류를 미리 잡아낼 필요가 있기 때문이다.

Kubernetes 오퍼레이터 및 컨트롤러 개발

CRD(Custom Resource Deginition)를 제어하거나 Kubernetes API 서버와 상호작용하는 로직을 개발할 때 Kubernetes 테스트를 거치면 더욱 정확한 동작 검증이 가능하다.

복잡한 인프라 종속성 확인

애플리케이션이 실행되기 위해 특정 ConfigMap, Secret, Sidecar 컨테이너가 반드시 필요한 경우라면 Kubernetes 테스트가 필요하다. 앞서 말한 구성요소가 모두 배포되어 Ready 상태가 되는지 확인해야 하기 때문이다.


2. GitHub Actions으로 경량화 Kubernetes 사용하기

CI에 Kubernetes 테스트가 필요한 경우에 대해 알아봤으니 이제 직접 경험해볼 차례다.

GitHub Actions를 통해 동작하는 워크플로우 환경은 Docker를 지원한다. 즉, Docker로 구축 가능한 경량화 클러스터로 Kubernetes 테스트가 가능한 것이다.

우리는 Docker로 Kubernetes 클러스터를 가볍게 구축할 수 있는 Kind를 테스트에 사용할 예정이다.

CI 파이프라인을 통해 만들어지는 Kubernetes 클러스터는 따로 상태 관리가 필요하지 않다는 특징이 있다. 테스트가 끝나면 클러스터도 함께 정리되기 때문이다. 진정으로 클라우드 네이티브한 방식이라고 할 수 있다.

혹시나 GitHub Actions을 사용하면서 비용이 들까 걱정이 될 수 있다. 그래서 GitHub 무료 티어의 최신 가격 정책을 가져왔다. 공개(Public) 레파지토리에서 GitHub Actions 사용은 완전 무료이며, 비공개(Private) 레파지토리는 매달 2,000분까지 무료로 사용 가능하다.

(출처: GitHub Pricing)

우리가 이번에 경험할 Kubernetes 테스트는 길어도 3분 내로 끝나기 때문에 비공개 레파지토리에서도 충분한 수준이다.

이제 본격적으로 실습해보자. GitHub Actions 워크플로우로 수행할 Kubernetes 테스트 시나리오는 아래와 같다.

  1. Kind 클러스터 생성
  2. 샘플 Deployment 및 Service 리소스 배포
  3. 배포한 리소스의 상태가 오류 없이 Ready 상태가 되는지 확인

먼저 레파지토리를 하나 만들고, manifests 폴더에 아래 샘플 Depolyment 및 Service 매니페스트 파일(sample.yaml)을 저장한다. 우리가 Kubernetes 테스트를 진행할 대상인 것이다.

sample.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: sample-nginx-svc
spec:
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

다음은 GitHub Actions 워크플로우 코드를 작성해보자. .github/workflows 폴더 안에 k8s-test.yml이라는 파일명으로 저장한다.

k8s-test.yml
name: Kubernetes Manifest Integration Test
 
#Git Push 및 PR 이벤트 발생 시 워크플로우 동작
on: [push, pull_request] 
 
jobs:
  k8s-test:
    # Ubuntu 환경에서 실행
    runs-on: ubuntu-latest
    
    steps:
      # 레포지토리의 소스 코드(manifests 폴더 포함)를 체크아웃
      - name: Checkout Code
        uses: actions/checkout@v4
 
      # kind 클러스터 생성
      - name: Create kind cluster
        uses: helm/kind-action@v1.14.0
        with:
          cluster_name: aiden-lab-test
 
      # manifests 폴더 내의 모든 리소스 클러스터에 배포
      - name: Apply Kubernetes Manifests
        run: |
          kubectl apply -f manifests/
          
      # 배포된 리소스 상태 확인 및 Ready 대기
      - name: Wait for Deployment Ready
        run: |
          # 'sample-nginx'라는 이름의 Deployment가 준비될 때까지 최대 60초 대기
          kubectl wait --for=condition=available --timeout=60s deployment/sample-nginx
          
          # 전체적인 상태 확인
          kubectl get all
 
      # 상세 상태 및 로그 추출
      # 테스트 성공/실패 여부와 상관없이(always) 실행
      - name: Collect K8s Logs & Describe
        if: always()
        run: |
          mkdir -p debug-outputs
          
          # Deployment 상세 상태 저장
          kubectl describe deployment sample-nginx > debug-outputs/deployment-info.txt
          
          # Pod 로그 저장 (레이블 기준으로 전체 추출)
          kubectl logs -l app=nginx --all-containers=true > debug-outputs/pod-logs.txt
          
          # 클러스터 내 발생 이벤트 저장
          kubectl get events --sort-by='.lastTimestamp' > debug-outputs/cluster-events.txt
 
      # 로그 파일을 아티팩트로 업로드 (나중에 확인 가능)
      - name: Upload Debug Artifacts
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: k8s-test-debug-logs
          path: debug-outputs/

.github/workflows 폴더에 올바른 양식의 yml 파일이 존재하면 GitHub 웹 UI의 Actions 탭에 해당 워크플로우가 자동으로 표시될 것이다.

워크플로우 파일의 on 필드에 pushpull_request를 명시했기 때문에 우리의 워크플로우는 해당 레파지토리에 Push 또는 PR 이벤트가 있을 때마다 동작할 것이다. 만약 Actions 페이지에서 워크플로우가 표시되지 않는다면 페이지 새로고침을 하거나 워크플로우 yml 파일을 다시 한 번 살펴보자.

만약 Kubernetes 테스트를 통과해 워크플로우가 성공했다면 아래와 같은 화면이 나올 것이다.

네모로 강조한 곳을 클릭하면 아래와 같이 해당 워크플로우 동작 결과에 대한 상세 페이지로 이동할 수 있다.

여기서 1번을 클릭하면 워크플로우의 각 스텝의 상태와 로그를 확인할 수 있다.

다시 이전 페이지로 돌아와 2번(k8s-test-debug-logs)을 클릭하면 Collect K8s Logs & Describe 스텝에서 정의한 리소스 관련 로그의 압축 파일을 다운로드해서 확인할 수 있다.

이 로그들이 Artifacts로 저장되어 GitHub Actions 페이지에서 접근 가능한 것은 Upload Debug Artifacts 스텝 덕분이다.


마무리

우리는 이번 아티클에서 Kubernetes 테스트가 필요한 사례와 GitHub Actions로 Kubernetes 테스트 수행하는 방법에 대해 살펴봤다.

본 실습에서는 샘플 매니페스트에서 정의한 리소스가 잘 배포되었는지 확인하는 테스트만 진행했지만, 여기서 더 나아가 다양한 Kubernetes 테스트가 가능하다.

예를 들면 워크플로우 내에서 Helm을 활용해 개발 중인 Helm 차트의 동작 검증을 할 수도 있고, 배포한 Service의 응답 상태를 curl로 확인하는 테스트도 가능한 것이다.

CI 파이프라인에서 진행하는 Kubernetes 테스트가 생소할 수도 있었을 것이다. 하지만 CI는 결국 개발 중인 소프트웨어 코드 병합에 필요한 반복 작업을 자동화하는 것이므로 Kubernetes 테스트도 CI 파이프라인에 포함될 수 있다는 점을 기억해두자.

만약 지금 Kubernetes에서 동작해야 하는 소프트웨어를 개발 중이라면 이번 아티클에서 다뤘던 Kubernetes 테스트를 고려해볼 것을 추천한다.


함께 읽어보면 좋은 아티클