饮墨

子安饮墨馀三斗,留与卿儿作赋来

用 Crossplane 在 Kubernetes 中管理云基础设施:3 步实现 IaC 从 Terraform 到 K8s 原生的演进

痛点:Terraform 好用,但和 Kubernetes 生态割裂

运维团队普遍面临一个尴尬局面:应用部署用 Kubernetes + GitOps(ArgoCD/Flux),基础设施管理用 Terraform + 独立 Pipeline。两套工具、两套工作流、两套状态管理,带来几个实际问题:

  1. 状态分裂 — Terraform state 在 S3/Consul 里,K8s 资源状态在 etcd 里,跨系统联动靠胶水脚本
  2. 权限模型不统一 — Terraform 需要云账号 AK/SK,K8s 用 RBAC,开发者自助申请资源要走两套审批
  3. 漂移检测各管各 — Terraform plan 检测 IaC 漂移,K8s controller 检测应用漂移,没有统一视图
  4. 团队协作摩擦 — 开发写 YAML 申请数据库,运维切到 Terraform 创建实例,再回填连接信息到 Secret

Crossplane 的核心思路:把云基础设施变成 Kubernetes CRD,用 K8s 原生的声明式 API + 控制器循环来管理一切。

方案:Crossplane 架构与核心概念

Crossplane 是 CNCF 毕业项目,本质是一组 Kubernetes Controller + CRD,核心组件:

概念 作用 类比
Provider 对接云 API(AWS/GCP/Azure) Terraform Provider
Managed Resource (MR) 单个云资源的 CRD 表示 Terraform Resource
Composition 资源编排模板,组合多个 MR Terraform Module
XRD (CompositeResourceDefinition) 自定义 API 抽象层 对外暴露的平台 API
Claim 开发者使用的简化资源申请 类似 PVC 之于 PV

整体流程:开发者提交 Claim → Crossplane 根据 Composition 展开为 MR → Provider Controller 调用云 API 创建资源 → 状态回写到 K8s Status

实操步骤

第 1 步:安装 Crossplane 及 AWS Provider

# 安装 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"}'

# 等待 Pod 就绪
kubectl get pods -n crossplane-system -w

# 安装 AWS Provider(以 S3 + RDS 为例,使用 Provider Family)
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.14.0
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws-rds
spec:
  package: xpkg.upbound.io/upbound/provider-aws-rds:v1.14.0
EOF

# 验证 Provider 安装状态
kubectl get providers
# NAME               INSTALLED   HEALTHY   PACKAGE                                           AGE
# provider-aws-s3    True        True      xpkg.upbound.io/upbound/provider-aws-s3:v1.14.0  60s
# provider-aws-rds   True        True      xpkg.upbound.io/upbound/provider-aws-rds:v1.14.0 60s

第 2 步:配置云凭证与 ProviderConfig

# 创建 AWS 凭证 Secret(生产建议用 IRSA / Pod Identity)
kubectl create secret generic aws-creds \
  -n crossplane-system \
  --from-file=creds=./aws-credentials.txt

# aws-credentials.txt 格式:
# [default]
# aws_access_key_id = AKIA...
# aws_secret_access_key = xxx

# 创建 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: creds
EOF

生产最佳实践: 用 IRSA(IAM Roles for Service Accounts)替代静态 AK/SK:

spec:
  credentials:
    source: IRSA  # Pod 通过 ServiceAccount 自动获取临时凭证

第 3 步:定义 Composition + XRD,暴露平台 API

这是 Crossplane 的精华 — 运维定义模板,开发者只需提交简化的 Claim:

# xrd.yaml — 定义平台级 API
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xpostgresqlinstances.database.example.org
spec:
  group: database.example.org
  names:
    kind: XPostgreSQLInstance
    plural: xpostgresqlinstances
  claimNames:
    kind: PostgreSQLInstance
    plural: postgresqlinstances
  versions:
    - name: v1alpha1
      served: true
      referenceable: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                parameters:
                  type: object
                  properties:
                    storageGB:
                      type: integer
                      default: 20
                    instanceClass:
                      type: string
                      default: db.t3.micro
                      enum: [db.t3.micro, db.t3.small, db.t3.medium]
                  required: [storageGB]
# composition.yaml — 运维定义资源编排逻辑
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: postgresql-aws
  labels:
    provider: aws
