Argo CD는 Git → k8s 동기화를 담당한다. Crossplane은 k8s CR → AWS 인프라 프로비저닝을 담당한다. 이 둘을 결합하면 Git이 k8s 앱과 AWS 인프라 모두의 단일 진실 원천이 된다.
Git 저장소
├── apps/production/order-service/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── db-claim.yaml ← PostgreSQLInstance Claim
└── infrastructure/
├── composition.yaml ← Crossplane Composition
└── provider-config.yaml
↓ (Argo CD가 감시 → 적용)
k8s 클러스터
├── Deployment, Service (앱)
└── PostgreSQLInstance CR (Crossplane Claim)
↓ (Crossplane Operator가 처리)
AWS
└── RDS 인스턴스
개발자는 Git에 코드와 Claim YAML을 올린다. 이후 인프라 프로비저닝까지 자동이다.
실제 파이프라인
1. Git 저장소 구조
my-gitops/
├── platform/ # 인프라 팀 관리
│ ├── crossplane/
│ │ ├── provider-aws.yaml
│ │ ├── provider-config.yaml
│ │ └── compositions/
│ │ └── postgresql.yaml # Composition 정의
│ └── argocd/
│ └── root-app.yaml
└── apps/
└── production/
└── order-service/
├── deployment.yaml
├── service.yaml
├── ingress.yaml
└── db-claim.yaml # 앱 팀이 요청하는 DB
2. 앱 팀의 DB 요청
# apps/production/order-service/db-claim.yaml
apiVersion: platform.example.com/v1alpha1
kind: PostgreSQLInstance
metadata:
name: order-db
namespace: order-service
spec:
parameters:
size: medium
storageGB: 100
writeConnectionSecretToRef:
name: order-db-credentials
앱 팀은 이 파일을 추가하고 PR을 올린다. 인프라 팀이 리뷰하고 merge하면 자동으로 진행된다.
3. Argo CD 동기화
Argo CD가 apps/production/order-service/ 변경을 감지하고 k8s에 적용한다.
# Argo CD UI에서 또는
kubectl get applications -n argocd order-service
# STATUS: Synced
kubectl get postgresqlinstances -n order-service
# NAME READY SYNCED
# order-db False True ← 아직 프로비저닝 중
4. Crossplane 프로비저닝
Crossplane이 PostgreSQLInstance CR을 감지하고 Composition을 실행한다.
# 프로비저닝 완료 후
kubectl get postgresqlinstances -n order-service
# NAME READY SYNCED
# order-db True True
# 연결 정보가 Secret에 자동 저장됨
kubectl get secret order-db-credentials -n order-service
# NAME TYPE DATA AGE
# order-db-credentials Opaque 5 2m
앱 Deployment가 이 Secret을 환경변수로 마운트해 DB에 접근한다.
프로비저닝 시간 처리
RDS 인스턴스는 만드는 데 수 분이 걸린다. 앱 Deployment가 DB 연결을 시도하는 시점에 DB가 아직 준비 중일 수 있다.
두 가지 방법으로 처리한다.
Init Container로 DB 대기: 앱 컨테이너가 시작하기 전에 DB 연결이 될 때까지 기다린다.
initContainers:
- name: wait-for-db
image: busybox
command:
- sh
- -c
- |
until nc -z $DB_HOST 5432; do
echo "DB 대기 중..."
sleep 5
done
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: order-db-credentials
key: host
Crossplane readiness를 Argo CD Sync Wave에 연결: DB Claim을 먼저 배포하고(wave 0), DB가 Ready 상태가 된 이후 앱을 배포(wave 1)한다.
# db-claim.yaml
metadata:
annotations:
argocd.argoproj.io/sync-wave: "0"
# deployment.yaml
metadata:
annotations:
argocd.argoproj.io/sync-wave: "1"
Argo CD는 wave 0의 모든 리소스가 Healthy가 된 후 wave 1을 배포한다. Crossplane CR의 READY: True가 Healthy 기준이다.
환경 분리
여러 환경을 같은 패턴으로 관리한다.
apps/
├── staging/
│ └── order-service/
│ └── db-claim.yaml # size: small, storageGB: 20
└── production/
└── order-service/
└── db-claim.yaml # size: large, storageGB: 500
환경별 값만 다르고 구조는 동일하다. Kustomize로 공통 base에 환경별 overlay를 적용하는 방식으로 중복을 줄일 수 있다.
트레이드오프
Crossplane Composition이 있는 k8s 클러스터와 앱이 배포되는 클러스터를 분리하는 것이 일반적이다. Management Cluster (Crossplane 운영)와 Workload Cluster (앱 운영)를 나누면 인프라 프로비저닝 실패가 앱 클러스터에 영향을 주지 않는다.
Crossplane으로 만든 리소스를 삭제할 때 주의가 필요하다. Claim을 삭제하면 Crossplane이 실제 AWS 리소스를 삭제한다. Argo CD의 prune: true가 켜져 있으면 Git에서 Claim을 제거했을 때 RDS가 삭제된다. 프로덕션 데이터베이스가 PR merge 한 번으로 사라질 수 있다. deletionPolicy: Orphan을 설정해 k8s CR 삭제 시 실제 AWS 리소스는 보존하는 것이 안전하다.
spec:
forProvider:
...
managementPolicies: ["Observe", "Create", "Update"] # Delete 제외