天天看點

Crossplane 詳解1——基礎設施即代碼假設一個場景解決Crossplane詳解平台開發者需要做什麼

目錄

  • 假設一個場景
  • 解決
  • Crossplane詳解
  • 平台開發者需要做什麼

假設一個場景

貴司的開發團隊的開發者A,需要在代碼裡使用AWS的S3服務,如果沒有Crossplane 的話。他需要:

  1. 自己注冊一個AWS賬戶,或者拿到貴司的AWS賬戶
  2. 去AWS上,買一個S3服務,自己填一大堆的配置。
  3. 把這個S3的服務的配置放在一個secret 裡,并挂載到了pod裡。

過了幾天,貴司的另外一個開發團隊開發者B,需要在代碼裡使用AWS的MySQL服務,他需要走完上一個同學一樣的曆程。

又過了幾天,貴司的運維同學C,想要一個虛拟機,再來一遍。。

這?沒有任何聲明式!一大堆的莫名其妙的配置,直接預設就行了!這太不“K8S"了。

解決

crossplane是k8s下的一個管理應用和基礎設施的k8s operator,通過隔離雲服務提供商的基礎設施實作k8s聲明式API,将雲服務提供商的資源和服務标準化和自動化,友善地實作IaC(基礎設施即代碼)。

它提供了多種抽象,使得我們的平台開發者在使用雲資源時,尤其是公有雲服務無比的便利。

剛才的描述,如果你還是一頭霧水的話,可以想象成,服務目錄,我認為crossplane 非常像Service catalog服務目錄。

Crossplane詳解

crossplane 的概念還是蠻繞的,尤其這個項目裡的英文術語,很容易混淆,目前中文裡,隻有跟OAM團隊有關的應用相關的文章,他們并沒有突出Crossplane的核心概念,官方文檔都是英文,而且它的例子,又臭又長,可以說crossplane 社群在用實力勸退衆多中文從業者。

  1. provider

    基礎設施資源提供者,它是一組k8s 的CRD和controllers的組合,用于一對一的定義各個provider 提供的資源。官方提供的provider 有:

    • AWS provider
    • GCP provider
    • Azure
    • Alibaba
    • 等等
    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 的資源。
  2. 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 提供了一種組合資源。

  3. 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吧。

    因為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(組合)。

  4. Composition

    組合,它是XRD的一種實作,它具體指明了,某個XR究竟有哪些Managed Resource組成。它具體有三個部分組成:

    • compositeTypeRef

      指明了這個Composition實作的是哪個XR。

    • patchSets

      這個地方,可以叫更新檔,也可以叫overlay,就像面向對象程式設計,多态的時候,子類有哪些地方可以覆寫的。

    • resouce
      • base

        這個就是基集

      • patch

        這裡就是替換或者覆寫的規則。

    用官方的例子展示下,我去掉了注釋,否則太長了,這裡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://%[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
               
  5. 使用

    還記得我們的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對象。就像下面這樣:

    1. 用現成的,用的是上一步我們建立的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
                 
    2. 動态生成,這時候,就得指定,你要用哪個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
                 

平台開發者需要做什麼

這時候平台開發者的任務就轉變為,利用已有的各自Provider,為了各自公司的開發人員,開發好用,易用,絲滑的XRD和Composition了,對于應用開發者而言,以後自身元件如果有些依賴的話,就直接寫Claim了。基礎設施的同學又可以跟業務部門愉快地玩耍了。