目錄
- 假設一個場景
- 解決
- Crossplane詳解
- 平台開發者需要做什麼
假設一個場景
貴司的開發團隊的開發者A,需要在代碼裡使用AWS的S3服務,如果沒有Crossplane 的話。他需要:
- 自己注冊一個AWS賬戶,或者拿到貴司的AWS賬戶
- 去AWS上,買一個S3服務,自己填一大堆的配置。
- 把這個S3的服務的配置放在一個secret 裡,并挂載到了pod裡。
過了幾天,貴司的另外一個開發團隊開發者B,需要在代碼裡使用AWS的MySQL服務,他需要走完上一個同學一樣的曆程。
又過了幾天,貴司的運維同學C,想要一個虛拟機,再來一遍。。
這?沒有任何聲明式!一大堆的莫名其妙的配置,直接預設就行了!這太不“K8S"了。
解決
crossplane是k8s下的一個管理應用和基礎設施的k8s operator,通過隔離雲服務提供商的基礎設施實作k8s聲明式API,将雲服務提供商的資源和服務标準化和自動化,友善地實作IaC(基礎設施即代碼)。
它提供了多種抽象,使得我們的平台開發者在使用雲資源時,尤其是公有雲服務無比的便利。
剛才的描述,如果你還是一頭霧水的話,可以想象成,服務目錄,我認為crossplane 非常像Service catalog服務目錄。
Crossplane詳解
crossplane 的概念還是蠻繞的,尤其這個項目裡的英文術語,很容易混淆,目前中文裡,隻有跟OAM團隊有關的應用相關的文章,他們并沒有突出Crossplane的核心概念,官方文檔都是英文,而且它的例子,又臭又長,可以說crossplane 社群在用實力勸退衆多中文從業者。
-
provider
基礎設施資源提供者,它是一組k8s 的CRD和controllers的組合,用于一對一的定義各個provider 提供的資源。官方提供的provider 有:
- AWS provider
- GCP provider
- Azure
- Alibaba
- 等等
它主要定義了Provider 的名稱和安裝包資訊。ProviderConfig,長這樣:apiVersion: pkg.crossplane.io/v1 kind: Provider metadata: name: provider-aws spec: package: "crossplane/provider-aws:master"
ProviderConfig 對象描述了以何種身份或者使用者去使用provider 的資源,ProviderConfig和Provider 顯然是“多對一”的關系。也就是說k8s可以使用不同的身份去使用Provider 的資源。apiVersion: aws.crossplane.io/v1beta1 kind: ProviderConfig metadata: name: aws-provider spec: credentials: source: Secret secretRef: namespace: crossplane-system name: aws-creds key: key
-
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多了三個字段:
-
connectionSecretKeys
可選,用于向使用者暴露一個連接配接Secret
-
defaultCompositionRef
預設的Composition對象,這個字段就是指定,當開發者建立XR的時候,應該用哪種Composition。
-
claimNames
可選,這個XR的Claim的類型,Claim,去類比下PVC吧。
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組成。它具體有三個部分組成:
-
compositeTypeRef
指明了這個Composition實作的是哪個XR。
-
patchSets
這個地方,可以叫更新檔,也可以叫overlay,就像面向對象程式設計,多态的時候,子類有哪些地方可以覆寫的。
- resouce
-
base
這個就是基集
-
patch
這裡就是替換或者覆寫的規則。
-
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://%[email protected]%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對象。就像下面這樣:
- 用現成的,用的是上一步我們建立的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
- 動态生成,這時候,就得指定,你要用哪個Compostion,也就是用誰家的資源,就得指定Composition了。因為Composition,指明了,用到了哪些具體資源。
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
- 用現成的,用的是上一步我們建立的XR對象。
平台開發者需要做什麼
這時候平台開發者的任務就轉變為,利用已有的各自Provider,為了各自公司的開發人員,開發好用,易用,絲滑的XRD和Composition了,對于應用開發者而言,以後自身元件如果有些依賴的話,就直接寫Claim了。基礎設施的同學又可以跟業務部門愉快地玩耍了。