spec:
  compositeTypeRef:
    apiVersion: database.example.org/v1alpha1
    kind: XPostgreSQLInstance
  resources:
    - name: rds-instance
      base:
        apiVersion: rds.aws.upbound.io/v1beta2
        kind: Instance
        spec:
          forProvider:
            region: ap-northeast-1
            engine: postgres
            engineVersion: "15"
            instanceClass: db.t3.micro
            allocatedStorage: 20
            publiclyAccessible: false
            skipFinalSnapshot: true
            masterUsername: admin
            masterPasswordSecretRef:
              namespace: crossplane-system
              name: rds-master-password
              key: password
      patches:
        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.storageGB
          toFieldPath: spec.forProvider.allocatedStorage
        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.instanceClass
          toFieldPath: spec.forProvider.instanceClass
    - name: subnet-group
      base:
        apiVersion: rds.aws.upbound.io/v1beta1
        kind: SubnetGroup
        spec:
          forProvider:
            region: ap-northeast-1
            subnetIds:
              - subnet-xxxxxxxx
              - subnet-yyyyyyyy
            description: "Managed by Crossplane"
# claim.yaml — 开发者提交的资源申请(极简)
apiVersion: database.example.org/v1alpha1
kind: PostgreSQLInstance
metadata:
  name: order-service-db
  namespace: team-order
spec:
  parameters:
    storageGB: 50
    instanceClass: db.t3.small
  compositionSelector:
    matchLabels:
      provider: aws

开发者只需 kubectl apply -f claim.yaml,Crossplane 自动创建 RDS 实例 + Subnet Group,并将连接信息写入指定 Secret。

避坑指南

1. Provider 版本与 CRD 膨胀

Crossplane AWS Provider 全量安装会注册 900+ CRD,导致 API Server 内存飙升。

解法: 使用 Provider Family 按需安装(如只装 provider-aws-s3 + provider-aws-rds),不要装 monolithic provider-aws

# 查看 CRD 数量
kubectl get crds | grep -c upbound
# 目标:控制在 100 个以内

2. 资源删除策略必须显式配置

默认 deletionPolicy: Delete,删除 Claim 会级联删除云资源(包括 RDS 数据!)。

解法: 生产环境强制设置 deletionPolicy: Orphan

spec:
  forProvider:
    ...
  deletionPolicy: Orphan  # 删除 CR 不删除云资源

配合 Usage 资源做删除保护:

apiVersion: apiextensions.crossplane.io/v1alpha1
kind: Usage
metadata:
  name: protect-order-db
spec:
  of:
    apiVersion: rds.aws.upbound.io/v1beta2
    kind: Instance
    resourceRef:
      name: order-service-db-xxxxx
  reason: "Production database - cannot delete"

3. 调试技巧:资源创建失败时的排查路径

# 1. 查看 Claim 状态
kubectl describe postgresqlinstance order-service-db -n team-order

# 2. 查看 Composite Resource 事件
kubectl get composite -o wide
kubectl describe xpostgresqlinstance <name>

# 3. 查看 Managed Resource 具体报错
kubectl get managed | grep -i false
kubectl describe instance.rds <name>

# 4. Provider Pod 日志
kubectl logs -n crossplane-system -l pkg.crossplane.io/revision -c package-runtime --tail=50

常见报错及对策: - cannot resolve references → 检查跨资源引用的 label/name - access denied → 检查 IAM 权限,Provider IRSA Role 是否绑定正确 - resource already exists → 加 crossplane.io/external-name annotation 导入已有资源

总结

对比维度 Terraform Crossplane
状态存储 外部 Backend(S3/Consul) Kubernetes etcd
漂移修复 手动 terraform apply Controller 自动 reconcile
权限模型 云 IAM 独立管理 K8s RBAC 统一管控
开发者自助 需额外平台(Atlantis/Spacelift) kubectl apply Claim
生态成熟度 极成熟,Provider 覆盖广 快速追赶,核心云资源已覆盖
适用场景 一次性 provisioning、多云统一 持续 reconcile、平台工程

核心结论: Crossplane 不是 Terraform 的替代品,而是面向平台工程团队的补充方案。如果你已经在用 Kubernetes + GitOps 管理应用,用 Crossplane 管理基础设施可以实现 统一控制面 + 开发者自助 + 自动漂移修复 三合一。建议从非核心资源(S3 Bucket、SQS Queue)开始试点,再逐步扩展到数据库和网络。

您还没有登录,请登录后发表评论。