Crossplane Composition Remote Work Setup คืออะไร
Crossplane เป็น open source Kubernetes add-on ที่เปลี่ยน Kubernetes cluster ให้เป็น universal control plane สำหรับจัดการ cloud infrastructure ผ่าน Kubernetes APIs Compositions คือ feature หลักที่ช่วยสร้าง abstractions สำหรับ infrastructure — รวมหลาย cloud resources เป็น single API Remote Work Setup คือการจัดสภาพแวดล้อมให้ทีม distributed ทำงานกับ cloud infrastructure ได้อย่างมีประสิทธิภาพ การรวมแนวคิดเหล่านี้ช่วยให้ทีม remote สร้าง self-service infrastructure platform ที่ developers สั่ง provision resources ผ่าน kubectl โดยไม่ต้องรู้ cloud provider details
Crossplane Architecture
# crossplane_arch.py — Crossplane architecture
import json
class CrossplaneArch:
COMPONENTS = {
"providers": {
"name": "Providers",
"description": "Plugins ที่เชื่อม Crossplane กับ cloud providers",
"examples": ["provider-aws", "provider-gcp", "provider-azure", "provider-kubernetes"],
},
"managed_resources": {
"name": "Managed Resources (MR)",
"description": "Kubernetes CRDs ที่ represent cloud resources (1:1 mapping)",
"examples": ["RDSInstance", "S3Bucket", "VPC", "GKECluster"],
},
"compositions": {
"name": "Compositions",
"description": "Template ที่รวมหลาย MRs เป็น single resource",
"examples": ["CompositeDatabase (RDS + SecurityGroup + SubnetGroup)"],
},
"xrds": {
"name": "CompositeResourceDefinitions (XRDs)",
"description": "กำหนด API schema สำหรับ Composite Resources",
"examples": ["XDatabase, XNetwork, XCluster"],
},
"claims": {
"name": "Claims",
"description": "Developer-facing API สำหรับขอ resources (namespace-scoped)",
"examples": ["Database claim → provisions RDS + security group"],
},
}
FLOW = """
Crossplane Flow:
[Developer] → kubectl apply -f database-claim.yaml
↓
[Claim] → XDatabase (namespace-scoped)
↓
[XRD] → validates schema
↓
[Composition] → creates Managed Resources
↓
[Provider-AWS] → provisions RDS + SecurityGroup + SubnetGroup
↓
[AWS Cloud] → actual resources created
"""
def show_components(self):
print("=== Crossplane Components ===\n")
for key, comp in self.COMPONENTS.items():
print(f"[{comp['name']}]")
print(f" {comp['description']}")
print(f" Examples: {', '.join(comp['examples'][:2])}")
print()
def show_flow(self):
print("=== Resource Provisioning Flow ===")
print(self.FLOW)
arch = CrossplaneArch()
arch.show_components()
arch.show_flow()
Composition สำหรับ Remote Teams
# composition.py — Crossplane Compositions for remote teams
import json
class RemoteComposition:
XRD = """
# xrd-database.yaml — CompositeResourceDefinition
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:
engine:
type: string
enum: [postgres, mysql]
default: postgres
size:
type: string
enum: [small, medium, large]
default: small
region:
type: string
default: ap-southeast-1
required: [engine, size]
"""
COMPOSITION = """
# composition-database.yaml — Composition template
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: database-aws
labels:
provider: aws
spec:
compositeTypeRef:
apiVersion: platform.example.com/v1alpha1
kind: XDatabase
resources:
- name: rds-instance
base:
apiVersion: rds.aws.crossplane.io/v1alpha1
kind: RDSInstance
spec:
forProvider:
dbInstanceClass: db.t3.micro
engine: postgres
engineVersion: "15"
masterUsername: admin
allocatedStorage: 20
publiclyAccessible: false
vpcSecurityGroupIds: []
providerConfigRef:
name: aws-provider
patches:
- fromFieldPath: spec.parameters.engine
toFieldPath: spec.forProvider.engine
- fromFieldPath: spec.parameters.size
toFieldPath: spec.forProvider.dbInstanceClass
transforms:
- type: map
map:
small: db.t3.micro
medium: db.t3.medium
large: db.r6g.large
- fromFieldPath: spec.parameters.region
toFieldPath: spec.forProvider.region
- name: security-group
base:
apiVersion: ec2.aws.crossplane.io/v1alpha1
kind: SecurityGroup
spec:
forProvider:
description: "Database security group"
ingress:
- fromPort: 5432
toPort: 5432
protocol: tcp
cidrBlocks: ["10.0.0.0/8"]
"""
CLAIM = """
# database-claim.yaml — Developer claim
apiVersion: platform.example.com/v1alpha1
kind: Database
metadata:
name: my-app-db
namespace: team-alpha
spec:
parameters:
engine: postgres
size: small
region: ap-southeast-1
compositionSelector:
matchLabels:
provider: aws
"""
def show_xrd(self):
print("=== XRD (API Definition) ===")
print(self.XRD[:500])
def show_composition(self):
print(f"\n=== Composition ===")
print(self.COMPOSITION[:500])
def show_claim(self):
print(f"\n=== Developer Claim ===")
print(self.CLAIM)
comp = RemoteComposition()
comp.show_xrd()
comp.show_composition()
comp.show_claim()
Self-Service Platform
# platform.py — Self-service platform for remote teams
import json
class SelfServicePlatform:
RESOURCES = {
"database": {
"claim_kind": "Database",
"sizes": {"small": "db.t3.micro (2 vCPU, 1GB)", "medium": "db.t3.medium (2 vCPU, 4GB)", "large": "db.r6g.large (2 vCPU, 16GB)"},
"engines": ["postgres", "mysql"],
},
"cache": {
"claim_kind": "Cache",
"sizes": {"small": "cache.t3.micro", "medium": "cache.t3.medium", "large": "cache.r6g.large"},
"engines": ["redis", "memcached"],
},
"storage": {
"claim_kind": "ObjectStore",
"options": {"private": "Private bucket + encryption", "public": "Public read CDN"},
"features": ["versioning", "lifecycle rules", "replication"],
},
"network": {
"claim_kind": "Network",
"options": {"dev": "Single AZ, no NAT", "staging": "Multi-AZ, NAT", "production": "Multi-AZ, NAT, VPN"},
},
}
BACKSTAGE_INTEGRATION = """
# backstage-template.yaml — Backstage + Crossplane integration
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: provision-database
title: Provision Database
spec:
owner: platform-team
type: infrastructure
parameters:
- title: Database Configuration
properties:
engine:
type: string
enum: [postgres, mysql]
default: postgres
size:
type: string
enum: [small, medium, large]
default: small
team:
type: string
description: Your team namespace
steps:
- id: apply-claim
name: Create Database Claim
action: kubernetes:apply
input:
manifest:
apiVersion: platform.example.com/v1alpha1
kind: Database
metadata:
name: "}-db"
namespace: "}"
spec:
parameters:
engine: "}"
size: "}"
"""
def show_resources(self):
print("=== Self-Service Resources ===\n")
for key, res in self.RESOURCES.items():
print(f"[{res['claim_kind']}]")
if "sizes" in res:
for size, spec in res["sizes"].items():
print(f" {size}: {spec}")
print()
def show_backstage(self):
print("=== Backstage Integration ===")
print(self.BACKSTAGE_INTEGRATION[:500])
platform = SelfServicePlatform()
platform.show_resources()
platform.show_backstage()
GitOps Workflow
# gitops.py — GitOps workflow for Crossplane
import json
class GitOpsWorkflow:
WORKFLOW = {
"step1": {"name": "Developer creates PR", "action": "Add/modify claim YAML in Git repo"},
"step2": {"name": "PR Review", "action": "Platform team reviews infrastructure request"},
"step3": {"name": "Merge to main", "action": "ArgoCD/Flux detects change"},
"step4": {"name": "Sync to cluster", "action": "Claim applied to Kubernetes"},
"step5": {"name": "Crossplane provisions", "action": "Cloud resources created/updated"},
"step6": {"name": "Status update", "action": "Claim status shows Ready=True"},
}
ARGOCD_APP = """
# argocd-app.yaml — ArgoCD Application for Crossplane claims
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: team-alpha-infra
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/company/infra-claims.git
targetRevision: main
path: teams/alpha
destination:
server: https://kubernetes.default.svc
namespace: team-alpha
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
"""
REPO_STRUCTURE = """
infra-claims/
├── teams/
│ ├── alpha/
│ │ ├── database.yaml # Database claim
│ │ ├── cache.yaml # Cache claim
│ │ └── storage.yaml # S3 bucket claim
│ ├── beta/
│ │ ├── database.yaml
│ │ └── network.yaml
│ └── gamma/
│ └── database.yaml
├── platform/
│ ├── compositions/ # Composition templates
│ ├── xrds/ # XRD definitions
│ └── providers/ # Provider configs
└── .github/
└── workflows/
└── validate.yml # PR validation
"""
def show_workflow(self):
print("=== GitOps Workflow ===\n")
for key, step in self.WORKFLOW.items():
print(f" [{step['name']}] → {step['action']}")
def show_argocd(self):
print(f"\n=== ArgoCD App ===")
print(self.ARGOCD_APP[:400])
def show_structure(self):
print(f"\n=== Repo Structure ===")
print(self.REPO_STRUCTURE)
gitops = GitOpsWorkflow()
gitops.show_workflow()
gitops.show_argocd()
gitops.show_structure()
Monitoring & Troubleshooting
# monitoring.py — Crossplane monitoring
import json
import random
class CrossplaneMonitoring:
def resource_status(self):
print("=== Resource Status ===\n")
resources = [
{"team": "alpha", "type": "Database", "name": "app-db", "ready": True, "synced": True, "age": "5d"},
{"team": "alpha", "type": "Cache", "name": "app-cache", "ready": True, "synced": True, "age": "3d"},
{"team": "beta", "type": "Database", "name": "api-db", "ready": True, "synced": True, "age": "10d"},
{"team": "beta", "type": "Network", "name": "vpc", "ready": random.choice([True, False]), "synced": True, "age": "15d"},
{"team": "gamma", "type": "Database", "name": "ml-db", "ready": True, "synced": True, "age": "2d"},
]
for r in resources:
ready = "Ready" if r["ready"] else "NOT_READY"
print(f" [{ready:>9}] {r['team']}/{r['type']}/{r['name']} (age: {r['age']})")
def troubleshooting(self):
print(f"\n=== Common Issues ===")
issues = [
{"issue": "Claim stuck in Creating", "fix": "kubectl describe → check events, provider credentials"},
{"issue": "Provider not installed", "fix": "kubectl get providers → install missing provider"},
{"issue": "Composition not matching", "fix": "Check compositionSelector labels match composition labels"},
{"issue": "Cloud quota exceeded", "fix": "Check cloud provider quotas, request increase"},
]
for i in issues:
print(f" Issue: {i['issue']}")
print(f" Fix: {i['fix']}")
print()
def useful_commands(self):
print("=== Useful Commands ===")
cmds = [
"kubectl get managed # All managed resources",
"kubectl get composite # All composite resources",
"kubectl get claim --all-namespaces # All claims",
"kubectl describe database my-db -n team-alpha # Claim details",
"kubectl get events --field-selector involvedObject.name=my-db",
]
for cmd in cmds:
print(f" {cmd}")
mon = CrossplaneMonitoring()
mon.resource_status()
mon.troubleshooting()
mon.useful_commands()
FAQ - คำถามที่พบบ่อย
Q: Crossplane กับ Terraform อันไหนดี?
A: Crossplane: Kubernetes-native, continuous reconciliation, self-service claims Terraform: mature, HCL language, state management, module ecosystem ใช้ Crossplane: K8s-centric teams, self-service platform, GitOps, continuous drift detection ใช้ Terraform: multi-tool teams, complex provisioning, one-time infrastructure หลายทีมใช้ทั้งคู่: Terraform สำหรับ foundational infra + Crossplane สำหรับ app-level resources
Q: Remote team ใช้ Crossplane ยากไหม?
A: สำหรับ platform team (สร้าง Compositions): ต้องเรียนรู้ Crossplane concepts สำหรับ developers (ใช้ Claims): ง่ายมาก — เขียน simple YAML apply ด้วย kubectl Self-service: developer ไม่ต้องรู้ cloud provider details GitOps: ทุกอย่างผ่าน Git PR — remote-friendly
Q: Crossplane รองรับ multi-cloud ไหม?
A: รองรับ — เป็น key feature ของ Crossplane Providers: AWS, GCP, Azure, Kubernetes, Helm, etc. Composition เดียว provision ข้าม clouds ได้ ใช้ compositionSelector เลือก provider ตามต้องการ เหมาะสำหรับ organizations ที่ใช้หลาย clouds
Q: Production-ready ไหม?
A: Crossplane: CNCF incubating project, ใช้ใน production หลายบริษัทใหญ่ Providers: maturity ไม่เท่ากัน (AWS provider mature ที่สุด) แนะนำ: เริ่มด้วย non-critical resources → ค่อยขยาย สำคัญ: ตั้ง RBAC, backup provider credentials, monitor resources
