简介
Crossplane 是一种开源 Kubernetes 扩展,可将 Kubernetes 集群转换为通用控制平面。借助 Crossplane,平台团队可以利用 Kubernetes 策略、命名空间、基于角色的访问控制等的全部功能创建新的抽象和自定义 API, Crossplane 将所有非 Kubernetes 资源集中管理。
控制平面创建和管理资源的生命周期。控制平面不断检查预期资源是否存在,当预期状态与现实不符时报告并采取行动纠正错误。Crossplane 将 Kubernetes 控制平面扩展为通用控制平面,可以在任何地方检查、报告和处理任何资源。
基本概念
- Package:OCI 镜像标准的包,包含 Provider CRDs、Provider 镜像、ServiceAccount 权限等信息。
- Provider:云提供商插件,由一组 CRD 和 Controller 组成,CRD 定义了资源信息,Controller 通过 SDK 连接外部云服务,根据资源字段变更,执行相关操作。
- Managed Resource:Provider 内部 CRD 定义的资源,创建并由 Crossplane 托管之后,称为 Managed Resource。
- Compositions:Managed Resource 集合的模板,用于需要多个资源组合使用的场景,类似 Terraform Module。如 ECS + VPC + EBS。
- Composite Resource(XR):使用 Composition 模板创建的一组 Managed Resource。
- Claims:开发人员与 Crossplane 交互的主要方式,开发人员通过 Claim 声明由 SRE 团队定义的 XRD 中的自定义字段值,最终在 XR 定义中引用。
和 Terraform 对比,XRD 比较类似 Terraform Module 中的 variables块;Composition 是模块的 HCL 代码的其余部分,它描述了如何使用这些变量来创建一堆资源;XR 和 Claim 类似为模块提供输入的 tfvars 文件。
Crossplane vs Terraform
相同点
- 都属于 IaC 工具,通过声明式配置管理基础设施
- 通过 Provider 插件管理各种各样的基础设施
不同点
- Crossplane 是一个控制平面,能够不断检查资源状态,管理资源全生命周期。
- Terraform 是一个命令行工具,而不是控制平面,需要通过 CLI 手动或自动触发资源生命周期管理。
- Terraform 没有暴露 API,只能通过 HCL + 命令行操作与 Terraform 集成;Crossplane 基于 Kubernetes API 构建,除了 API,也支持使用命令行工具 kubectl,方便应用集成。
Provider 接入 & 实践
Provider 实现方式
Crossplane 原生实现:Crossplane 运行时定义了 ExternalClient 接口,接口中定义了资源的 CURD 操作。Provider 中的自定义 Controller 中通过 OpenAPI 实现 ExternalClient 接口,最终由 Crossplane 触发 Provider 执行资源 CURD 操作。这种方法类似 Terraform。
Upjet:Crossplane 商业公司 Upbound 开发的工具,基于 Terraform Provider 自动生成 Crossplane Provider,内部实际是 Terraform 的封装,使用 Terraform CLI 实现了 ExternalClient 接口。
Upjet 接入流程
构建生成
- 使用 upjet-provider-template 模板创建仓库,仓库名为 provider-[provider_name]
- 克隆代码,进入仓库目录,拉取 upbound/build 模块
make submodules
- 替换模板内容
提前注释掉 prepare.sh 最后一行 rm -rf apis/null,这个是示例资源,这里没有删除干净,如果不加上注释,最后构建时会编译失败。
./hack/prepare.sh
- Makefile 环境变量配置
export TERRAFORM_VERSION ?= 1.2.1
export TERRAFORM_PROVIDER_SOURCE ?= volcengine/volcengine
export TERRAFORM_PROVIDER_REPO ?= https://github.com/volcengine/terraform-provider-volcengine
export TERRAFORM_PROVIDER_VERSION ?= 0.0.72
export TERRAFORM_PROVIDER_DOWNLOAD_NAME ?= terraform-provider-volcengine
export TERRAFORM_NATIVE_PROVIDER_BINARY ?= terraform-provider-volcengine_v0.0.72
export TERRAFORM_DOCS_PATH ?= website/docs/r
TERRAFORM_VERSION 使用 1.2.1,upjet 实现不兼容 0.12.xx 版本。
- 在 internal/clients/volcengine.go 中,实现 ProviderConfig 逻辑,加入 Terraform Provider 相关配置信息
const (
keyAccessKey = "access_key"
keySecretKey = "secret_key"
keyRegion = "region"
keyEndpoint = "endpoint"
)
func TerraformSetupBuilder(version, providerSource, providerVersion string) terraform.SetupFn {
...
// set provider configuration
ps.Configuration = map[string]any{}
ps.Configuration = map[string]any{}
if v, ok := creds[keyAccessKey]; ok {
ps.Configuration[keyAccessKey] = v
}
if v, ok := creds[keySecretKey]; ok {
ps.Configuration[keySecretKey] = v
}
if v, ok := creds[keyRegion]; ok {
ps.Configuration[keyRegion] = v
}
if v, ok := creds[keyEndpoint]; ok {
ps.Configuration[keyEndpoint] = v
}
return ps, nil
}
- config/external_name.go 中添加资源信息(以 VPC + Subnet 为例)
// ExternalNameConfigs contains all external name configurations for this
// provider.
var ExternalNameConfigs = map[string]config.ExternalName{
// Name is a parameter and it is also used to import the resource.
"volcengine_vpc": config.IdentifierFromProvider,
"volcengine_subnet": config.IdentifierFromProvider,
}
- 添加资源自定义配置
mkdir config/vpc
cat <<EOF > config/vpc/config.go
package vpc
import "github.com/upbound/upjet/pkg/config"
// Configure configures individual resources by adding custom ResourceConfigurators.
func Configure(p *config.Provider) {
p.AddResourceConfigurator("volcengine_vpc", func(r *config.Resource) {
// We need to override the default group that upjet generated for
r.ShortGroup = "vpc"
r.Kind = "VPC"
})
p.AddResourceConfigurator("volcengine_subnet", func(r *config.Resource) {
// We need to override the default group that upjet generated for
r.ShortGroup = "subnet"
r.References["vpc_id"] = config.Reference{
TerraformName: "volcengine_vpc",
}
})
}
EOF
在 config/provider.go 中注册自定义配置
import (
...
tjconfig "github.com/upbound/upjet/pkg/config"
"github.com/upbound/upjet/pkg/types/conversion/cli"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/myorg/provider-volcengine/config/vpc"
)
func GetProvider() *tjconfig.Provider {
...
for _, configure := range []func(provider *tjconfig.Provider){
// add custom config functions
- null.Configure,
+ vpc.Configure,
} {
configure(pc)
}
- 生成 Upjet Provider
make generate
随后会自动下载 Terraform、Terraform Provider 包,生成 VPC 相关资源信息。
运行测试
需要依赖 Kubernetes 运行时,可以提前在本机安装 minikube 用于测试。
- 在 examples 目录中,创建资源定义 yaml 配置
mkdir examples/vpc
创建 Provider 配置 Secret:
cat <<EOF > examples/providerconfig/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: example-creds
namespace: crossplane-system
type: Opaque
stringData:
credentials: |
{
"access_key": "demoak",
"secret_key": "demosk",
"region": "cn-beijing",
"endpoint": "open.volcengineapi.com"
}
EOF
使用前把 demoak & demosk 替换为真实 AK。
创建 VPC 配置:
cat <<EOF > examples/vpc/vpc.yaml
apiVersion: vpc.volcengine.upbound.io/v1alpha1
kind: VPC
metadata:
name: demo-vpc
spec:
forProvider:
description: "Managed with Crossplane."
cidrBlock: 192.168.0.0/16
vpcName: crossplane-demo
providerConfigRef:
name: default
EOF
创建 Subnet 配置:
cat <<EOF > examples/vpc/subnet.yaml
apiVersion: vpc.volcengine.upbound.io/v1alpha1
kind: Subnet
metadata:
name: demo-subnet
spec:
forProvider:
description: "Managed with Crossplane."
cidrBlock: 192.168.1.0/24
subnetName: crossplane-subnet-1
zoneId: cn-beijing-a
# vpcId: vpc-xxx
vpcIdRef:
name: demo-vpc
providerConfigRef:
name: default
EOF
- 安装 CRD
kubectl apply -f package/crds
- 本地运行 Provider
make run
- 安装 ProviderConfig 和示例 VPC 配置:
# Create "crossplane-system" namespace if not exists
kubectl create namespace crossplane-system --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f examples/providerconfig/
kubectl apply -f examples/vpc/vpc.yaml
kubectl apply -f examples/vpc/subnet.yaml
- 查看资源
kubectl get managed
- 清理资源
kubectl delete -f examples/vpc/subnet.yaml
kubectl delete -f examples/vpc/vpc.yaml