앞에서 Kubernetes 운영 환경을 구축하면서 다음과 같은 구성까지 살펴봤다.
Kubernetes
Helm
FastAPI
PostgreSQL
Redis
Nginx
Ingress
HTTPS
Prometheus
Grafana
Alertmanager
Loki
Tempo
Kubernetes 자동 복구
SRE 운영 체계
여기까지 구성하면 Kubernetes의 상태를 관찰하고 장애를 감지하며 일부 장애를 자동으로 복구할 수 있다.
하지만 실제 운영 환경에서는 또 하나의 중요한 문제가 남는다.
바로 **애플리케이션을 어떻게 안전하고 일관되게 배포할 것인가?**이다.
전통적인 Kubernetes 배포는 관리자가 직접 다음과 같은 명령을 실행하는 방식으로 이루어질 수 있다.
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
또는 Helm을 사용한다면 다음과 같이 배포할 수도 있다.
helm upgrade --install tax-app ./helm/tax-app \
-f values-prod.yaml
개발 환경에서는 큰 문제가 없지만 운영 환경에서는 다음과 같은 문제가 발생할 수 있다.
누가 배포했는지 추적하기 어렵다.
현재 Kubernetes에 어떤 설정이 적용되어 있는지 Git과 다를 수 있다.
운영자가 직접 YAML을 수정할 수 있다.
여러 Kubernetes 클러스터의 설정이 달라질 수 있다.
롤백 기준이 명확하지 않을 수 있다.
CI/CD와 Kubernetes 배포 과정이 분리될 수 있다.
이 문제를 해결하기 위한 대표적인 방식이 바로 GitOps다.
그리고 Kubernetes GitOps 환경에서 대표적으로 사용되는 도구가 Argo CD다.
GitOps는 간단하게 표현하면 다음과 같다.
Git을 Kubernetes 운영 환경의 최종 상태를 정의하는 Source of Truth로 사용하는 방식
즉 Kubernetes에 직접 명령을 내려 상태를 변경하는 것이 아니라 Git에 원하는 상태를 기록한다.
예를 들어 Git에 다음과 같은 Deployment가 있다고 가정하자.
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 3
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: tax-backend:1.2.0
Git에는 다음 상태가 저장되어 있다.
backend
replicas = 3
image = tax-backend:1.2.0
Kubernetes가 실제로 다음 상태라고 가정하자.
backend
replicas = 2
image = tax-backend:1.1.0
GitOps에서는 이 차이를 Drift라고 한다.
즉,
Git 상태
│
│ desired state
▼
replicas = 3
image = 1.2.0
≠
Kubernetes 실제 상태
replicas = 2
image = 1.1.0
GitOps 시스템은 이 차이를 감지하고 Kubernetes를 Git의 상태와 일치시키는 작업을 수행한다.
GitOps를 이해하려면 다음 네 가지 개념을 이해해야 한다.
Git에 정의된 원하는 상태다.
예:
Deployment replicas = 3
Image = 1.2.0
CPU request = 500m
Memory request = 512Mi
현재 Kubernetes에서 실제로 동작하고 있는 상태다.
Deployment replicas = 2
Image = 1.1.0
CPU request = 500m
Memory request = 512Mi
Desired State와 Actual State의 차이다.
Desired State
│
│
▼
replicas 3
│
│ Drift
▼
Actual State
replicas 2
GitOps 시스템이 Desired State와 Actual State를 비교하고 차이를 수정하는 과정이다.
Git
│
│ Desired State
▼
Argo CD
│
│ compare
▼
Kubernetes
│
│ Actual State
▼
Difference?
│
├── No → Synced
│
└── Yes → Sync
이 과정을 계속 반복한다.
Argo CD는 Kubernetes를 위한 GitOps Continuous Delivery 도구다.
핵심 역할은 다음과 같다.
Git Repository
│
│ YAML / Helm
▼
Argo CD
│
│ Reconciliation
▼
Kubernetes
Argo CD는 Git Repository에 저장된 Kubernetes 설정을 읽고 Kubernetes의 실제 상태와 비교한다.
그리고 설정에 따라 자동으로 동기화할 수 있다.
지금까지 구성한 시스템에 Argo CD를 추가하면 전체 구조는 다음과 같이 발전한다.
Developer
│
▼
Git Push
│
▼
┌──────────────┐
│ Git │
│ Application │
│ Helm / YAML │
└──────┬───────┘
│
▼
┌──────────────┐
│ CI Build │
│ GitHub │
│ Actions │
└──────┬───────┘
│
▼
Container
Image
│
▼
Container
Registry
│
│ Image Tag
▼
┌──────────────┐
│ GitOps Repo │
│ values-prod │
└──────┬───────┘
│
▼
┌─────────┐
│ Argo CD │
└────┬────┘
│
▼
Kubernetes
│
┌──────────┼──────────┐
▼ ▼ ▼
FastAPI Redis PostgreSQL
│
▼
┌───────────────┐
│ Observability │
└───────┬───────┘
│
┌─────┼─────┐
▼ ▼ ▼
Prometheus Loki Tempo
│ │ │
└─────┼─────┘
▼
Grafana
│
▼
Alertmanager
│
▼
Slack / Email
이 구조가 바로 실전적인 CI/CD → GitOps → Observability → 자동 복구 구조다.
많은 사람들이 CI/CD와 GitOps를 같은 개념으로 생각하지만 역할이 다르다.
CI는 주로 코드 변경을 검증하고 빌드하는 과정이다.
Git Push
↓
Test
↓
Build
↓
Docker Image
↓
Registry
예:
tax-backend:1.2.0
CD는 변경된 애플리케이션을 배포하는 과정이다.
전통적인 방식에서는 GitHub Actions가 직접 Kubernetes에 접근할 수 있다.
GitHub Actions
│
│ kubectl
▼
Kubernetes
하지만 GitOps에서는 구조가 다르다.
GitHub Actions
│
▼
Container Registry
GitOps Repository
│
▼
Argo CD
│
▼
Kubernetes
즉 CI가 Kubernetes에 직접 배포하는 것이 아니라 Git의 배포 설정을 변경하고 Argo CD가 배포를 수행한다.
운영 환경에서 가장 중요한 장점 중 하나는 변경 이력이다.
예를 들어 운영 서버의 Deployment 이미지가 다음과 같이 변경되었다고 가정하자.
1.1.0
↓
1.2.0
GitOps에서는 Git Commit으로 추적할 수 있다.
commit abc123
image: 1.2.0
누가 변경했는지 확인할 수 있다.
git log
특정 변경도 확인할 수 있다.
git show abc123
그리고 문제가 발생하면 이전 Commit으로 되돌릴 수 있다.
git revert abc123
Argo CD가 다시 Kubernetes에 반영한다.
즉,
Rollback
Git
│
│ revert
▼
Argo CD
│
▼
Kubernetes
라는 구조가 만들어진다.
실전에서는 Application Repository와 GitOps Repository를 분리하는 방식도 많이 사용한다.
예를 들어 다음과 같이 구성할 수 있다.
application-repo
├── backend
│ ├── main.py
│ ├── requirements.txt
│ └── Dockerfile
│
├── frontend
│ └── ...
│
└── .github
└── workflows
└── build.yml
GitOps Repository:
gitops-repo
├── apps
│ └── tax-app
│ ├── Chart.yaml
│ ├── values.yaml
│ ├── values-dev.yaml
│ ├── values-prod.yaml
│ │
│ └── templates
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ └── pvc.yaml
│
└── argocd
└── tax-app.yaml
Argo CD는 YAML뿐만 아니라 Helm Chart도 사용할 수 있다.
기존에 작성한 Helm Chart를 그대로 GitOps에 사용할 수 있다.
예:
tax-app
├── Chart.yaml
├── values.yaml
├── values-dev.yaml
├── values-prod.yaml
└── templates
환경별 설정은 다음과 같이 관리할 수 있다.
values-dev.yaml
replicaCount: 1
image:
tag: "1.2.0"
values-prod.yaml
replicaCount: 3
image:
tag: "1.2.0"
그러면 Argo CD가 해당 Helm Chart를 렌더링하여 Kubernetes에 적용한다.
먼저 Namespace를 생성한다.
kubectl create namespace argocd
Argo CD 공식 설치 방법을 기준으로 설치할 수 있다.
kubectl apply \
-n argocd \
-f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
설치 상태를 확인한다.
kubectl get pods -n argocd
정상적으로 설치되었다면 여러 Argo CD Pod가 실행된다.
argocd-server
argocd-repo-server
argocd-application-controller
argocd-dex-server
argocd-redis
CLI 설치 후 서버를 확인한다.
argocd version
외부 LoadBalancer가 없다면 Port Forward를 사용할 수 있다.
kubectl port-forward svc/argocd-server \
-n argocd 8080:443
브라우저에서 다음 주소로 접근할 수 있다.
https://localhost:8080
초기 관리자 Password는 다음과 같이 확인할 수 있다.
kubectl -n argocd \
get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" \
| base64 -d
로그인:
argocd login localhost:8080
Argo CD의 핵심 객체가 바로 Application이다.
Application은 다음을 정의한다.
Git Repository
│
├── Repository URL
├── Path
├── Revision
│
▼
Kubernetes
│
├── Cluster
└── Namespace
즉,
이 Git Repository의 특정 경로에 있는 Kubernetes 설정을 특정 Kubernetes Namespace에 배포하라.
라는 의미다.
예를 들어 다음과 같은 Application을 만들 수 있다.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: tax-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/example/tax-gitops.git
targetRevision: main
path: apps/tax-app
helm:
valueFiles:
- values-prod.yaml
destination:
server: https://kubernetes.default.svc
namespace: tax-prod
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
적용한다.
kubectl apply -f tax-app.yaml
Argo CD의 중요한 기능 중 하나가 자동 Sync다.
다음 설정을 보면:
syncPolicy:
automated:
prune: true
selfHeal: true
Git 변경을 감지하면 자동으로 Kubernetes에 반영한다.
예를 들어:
Git
image:
tag: 1.3.0
변경:
image:
tag: 1.4.0
Git Push가 발생한다.
Git Push
↓
Argo CD 감지
↓
Diff 확인
↓
Sync
↓
Kubernetes Deployment 변경
↓
Rolling Update
selfHeal은 운영에서 매우 중요한 기능이다.
Git에는 다음과 같이 정의되어 있다고 가정하자.
replicas: 3
그런데 운영자가 Kubernetes에서 직접 변경했다.
kubectl scale deployment backend \
--replicas=1
실제 상태:
Git:
replicas = 3
Kubernetes:
replicas = 1
Argo CD는 Drift를 감지한다.
OutOfSync
selfHeal이 활성화되어 있다면 다시 Git 상태로 맞춘다.
replicas 1
↓
Argo CD
↓
replicas 3
이것이 GitOps의 핵심이다.
Git에서 Kubernetes Resource를 제거했는데 Kubernetes에는 여전히 Resource가 남아 있을 수 있다.
예:
Git:
deployment.yaml
service.yaml
configmap.yaml
변경 후:
deployment.yaml
service.yaml
ConfigMap을 Git에서 삭제했다면 prune: true일 때 Argo CD가 Kubernetes에서도 해당 Resource를 제거할 수 있다.
Git Resource 삭제
↓
Argo CD
↓
Kubernetes Resource 삭제
다만 운영 환경에서는 데이터가 포함된 Resource에 특히 주의해야 한다.
특히 다음은 무조건적인 자동 삭제를 신중하게 설계해야 한다.
PVC
Database
StatefulSet
Secret
Argo CD CLI에서 확인할 수 있다.
argocd app list
예:
NAME SYNC STATUS HEALTH STATUS
tax-app Synced Healthy
상세 정보:
argocd app get tax-app
Sync:
argocd app sync tax-app
History:
argocd app history tax-app
Argo CD는 단순히 Git과 Kubernetes Resource가 같은지만 확인하지 않는다.
Application의 Health 상태도 확인한다.
대표적인 상태:
Healthy
Progressing
Degraded
Missing
Unknown
Suspended
예를 들어 Deployment가 정상적으로 Pod를 생성하고 Ready 상태가 되면:
Healthy
반대로 Pod가 계속 실패한다면:
Degraded
가 될 수 있다.
이제 전체 과정을 살펴보자.
개발자가 FastAPI 코드를 수정한다.
main.py
Git Push:
git add .
git commit -m "Update API"
git push
GitHub Actions가 실행된다.
GitHub Actions
1. Checkout
2. Test
3. Build
4. Docker Image
5. Push Registry
예:
tax-backend:1.5.0
다음으로 GitOps Repository의 Helm Values를 변경한다.
image:
repository: registry.example.com/tax-backend
tag: "1.5.0"
Commit:
Update backend image to 1.5.0
Argo CD가 변경을 감지한다.
GitOps Repository
↓
Argo CD
↓
Sync
↓
Kubernetes
GitHub Actions에서 이미지 빌드 후 GitOps Repository의 Image Tag를 변경하는 방법도 있다.
개념적으로는 다음과 같다.
- name: Build Docker Image
run: |
docker build -t registry.example.com/tax-backend:${GITHUB_SHA} .
- name: Push Image
run: |
docker push registry.example.com/tax-backend:${GITHUB_SHA}
- name: Update GitOps
run: |
sed -i \
"s/tag:.*/tag: ${GITHUB_SHA}/" \
values-prod.yaml
git add values-prod.yaml
git commit -m "Update backend image"
git push
그러면 Kubernetes에 직접 kubectl apply를 하지 않는다.
GitHub Actions에서 Kubernetes API에 직접 접근하지 않는 것도 중요한 장점이다.
전통적인 구조:
GitHub Actions
│
│ Kubeconfig
▼
Kubernetes API
이 구조에서는 CI 서버가 Kubernetes 접근 권한을 가져야 한다.
GitOps 구조:
GitHub Actions
│
▼
GitOps Repository
│
▼
Argo CD
│
▼
Kubernetes
Argo CD가 Kubernetes 내부 또는 접근 가능한 위치에서 동작한다면 CI 시스템에 Kubernetes 관리자 권한을 직접 제공할 필요를 줄일 수 있다.
운영 환경에서는 다음 원칙을 권장한다.
개발자
│
▼
Git
│
▼
Pull Request
│
▼
Code Review
│
▼
Merge
│
▼
Argo CD
│
▼
Kubernetes
운영자가 직접 다음 명령을 실행하는 것은 최소화한다.
kubectl edit deployment
또는:
kubectl scale deployment ...
운영 변경은 가능하면 Git을 통해 수행한다.
실제 운영에서는 환경을 분리해야 한다.
예:
GitOps
│
├── dev
│ └── values.yaml
│
├── staging
│ └── values.yaml
│
└── production
└── values.yaml
또는:
apps
└── tax-app
├── values-dev.yaml
├── values-stage.yaml
└── values-prod.yaml
환경별 설정:
DEV
replicas = 1
CPU = 100m
Memory = 256Mi
STAGE
replicas = 2
CPU = 500m
Memory = 512Mi
PROD
replicas = 3
CPU = 1000m
Memory = 1Gi
운영 환경에서는 모든 Git Commit이 즉시 Production에 반영되도록 하는 것이 항상 적절한 것은 아니다.
예를 들어:
DEV
↓
STAGE
↓
TEST
↓
PRODUCTION
Production은 Pull Request 승인 후 Merge하도록 구성할 수 있다.
Developer
↓
Pull Request
↓
Code Review
↓
Test
↓
Approval
↓
Merge
↓
Argo CD
↓
Production
이렇게 하면 변경 통제와 감사 추적이 쉬워진다.
대규모 서비스에서는 새로운 버전을 한 번에 전체 Pod에 배포하지 않는 방식도 사용할 수 있다.
대표적으로:
Rolling Update
Blue/Green
Canary
등이 있다.
예를 들어 Canary 배포:
Version 1
████████████████████ 90%
Version 2
██ 10%
새 버전에서 문제가 없다면:
Version 1
██████████ 50%
Version 2
██████████ 50%
이후:
Version 2
████████████████████ 100%
이러한 Progressive Delivery는 Argo CD 생태계의 추가 도구들과 함께 구성할 수 있다.
여기서 지금까지 구축한 Prometheus, Grafana, Loki, Tempo가 중요해진다.
단순히:
Argo CD = Synced
라고 해서 배포가 성공했다고 판단해서는 안 된다.
왜냐하면 Kubernetes Resource는 정상이어도 애플리케이션이 실제로 문제가 있을 수 있기 때문이다.
예:
Argo CD
Healthy
↓
Pod Running
↓
HTTP 500 증가
따라서 배포 이후에는 Observability 데이터로 실제 서비스 상태를 검증해야 한다.
배포 후 다음 지표를 확인할 수 있다.
CPU
Memory
Pod Restart
Request Rate
Error Rate
Latency
Availability
예를 들어 HTTP 5xx 비율을 확인할 수 있다.
sum(rate(http_requests_total{
status=~"5.."
}[5m]))
/
sum(rate(http_requests_total[5m]))
배포 직후:
Error Rate
Before Deploy
0.2%
After Deploy
8.5%
이런 상황이라면 Argo CD에서는:
Healthy
라고 보여도 실제 서비스에는 문제가 발생한 것이다.
Grafana Dashboard를 이용해 다음과 같이 비교할 수 있다.
Deployment
│
├── CPU
├── Memory
├── Request Rate
├── Error Rate
├── Latency
└── Pod Restart
예를 들어:
Deploy
│
Error Rate ────────┼────────────
│
│ /^^^^^^
│ /
│____/
배포 시점을 기준으로 Error Rate가 증가했다면 애플리케이션 변경과 장애 사이의 상관관계를 확인할 수 있다.
Prometheus가 문제가 있다는 것을 알려줬다면 Loki에서는 실제 로그를 확인한다.
예:
{namespace="tax-prod", app="backend"}
|= "ERROR"
HTTP 500:
{namespace="tax-prod", app="backend"}
|= "500"
Exception:
{namespace="tax-prod", app="backend"}
|~ "Exception|Traceback"
예를 들어 다음 로그를 발견했다고 가정하자.
ERROR Database connection failed
ERROR psycopg2.OperationalError
그러면 다음과 같은 가설을 세울 수 있다.
Deployment
↓
Backend 1.5.0
↓
DB Connection Error
↓
HTTP 500
↓
Error Rate 증가
로그만으로 원인을 찾기 어려운 경우 Tempo를 사용한다.
예를 들어 사용자의 요청:
GET /api/expenses
Trace:
HTTP Request
│
▼
FastAPI
│
├── authentication
│
├── PostgreSQL
│
└── Redis
Trace ID:
4f7c8a...
각 Span의 시간을 확인할 수 있다.
HTTP Request 1200ms
├─ FastAPI 50ms
├─ PostgreSQL 950ms
└─ Redis 20ms
그러면 PostgreSQL에서 지연이 발생하고 있다는 사실을 빠르게 확인할 수 있다.
실전 Observability에서는 세 가지 데이터를 연결해야 한다.
┌─────────────┐
│ Prometheus │
│ Metrics │
└──────┬──────┘
│
▼
Grafana
▲
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
Prometheus Loki Tempo
Metrics Logs Traces
예를 들어:
Alert
↓
Error Rate 증가
↓
Grafana
↓
Loki
↓
ERROR 로그 확인
↓
Trace ID 확인
↓
Tempo
↓
DB Query 지연 확인
이렇게 장애 분석 시간을 줄일 수 있다.
이제 중요한 부분이다.
배포 시스템과 관측 시스템을 하나의 흐름으로 연결한다.
Developer
│
▼
Git
│
▼
CI
│
▼
Container Registry
│
▼
GitOps Repository
│
▼
Argo CD
│
▼
Kubernetes
│
├──────────────┐
▼ ▼
Application Infrastructure
│
▼
Observability
│
┌──┼─────────┐
▼ ▼ ▼
Prometheus Loki Tempo
│ │ │
└──────┼───────────┘
▼
Grafana
│
▼
Alertmanager
│
▼
Slack / Email
실제 상황을 하나 만들어보자.
개발자가 FastAPI 코드를 수정했다.
Version 1.4.0
↓
Version 1.5.0
CI가 이미지를 만든다.
tax-backend:1.5.0
GitOps Repository가 변경된다.
image:
tag: "1.5.0"
Argo CD가 자동 Sync한다.
Argo CD
Synced
Healthy
하지만 배포 후 문제가 발생한다.
HTTP 500
증가
Prometheus Alertmanager가 알림을 발생시킨다.
HighHTTP5xxRate
Slack:
[CRITICAL]
Backend HTTP 5xx rate exceeded threshold
운영자는 Grafana를 확인한다.
Error Rate
0.2%
↓
12.8%
Loki를 검색한다.
{app="backend"} |= "ERROR"
로그:
DatabaseError
relation "expense_items" does not exist
Tempo를 확인한다.
HTTP
↓
FastAPI
↓
PostgreSQL
↓
SQL Error
원인은 DB Migration 누락으로 판단할 수 있다.
여기서 중요한 것은 모든 장애를 자동 복구하면 안 된다는 것이다.
예를 들어 Pod 하나가 죽었다.
Pod
↓
Crash
Kubernetes가 자동으로 재시작하는 것이 적절하다.
하지만 다음 상황은 다르다.
새로운 Application Version
↓
Database Schema Error
↓
HTTP 500 증가
이 경우 단순히 Pod를 계속 재시작하면 문제가 해결되지 않는다.
오히려:
Pod Restart
↓
Application Start
↓
DB Error
↓
Pod Restart
↓
DB Error
무한 반복이 될 수 있다.
따라서 자동화 수준을 구분해야 한다.
Pod 장애
↓
ReplicaSet
↓
Pod 재생성
자동화해도 좋다.
Liveness Probe 실패
↓
Container Restart
자동화 가능하다.
CPU 증가
↓
HPA
↓
Replica 증가
자동화 가능하다.
Deployment
↓
Error Rate 증가
↓
자동 Rollback
운영 환경에서는 매우 신중하게 설계해야 한다.
DB Migration
이것은 자동화 전에 반드시 충분한 검증과 승인 절차가 필요하다.
Argo CD에서는 배포 History를 확인할 수 있다.
argocd app history tax-app
이전 Revision으로 되돌리는 방식도 사용할 수 있다.
argocd app rollback tax-app <ID>
하지만 GitOps 환경에서는 단순히 Kubernetes 상태만 이전으로 돌리는 것보다 Git의 선언 상태를 되돌리는 방식을 운영 원칙으로 삼는 것이 좋다.
예:
Git
1.5.0
↓
revert
↓
1.4.0
↓
Argo CD
↓
Kubernetes
이렇게 하면 Git과 실제 클러스터 상태가 일치한다.
여기서 매우 중요한 차이가 있다.
Kubernetes Self-Healing:
Runtime State 복구
Argo CD Self-Healing:
Configuration Drift 복구
예:
Pod 죽음
↓
Kubernetes
↓
Pod 재생성
반면:
Deployment replicas 변경
↓
Argo CD
↓
Git 상태로 복구
둘은 서로 다른 문제를 해결한다.
이제 앞서 만든 SRE 체계와 연결해보자.
Incident
│
▼
Alertmanager
│
▼
Grafana
│
┌──────────┼──────────┐
▼ ▼ ▼
Metrics Logs Traces
Prometheus Loki Tempo
│ │ │
└──────────┼──────────┘
▼
Root Cause
│
▼
Git Repository
│
▼
Fix PR
│
▼
CI
│
▼
GitOps Merge
│
▼
Argo CD
│
▼
Kubernetes
│
▼
Self Healing
이 구조에서는 장애 대응 자체가 다시 GitOps 프로세스로 연결된다.
GitOps 환경에서는 Git Repository가 사실상 운영 인프라의 중요한 관리 지점이 된다.
따라서 다음을 반드시 고려해야 한다.
Developer
↓
Read / PR
DevOps
↓
Write
Production 설정은 제한한다.
Password나 API Key를 Git에 평문으로 저장하면 안 된다.
잘못된 예:
password: mypassword123
대신 다음과 같은 방식을 고려할 수 있다.
External Secrets
Sealed Secrets
Vault
Cloud Secret Manager
Git에는 실제 Secret 대신 참조 정보만 저장한다.
Argo CD 역시 사용자별 권한을 분리할 수 있다.
예:
Admin
├── Application 생성
├── Sync
├── Delete
└── 변경
Developer
├── Application 조회
└── 로그/상태 확인
Viewer
└── Read Only
운영 환경에서는 최소 권한 원칙을 적용하는 것이 중요하다.
Argo CD가 Kubernetes에 접근하기 때문에 Argo CD Service Account의 권한도 중요하다.
구조:
Argo CD
│
▼
ServiceAccount
│
▼
RBAC
│
▼
Kubernetes API
필요하지 않은 Cluster Admin 권한을 무조건 부여하지 않는 것이 좋다.
다음과 같은 운영 습관은 피하는 것이 좋다.
kubectl edit deployment
kubectl patch deployment ...
kubectl scale deployment ...
그리고 Git에 기록하지 않는 변경:
Manual kubectl
↓
Kubernetes 변경
↓
Git에는 없음
이 상태가 반복되면 GitOps의 의미가 사라진다.
운영 변경:
Issue
↓
PR
↓
Review
↓
Merge
↓
Argo CD
↓
Kubernetes
긴급 장애 상황에서는 직접 조치할 수 있지만 이후 반드시 Git에 변경사항을 반영해야 한다.
즉:
Emergency Change
↓
Temporary Runtime Fix
↓
Git Update
↓
Reconciliation
이라는 원칙을 유지하는 것이 좋다.
실전 DevOps에서는 "Pod가 Running이면 배포 성공"이라고 판단하지 않는다.
배포 성공 조건을 여러 단계로 나눈다.
1. Git Sync
↓
2. Kubernetes Resource Healthy
↓
3. Pod Ready
↓
4. HTTP Health Check
↓
5. Error Rate 정상
↓
6. Latency 정상
↓
7. Logs 정상
↓
8. Trace 정상
↓
9. Deployment Success
이것이 Observability 기반 배포 검증이다.
예를 들어 다음 기준을 만들 수 있다.
Deployment Success
Argo CD
= Synced
AND
Health
= Healthy
AND
Ready Pods
= Desired Pods
AND
HTTP 5xx
< 1%
AND
P95 Latency
< 500ms
이러한 기준을 기반으로 배포 자동화를 설계할 수 있다.
예를 들어 배포 직후 다음 Alert를 사용할 수 있다.
groups:
- name: application
rules:
- alert: HighHTTP5xxRate
expr: |
(
sum(rate(http_requests_total{
status=~"5.."
}[5m]))
/
sum(rate(http_requests_total[5m]))
) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "HTTP 5xx rate is high"
5분 이상 5xx가 5%를 초과하면 장애 알림을 발생시킬 수 있다.
자동 Rollback은 다음과 같은 구조로 생각할 수 있다.
New Version
↓
Argo CD Deploy
↓
Wait
↓
Prometheus Metrics
↓
Error Rate?
│
┌───┴────┐
│ │
No Yes
│ │
▼ ▼
Success Rollback
하지만 실제 운영에서는 단순 Alert 발생만으로 Rollback을 결정하기보다 다음 조건을 함께 고려하는 것이 좋다.
Error Rate
+
Latency
+
Availability
+
Pod Restart
+
Business Metrics
그리고 DB Migration이나 비가역적인 데이터 변경이 포함된 배포는 자동 Rollback과 충돌할 수 있으므로 별도의 전략이 필요하다.
이제 전체 시스템을 SRE 관점에서 정리할 수 있다.
Git
│
▼
CI
│
┌──────┴──────┐
▼ ▼
Test/Build Security
│
▼
Container
Registry
│
▼
GitOps Repo
│
▼
Argo CD
│
▼
Kubernetes
│
┌──────┼──────┐
▼ ▼ ▼
Metrics Logs Traces
│ │ │
▼ ▼ ▼
Prometheus Loki Tempo
└──────┼──────┘
▼
Grafana
│
▼
Alertmanager
│
┌────┴────┐
▼ ▼
Slack Email
여기에 자동 복구를 추가한다.
Alert
↓
Analysis
↓
Automation
↓
Recovery
↓
Verification
다음과 같은 상황을 생각해보자.
FastAPI 1.5.0
Test
↓
Build
↓
Docker Image
↓
Registry
image:
tag: "1.5.0"
OutOfSync
↓
Sync
↓
Progressing
↓
Healthy
HTTP 500 증가
HighHTTP5xxRate
Error Rate ↑
Latency ↑
DatabaseError
FastAPI
↓
PostgreSQL
↓
SQL Error
Database migration 누락
Migration 추가
Test
↓
Build
↓
Image
1.5.1
Sync
5xx ↓
Latency ↓
Logs 정상
Trace 정상
Incident
↓
Recovery
↓
Verification
↓
Postmortem
이것이 실제 SRE 운영 프로세스에 가까운 구조다.
장애가 끝났다고 운영이 끝나는 것은 아니다.
장애 기록을 남긴다.
Incident ID
INC-2026-001
2026-09-16 10:30
API 5xx 증가
DB Migration 누락
Prometheus
Grafana
Loki
Tempo
Git Fix
Argo CD Deployment
CI Migration Test 추가
Deployment Verification 추가
이런 Postmortem이 반복되면 운영 시스템 자체가 점점 개선된다.
지금까지 배운 기술을 하나로 연결하면 다음과 같다.
Developer
│
▼
Git Repository
│
▼
GitHub Actions
│
┌──────┴──────┐
▼ ▼
Test Build
│
▼
Container Image
│
▼
Container Registry
│
▼
GitOps Repo
│
▼
Argo CD
│
▼
Kubernetes
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
FastAPI Redis PostgreSQL
│
▼
┌────────────────────────┐
│ Observability │
├────────────────────────┤
│ Prometheus │
│ Grafana │
│ Loki │
│ Tempo │
│ Alertmanager │
└────────────┬───────────┘
│
▼
Slack / Email
│
▼
Incident Response
│
▼
Auto Recovery
│
▼
Git Fix
│
▼
Argo CD
전체 시스템을 한 문장으로 표현하면 다음과 같다.
코드를 변경한다
↓
CI가 테스트한다
↓
Container Image를 만든다
↓
Registry에 저장한다
↓
GitOps Repository의 배포 설정을 변경한다
↓
Argo CD가 변경을 감지한다
↓
Kubernetes에 배포한다
↓
Prometheus가 Metrics를 수집한다
↓
Grafana에서 상태를 확인한다
↓
Loki에서 로그를 분석한다
↓
Tempo에서 Trace를 분석한다
↓
Alertmanager가 장애를 알린다
↓
Kubernetes가 가능한 장애를 자동 복구한다
↓
필요하면 Git 변경으로 근본 원인을 수정한다
↓
Argo CD가 다시 배포한다
↓
Observability로 정상 상태를 검증한다
이렇게 연결하면 단순한 Kubernetes 클러스터가 아니라 운영 자동화 플랫폼이 된다.
Argo CD를 단순히 "Kubernetes 배포 도구"로 이해하면 부족하다.
중요한 개념은 다음과 같다.
| 기술 | 핵심 역할 |
|---|---|
| Git | Desired State 저장 |
| GitHub Actions | CI / Build / Test |
| Registry | Container Image 저장 |
| Helm | Kubernetes 패키징 |
| Argo CD | Git → Kubernetes 동기화 |
| Kubernetes | 실제 애플리케이션 실행 |
| Prometheus | Metrics |
| Grafana | Visualization |
| Loki | Logs |
| Tempo | Traces |
| Alertmanager | Alert Routing |
| Kubernetes Self-Healing | Runtime 복구 |
| GitOps Self-Healing | Configuration Drift 복구 |
| SRE | 전체 운영 방법론 |
특히 다음 세 가지는 구분해야 한다.
Kubernetes
= 서비스를 실행한다
Argo CD
= 원하는 상태를 유지한다
Observability
= 실제 상태를 관찰한다
그리고 이 세 가지가 연결되면서 자동화 수준이 올라간다.
최종적으로 우리가 만들고 있는 Kubernetes 운영 환경은 다음과 같다.
┌─────────────┐
│ Developer │
└──────┬──────┘
│
▼
┌─────────────┐
│ Git │
└──────┬──────┘
│
▼
┌─────────────┐
│ CI │
│ Test / Build│
└──────┬──────┘
│
▼
┌─────────────┐
│ Registry │
└──────┬──────┘
│
▼
┌─────────────┐
│ GitOps │
│ Repository │
└──────┬──────┘
│
▼
┌─────────────┐
│ Argo CD │
└──────┬──────┘
│
▼
┌────────────────────┐
│ Kubernetes │
└─────────┬──────────┘
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
Metrics Logs Traces
│ │ │
▼ ▼ ▼
Prometheus Loki Tempo
│ │ │
└────────────┼────────────┘
▼
Grafana
│
▼
Alertmanager
│
┌──────┴──────┐
▼ ▼
Slack Email
│
▼
Incident
Response
│
▼
Auto Recovery
│
▼
Git Fix
│
└──────────────► Argo CD
이 구조를 이해하면 Kubernetes를 단순히 "Pod를 실행하는 기술"로 보는 단계에서 벗어나 애플리케이션의 배포, 운영, 모니터링, 장애 대응, 복구까지 자동화하는 플랫폼으로 이해할 수 있다.
Kubernetes 운영 자동화의 핵심은 특정 도구 하나를 설치하는 것이 아니다.
중요한 것은 각 기술의 역할을 연결하는 것이다.
Git
↓
CI
↓
Container
↓
Registry
↓
GitOps
↓
Argo CD
↓
Kubernetes
↓
Prometheus
↓
Grafana
↓
Loki
↓
Tempo
↓
Alertmanager
↓
자동 복구
↓
Postmortem
↓
Git 개선
이 구조에서는 사람이 모든 작업을 직접 수행하지 않는다.
사람은 코드와 운영 정책을 Git에 선언하고, 시스템은 그 선언된 상태를 기반으로 배포하고 관찰하고 복구한다.
결국 목표는 다음과 같다.
"운영자가 서버에 접속해서 명령을 실행하는 환경"에서 "Git에 변경을 기록하면 플랫폼이 배포·검증·관찰·복구하는 환경"으로 전환하는 것
이것이 Kubernetes 기반 GitOps + Observability + SRE 운영 체계의 핵심이다.