Skip to content

필사 모드: Crossplane으로 Kubernetes-Native IaC 구현하기: Terraform을 대체하는 클라우드 제어 플레인

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

들어가며

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 수동 실행, 드리프트 감지의 어려움 등...

작성 글자: 0원문 글자: 8,287작성 단락: 0/310