Crossplane이란?
Crossplane은 Kubernetes 위에서 클라우드 인프라를 YAML로 관리하는 도구다. Terraform이 HCL 파일과 CLI로 인프라를 프로비저닝한다면, Crossplane은 Kubernetes CRD(Custom Resource Definition)로 AWS RDS, S3, GCP CloudSQL 등을 선언한다. kubectl로 인프라를 생성하고, Kubernetes의 Reconciliation Loop가 상태를 지속적으로 동기화한다.
핵심 차이는 이렇다: Terraform은 terraform apply를 실행하는 순간에만 동작하는 명령형 워크플로우다. 누군가 콘솔에서 설정을 바꾸면 드리프트가 발생한다. Crossplane은 Kubernetes처럼 지속적으로 desired state를 유지한다. 콘솔에서 변경해도 자동으로 원래 상태로 복구된다.
설치
# Crossplane 설치 (Helm)
helm repo add crossplane-stable https://charts.crossplane.io/stable
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
핵심 개념: 4계층 아키텍처
| 계층 | 역할 | 담당자 |
|---|---|---|
| Provider | 클라우드 API 연결 (AWS, GCP, Azure) | 플랫폼 팀 |
| Managed Resource | 개별 클라우드 리소스 (RDS, S3, VPC) | 플랫폼 팀 |
| Composition | 리소스 조합 템플릿 | 플랫폼 팀 |
| Claim (XR) | 추상화된 인프라 요청 | 개발팀 |
Provider 설정: AWS 연결
# AWS Provider 설치
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-s3
spec:
package: xpkg.upbound.io/upbound/provider-aws-s3:v1.7.0
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-rds
spec:
package: xpkg.upbound.io/upbound/provider-aws-rds:v1.7.0
---
# AWS 자격증명 Secret
apiVersion: v1
kind: Secret
metadata:
name: aws-credentials
namespace: crossplane-system
type: Opaque
stringData:
credentials: |
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
---
# ProviderConfig: 자격증명 연결
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
name: default
spec:
credentials:
source: Secret
secretRef:
namespace: crossplane-system
name: aws-credentials
key: credentials
Managed Resource: 개별 리소스 생성
# S3 버킷 생성 — kubectl apply만으로 AWS에 버킷 생성
apiVersion: s3.aws.upbound.io/v1beta2
kind: Bucket
metadata:
name: my-app-uploads
spec:
forProvider:
region: ap-northeast-2
tags:
Environment: production
Team: backend
providerConfigRef:
name: default
---
# RDS 인스턴스 생성
apiVersion: rds.aws.upbound.io/v1beta2
kind: Instance
metadata:
name: my-app-db
spec:
forProvider:
region: ap-northeast-2
engine: mysql
engineVersion: "8.0"
instanceClass: db.t3.medium
allocatedStorage: 50
dbName: myapp
masterUsername: admin
masterPasswordSecretRef:
name: db-password
namespace: crossplane-system
key: password
publiclyAccessible: false
vpcSecurityGroupIdRefs:
- name: db-security-group
dbSubnetGroupNameRef:
name: db-subnet-group
backupRetentionPeriod: 7
multiAz: true
storageEncrypted: true
tags:
Environment: production
providerConfigRef:
name: default
writeConnectionSecretToRef:
name: db-connection
namespace: default
writeConnectionSecretToRef가 핵심이다. RDS가 생성되면 엔드포인트, 포트, 사용자명, 비밀번호를 Kubernetes Secret에 자동 저장한다. 애플리케이션 Pod에서 이 Secret을 마운트하면 연결 정보를 수동으로 관리할 필요가 없다.
Composition: 리소스 조합 템플릿
실전에서는 DB 하나를 만들 때 서브넷 그룹, 보안 그룹, 파라미터 그룹, 모니터링까지 함께 생성해야 한다. Composition으로 이 모든 것을 하나의 템플릿으로 묶는다.
# CompositeResourceDefinition (XRD) — 추상 인터페이스 정의
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]
description: "DB 인스턴스 크기"
engine:
type: string
enum: [mysql, postgresql]
default: postgresql
region:
type: string
default: ap-northeast-2
required: [size]
required: [parameters]
# Composition — 실제 리소스 조합 정의
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: database-aws
labels:
provider: aws
spec:
compositeTypeRef:
apiVersion: platform.example.com/v1alpha1
kind: XDatabase
resources:
# 1. 서브넷 그룹
- name: subnet-group
base:
apiVersion: rds.aws.upbound.io/v1beta1
kind: SubnetGroup
spec:
forProvider:
region: ap-northeast-2
subnetIds:
- subnet-abc123
- subnet-def456
description: "Managed by Crossplane"
# 2. 보안 그룹
- name: security-group
base:
apiVersion: ec2.aws.upbound.io/v1beta1
kind: SecurityGroup
spec:
forProvider:
region: ap-northeast-2
vpcId: vpc-xyz789
description: "DB Security Group"
# 3. 보안 그룹 규칙
- name: sg-rule-ingress
base:
apiVersion: ec2.aws.upbound.io/v1beta1
kind: SecurityGroupIngressRule
spec:
forProvider:
region: ap-northeast-2
cidrIpv4: "10.0.0.0/16"
fromPort: 5432
toPort: 5432
ipProtocol: tcp
securityGroupIdSelector:
matchControllerRef: true
# 4. RDS 인스턴스
- name: rds-instance
base:
apiVersion: rds.aws.upbound.io/v1beta2
kind: Instance
spec:
forProvider:
region: ap-northeast-2
engine: postgresql
engineVersion: "16"
publiclyAccessible: false
backupRetentionPeriod: 7
storageEncrypted: true
dbSubnetGroupNameSelector:
matchControllerRef: true
vpcSecurityGroupIdSelector:
matchControllerRef: true
patches:
# size 파라미터에 따라 인스턴스 클래스 변경
- 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에 따라 스토리지 크기 변경
- 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
connectionDetails:
- type: FromConnectionSecretKey
fromConnectionSecretKey: endpoint
name: host
- type: FromConnectionSecretKey
fromConnectionSecretKey: port
name: port
- type: FromConnectionSecretKey
fromConnectionSecretKey: username
name: username
- type: FromConnectionSecretKey
fromConnectionSecretKey: password
name: password
Claim: 개발자가 사용하는 인터페이스
개발자는 Composition의 내부 구현을 몰라도 된다. Claim만 제출하면 플랫폼 팀이 설계한 모범 사례에 맞게 인프라가 생성된다.
# 개발자가 작성하는 YAML — 이것만으로 RDS + 서브넷 + SG 전부 생성
apiVersion: platform.example.com/v1alpha1
kind: Database
metadata:
name: order-db
namespace: order-service
spec:
parameters:
size: medium # small/medium/large 중 선택
engine: postgresql
compositionSelector:
matchLabels:
provider: aws
writeConnectionSecretToRef:
name: order-db-connection
---
# 애플리케이션에서 자동 생성된 Secret 사용
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
template:
spec:
containers:
- name: app
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: order-db-connection
key: host
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: order-db-connection
key: password
Terraform vs Crossplane 비교
| 항목 | Terraform | Crossplane |
|---|---|---|
| 상태 관리 | tfstate 파일 (S3 등) | Kubernetes etcd |
| 드리프트 감지 | plan 실행 시만 | 지속적 자동 감지·복구 |
| 셀프 서비스 | PR 기반 워크플로우 | Claim YAML 직접 적용 |
| 학습 곡선 | HCL 학습 필요 | K8s YAML (이미 친숙) |
| 권한 제어 | 별도 정책 도구 필요 | K8s RBAC 그대로 사용 |
| 적합한 환경 | 다양한 인프라, 초기 구축 | K8s 중심, 개발자 셀프서비스 |
GitOps 연동: ArgoCD + Crossplane
# Git 저장소 구조
# infrastructure/
# ├── crossplane/
# │ ├── providers/
# │ │ └── aws-providers.yaml
# │ ├── compositions/
# │ │ ├── database.yaml
# │ │ └── cache.yaml
# │ └── claims/
# │ ├── production/
# │ │ ├── order-db.yaml
# │ │ └── user-db.yaml
# │ └── staging/
# │ └── order-db.yaml
# ArgoCD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: infrastructure-claims
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/infrastructure
path: crossplane/claims/production
targetRevision: main
destination:
server: https://kubernetes.default.svc
syncPolicy:
automated:
prune: false # 인프라 리소스는 자동 삭제 금지
selfHeal: true
상태 확인 및 트러블슈팅
# Claim 상태 확인
kubectl get database order-db -o yaml
# status.conditions에서 Ready, Synced 확인
# 하위 Managed Resource 상태
kubectl get managed
# NAME READY SYNCED AGE
# instance.rds.aws/order-db-xxxxx True True 5m
# subnetgroup.rds.aws/order-db-xxxxx True True 5m
# securitygroup.ec2.aws/order-db-xxxxx True True 5m
# 이벤트 확인 (에러 디버깅)
kubectl describe database order-db
# Events:
# Type Reason Message
# Normal CompositionRevisionReady Composition revision is ready
# Normal SelectComposition Successfully selected composition
# Provider 로그 확인
kubectl logs -n crossplane-system
-l pkg.crossplane.io/revision=provider-aws-rds
--tail=50
Usage 정책: 삭제 보호
# 실수로 DB Claim을 삭제하면 실제 RDS도 삭제됨
# Usage로 보호
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: Usage
metadata:
name: protect-order-db
spec:
of:
apiVersion: platform.example.com/v1alpha1
kind: XDatabase
resourceRef:
name: order-db
reason: "프로덕션 데이터베이스 — 삭제 전 승인 필요"
Crossplane은 “Kubernetes가 곧 컨트롤 플레인이다”라는 철학의 결정체다. 애플리케이션과 인프라를 동일한 GitOps 워크플로우로 관리하고, 개발자에게 Claim 기반 셀프 서비스를 제공할 수 있다. Kubernetes 중심 조직이라면, Terraform에서 Crossplane으로의 전환을 검토할 시점이다.