Technology

Crossplane Composition Remote Work Setup

crossplane composition remote work setup
Crossplane Composition Remote Work Setup | SiamCafe Blog
2025-10-15· อ. บอม — SiamCafe.net· 1,551 คำ

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

📖 บทความที่เกี่ยวข้อง

Strapi CMS Remote Work Setupอ่านบทความ → Crossplane Composition Capacity Planningอ่านบทความ → Crossplane Composition GreenOps Sustainabilityอ่านบทความ → Crossplane Composition Stream Processingอ่านบทความ →

📚 ดูบทความทั้งหมด →