Skip to content
Published on

CrossplaneでKubernetes-Native IaCを実現する:Terraformを代替するクラウドコントロールプレーン

Authors
  • Name
    Twitter

はじめに

TerraformはIaCのデファクトスタンダードでしたが、Kubernetes中心の環境では限界があります。ステートファイルの管理、Plan/Applyの手動実行、ドリフト検出の困難さなどが代表的です。Crossplaneはこの問題を、Kubernetesの宣言的モデルとコントローラーパターンで解決します。

2025年10月にCNCF Graduatedプロジェクトに昇格したCrossplaneは、今やプロダクション環境で十分に検証されています。

Crossplane vs Terraform

項目TerraformCrossplane
実行モデルCLI(Plan/Apply)Kubernetesコントローラー(継続的調整)
状態管理tfstateファイル(外部バックエンド必要)etcd(Kubernetesネイティブ)
ドリフト検出手動planが必要自動(reconciliation loop)
抽象化ModuleComposition
権限管理別途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を1つの高レベル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の既存権限体系をそのまま活用
  • ドリフト自動復旧:誰かがコンソールで手動変更しても自動的に復元

クイズ:Crossplane理解度チェック(7問)

Q1. CrossplaneがTerformと比較してドリフト検出に強い理由は?

Kubernetesコントローラーのreconciliation loopが継続的に実際の状態と宣言された状態を比較し、自動的に復旧します。

Q2. Managed ResourceとComposite Resourceの違いは?

Managed Resourceは実際のクラウドリソース(S3、RDSなど)を1:1でマッピングし、Composite Resourceは複数のManaged Resourceを1つの高レベル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できます。