贵司的开发团队的开发者A,需要在代码里使用AWS的S3服务,如果没有Crossplane 的话。他需要:
过了几天,贵司的另外一个开发团队开发者B,需要在代码里使用AWS的MySQL服务,他需要走完上一个同学一样的历程。
又过了几天,贵司的运维同学C,想要一个虚拟机,再来一遍。。
这?没有任何声明式!一大堆的莫名其妙的配置,直接默认就行了!这太不“K8S"了。
crossplane是k8s下的一个管理应用和基础设施的k8s operator,通过隔离云服务提供商的基础设施实现k8s声明式API,将云服务提供商的资源和服务标准化和自动化,方便地实现IaC(基础设施即代码)。
它提供了多种抽象,使得我们的平台开发者在使用云资源时,尤其是公有云服务无比的便利。
刚才的描述,如果你还是一头雾水的话,可以想象成,服务目录,我认为crossplane 非常像Service catalog服务目录。
crossplane 的概念还是蛮绕的,尤其这个项目里的英文术语,很容易混淆,目前中文里,只有跟OAM团队有关的应用相关的文章,他们并没有突出Crossplane的核心概念,官方文档都是英文,而且它的例子,又臭又长,可以说crossplane 社区在用实力劝退众多中文从业者。
provider
基础设施资源提供者,它是一组k8s 的CRD和controllers的组合,用于一对一的定义各个provider 提供的资源。官方提供的provider 有:
provider 主要有两种资源组成,Provider 和 ProviderConfig,Provider大概长这样:
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws
spec:
package: "crossplane/provider-aws:master"
它主要定义了Provider 的名称和安装包信息。ProviderConfig,长这样:
apiVersion: aws.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
name: aws-provider
spec:
credentials:
source: Secret
secretRef:
namespace: crossplane-system
name: aws-creds
key: key
ProviderConfig 对象描述了以何种身份或者用户去使用provider 的资源,ProviderConfig和Provider 显然是“多对一”的关系。也就是说k8s可以使用不同的身份去使用Provider 的资源。
Managed Resource
Managed Resource 即管理对象,在crossplane里,这是一种provider 提供的,最底层、且无法分割的元资源。Managed Resource 在k8s集群里,可以直接使用,用来创建one-to-one 的云资源。以AWS 的 Redis 服务为例:
apiVersion: database.aws.crossplane.io/v1beta1
kind: RDSInstance
metadata:
name: foodb
spec:
forProvider:
dbInstanceClass: db.t2.small
masterUsername: root
allocatedStorage: 20
engine: mysql
writeConnectionSecretToRef:
name: mysql-secret
namespace: crossplane-system
providerConfigRef:
name: default
deletionPolicy: Delete
我们看下,这个CRD对象的Group显然只有AWS的Provider Controller 能认识,别家的Provider肯定是不认识的。
那么AWS家的Provider 认识到这个Managed Controller 对象,也就是RDSInstance以后,会从它spec里指定的Provider Config对象里拿到用户认证信息,接着就去消费AWS这个Cloud Provider 里的云服务资源了。
之所以称之为low level的“元资源”,是因为对于我们的众多开发者而言,他们不太可能只用到单独一种Managed Resource,通常他们会使用一组这种Managed Resources,那么如果用一组这种资源的时候,我们还是要求开发者,一个个low level 的资源去创建的话,显然开发者要骂娘了,这增加了他们的学习成本,且一旦Provider 的云资源配置修改了,开发者的部署包也得跟着改。
好在Crossplane 提供了一种组合资源。
Composite Resource Definition
翻译成组合资源定义,或者XRD,根据社区的解释,XRD的含义和k8s 的CRD意义是一样的,它定义了一种组合资源,为啥叫XRD呢?它明明是CRD,因为如果叫CRD就和k8s 的CRD冲突了。而X,意味着Cross,交叉,也是Crossplane 的前缀。
它的意义和使用方法跟K8S的CRD一模一样,你可以XRD里定义你希望将来给开发者提供什么样的API 接口,注意,它只是一个接口,并不定义组合资源由那些资源组成。XRD比你熟悉的CRD多了三个字段:
因为XRD比较长,我们看下XR:
apiVersion: example.org/v1alpha1
kind: CompositeMySQLInstance
metadata:
name: example
spec:
parameters:
location: au-east
storageGB: 20
version: "5.7"
compositionRef:
name: example-azure
writeConnectionSecretToRef:
namespace: infra-secrets
name: example-mysqlinstance
这个资源就是给人看的,给人用的,因为它说人话,开发者A,他就想要一个au-east区的Mysql 实例,存储20G,版本5.7,至于是什么db engine,什么规格,全公司给一个默认的就行了。
那么开发者A,创建了这个名为:example的CompositeMySQLInstance以后,究竟用的是哪家的云资源呢?是AWS的?是Azure的?还是阿里云的?毕竟每一家,对Mysql 的管理都是不一样的。
compositionRef,这个字段就是指定了具体哪一个的Composition(组合)。
Composition
组合,它是XRD的一种实现,它具体指明了,某个XR究竟有哪些Managed Resource组成。它具体有三个部分组成:
用官方的例子展示下,我去掉了注释,否则太长了,这里resource 的部分,就描绘了,这个Compostion 究竟有哪些Managed Resource 组成。
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: example-azure
labels:
purpose: example
provider: azure
spec:
compositeTypeRef:
apiVersion: example.org/v1alpha1
kind: CompositeMySQLInstance
patchSets:
- name: metadata
patches:
- fromFieldPath: metadata.labels
- fromFieldPath: metadata.annotations[example.org/app-name]
- name: external-name
patches:
- type: FromCompositeFieldPath
fromFieldPath: metadata.annotations[crossplane.io/external-name]
policy:
fromFieldPath: Required
resources:
- name: resourcegroup
base:
apiVersion: azure.crossplane.io/v1alpha3
kind: ResourceGroup
spec: {}
patches:
- type: PatchSet
patchSetName: metadata
- fromFieldPath: "spec.parameters.location"
toFieldPath: "spec.location"
transforms:
- type: map
map:
us-west: West US
us-east: East US
au-east: Australia East
- name: mysqlserver
base:
apiVersion: database.azure.crossplane.io/v1beta1
kind: MySQLServer
spec:
forProvider:
resourceGroupNameSelector:
matchControllerRef: true
sslEnforcement: Disabled
sku:
tier: GeneralPurpose
capacity: 8
family: Gen5
storageProfile:
backupRetentionDays: 7
geoRedundantBackup: Disabled
writeConnectionSecretToRef:
namespace: crossplane-system
patches:
- type: PatchSet
patchSetName: metadata
- fromFieldPath: "metadata.uid"
toFieldPath: "spec.writeConnectionSecretToRef.name"
transforms:
- type: string
string:
fmt: "%s-mysqlserver"
- fromFieldPath: "spec.parameters.version"
toFieldPath: "spec.forProvider.version"
- fromFieldPath: "spec.parameters.location"
toFieldPath: "spec.forProvider.location"
transforms:
- type: map
map:
us-west: West US
us-east: East US
au-east: Australia East
- fromFieldPath: "spec.parameters.storageGB"
toFieldPath: "spec.forProvider.storageProfile.storageMB"
# Transform the value from the CompositeMySQLInstance by multiplying it by
# 1024 to convert Gigabytes to Megabytes.
transforms:
- type: math
math:
multiply: 1024
- type: CombineFromComposite
combine:
variables:
- fromFieldPath: spec.parameters.location
- fromFieldPath: metadata.annotations[crossplane.io/claim-name]
strategy: string
string:
fmt: "%s-%s"
toFieldPath: spec.forProvider.administratorLogin
policy:
fromFieldPath: Required
- type: ToCompositeFieldPath
fromFieldPath: "status.atProvider.fullyQualifiedDomainName"
toFieldPath: "status.address"
- type: CombineToComposite
combine:
variables:
- fromFieldPath: "spec.parameters.administratorLogin"
- fromFieldPath: "status.atProvider.fullyQualifiedDomainName"
strategy: string
string:
fmt: "mysql://%s@%s:3306/my-database-name"
toFieldPath: status.adminDSN
policy:
fromFieldPath: Optional
connectionDetails:
- fromConnectionSecretKey: username
- fromConnectionSecretKey: password
- name: hostname
fromConnectionSecretKey: endpoint
- type: FromValue
name: port
value: "3306"
readinessChecks:
- type: MatchString
fieldPath: "status.atProvider.userVisibleState"
matchString: "Ready"
- name: firewallrule
base:
apiVersion: database.azure.crossplane.io/v1alpha3
kind: MySQLServerFirewallRule
spec:
forProvider:
resourceGroupNameSelector:
matchControllerRef: true
serverNameSelector:
matchControllerRef: true
properties:
startIpAddress: 10.10.0.0
endIpAddress: 10.10.255.254
virtualNetworkSubnetIdSelector:
name: sample-subnet
patches:
- type: PatchSet
patchSetName: metadata
writeConnectionSecretsToNamespace: crossplane-system
使用
还记得我们的XR对象吧,这个给人用的东西,最终会产生一个Secret,它位于infra-secrets 这个namespace 它叫example-mysqlinstance。这样,平台的消费者,就可以直接访问他渴望的资源了。
apiVersion: example.org/v1alpha1
kind: CompositeMySQLInstance
metadata:
name: example
spec:
parameters:
location: au-east
storageGB: 20
version: "5.7"
compositionRef:
name: example-azure
writeConnectionSecretToRef:
namespace: infra-secrets
name: example-mysqlinstance
还有另外一种使用方式,那就是Claim的方式,Claim 是一XR资源的代理,它可以完全无视XRD的定义的变化,你们基础设施部门,或者云计算部门的同学,想怎么改XRD就怎么改,我永远只用XRClaim去访问我想要访问的资源。
Claim 可以使用一个现成的XR对象,也可以每次在创建Claim 的时候,动态生成一个新的XR对象。就像下面这样:
# The MySQLInstance always has the same API group and version as the
# resource it requires. Its kind is always suffixed with .
apiVersion: example.org/v1alpha1
kind: MySQLInstance
metadata:
# Infrastructure claims are namespaced.
namespace: default
name: example
spec:
# The schema of the spec.parameters object is defined by the earlier example
# of an CompositeResourceDefinition. The location, storageGB, and version fields
# are patched onto the ResourceGroup, MySQLServer, and MySQLServerFirewallRule
# composed by the required MySQLInstance.
parameters:
location: au-east
storageGB: 20
version: "5.7"
# Support for a resourceRef is automatically injected into the schema of all
# resource claims. The resourceRef requests a CompositeMySQLInstance
# explicitly.
resourceRef:
apiVersion: example.org/v1alpha1
kind: CompositeMySQLInstance
name: example
# Support for a writeConnectionSecretToRef is automatically injected into the
# schema of all published infrastructure claim resources. This allows
# the resource to write a connection secret containing any details required to
# connect to it - in this case the hostname, username, and password.
writeConnectionSecretToRef:
name: example-mysqlinstance
apiVersion: example.org/v1alpha1
kind: MySQLInstance
metadata:
namespace: default
name: example
spec:
parameters:
location: au-east
storageGB: 20
version: "5.7"
# Support for a compositionSelector is automatically injected into the schema
# of all published infrastructure claim resources. This selector selects
# the example-azure composition by its labels.
compositionSelector:
matchLabels:
purpose: example
provider: azure
writeConnectionSecretToRef:
name: example-mysqlinstance
这时候平台开发者的任务就转变为,利用已有的各自Provider,为了各自公司的开发人员,开发好用,易用,丝滑的XRD和Composition了,对于应用开发者而言,以后自身组件如果有些依赖的话,就直接写Claim了。基础设施的同学又可以跟业务部门愉快地玩耍了。