SiamCafe · Blog
Crossplane Composition Remote Work Setup
บทความ

Crossplane Composition Remote Work Setup

เผยแพร่ 28 พฤษภาคม 2569

Crossplane Composition Remote Work Setup คืออะไร

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

Crossplane Composition Remote Work Setup
# 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