목차
- 1. Crossplane을 왜 멀티 클라우드 관리에 쓰는가
- 2. 핵심 개념 정리: Provider, Composition, Reconciliation
- 3. 실전 구현: 기본 구성과 관찰 포인트
- 3-1. Provider와 인증 Secret 준비
- 3-2. Composite Resource와 Claim 정의
- 4. Crossplane 장애 사례 1: 리소스는 생성됐는데 Ready가 안 올라오는 경우
- 5. Crossplane 장애 사례 2: Composition 패치 오류로 엉뚱한 값이 들어간 경우
- 5-1. 문제를 줄이는 운영 팁
- 6. Crossplane 장애 사례 3: 멀티 클라우드 관리 환경에서 권한과 한도 이슈가 섞여 터진 경우
- 7. ⚠️ 삭제가 안 되는 장애: Finalizer와 외부 리소스 정리 실패
- 8. 검증과 결과: 어디까지 자동화됐는지 확인하는 방법
- 9. 정리: Crossplane은 편한 도구가 아니라, 잘 설계해야 편해지는 도구입니다
[인프라] Crossplane 장애 사례로 배우는 멀티 클라우드 인프라 관리
Crossplane 장애 사례를 한 번이라도 겪어보신 분들은 아실 겁니다. 처음엔 "쿠버네티스(Kubernetes, 컨테이너 오케스트레이션 플랫폼)처럼 리소스를 선언형으로 관리하면 인프라도 깔끔해지겠네?" 싶거든요. 저도 홈랩하고 업무 환경에서 멀티 클라우드 관리 구조를 정리하려고 Crossplane을 붙였었는데, 막상 운영에 들어가니까 컨트롤 플레인(Control Plane, 전체 상태를 조정하는 중앙 제어 계층) 특성 때문에 장애가 생각보다 교묘하게 터지더라고요. 특히 인프라스트럭처 코드(Infrastructure as Code, 코드로 인프라를 정의하는 방식)와 쿠버네티스 API 감각이 섞이면서 원인을 잘못 짚으면 복구가 더 늦어집니다.
이번 글은 제품 소개보다는 troubleshooting 중심입니다. 제가 직접 해보니 Crossplane은 잘만 쓰면 멀티 클라우드 관리 복잡도를 꽤 줄여주는데, 장애가 났을 때는 "어디서 상태가 꼬였는지"를 읽는 눈이 정말 중요하더라고요. 그래서 오늘은 Crossplane 장애 사례를 바탕으로 어떤 식으로 문제가 드러났고 어떻게 풀어갔는지, 그리고 운영하면서 꼭 챙겨야 할 포인트를 정리해보겠습니다.
Crossplane이 여러 클라우드 리소스를 쿠버네티스 컨트롤 플레인으로 관리하는 전체 흐름을 보여주는 이미지입니다.
1. Crossplane을 왜 멀티 클라우드 관리에 쓰는가
쉽게 말해 Crossplane은 쿠버네티스 API로 외부 인프라를 다루게 해주는 도구입니다. AWS, GCP, Azure 같은 퍼블릭 클라우드 자원을 쿠버네티스 리소스처럼 선언하고, 원하는 상태(desired state)와 실제 상태(actual state)를 맞추도록 계속 reconcile(리컨실, 상태를 일치시키는 반복 제어)하는 구조죠.
이게 왜 좋냐면요. 클라우드마다 콘솔도 다르고 권한 체계도 다르고 Terraform 상태 파일 관리도 따로 고민해야 하는데, Crossplane은 적어도 운영 관점에서 제어면을 하나로 모으는 효과가 있습니다. 특히 팀 단위로 표준화된 Composite Resource(복합 리소스)나 Claim(클레임, 사용자 요청 객체)을 만들어두면 개발팀은 세부 클라우드 차이를 몰라도 공통 인터페이스로 인프라를 요청할 수 있거든요.
- 장점 1: 멀티 클라우드 관리 진입점이 쿠버네티스로 통일돼요.
- 장점 2: 인프라스트럭처 코드와 GitOps 흐름을 연결하기 좋습니다.
- 장점 3: 플랫폼 팀이 정책과 표준 구성을 감싸서 제공하기 좋습니다.
반대로 단점도 분명합니다. Crossplane 자체가 또 하나의 컨트롤 플레인이기 때문에 장애 포인트가 사라지는 게 아니라 다른 계층으로 이동하는 느낌이 있어요. 저도 처음엔 이게 뭔가 싶었는데, 실제로 써보니까 "리소스를 만드는 일"보다 "상태를 해석하는 일"이 더 중요하더라고요.
2. 핵심 개념 정리: Provider, Composition, Reconciliation
Crossplane 장애 사례를 이해하려면 구조를 먼저 아주 간단히 잡고 가는 게 좋아요.
| 개념 | 쉽게 말하면 | 운영 시 체크 포인트 |
|---|---|---|
| Provider | 클라우드 API와 통신하는 드라이버 | 인증 정보, 권한, CRD 설치 상태 |
| Managed Resource | 실제 클라우드 리소스와 매핑되는 객체 | Ready 조건, 외부 이름, 이벤트 |
| Composition | 여러 리소스를 묶는 설계도 | 패치, 참조, 필드 연결 오류 |
| Claim | 사용자가 요청하는 추상화된 리소스 | 상위 상태는 정상인데 하위가 실패할 수 있음 |
| Reconciliation | 원하는 상태로 계속 맞추는 루프 | 반복 에러, 드리프트, 재시도 패턴 |
여기서 중요한 포인트! 겉으로 보이는 Claim이 멀쩡해 보여도 하위 Managed Resource가 실패 중일 수 있어요. 반대로 하위 리소스 하나가 계속 에러를 내면서 전체 Composition이 완료되지 않는 경우도 흔합니다. 저는 초반에 상위 객체만 보고 "왜 안 되지?" 하다가 삽질 좀 했습니다 ㅎㅎ
3. 실전 구현: 기본 구성과 관찰 포인트
아래 예시는 개념 설명용으로 단순화한 구조입니다. 특정 클라우드 벤더 기능을 깊게 파기보다는 Crossplane troubleshooting 흐름을 보는 데 집중하시면 됩니다.
3-1. Provider와 인증 Secret 준비
- Crossplane과 Provider가 설치되어 있는지 확인하세요.
- 클라우드 인증 정보가 담긴 Secret(시크릿, 민감 정보 저장 객체)을 만듭니다.
- ProviderConfig(프로바이더 설정)가 Secret을 올바르게 참조하는지 봅시다.
kubectl get pods -n crossplane-system
kubectl get providers
kubectl get providerconfigs
kubectl get secrets -n crossplane-system
제가 실제로 써보니까 첫 장애는 생각보다 단순했어요. 리소스 생성 로직이 아니라 인증 Secret 네임스페이스(namespace, 쿠버네티스 논리적 격리 단위)가 어긋나 있었거든요. 이벤트를 보기 전까지는 Composition 문제인 줄 알았습니다.
apiVersion: v1
kind: Secret
metadata:
name: cloud-creds
namespace: crossplane-system
type: Opaque
stringData:
creds: |
{
"example": "replace-with-real-credentials"
}
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: example-provider
spec:
package: xpkg.example/provider
---
apiVersion: example.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
name: default
spec:
credentials:
source: Secret
secretRef:
namespace: crossplane-system
name: cloud-creds
key: creds
3-2. Composite Resource와 Claim 정의
플랫폼 팀이 표준 리소스를 만들 때는 보통 Composition을 씁니다. 예를 들어 네트워크, 데이터베이스, 스토리지를 조합해서 하나의 "애플리케이션용 환경"처럼 제공하는 식이죠.
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: xappenvs.platform.example.org
spec:
compositeTypeRef:
apiVersion: platform.example.org/v1alpha1
kind: XAppEnv
resources:
- name: bucket
base:
apiVersion: storage.example.crossplane.io/v1beta1
kind: Bucket
spec:
forProvider:
region: us-east-1
providerConfigRef:
name: default
patches:
- fromFieldPath: "spec.parameters.region"
toFieldPath: "spec.forProvider.region"
apiVersion: platform.example.org/v1alpha1
kind: AppEnv
metadata:
name: demo-appenv
spec:
parameters:
region: us-east-1
여기서부터는 단순 생성보다 관찰이 중요해요.
kubectl get appenv
kubectl describe appenv demo-appenv
kubectl get managed
kubectl get events --sort-by=.metadata.creationTimestamp
Claim에서 Composition을 거쳐 실제 Managed Resource가 생성되는 연결 관계를 보여주는 구성도입니다.
4. Crossplane 장애 사례 1: 리소스는 생성됐는데 Ready가 안 올라오는 경우
이 사례는 꽤 자주 봐요. 클라우드 콘솔에서는 리소스가 보이는데 쿠버네티스 쪽 상태는 계속 Creating 또는 NotReady에 머무는 거죠. 처음엔 "분명 만들어졌는데 왜 실패지?" 싶었습니다.
제가 겪었던 원인은 크게 세 가지였어요.
- 외부 리소스 식별자(external-name) 불일치
- Provider 권한 부족
- 후속 조회 API 실패
Crossplane은 생성만 하는 게 아니라 이후에도 상태를 조회하고 맞춰야 해요. 그래서 create 권한만 있고 read 또는 describe 계열 권한이 빠져 있으면 리소스는 생겨도 Ready 조건이 정상으로 못 올라올 수 있거든요.
kubectl describe <managed-resource-kind> <resource-name>
kubectl get <managed-resource-kind> <resource-name> -o yaml
이때 꼭 볼 부분은 아래입니다.
- status.conditions: Ready, Synced 상태가 어떻게 찍히는지
- metadata.annotations: external-name 같은 외부 식별자
- Events: API 호출 실패 메시지
실제로 써보니까 "리소스가 존재하니 성공"이라고 보면 안 되더라고요. Crossplane은 상태 일치가 끝나야 진짜 성공이에요.
5. Crossplane 장애 사례 2: Composition 패치 오류로 엉뚱한 값이 들어간 경우
이건 진짜 많이 헷갈려요. Claim에 값을 넣었는데 하위 리소스에 반영이 안 되거나 전혀 다른 필드로 들어가는 경우죠. 문법 에러가 아니라서 더 무섭습니다. YAML은 적용됐는데 결과가 이상하거든요.
제가 처음 삽질했던 포인트는 fromFieldPath와 toFieldPath 오타였어요. 한 글자만 틀려도 조용히 의도와 다르게 흘러갈 수 있습니다. 그리고 일부 필드는 하위 리소스 스키마에 실제로 존재해야 하니까 Crossplane 문제처럼 보여도 사실은 CRD 필드 구조를 잘못 이해한 경우도 많아요.
kubectl get composition
kubectl describe composition xappenvs.platform.example.org
kubectl get xr
kubectl describe xr <composite-resource-name>
제가 정리한 확인 순서는 이렇습니다.
- Claim의 spec 값이 기대한 형태인지 확인하세요.
- Composite Resource(XR)에 값이 전달됐는지 봅시다.
- Managed Resource spec에 최종 반영됐는지 확인해요.
- 필드 타입이 문자열인지 배열인지, 맵인지 다시 봅시다.
근데 여기서 중요한 건 Crossplane은 선언형이라 "중간 단계"를 하나씩 따라가야 한다는 점이에요. Terraform처럼 plan 출력 하나 보고 감 잡는 방식과는 결이 좀 다르더라고요.
5-1. 문제를 줄이는 운영 팁
- Composition 변수명 규칙을 팀 내에서 고정하세요.
- region, size, class 같은 공통 필드는 네이밍을 통일해요.
- 복잡한 패치는 처음부터 크게 만들지 말고 작은 단위로 검증합니다.
- 변경 후에는 테스트용 Claim을 바로 적용해 이벤트를 확인하세요.
Crossplane 리소스 이벤트와 상태 조건을 보면서 원인을 추적하는 troubleshooting 상황을 표현한 이미지입니다.
6. Crossplane 장애 사례 3: 멀티 클라우드 관리 환경에서 권한과 한도 이슈가 섞여 터진 경우
멀티 클라우드 관리가 어려운 이유는 에러 형태가 벤더마다 다르게 보인다는 데 있어요. 어떤 곳은 권한 부족이 명확하게 찍히고, 어떤 곳은 rate limit(요청 제한)이나 quota(할당량) 문제처럼 보이다가 결국 재시도만 반복하기도 하거든요.
제가 겪은 케이스는 이랬습니다. 한 클라우드에서는 리소스가 잘 만들어졌는데 다른 쪽은 동일한 Claim 패턴으로 계속 실패했어요. 처음엔 Composition 차이인 줄 알았는데 알고 보니 Provider가 쓰는 계정의 권한 범위가 환경마다 달랐습니다. 즉, 코드가 아니라 운영 계정 표준화가 문제였던 거죠.
| 증상 | 겉으로 보이는 현상 | 실제 원인 후보 |
|---|---|---|
| 계속 Pending | 상위 Claim만 오래 대기 | 하위 Managed Resource 생성 실패 |
| 반복 재시도 | 이벤트가 주기적으로 누적 | 권한 부족, API 제한, 잘못된 참조 |
| 일부만 생성 | 네트워크는 되고 DB는 실패 | Composition 내 특정 리소스 설정 누락 |
| 삭제 지연 | 오브젝트는 지웠는데 외부 리소스 잔존 | finalizer, 외부 API 에러, 종속성 문제 |
이런 상황에서는 쿠버네티스 내부만 보면 안 돼요. 클라우드 측 감사 로그나 API 에러도 같이 봐야 합니다. 컨트롤 플레인이 하나라고 해서 장애 원인까지 하나로 줄어드는 건 아니더라고요. 이건 정말 운영하면서 체감했습니다.
7. ⚠️ 삭제가 안 되는 장애: Finalizer와 외부 리소스 정리 실패
개인적으로 제일 식은땀 나는 건 삭제 문제였어요. 리소스를 지웠는데 오브젝트가 계속 Terminating 상태로 남아 있고 외부 클라우드 리소스도 깔끔하게 정리되지 않는 경우요. 비용도 문제고 나중에 이름 충돌이나 의존성 꼬임으로 이어지기도 합니다.
Crossplane은 finalizer(파이널라이저, 삭제 전에 정리 작업을 보장하는 메커니즘)를 사용해서 외부 리소스를 정리한 뒤 객체를 제거해요. 따라서 외부 API 호출이 실패하거나 참조 관계가 꼬이면 삭제가 길어질 수 있습니다.
kubectl get <managed-resource-kind> <resource-name> -o yaml
kubectl describe <managed-resource-kind> <resource-name>
kubectl get events --sort-by=.metadata.creationTimestamp
여기서 제가 배운 건 무작정 finalizer를 건드리면 안 된다는 점이에요. 물론 정말 예외적인 복구 상황은 있겠지만 먼저 확인해야 합니다.
- 외부 리소스가 실제로 삭제 가능한 상태인지 봅시다.
- 연결된 종속 리소스가 남아 있는지 확인하세요.
- Provider 권한이 delete와 observe까지 포함하는지 다시 봐요.
- 이벤트 로그에서 반복되는 에러 메시지를 확인하세요.
⚠️ 운영 팁: 삭제 장애는 생성 장애보다 복구 비용이 커요. 그래서 테스트 환경에서 생성뿐 아니라 삭제 시나리오까지 꼭 검증해야 합니다. 저도 예전엔 만드는 데만 집중했었는데 실제로는 지우는 흐름이 더 중요하더라고요.
8. 검증과 결과: 어디까지 자동화됐는지 확인하는 방법
문제를 고치고 나면 "이제 됐다"로 끝내면 안 돼요. 다시 같은 Crossplane 장애 사례가 반복되는지 봐야 하거든요. 저는 아래 체크리스트로 검증합니다.
- Claim 생성부터 Ready까지 걸리는 흐름을 확인하세요.
- 하위 Managed Resource가 모두 Synced 상태인지 봅시다.
- 외부 클라우드 콘솔에서도 리소스 속성이 기대값과 같은지 확인해요.
- 삭제 테스트까지 수행하세요.
- 이벤트 로그에 경고가 남는지 다시 봅니다.
kubectl get appenv
kubectl get xr
kubectl get managed
kubectl get events --sort-by=.metadata.creationTimestamp
제가 직접 해보니 결과가 눈에 보이게 달라졌어요. 장애가 완전히 사라진다기보다 문제가 생겨도 어디를 봐야 하는지 감이 생긴다는 게 커요. 이건 운영 피로도를 많이 줄여줍니다. 특히 멀티 클라우드 관리 환경에서는 "도구를 더 넣는 것"보다 "상태를 읽는 기준을 팀이 공유하는 것"이 훨씬 중요하더라고요.
문제 해결 후 Crossplane 리소스들이 Ready 상태로 정렬되고 운영 지표가 안정된 결과를 보여주는 이미지입니다.
9. 정리: Crossplane은 편한 도구가 아니라, 잘 설계해야 편해지는 도구입니다
정리해보면 Crossplane은 멀티 클라우드 관리와 인프라스트럭처 코드 표준화에 꽤 강력해요. 다만 처음 붙일 때 "쿠버네티스로 클라우드를 다룬다"는 멋진 그림만 보면 안 돼요. 실제 운영에선 Provider 인증, Composition 패치, 상태 조건, 삭제 흐름, 권한 범위 같은 현실 이슈가 계속 튀어나오거든요.
저도 처음엔 Crossplane 장애 사례를 겪을 때마다 도구 자체를 의심했었는데 실제로 써보니까 대부분은 관찰 포인트 부족이나 운영 표준 미정리에서 시작하더라고요. 드디어 됐다! 싶은 순간도 있었고, 반대로 "왜 어제 되던 게 오늘 안 되지?" 하면서 로그만 한참 본 날도 있었어요. 근데 그런 삽질이 쌓이니까 구조가 보이더군요.
- 💡 기억할 점 1: 상위 Claim만 보지 말고 XR과 Managed Resource까지 따라가세요.
- 💡 기억할 점 2: 이벤트와 조건(status.conditions)은 가장 먼저 봐야 합니다.
- 💡 기억할 점 3: 생성 성공보다 삭제 성공까지 확인해야 진짜 운영 준비가 끝납니다.
- 💡 기억할 점 4: 멀티 클라우드 관리는 도구보다 표준화가 먼저에요.
혹시 지금 Crossplane 장애 사례 때문에 막혀 계신가요? 그러면 가장 먼저 객체 계층을 위에서 아래로, 그리고 이벤트를 시간순으로 보시는 걸 추천드립니다. 이 흐름만 익혀도 troubleshooting 속도가 꽤 빨라집니다. 이전 글에서 다뤘던 쿠버네티스 운영 체크리스트와도 연결되는 이야기고 다음 글에서는 Composition 설계를 어떻게 단순화하면 장애를 줄일 수 있는지 이어서 다뤄볼 예정입니다.
장애 원인 파악 순서와 운영 체크포인트를 한눈에 정리한 요약 인포그래픽 이미지입니다.
'IT > Cloud' 카테고리의 다른 글
| [Infra] SSO 로그인 장애 발생 시 디버깅 체크리스트와 해결 전략 (1) | 2026.08.05 |
|---|---|
| [Cloud] Okta, Keycloak, Auth0: 클라우드 SSO 솔루션 비교 분석 (0) | 2026.08.05 |
| [Cloud] New Relic 사용 후기: 1년 APM 운영하며 배운 성능 모니터링 실전 경험 (0) | 2026.07.20 |
| [Cloud] Spot Instance 활용 극대화: 비용 절감과 안정성 확보 전략 (1) | 2026.07.20 |
| [Cloud] Cloudflare Workers AI 활용 사례: 엣지 AI 서비스 구축기 (0) | 2026.07.20 |
| [DevOps] Argo CD vs Spinnaker: CI/CD 파이프라인 구축 비교 분석 (1) | 2026.07.20 |