- Authors
- Name
- 들어가며
- 1. GitOps 핵심 원칙
- 2. 브랜치 전략 비교
- 3. PR(Pull Request) 규칙
- 4. 커밋 컨벤션
- 5. ArgoCD를 활용한 GitOps 파이프라인
- 6. CI/CD 파이프라인 설계
- 7. 환경별 배포 전략
- 8. 운영 체크리스트
- 9. 흔한 실수
- 10. 요약
- 퀴즈
들어가며
"코드가 곧 인프라다." GitOps는 이 한 문장을 운영 현실로 만든 패러다임이다. Git 저장소를 Single Source of Truth(SSOT)로 삼고, 선언적 설정 파일의 변경 이력 하나하나가 곧 인프라와 애플리케이션의 상태가 된다. 더 이상 수동으로 kubectl apply를 실행하거나, 슬랙에서 "배포 해주세요"라고 요청할 필요가 없다.
하지만 GitOps를 도입한다고 해서 자동으로 모든 것이 정리되지는 않는다. 브랜치 전략이 엉망이면 배포 파이프라인도 엉망이 되고, PR 규칙이 없으면 코드 품질도 보장되지 않으며, 커밋 메시지가 중구난방이면 변경 이력 추적이 불가능해진다. GitOps의 진정한 힘은 Git 워크플로우 전체가 체계적으로 설계되었을 때 비로소 발휘된다.
이 글에서는 GitOps의 핵심 원칙부터 브랜치 전략 비교, PR 규칙 설계, 커밋 컨벤션, ArgoCD 파이프라인 구성, 환경별 배포 전략, 그리고 운영 체크리스트까지 팀에서 바로 적용할 수 있는 수준으로 총정리한다.
1. GitOps 핵심 원칙
GitOps는 Weaveworks가 2017년에 제안한 개념으로, 다음 4가지 원칙을 기반으로 한다.
1.1 선언적(Declarative)
모든 시스템 상태를 선언적으로 정의한다. "이 명령을 실행하라"가 아니라 **"이 상태가 되어야 한다"**를 기술한다.
# 선언적 상태 정의 예시 - Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api-server
image: ghcr.io/myorg/api-server:v2.4.1
ports:
- containerPort: 8080
1.2 버전 관리(Versioned and Immutable)
모든 변경은 Git에 커밋으로 기록된다. 누가, 언제, 왜, 무엇을 변경했는지 완벽한 감사 로그(Audit Log)가 자동으로 남는다. 롤백은 git revert로 끝난다.
1.3 자동 적용(Pulled Automatically)
승인된 변경 사항은 자동으로 시스템에 적용된다. Push 방식(CI가 직접 배포)이 아닌 **Pull 방식(에이전트가 Git을 감시하고 변경을 감지하여 적용)**을 사용한다.
1.4 자가 치유(Continuously Reconciled)
실제 시스템 상태가 Git에 정의된 상태와 다르면 자동으로 교정한다. 누군가 kubectl로 직접 변경해도 GitOps 에이전트가 원래 상태로 되돌린다.
| 원칙 | 핵심 질문 | 실패 시 증상 |
|---|---|---|
| 선언적 | 원하는 상태가 코드로 정의되어 있는가? | 설정 드리프트, 재현 불가 |
| 버전 관리 | 모든 변경이 Git에 기록되는가? | 변경 추적 불가, 책임 소재 불명확 |
| 자동 적용 | 변경이 수동 개입 없이 적용되는가? | 배포 지연, 휴먼 에러 |
| 자가 치유 | 드리프트가 자동으로 교정되는가? | 환경 불일치, 장애 확산 |
2. 브랜치 전략 비교
브랜치 전략은 팀의 크기, 릴리스 주기, 제품 특성에 따라 선택해야 한다. 세 가지 주요 전략을 비교한다.
2.1 Git Flow
Vincent Driessen이 2010년에 제안한 클래식 모델이다. main, develop, feature/*, release/*, hotfix/* 5가지 브랜치 타입을 사용한다.
main ────●─────────────●──────●── (프로덕션)
\ / \ /
release \ ●────● \ /
\ / \/
develop ─────●──●──●──●───●──●── (통합)
\ \ /
feature ●──●────●
적합한 경우: 정기 릴리스 주기가 있는 팀, 모바일 앱, 패키지 라이브러리
2.2 GitHub Flow
main 브랜치와 feature 브랜치만 사용하는 단순한 모델이다. feature 브랜치에서 작업하고 PR을 통해 main에 병합한다.
main ────●──●──●──●──●── (항상 배포 가능)
\ / \ /
feature ●──● ●──●
적합한 경우: 지속적 배포(CD)를 실천하는 SaaS 팀, 소규모 팀
2.3 Trunk-Based Development (TBD)
모든 개발자가 trunk(main)에 직접 커밋하거나, 매우 짧은 수명의 브랜치(1일 이내)를 사용한다.
main ────●──●──●──●──●──●──●── (모든 커밋이 배포 후보)
\/ \/ \/
short ● ● ● (1일 이내 병합)
적합한 경우: 대규모 엔지니어링 조직(Google, Meta), Feature Flag 기반 릴리스
비교 표
| 항목 | Git Flow | GitHub Flow | Trunk-Based |
|---|---|---|---|
| 브랜치 수 | 5종 이상 | 2종 | 1~2종 |
| 병합 빈도 | 주 1~2회 | 일 1~3회 | 일 5~10회+ |
| 릴리스 주기 | 격주/월간 | 수시 | 수시 |
| 복잡도 | 높음 | 낮음 | 낮음 |
| Feature Flag 필요 | 아니오 | 선택 | 필수 |
| CI 요구 수준 | 중간 | 높음 | 매우 높음 |
| 롤백 용이성 | release 태그 | 커밋 단위 | 커밋/플래그 |
| GitOps 궁합 | 보통 | 좋음 | 매우 좋음 |
권장: GitOps 환경에서는 GitHub Flow 또는 Trunk-Based Development를 추천한다. Git Flow의 release 브랜치는 GitOps의 자동 동기화 패턴과 마찰이 크다.
3. PR(Pull Request) 규칙
PR은 단순한 코드 리뷰 도구가 아니다. 변경의 의도를 문서화하고, 품질 게이트를 통과시키며, 팀 지식을 공유하는 핵심 프로세스다.
3.1 PR 템플릿
.github/PULL_REQUEST_TEMPLATE.md 파일을 저장소 루트에 추가한다.
## 변경 사항
<!-- 무엇을 왜 변경했는지 간결하게 설명 -->
## 변경 유형
- [ ] 기능 추가 (feature)
- [ ] 버그 수정 (bugfix)
- [ ] 리팩토링 (refactor)
- [ ] 인프라/설정 변경 (infra)
- [ ] 문서 업데이트 (docs)
## 테스트
- [ ] 단위 테스트 추가/수정
- [ ] 통합 테스트 통과
- [ ] 로컬 환경에서 수동 검증
## 체크리스트
- [ ] 커밋 메시지가 Conventional Commits 형식을 따르는가?
- [ ] 불필요한 파일(로그, 임시 파일)이 포함되지 않았는가?
- [ ] 기존 API와 하위 호환되는가?
- [ ] 보안 민감 정보(시크릿, 키)가 포함되지 않았는가?
## 관련 이슈
Closes #
## 스크린샷 (UI 변경 시)
3.2 리뷰 체크리스트
리뷰어가 확인해야 할 핵심 항목:
- 기능 정확성 — 변경이 의도한 대로 동작하는가?
- 에지 케이스 — 빈 값, 대용량, 동시 요청 등 예외 상황을 처리하는가?
- 보안 — SQL Injection, XSS, 시크릿 노출 위험이 없는가?
- 성능 — N+1 쿼리, 불필요한 연산, 메모리 누수가 없는가?
- 테스트 커버리지 — 변경된 로직에 대한 테스트가 있는가?
- 명명 규칙 — 변수, 함수, 클래스 이름이 의미를 잘 전달하는가?
3.3 Merge 전략
| 전략 | 커밋 이력 | 적합한 상황 |
|---|---|---|
| Squash Merge | feature의 모든 커밋을 하나로 합침 | main 이력을 깔끔하게 유지하고 싶을 때 |
| Rebase Merge | feature 커밋을 main 위에 재배치 | 커밋 단위가 논리적으로 잘 나뉘어 있을 때 |
| Merge Commit | merge 커밋을 생성 | feature 브랜치의 컨텍스트를 보존하고 싶을 때 |
팀 권장 설정: Squash Merge를 기본으로 설정하고,
main브랜치에 직접 push를 금지한다. GitHub Settings > Branches > Branch protection rules에서 "Require a pull request before merging"을 활성화한다.
4. 커밋 컨벤션
4.1 Conventional Commits 형식
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
type 목록:
| type | 설명 | 예시 |
|---|---|---|
feat | 새로운 기능 | feat(auth): 소셜 로그인 추가 |
fix | 버그 수정 | fix(api): 페이지네이션 오프셋 오류 수정 |
docs | 문서 변경 | docs: API 스펙 업데이트 |
style | 포맷팅, 세미콜론 등 | style: ESLint 규칙 적용 |
refactor | 리팩토링 | refactor(db): 쿼리 최적화 |
test | 테스트 추가/수정 | test: 회원가입 E2E 테스트 추가 |
chore | 빌드, 도구 설정 | chore: Node.js 20으로 업그레이드 |
ci | CI 설정 변경 | ci: GitHub Actions 캐시 최적화 |
perf | 성능 개선 | perf(api): 응답 캐싱 적용 |
4.2 commitlint 설정
# 설치
npm install --save-dev @commitlint/cli @commitlint/config-conventional
# commitlint.config.js
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
# husky로 Git hook 연동
npx husky init
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
commitlint.config.js에서 팀 규칙을 커스터마이즈할 수 있다.
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'ci', 'perf', 'revert'],
],
'subject-max-length': [2, 'always', 72],
'body-max-line-length': [2, 'always', 100],
},
}
5. ArgoCD를 활용한 GitOps 파이프라인
5.1 ArgoCD 아키텍처
ArgoCD는 Kubernetes-native한 GitOps 배포 도구로, 다음 컴포넌트로 구성된다.
- API Server — Web UI, CLI, gRPC/REST API 제공
- Repository Server — Git 저장소에서 매니페스트를 렌더링
- Application Controller — 실제 상태와 목표 상태를 지속적으로 비교하고 동기화
5.2 Application 정의
# argocd/applications/api-server.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: api-server
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/myorg/k8s-manifests.git
targetRevision: main
path: apps/api-server/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # Git에서 삭제된 리소스 자동 제거
selfHeal: true # 드리프트 자동 교정
syncOptions:
- CreateNamespace=true
- PruneLast=true
retry:
limit: 3
backoff:
duration: 5s
factor: 2
maxDuration: 3m
5.3 Sync 정책 옵션
| 옵션 | 설명 | 권장 |
|---|---|---|
automated.prune | Git에서 삭제된 리소스 자동 제거 | 프로덕션: true |
automated.selfHeal | 수동 변경 시 자동 교정 | 프로덕션: true |
syncOptions.CreateNamespace | 네임스페이스 자동 생성 | 개발: true, 프로덕션: false |
retry.limit | 동기화 실패 시 재시도 횟수 | 3~5 |
5.4 Git 저장소 디렉토리 구조
k8s-manifests/
├── apps/
│ ├── api-server/
│ │ ├── base/
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ ├── hpa.yaml
│ │ │ └── kustomization.yaml
│ │ └── overlays/
│ │ ├── dev/
│ │ │ ├── kustomization.yaml
│ │ │ └── patches/
│ │ │ └── replicas.yaml
│ │ ├── staging/
│ │ │ ├── kustomization.yaml
│ │ │ └── patches/
│ │ │ └── replicas.yaml
│ │ └── production/
│ │ ├── kustomization.yaml
│ │ └── patches/
│ │ ├── replicas.yaml
│ │ └── resources.yaml
│ └── web-frontend/
│ ├── base/
│ └── overlays/
├── argocd/
│ ├── applications/
│ │ ├── api-server.yaml
│ │ └── web-frontend.yaml
│ └── projects/
│ └── default.yaml
└── README.md
6. CI/CD 파이프라인 설계
6.1 전체 흐름
graph LR
A[개발자 Push] --> B[GitHub Actions CI]
B --> C{테스트 통과?}
C -->|Yes| D[컨테이너 이미지 빌드]
C -->|No| E[실패 알림]
D --> F[이미지 Push - GHCR]
F --> G[매니페스트 저장소 PR 생성]
G --> H[PR 자동 병합 / 수동 승인]
H --> I[ArgoCD 감지]
I --> J[Kubernetes 동기화]
J --> K{Health Check}
K -->|Healthy| L[배포 완료]
K -->|Degraded| M[자동 롤백]
6.2 GitHub Actions 워크플로우
# .github/workflows/ci-cd.yaml
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Unit tests
run: npm run test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
build-and-push:
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
image-tag: ${{ steps.meta.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=
type=ref,event=branch
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
update-manifest:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- name: Checkout manifest repo
uses: actions/checkout@v4
with:
repository: myorg/k8s-manifests
token: ${{ secrets.MANIFEST_REPO_TOKEN }}
- name: Update image tag
run: |
cd apps/api-server/base
kustomize edit set image \
ghcr.io/myorg/api-server=ghcr.io/myorg/api-server:${{ needs.build-and-push.outputs.image-tag }}
- name: Create PR
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.MANIFEST_REPO_TOKEN }}
commit-message: 'chore: update api-server image to ${{ needs.build-and-push.outputs.image-tag }}'
title: 'deploy: api-server ${{ needs.build-and-push.outputs.image-tag }}'
body: |
자동 생성된 배포 PR입니다.
- 소스 커밋: ${{ github.sha }}
- 이미지 태그: ${{ needs.build-and-push.outputs.image-tag }}
branch: deploy/api-server-${{ needs.build-and-push.outputs.image-tag }}
base: main
7. 환경별 배포 전략
7.1 환경 분리 원칙
| 환경 | 목적 | 동기화 방식 | 승인 필요 |
|---|---|---|---|
| dev | 기능 개발, 통합 테스트 | 자동 (Auto Sync) | 아니오 |
| staging | QA, 성능 테스트, UAT | 자동 (Auto Sync) | 아니오 |
| production | 실 서비스 | 수동 (Manual Sync) | 예 |
7.2 Kustomize Overlay 구성
base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- hpa.yaml
commonLabels:
app: api-server
overlays/dev/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namePrefix: dev-
namespace: dev
patches:
- path: patches/replicas.yaml
images:
- name: ghcr.io/myorg/api-server
newTag: latest
overlays/dev/patches/replicas.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 1
template:
spec:
containers:
- name: api-server
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
overlays/production/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: production
patches:
- path: patches/replicas.yaml
- path: patches/resources.yaml
images:
- name: ghcr.io/myorg/api-server
newTag: v2.4.1 # 프로덕션은 명시적 태그 사용
overlays/production/patches/replicas.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 5
template:
spec:
containers:
- name: api-server
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: '1'
memory: 1Gi
7.3 Helm Values per Environment
Helm을 사용하는 경우 환경별 values 파일로 분리한다.
helm-charts/
├── api-server/
│ ├── Chart.yaml
│ ├── templates/
│ ├── values.yaml # 기본값
│ ├── values-dev.yaml # dev 환경
│ ├── values-staging.yaml # staging 환경
│ └── values-prod.yaml # production 환경
# values-prod.yaml
replicaCount: 5
image:
repository: ghcr.io/myorg/api-server
tag: v2.4.1
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: '1'
memory: 1Gi
autoscaling:
enabled: true
minReplicas: 5
maxReplicas: 20
targetCPUUtilization: 70
ingress:
enabled: true
hosts:
- host: api.myservice.com
paths:
- path: /
pathType: Prefix
8. 운영 체크리스트
팀에서 GitOps 워크플로우를 운영할 때 반드시 확인해야 할 항목들이다.
| 카테고리 | 항목 | 상태 |
|---|---|---|
| 브랜치 보호 | main 브랜치 직접 push 금지 | [ ] |
| 브랜치 보호 | PR 없이 merge 금지 | [ ] |
| 브랜치 보호 | Force push 금지 | [ ] |
| 코드 리뷰 | 최소 1인 리뷰 승인 필수 | [ ] |
| 코드 리뷰 | CODEOWNERS 파일 설정 | [ ] |
| 코드 리뷰 | Stale review 자동 해제 설정 | [ ] |
| CI 게이트 | PR에 CI 통과 필수 설정 (required status checks) | [ ] |
| CI 게이트 | 테스트 커버리지 임계값 설정 | [ ] |
| CI 게이트 | 린트/포맷 검사 통과 필수 | [ ] |
| 커밋 | commitlint + husky 설정 | [ ] |
| 커밋 | Conventional Commits 가이드 문서화 | [ ] |
| 보안 | 시크릿 스캐닝 활성화 (GitHub Secret Scanning) | [ ] |
| 보안 | .gitignore에 민감 파일 패턴 추가 | [ ] |
| 보안 | Dependabot / Renovate 활성화 | [ ] |
| ArgoCD | Application 리소스 Git 관리 | [ ] |
| ArgoCD | Sync 알림 연동 (Slack/Teams) | [ ] |
| ArgoCD | RBAC 설정 (팀별 프로젝트 권한) | [ ] |
| ArgoCD | 프로덕션 수동 Sync 설정 | [ ] |
CODEOWNERS 파일 예시:
# .github/CODEOWNERS
# 기본 소유자
* @myorg/platform-team
# 인프라 매니페스트
/apps/ @myorg/sre-team
/argocd/ @myorg/sre-team
# 프론트엔드
/apps/web-frontend/ @myorg/frontend-team
# 백엔드 API
/apps/api-server/ @myorg/backend-team
9. 흔한 실수
GitOps와 Git 워크플로우를 도입할 때 팀들이 자주 범하는 실수 목록이다.
앱 코드와 매니페스트를 같은 저장소에 관리 — 앱 CI가 돌 때마다 ArgoCD 동기화가 트리거된다. 매니페스트는 반드시 별도 저장소로 분리하라.
프로덕션에 Auto Sync + Auto Prune 설정 — 실수로 merge된 삭제가 즉시 프로덕션에 반영된다. 프로덕션은 Manual Sync를 사용하거나 최소한
selfHeal만 활성화하라.latest 태그를 프로덕션에 사용 — 어떤 버전이 배포되었는지 추적이 불가능하다. 반드시 immutable 태그(SHA, 시맨틱 버전)를 사용하라.
PR 리뷰 없이 자동 병합 — 매니페스트 변경 PR도 반드시 리뷰를 거쳐야 한다. 자동 병합은 dev 환경에만 허용하라.
RBAC 설정 없이 ArgoCD 운영 — 모든 팀원이 모든 애플리케이션을 Sync/Delete할 수 있으면 사고가 난다. Project + RBAC으로 권한을 최소화하라.
커밋 메시지에 의미 없는 내용 작성 —
fix,update,test같은 커밋 메시지는 3개월 후 아무도 이해할 수 없다. Conventional Commits를 강제하라.Feature 브랜치를 오래 유지 — 3일 이상 살아있는 feature 브랜치는 merge conflict의 온상이다. 작은 PR로 자주 병합하라.
Secret을 Git에 커밋 — 한 번이라도 Git에 올라간 시크릿은 히스토리에 영원히 남는다. Sealed Secrets, External Secrets Operator, 또는 Vault를 사용하라.
환경별 차이를 하드코딩 — dev/staging/prod의 차이를 별도 YAML 파일로 만들지 말고, Kustomize overlays나 Helm values로 관리하라.
롤백 절차를 문서화하지 않음 — 장애 상황에서 "git revert 후 ArgoCD sync"라는 절차를 모든 팀원이 알고 있어야 한다. Runbook을 만들어라.
10. 요약
| 영역 | 핵심 포인트 |
|---|---|
| GitOps 원칙 | 선언적, 버전 관리, 자동 적용, 자가 치유 — 4가지를 모두 만족해야 진정한 GitOps |
| 브랜치 전략 | GitOps에는 GitHub Flow 또는 Trunk-Based가 최적. 팀 규모와 릴리스 주기에 맞춰 선택 |
| PR 규칙 | 템플릿 + 체크리스트 + 최소 1인 리뷰 + CI 게이트 = 품질의 마지막 방어선 |
| 커밋 컨벤션 | Conventional Commits + commitlint으로 기계적 강제. 사람에게 의지하지 마라 |
| ArgoCD | Application YAML, syncPolicy, 디렉토리 구조를 표준화하고 Git으로 관리 |
| CI/CD | App Repo → CI(빌드/테스트/푸시) → Manifest Repo PR → ArgoCD Sync의 흐름을 자동화 |
| 환경 분리 | Kustomize overlays 또는 Helm values-env.yaml로 환경별 차이를 관리 |
| 운영 | 체크리스트를 팀 온보딩 문서에 포함시키고 정기적으로 점검 |
GitOps는 도구가 아니라 문화다. ArgoCD를 설치하는 것이 끝이 아니라, 팀 전체가 Git 중심의 워크플로우를 체화하고 규칙을 지키는 것이 핵심이다. 이 글에서 다룬 내용을 하나씩 적용하면서 팀에 맞는 워크플로우를 만들어가길 바란다.
퀴즈
Q1: GitOps의 4가지 핵심 원칙은 무엇인가?
선언적(Declarative), 버전 관리(Versioned and Immutable), 자동 적용(Pulled Automatically), 자가 치유(Continuously Reconciled)이다. 이 4가지 원칙이 모두 충족되어야 진정한 GitOps라 할 수 있으며, 하나라도 빠지면 단순한 "Git 기반 배포"에 불과하다.
Q2: Git Flow, GitHub Flow, Trunk-Based Development 중 GitOps 환경에 가장 적합한 브랜치 전략은?
GitHub Flow 또는 Trunk-Based Development가 GitOps 환경에 가장 적합하다. Git Flow의 release 브랜치는 GitOps의 자동 동기화 패턴과 마찰이 크며, 간결한 브랜치 구조가 선언적 상태 관리와 더 잘 어울린다. 특히 Trunk-Based Development는 Feature Flag와 함께 사용하면 가장 빠른 배포 주기를 달성할 수 있다.
Q3: Squash Merge, Rebase Merge, Merge Commit의 차이점과 각각 적합한 상황은?
Squash Merge는 feature 브랜치의 모든 커밋을 하나로 합쳐 main에 깔끔한 이력을 유지하고 싶을 때 적합하다. Rebase Merge는 커밋 단위가 논리적으로 잘 나뉘어 있어 각 커밋을 보존하고 싶을 때 사용한다. Merge Commit은 feature 브랜치의 컨텍스트(언제 시작되고 병합되었는지)를 보존하고 싶을 때 적합하다. 대부분의 팀에서는 Squash Merge를 기본값으로 권장한다.
Q4: ArgoCD의 selfHeal 옵션은 무엇이며 왜 중요한가?
selfHeal은 실제 Kubernetes 클러스터의 상태가 Git에 정의된 상태와 다를 때 자동으로 교정하는 기능이다. 예를 들어 누군가 kubectl로 직접 Deployment의 replicas를 변경하면, ArgoCD가 이를 감지하고 Git에 정의된 값으로 되돌린다. 이는 GitOps의 "자가 치유" 원칙을 구현하며, 설정 드리프트를 방지하고 Git을 유일한 진실의 원천(SSOT)으로 유지하는 데 핵심적인 역할을 한다.
Q5: 앱 코드 저장소와 매니페스트 저장소를 분리해야 하는 이유는?
앱 코드와 매니페스트를 같은 저장소에 두면, 앱 CI가 실행될 때마다 ArgoCD가 불필요한 동기화를 트리거한다. 또한 앱 개발자와 인프라 담당자의 접근 권한을 분리하기 어렵고, 매니페스트 변경에 대한 독립적인 리뷰 프로세스를 운영하기 어렵다. 저장소를 분리하면 관심사 분리, 권한 관리, 배포 추적이 훨씬 명확해진다.
Q6: Conventional Commits에서 feat, fix, chore의 의미는 무엇인가?
feat는 사용자에게 제공되는 새로운 기능을 추가할 때, fix는 기존 기능의 버그를 수정할 때, chore는 사용자 기능에 직접적인 영향을 주지 않는 빌드 시스템이나 도구 설정 변경 시에 사용한다. 이 규칙은 commitlint로 자동 검증할 수 있으며, semantic-release와 연동하면 커밋 타입에 따라 자동으로 버전을 올릴 수 있다.
Q7: Kustomize overlays로 환경별 배포를 관리하는 방법은?
base 디렉토리에 공통 매니페스트를 두고, overlays/{dev, staging, production} 디렉토리에 환경별 차이를 patches로 정의한다. 예를 들어 base에는 Deployment, Service, HPA 등 공통 리소스를 두고, dev overlay에서는 replicas를 1로, production overlay에서는 5로 패치한다. ArgoCD Application의 source.path를 환경별 overlay 디렉토리로 지정하면 환경별 배포가 완성된다.
Q8: GitOps 환경에서 Secret을 안전하게 관리하는 방법은?
Secret을 절대 Git에 평문으로 커밋하면 안 된다. 대안으로 Sealed Secrets(Bitnami)는 공개키로 암호화하여 Git에 저장하고 클러스터 내에서만 복호화한다. External Secrets Operator는 AWS Secrets Manager, HashiCorp Vault 등 외부 시크릿 관리 서비스와 연동하여 런타임에 Secret을 주입한다. SOPS(Mozilla)는 KMS 키로 YAML 파일 값을 암호화하여 Git에 저장할 수 있게 한다. 팀 규모와 인프라에 따라 적절한 도구를 선택하면 된다.