0biglife.

Github Actions로 사내 프로젝트 CI/CD 구축하기(2)

Productivity/CI/CD

· 2025-03-19

Github Actions로 사내 프로젝트 CI/CD 구축하기(2)

들어가며

지난 게시글에서는 Github Actions를 통하여 빌드 자동화하는 방법을 정리하였다. 현재 연구소에서는 설치형 솔루션을 Azure 클라우드 환경에서 Kubernetes로 관리하고 있으며 배포 자동화에 대해 정해진 프로세스가 없는 상황이다. 따라서, 이미지가 빌드되었다면, 클러스터 워크로드에 직접 접근하여 이미지 태그명을 업데이트하고 kubectledit 또는 apply -f로 배포하고 있다. 이 과정을 자동화시켜보자.

배포하기 위한 고려 사항

배포하기 앞서 무엇이 필요한지 생각해보자. Github Action은 워크플로우로서 개발자가 원하는 특정 작업을 수행하도록 yaml에 명시해주고 git push로 업로드해주면 되었다. 필요한 프로퍼티들은 문서를 따라 살펴볼 수 있었고 그 과정에서 필요한 환경 변수 또는 시크릿 값들은 웹 상에서 추가해주고 변수로 받아서 워크플로우 잡에서 직접 선언하여 쓸 수 있었다.

배포를 위한 구현은 크게 어려워보이진 않는다. 먼저, 사용자가 입력할 값들을 지정해주자. 필요한 정보들을 잡이 받아서 쓸 수 있도록 해주고, 웹 상에서 Credential을 넣어주면 끝날 것만 같다. 구체적으로 어떤게 필요할까?

  1. Azure Login을 위한 Credentials
  2. Cluster, Resource Group Name
  3. Target Namespace, Deployment Name

Custom Inputs

먼저, 클러스터 이름과 리소스 그룹, 네임스페이스 등은 시크릿으로 관리될 필요가 없으니, 사용자가 직접 드롭 다운에서 선택 또는 입력하도록 세팅해보자.

1name: CI/CD Pipeline # 이제 CD가 추가되었으니 'Manually Build and Push Docker Image'에서 바꿔주자
2on:
3  workflow_dispatch:
4    inputs:
5      TARGET_NAME:
6        type: choice
7        description: "Container Registry Target Repo Name (e.g. front-dev, front-stg)"
8        required: true
9        options:
10          - front-dev
11          - front-stag
12          - front-prod
13
14      CUSTOM_TAG:
15        type: string
16        description: "Optional: Custom image tag (overrides secrets.VERSION if provided)"
17        required: true
18
19      RESOURCE_GROUP:
20        type: choice
21        description: "Select AKS Resource Group"
22        required: true
23        options:
24          - dev-aks
25          - stg-aks
26          - prod-aks
27
28      AKS_CLUSTER:
29        type: choice
30        description: "Select AKS cluster for deployment"
31        required: true
32        options:
33          - dev-cluster
34          - stg-cluster
35          - prod-cluster
36
37      NAMESPACE:
38        type: choice
39        description: "Select Kubernetes namespace"
40        required: true
41        options:
42          - default
43          - custom-system

추가해주고 git push해주면 다음과 같이 기본 세팅이 완료된다. 깔끔하지 않은가?

Setting for CDSetting for CD

Azure Login

Azure Credential 시크릿을 추가해주고 로그인을 해준다.

1jobs:
2  build-and-push:
3    # ... 기존 CI 작업 유지...
4
5  deploy-to-aks:
6    needs: build-and-push # CI 작업이 완료된 후 실행
7    runs-on: ubuntu-latest
8
9    steps:
10      - name: Checkout code
11        uses: actions/checkout@v4
12
13      - name: Azure login
14        uses: azure/login@v1
15        with:
16          creds: ${{ secrets.AZURE_CREDENTIALS }}
17
18      - name: Set AKS context
19        uses: azure/aks-set-context@v3
20        with:
21          # input으로 받은 리소스 그룹명, 클러스터 이름 준비
22          resource-group: ${{ inputs.RESOURCE_GROUP }}
23          cluster-name: ${{ inputs.AKS_CLUSTER }}

Resource Yaml Setting

배포에 필요한 리소스(디플로이먼트, 서비스) yaml들은 프로젝트 내부 /manifest 경로에서 관리해줄 것이다. 이 매니페스트 파일들은 일종의 템플릿처럼 쓸 수 있으며, Github Actions 실행시 실제 값들로 치환된다.

