CI/CD 구조와 자동화 — GitHub Actions와 GitOps
2026. 06. 22.
KubernetesCI/CDGitHub ActionsArgoCDGitOps
이 글의 목표: 수동 배포 흐름을 GitHub Actions + ArgoCD 패턴으로 자동화하는 그림과 단계를 본다.
두 가지 큰 패턴: Push vs Pull
Push 모델 (전통적)
GitHub → Actions가 빌드 + push + helm upgrade → EKS
▲
└ kubeconfig가 필요
- CI 도구가 클러스터에 직접 명령
- 빠르고 단순
- 단점: CI에 클러스터 접근 권한 필요 (보안 위협면)
Pull 모델 (GitOps)
GitHub → Actions가 빌드 + push
↓
manifest 저장소 업데이트 (PR/commit)
↑
└ ArgoCD/Flux가 폴링/감지
EKS ← ArgoCD가 클러스터 안에서 manifest를 apply
- 클러스터가 스스로 git을 보고 동기화
- Git이 Single Source of Truth
- 클러스터 외부 권한 노출 X (보안 ↑)
- 단점: 약간 복잡함, 도구 추가 학습 필요
→ 요즘은 Pull(GitOps) 모델이 표준.
권장 저장소 구조
조직/
├── meeny-backend/ # 앱 코드
│ ├── src/
│ ├── Dockerfile
│ └── .github/workflows/
│ └── build.yml # 이미지 빌드 + push
│
└── meeny-deploy/ # 배포 manifest (분리!)
└── helm/
├── backend/
│ ├── values.yaml
│ ├── values-dev.yaml
│ └── values-prod.yaml
├── frontend/
└── ingress/
왜 분리?
- 앱 저장소: 개발자 PR 활발
- 배포 저장소: 운영팀/리뷰 강한 컨트롤
- 권한·보안 정책 분리 가능
- ArgoCD가 보는 게 배포 저장소
💡 작은 팀이면 단일 저장소 안에
apps/와deploy/디렉터리 분리도 흔하다.
표준 빌드 워크플로우 (GitHub Actions)
meeny-backend/.github/workflows/build.yml 예시:
name: Build & Push to ECR
on:
push:
branches: [main]
tags: ['v*']
env:
AWS_REGION: ap-northeast-2
ECR_REGISTRY: 505947591451.dkr.ecr.ap-northeast-2.amazonaws.com
ECR_REPOSITORY: meeny/backend
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write # OIDC로 AWS 인증
contents: read
steps:
- uses: actions/checkout@v4
- name: AWS 자격증명 (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::505947591451:role/GitHubActionsECRPushRole
aws-region: ${{ env.AWS_REGION }}
- name: ECR 로그인
uses: aws-actions/amazon-ecr-login@v2
- name: 태그 결정
id: tag
run: |
SHORT_SHA=${GITHUB_SHA::7}
echo "image_tag=git-${SHORT_SHA}" >> $GITHUB_OUTPUT
- name: buildx 셋업
uses: docker/setup-buildx-action@v3
- name: 빌드 + Push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64
push: true
tags: |
${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ steps.tag.outputs.image_tag }}
${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
- name: 배포 저장소 업데이트 (GitOps)
uses: actions/checkout@v4
with:
repository: meeny-org/meeny-deploy
token: ${{ secrets.DEPLOY_REPO_PAT }}
path: deploy
- name: values.yaml 이미지 태그 패치
run: |
cd deploy/helm/backend
yq -i '.image.tag = "${{ steps.tag.outputs.image_tag }}"' values-dev.yaml
git config user.name "github-actions"
git config user.email "actions@github.com"
git commit -am "chore(backend): bump image to ${{ steps.tag.outputs.image_tag }}"
git push이 워크플로우의 핵심
| 요소 | 의미 |
|---|---|
| OIDC 인증 | AWS Access Key 저장 X. 임시 자격증명 발급 |
| 이미지 태그 = git SHA | 어떤 커밋이 어떤 이미지인지 1:1 |
| buildx + cache-from | 두 번째 빌드부터 빠름 |
| deploy 저장소에 commit | ArgoCD가 이 변화를 감지해서 배포 |
ArgoCD 도입 (Pull 모델)
설치 (요약)
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd \
-n argocd --create-namespace \
--set server.service.type=LoadBalancerApplication 리소스 등록
backend-app.yaml:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: backend
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/meeny-org/meeny-deploy.git
targetRevision: main
path: helm/backend
helm:
valueFiles:
- values.yaml
- values-prod.yaml
destination:
server: https://kubernetes.default.svc
namespace: backend
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true→ ArgoCD가 3분마다 git 폴링 + manifest 변경 감지 즉시 클러스터 동기화.
→ selfHeal: true : 누가 kubectl edit으로 손대도 git 상태로 되돌린다.
→ prune: true : 삭제된 리소스도 자동 제거.
환경별 분리 패턴 (dev/staging/prod)
values 파일 분리
helm/backend/
├── values.yaml # 공통 기본값
├── values-dev.yaml # 개발 오버라이드
├── values-staging.yaml
└── values-prod.yaml
values-prod.yaml 예시:
replicaCount: 4
springProfilesActive: prod
resources:
requests: { cpu: 1000m, memory: 1Gi }
limits: { cpu: 2000m, memory: 2Gi }ArgoCD Application도 환경별 분리
backend-dev.yaml→ dev 클러스터의 backend 네임스페이스backend-prod.yaml→ prod 클러스터의 backend 네임스페이스
또는 ApplicationSet 으로 한 번에 여러 환경 관리.
PR 기반 흐름 예시
1. 개발자가 backend 코드 수정 → PR 생성
↓
2. CI가 PR에서 테스트 실행 (push X)
↓
3. PR 머지 → main 브랜치 push
↓
4. CI: 이미지 빌드 + ECR push (tag = git SHA)
↓
5. CI: meeny-deploy/helm/backend/values-dev.yaml 의 image.tag 자동 업데이트 + commit
↓
6. ArgoCD가 deploy 저장소 변경 감지 → dev 클러스터 자동 배포
↓
7. dev 검증 후 PR로 values-prod.yaml 업데이트 → 사람 승인
↓
8. ArgoCD가 prod 배포
→ 운영 배포만 사람 승인, 그 전까진 자동화.
단계별 자동화 도입 추천
전부 한 번에 도입하면 부담. 점진적으로:
Phase 1: CI 자동 빌드만 (당장)
- GitHub Actions로
build.yml추가 - ECR에 자동 push
- 배포는 여전히 수동
helm upgrade
Phase 2: 배포 manifest 자동 패치
- CI가 deploy 저장소의
values.yaml을 자동으로 수정 - 사람은 PR로 승인 후 머지 → manifest 변경 후 수동 helm upgrade
Phase 3: ArgoCD 도입
- ArgoCD 설치
- Application 등록
- 자동 동기화
Phase 4: 환경 분리, 멀티 클러스터
- ApplicationSet, App-of-Apps 패턴
- 자동 카나리/블루-그린 (Argo Rollouts)
주요 도구 비교
| 도구 | 위치 | 역할 |
|---|---|---|
| GitHub Actions | CI | 빌드, 테스트, ECR push, manifest 패치 |
| GitLab CI / Jenkins | CI | 위와 같음 |
| ArgoCD | CD (GitOps) | manifest 보고 클러스터 동기화 |
| FluxCD | CD (GitOps) | ArgoCD 대안. CRD 중심 |
| Spinnaker | CD | 멀티 클라우드, 카나리 |
| Helm | 패키징 | 차트 단위 배포 |
| Kustomize | 패키징 | 패치 기반 (오버레이) |
| Argo Rollouts | 배포 전략 | Canary, BlueGreen |
→ 일단 GitHub Actions + ArgoCD + Helm 조합이면 충분하다.
ECR/EKS 측에서 준비할 것
① GitHub Actions OIDC 자격증명
EKS에 노드를 IRSA로 묶었던 것과 같은 원리. GitHub OIDC Provider를 IAM에 등록 후, role을 만들어 push 권한 부여:
# Terraform 예시
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}
resource "aws_iam_role" "github_ecr_push" {
name = "GitHubActionsECRPushRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Federated = aws_iam_openid_connect_provider.github.arn }
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:meeny-org/*:*"
}
}
}]
})
}② ECR Lifecycle Policy
latest만 두고 나머지 오래된 이미지는 자동 삭제:
aws ecr put-lifecycle-policy --repository-name meeny/backend \
--lifecycle-policy-text '{
"rules": [{
"rulePriority": 1,
"description": "Keep last 30 images",
"selection": {
"tagStatus": "any",
"countType": "imageCountMoreThan",
"countNumber": 30
},
"action": { "type": "expire" }
}]
}'핵심 요약
- CI는 빌드/푸시까지, CD는 GitOps(ArgoCD) 가 표준
- 이미지 태그 = git SHA (mutable
latest의존 X) - 앱 저장소와 배포 저장소 분리 (또는 디렉터리 분리)
- 환경별 values 파일 로 dev/staging/prod 분리
- OIDC로 AWS 인증 (정적 키 X)
- ECR Lifecycle Policy 로 오래된 이미지 정리
다음 글에서는 시크릿(DB 비밀번호, JWT secret 등)을 어떻게 안전하게 다룰지, AWS Secrets Manager + External Secrets Operator를 적용하는 패턴을 본다.