들어가며
Terraform은 IaC의 사실상 표준이었지만, Kubernetes 중심 환경에서는 한계가 있습니다. 상태 파일 관리, Plan/Apply 수동 실행, 드리프트 감지의 어려움 등이 대표적입니다. **Crossplane**은 이 문제를 Kubernetes의 선언적 모델과 컨트롤러 패턴으로 해결합니다.
2025년 10월 CNCF Graduated 프로젝트로 승격된 Crossplane은 이제 프로덕션 환경에서 충분히 검증되었습니다.
Crossplane vs Terraform
| 항목 | Terraform | Crossplane |
| ------------- | ------------------------------- | --------------------------------- |
| 실행 모델 | CLI (Plan/Apply) | Kubernetes 컨트롤러 (지속적 조정) |
| 상태 관리 | tfstate 파일 (외부 백엔드 필요) | etcd (Kubernetes 자체) |
| 드리프트 감지 | 수동 plan 필요 | 자동 (reconciliation loop) |
| 추상화 | Module | Composition |
| 권한 관리 | 별도 IAM 설정 | RBAC 통합 |
| GitOps | 별도 파이프라인 필요 | ArgoCD/Flux 네이티브 연동 |
Crossplane 설치
Helm으로 설치
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update
helm install crossplane crossplane-stable/crossplane \
--namespace crossplane-system \
--create-namespace \
--set args='{"--enable-usages"}'
설치 확인
kubectl get pods -n crossplane-system
kubectl api-resources | grep crossplane
AWS Provider 설정
AWS Provider 설치
cat <<EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-s3
spec:
package: xpkg.upbound.io/upbound/provider-aws-s3:v1.18.0
EOF
Provider 설치 확인
kubectl get providers
인증 설정
AWS 자격 증명 Secret 생성
kubectl create secret generic aws-creds \
-n crossplane-system \
--from-file=credentials=$HOME/.aws/credentials
ProviderConfig 생성
cat <<EOF | kubectl apply -f -
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
name: default
spec:
credentials:
source: Secret
secretRef:
namespace: crossplane-system
name: aws-creds
key: credentials
EOF
Managed Resources — 클라우드 리소스 직접 관리
s3-bucket.yaml
apiVersion: s3.aws.upbound.io/v1beta2
kind: Bucket
metadata:
name: my-crossplane-bucket
spec:
forProvider:
region: ap-northeast-2
tags:
Environment: production
ManagedBy: crossplane
providerConfigRef:
name: default
kubectl apply -f s3-bucket.yaml
상태 확인
kubectl get bucket my-crossplane-bucket
NAME READY SYNCED EXTERNAL-NAME AGE
my-crossplane-bucket True True my-crossplane-bucket 2m
상세 상태
kubectl describe bucket my-crossplane-bucket
RDS 인스턴스 생성
rds-instance.yaml
apiVersion: rds.aws.upbound.io/v1beta2
kind: Instance
metadata:
name: my-postgres
spec:
forProvider:
region: ap-northeast-2
allocatedStorage: 20
engine: postgres
engineVersion: '16.4'
instanceClass: db.t3.micro
dbName: myapp
masterUsername: admin
masterPasswordSecretRef:
name: rds-password
namespace: default
key: password
skipFinalSnapshot: true
publiclyAccessible: false
vpcSecurityGroupIdSelector:
matchLabels:
app: my-rds
providerConfigRef:
name: default
writeConnectionSecretToRef:
name: rds-connection
namespace: default
kubectl apply -f rds-instance.yaml
연결 정보가 자동으로 Secret에 저장됨
kubectl get secret rds-connection -o yaml
Composition — 추상화의 핵심
Composition은 여러 Managed Resource를 하나의 고수준 API로 묶습니다. Terraform의 Module과 비슷하지만 Kubernetes 네이티브입니다.
CompositeResourceDefinition (XRD)
xrd-database.yaml
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xdatabases.platform.example.com
spec:
group: platform.example.com
names:
kind: XDatabase
plural: xdatabases
claimNames:
kind: Database
plural: databases
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
parameters:
type: object
properties:
size:
type: string
enum: ['small', 'medium', 'large']
default: 'small'
engine:
type: string
enum: ['postgres', 'mysql']
default: 'postgres'
region:
type: string
default: 'ap-northeast-2'
required:
- size
Composition
composition-database.yaml
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: aws-database
labels:
provider: aws
spec:
compositeTypeRef:
apiVersion: platform.example.com/v1alpha1
kind: XDatabase
resources:
- name: rds-instance
base:
apiVersion: rds.aws.upbound.io/v1beta2
kind: Instance
spec:
forProvider:
region: ap-northeast-2
engine: postgres
engineVersion: '16.4'
skipFinalSnapshot: true
publiclyAccessible: false
providerConfigRef:
name: default
patches:
size -> instanceClass 매핑
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.size
toFieldPath: spec.forProvider.instanceClass
transforms:
- type: map
map:
small: db.t3.micro
medium: db.t3.medium
large: db.r6g.large
size -> allocatedStorage 매핑
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.size
toFieldPath: spec.forProvider.allocatedStorage
transforms:
- type: map
map:
small: '20'
medium: '100'
large: '500'
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.engine
toFieldPath: spec.forProvider.engine
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.region
toFieldPath: spec.forProvider.region
- name: security-group
base:
apiVersion: ec2.aws.upbound.io/v1beta1
kind: SecurityGroup
spec:
forProvider:
region: ap-northeast-2
description: 'Managed by Crossplane'
ingress:
- fromPort: 5432
toPort: 5432
protocol: tcp
cidrBlocks:
- '10.0.0.0/8'
Claim — 개발자가 사용하는 인터페이스
claim-database.yaml
apiVersion: platform.example.com/v1alpha1
kind: Database
metadata:
name: my-app-db
namespace: team-alpha
spec:
parameters:
size: medium
engine: postgres
region: ap-northeast-2
writeConnectionSecretToRef:
name: db-credentials
kubectl apply -f claim-database.yaml
개발자는 이것만 알면 됨
kubectl get database -n team-alpha
NAME READY CONNECTION-SECRET AGE
my-app-db True db-credentials 5m
플랫폼 엔지니어가 확인
kubectl get composite
kubectl get managed
Functions — 고급 Composition 로직
Crossplane Functions를 사용하면 Go/Python 등으로 복잡한 패치 로직을 구현할 수 있습니다.
function-go-templating 사용 예
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: dynamic-database
spec:
compositeTypeRef:
apiVersion: platform.example.com/v1alpha1
kind: XDatabase
mode: Pipeline
pipeline:
- step: render-resources
functionRef:
name: function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
template: |
apiVersion: rds.aws.upbound.io/v1beta2
kind: Instance
metadata:
annotations:
gotemplating.fn.crossplane.io/composition-resource-name: rds
spec:
forProvider:
region: {{ .observed.composite.resource.spec.parameters.region }}
engine: {{ .observed.composite.resource.spec.parameters.engine }}
instanceClass: {{ if eq .observed.composite.resource.spec.parameters.size "small" }}db.t3.micro{{ else if eq .observed.composite.resource.spec.parameters.size "medium" }}db.t3.medium{{ else }}db.r6g.large{{ end }}
- step: auto-ready
functionRef:
name: function-auto-detect-ready
ArgoCD 연동 GitOps
Crossplane 리소스는 일반 Kubernetes 매니페스트이므로 ArgoCD와 자연스럽게 통합됩니다.
argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: infrastructure
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/infra-repo.git
targetRevision: main
path: crossplane/claims
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Git에 Claim을 push하면 자동으로 인프라가 프로비저닝됨
git add claim-database.yaml
git commit -m "feat: provision medium postgres for team-alpha"
git push origin main
ArgoCD가 자동으로 sync
argocd app get infrastructure
Usages — 리소스 의존성 보호
삭제 순서를 보호하는 Usage 리소스입니다.
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: Usage
metadata:
name: db-used-by-app
spec:
of:
apiVersion: platform.example.com/v1alpha1
kind: Database
resourceRef:
name: my-app-db
by:
apiVersion: apps/v1
kind: Deployment
resourceRef:
name: my-app
namespace: team-alpha
reason: 'Database is used by the application'
트러블슈팅
Provider 상태 확인
kubectl get providers
kubectl describe provider provider-aws-s3
Managed Resource 이벤트 확인
kubectl describe bucket my-crossplane-bucket
Composition 디버깅
kubectl get composite -o wide
kubectl describe xdatabase my-app-db-xxxxx
Crossplane 로그
kubectl logs -n crossplane-system deploy/crossplane -f
특정 Provider 로그
kubectl logs -n crossplane-system \
$(kubectl get pods -n crossplane-system -l pkg.crossplane.io/revision -o name | head -1) -f
정리
Crossplane은 Terraform과 경쟁하는 것이 아니라, **Kubernetes 네이티브 환경에서 IaC를 한 단계 진화**시킵니다:
- **지속적 조정**: Plan/Apply 없이 선언만 하면 컨트롤러가 알아서 관리
- **셀프서비스 플랫폼**: Composition으로 복잡한 인프라를 추상화하여 개발자에게 제공
- **GitOps 네이티브**: ArgoCD/Flux와 자연스러운 통합
- **RBAC 통합**: Kubernetes의 기존 권한 체계 그대로 활용
- **드리프트 자동 복구**: 누군가 콘솔에서 수동 변경해도 자동으로 원복
**Q1. Crossplane이 Terraform 대비 드리프트 감지에 강한 이유는?**
Kubernetes 컨트롤러의 reconciliation loop가 지속적으로 실제 상태와 선언 상태를 비교하여 자동으로 복구합니다.
**Q2. Managed Resource와 Composite Resource의 차이는?**
Managed Resource는 실제 클라우드 리소스(S3, RDS 등)를 1:1로 매핑하고, Composite Resource는 여러 Managed Resource를 하나의 고수준 API로 묶은 것입니다.
**Q3. Claim의 역할은 무엇인가요?**
Claim은 네임스페이스 수준에서 개발자가 인프라를 요청하는 인터페이스입니다. Composite Resource를 직접 다루지 않고 추상화된 API를 사용합니다.
**Q4. Crossplane의 상태(state)는 어디에 저장되나요?**
Kubernetes의 etcd에 저장됩니다. Terraform처럼 별도의 상태 파일 관리가 필요 없습니다.
**Q5. Composition의 mode: Pipeline은 어떤 장점이 있나요?**
Go Templating, Python 등 다양한 Function을 체인으로 연결하여 복잡한 리소스 생성 로직을 구현할 수 있습니다.
**Q6. Usage 리소스의 역할은?**
리소스 간 의존성을 선언하여 사용 중인 리소스가 실수로 삭제되는 것을 방지합니다.
**Q7. Crossplane을 ArgoCD와 연동할 때 특별한 설정이 필요한가요?**
아닙니다. Crossplane 리소스는 일반 Kubernetes 매니페스트이므로 ArgoCD가 그대로 sync할 수 있습니다.
현재 단락 (1/310)
Terraform은 IaC의 사실상 표준이었지만, Kubernetes 중심 환경에서는 한계가 있습니다. 상태 파일 관리, Plan/Apply 수동 실행, 드리프트 감지의 어려움 등...