1# k8s/deployment.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5  name: ${TARGET_NAME}-deployment
6spec:
7  replicas: 3
8  selector:
9    matchLabels:
10      app: ${TARGET_NAME}
11  template:
12    metadata:
13      labels:
14        app: ${TARGET_NAME}
15    spec:
16      containers:
17        - name: ${TARGET_NAME}
18          image: ${SKUBER_PLUS_REGISTRY}/${TARGET_NAME}:${TAG}
19          ports:
20            - containerPort: 80
21          resources:
22            requests:
23              cpu: '100m'
24              memory: '128Mi'
25            limits:
26              cpu: '200m'
27              memory: '256Mi'
28
29# k8s/service.yaml
30apiVersion: v1
31kind: Service
32metadata:
33  name: ${TARGET_NAME}-service
34spec:
35  type: ClusterIP
36  ports:
37    - port: 80
38      targetPort: 80
39      protocol: TCP
40  selector:
41    app: ${TARGET_NAME}

최종 배포

마지막으로 실제 AKS에 배포하는 단계로, 먼저 sed 명령어를 사용하여 매니페스트 파일의 변수들을 실제 값으로 치환시키자. 이후 azure/k8s-deploy@v4 액션은 실제 K8s 클러스터 배포시킨다.

1# deployment.yaml과 service.yaml의 변수들을 실제 값으로 치환
2- name: Update Kubernetes manifests
3  run: |
4    # deployment.yaml의 변수들을 실제 값으로 치환
5    sed -i 's/${TARGET_NAME}/${{ inputs.TARGET_NAME }}/g' k8s/deployment.yaml k8s/service.yaml
6    sed -i 's/${SKUBER_PLUS_REGISTRY}/${{ env.SKUBER_PLUS_REGISTRY }}/g' k8s/deployment.yaml
7    sed -i 's/${TAG}/${{ inputs.CUSTOM_TAG }}/g' k8s/deployment.yaml
8
9- name: Deploy to AKS
10  uses: azure/k8s-deploy@v4
11  with:
12    namespace: ${{ inputs.NAMESPACE }}
13    manifests: |
14      k8s/deployment.yaml
15      k8s/service.yaml
16    images: |
17      ${{ env.SKUBER_PLUS_REGISTRY }}/${{ inputs.TARGET_NAME }}:${{ inputs.CUSTOM_TAG }}
18
19- name: Generate Deployment Summary
20  if: always()
21  run: |
22    echo "### Deployment Summary" >> $GITHUB_STEP_SUMMARY
23    echo "- **Cluster:** ${{ inputs.AKS_CLUSTER }}" >> $GITHUB_STEP_SUMMARY
24    echo "- **Namespace:** ${{ inputs.NAMESPACE }}" >> $GITHUB_STEP_SUMMARY
25    echo "- **Application:** ${{ inputs.TARGET_NAME }}" >> $GITHUB_STEP_SUMMARY
26    echo "- **Version:** ${{ inputs.CUSTOM_TAG }}" >> $GITHUB_STEP_SUMMARY

배포가 완료되면 Generate Deployment Summary 스텝에서 배포 결과를 요약하여 보여주도록 했다. 이 요약은 GitHub Actions의 실행 결과 페이지에서 확인할 수 있는 특별한 섹션(GITHUB_STEP_SUMMARY)에 기록되며, 이를 통해 어떤 클러스터의 어떤 네임스페이스에 어떤 버전이 배포되었는지 히스토리처럼 남도록 하였다.


개선점

사실 이미 편리한건 사실이다. 이미지 빌드 자동화부터 배포 자동화까지.. 그리고 분명 더 꼼꼼하게 검증하고자 하는 것들을 더 넣으면 보안과 관리 측면에서도 기능이 향상될테다. 개선사항으론 아래 항목들을 생각해보고 적용해보려고 한다.

  1. 리소스 그룹명과 클러스터 이름에 대한 Validation Step으로 예외케이스 핸들링

  2. Kubernetes 매니페스트 파일들을 프로젝트 디렉토리 내부에서 관리하는 것이 뭔가 신경쓰인다. 당장은 2~3가지 이상의 리소스 관리가 없기에 니즈가 없어보이지만, 금방이라도 피부로 와닿을 개선점 같기만 하다. 더 나은 방법이 있다면 이 쪽에 기입해둘 예정이다. → GitOps 전용 리포지토리 리서치해볼 것.

Index

들어가며배포하기 위한 고려 사항Custom InputsAzure LoginResource Yaml Setting최종 배포개선점