Crossplane 作為 Kubernetes 的擴充套件,簡化了雲端資源的管理。然而,傳統的 Crossplane 組合根據靜態 CRD 定義,缺乏彈性。組合函式的出現,為 Crossplane 帶來了新的可能性,允許開發者以更靈活的方式組態資源。本文將著重於 Go 範本函式的應用,透過一個動態建立資料函式庫的案例,展現其強大之處。過往,我們可能需要為每個資料函式庫建立一個獨立的 CRD,而現在,藉由組合函式和 Go 範本,我們可以根據使用者輸入,動態生成所需數量的資料函式庫資源。這不僅簡化了組態流程,也提升了資源管理的效率。更進一步地,我們可以將這個概念延伸到其他型別的資源,例如虛擬機器、網路等等,實作真正的 Infrastructure as Code。
在 Crossplane 的 Pipeline 模式下,組合不再直接定義資源,而是定義一系列步驟,每個步驟由一個函式處理。每個函式接收輸入,執行特定邏輯,然後產生輸出。這樣的設計賦予了 Crossplane 極大的靈活性,因為函式內部可以執行任何邏輯,只要它能夠接收輸入並傳回資源作為輸出。本文的案例中,我們使用 Go 範本函式來根據使用者提供的資料函式庫名稱列表,動態生成多個資料函式庫資源。Go 範本的語法簡潔易懂,可以方便地操作資料,生成所需的 YAML 組態。透過 observed 欄位,函式可以存取 Composite Resource 的所有資訊,包括使用者輸入的引數,以及先前步驟生成的資源。這使得函式之間可以相互協作,構建更複雜的資源組態邏輯。
擁抱函式式組合:Crossplane 的技術進化之路
在 Crossplane 的世界裡,我們不斷探索如何更靈活、更強大地管理雲端資源。本文將探討 Composition Functions,一種讓資源組合更具彈性的技術。玄貓將帶領大家瞭解如何運用 Function,以及為何 Crossplane 要朝這個方向發展。
Function 的本質:資源組合的新起點
Function 在 Crossplane 中扮演著核心角色,它本質上是一個獨立的運算單元,專門處理資源的產生、修改和轉換。Function 的定義方式與 Provider 和 Configuration 類別似,都是透過 YAML 檔案來描述。
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: crossplane-contrib-function-patch-and-transform
spec:
package: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.1.4
Function 的關鍵在於 kind: Function,這告訴 Crossplane 這是一個 Function 定義。雖然現在為了簡化流程,我們直接套用 Function,但實際上,Function 應該被封裝到 Configuration Packages 中,以便更好地管理和佈署。
Pipeline 模式:開發資源協調Pipeline
過去,我們的 Composition 執行在預設的 Resources 模式下,直接在 Composition 內部定義資源。現在,透過 Pipeline 模式,我們可以將資源的產生過程拆解為一系列步驟,每個步驟都由一個 Function 處理。
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: azure-postgresql
labels:
provider: azure
db: postgresql
spec:
compositeTypeRef:
apiVersion: devopstoolkitseries.com/v1alpha1
kind: SQL
mode: Pipeline
pipeline:
- functionRef:
name: crossplane-contrib-function-patch-and-transform
step: patch-and-transform
input:
apiVersion: pt.fn.crossplane.io/v1beta1
kind: Resources
patchSets:
- name: metadata
patches:
- fromFieldPath: metadata.annotations
toFieldPath: metadata.annotations
- fromFieldPath: spec.id
toFieldPath: metadata.name
resources:
- name: resourcegroup
base:
apiVersion: azure.upbound.io/v1beta1
kind: ResourceGroup
spec:
forProvider:
location: eastus
patches:
- type: PatchSet
patchSetName: metadata
- name: server
base:
...
在 Pipeline 模式下,spec.mode 被設定為 Pipeline。spec.pipeline 是一個步驟陣列,每個步驟都透過 functionRef.name 參照一個 Function。Function 的輸入則定義在 input 欄位中,這個 input 實際上就是過去我們在 Resources 模式下定義的資源。
Patch and Transform Function:功能轉移還是進化?
你可能會問,Patch and Transform Function 的作用是什麼?它不就是把原本內建在 Composition 裡的 Patch 和 Transform 功能搬出來而已嗎?
沒錯,表面上看起來是這樣。但實際上,Crossplane 正在將資源組合的邏輯從核心功能轉移到 Function 中。
為何要擁抱 Function?Crossplane 的未來藍圖
或許你會疑惑,如果沒有明顯的好處,為何要大費周章地改寫 Composition?玄貓認為有兩個主要原因:
- Pipeline 模式的強制要求:Pipeline 模式不支援內建的 Patch 和 Transform 功能,因此必須使用 Function。
- Crossplane 的發展方向:Crossplane 很有可能逐步淘汰 Resources 模式下內建的 Patch 和 Transform 功能,轉而全面擁抱 Function。
這意味著,Crossplane 不太可能在 Composition 中增加更多內建功能,而是會鼓勵開發者透過 Function 來擴充套件和客製化資源組合。
實戰演練:佈署與追蹤
讓我們實際套用新的 Composition,並追蹤 Claim 的狀態。
kubectl apply --filename compositions/sql-v8/$HYPERSCALER.yaml
crossplane beta trace sqlclaim my-db --namespace a-team
透過 crossplane beta trace 命令,我們可以清楚地看到資源的產生過程,以及 Function 在其中扮演的角色。
總而言之,Composition Functions 是 Crossplane 在資源組合方面的重要一步。透過擁抱 Function,我們可以開發更靈活、更可擴充套件的雲端資源管理平台。雖然目前看起來只是功能的轉移,但實際上,Crossplane 正在為我們描繪一個更具彈性的未來。
利用 Crossplane 組合函式實作資料函式庫靈活組態:玄貓的實戰分享
在雲原生時代,基礎設施即程式碼(Infrastructure as Code, IaC)已成為常態。Crossplane 作為一個 Kubernetes 的擴充套件,讓我們能夠使用 Kubernetes 的 API 來管理各種雲端資源。本文中,玄貓將分享如何利用 Crossplane 的組合函式(Composition Functions)來實作更靈活的資料函式庫組態。
為何要使用組合函式?擺脫傳統 CRD 的束縛
傳統的 Crossplane 組合依賴於靜態的 CRD(Custom Resource Definition)定義,缺乏彈性。若要實作更複雜的邏輯,例如根據使用者輸入動態建立多個資料函式庫,傳統方式就顯得捉襟見肘。這時,組合函式就派上用場了。
組合函式:賦予 Crossplane 無限可能
組合函式本質上是一個獨立的程式,它接收輸入,產生輸出。在 Crossplane 的 Pipeline 模式下,組合不再直接定義資源,而是定義一系列步驟,每個步驟將輸入傳遞給函式,函式再將資源傳回給組合。
這種模式的強大之處在於,函式內部可以執行任何邏輯。它可以新增、修改或刪除資源,只要能接收輸入並傳回資源作為輸出,Crossplane 就不會限制函式的行為。
案例:使用 Go 範本函式動態建立資料函式庫
假設我們希望使用者能夠在一個 PostgreSQL 伺服器中請求任意數量的資料函式庫,並且可以自訂資料函式庫名稱。傳統的組合方式難以實作這種需求,但透過 Go 範本函式,我們可以輕鬆解決。
首先,我們需要修改 Composite Resource Definition,新增一個 databases 欄位,允許使用者傳入一個資料函式庫名稱列表:
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: sqls.devopstoolkitseries.com
spec:
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
parameters:
type: object
properties:
databases:
description: 資料函式庫名稱列表
type: array
items:
type: string
接下來,我們需要安裝 Go 範本函式。玄貓這裡使用 Upbound 提供的 function-go-templating:
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: upbound-function-go-templating
spec:
package: xpkg.upbound.io/crossplane-contrib/function-go-templating:v0.4.0
然後,在組閤中新增一個步驟,使用 Go 範本函式根據使用者輸入的資料函式庫名稱列表生成相應的 Database 資源:
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: azure-postgresql
spec:
pipeline:
- functionRef:
name: crossplane-contrib-function-patch-and-transform
- functionRef:
name: upbound-function-go-templating
step: sql-db
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline: |
{{ range .observed.composite.resource.spec.parameters.databases }}
---
apiVersion: postgresql.sql.crossplane.io/v1alpha1
kind: Database
metadata:
name: {{ $.observed.composite.resource.spec.id }}-{{ . }}
annotations:
crossplane.io/external-name: {{ . }}
gotemplating.fn.crossplane.io/composition-resource-name: {{ $.observed.composite.resource.spec.id }}-{{ . }}
spec:
providerConfigRef:
name: azure-provider-config
forProvider:
name: {{ . }}
{{ end }}
在這個範例中,我們使用 Go 範本的 range 函式遍歷 databases 列表,為每個資料函式庫名稱生成一個 Database 資源。
玄貓解密:Go 範本程式碼
以下是 Go 範本程式碼的逐項解說:
{{ range .observed.composite.resource.spec.parameters.databases }}:這行程式碼使用range函式來遍歷databases列表。.observed.composite.resource.spec.parameters.databases表示我們從 Composite Resource 的spec.parameters.databases欄位中取得資料。{{ $.observed.composite.resource.spec.id }}-{{ . }}:這行程式碼用於生成資料函式庫的名稱。$.observed.composite.resource.spec.id表示 Composite Resource 的 ID,.表示當前遍歷到的資料函式庫名稱。crossplane.io/external-name: {{ . }}:這行程式碼設定external-nameannotation,用於指定資料函式庫的外部名稱。gotemplating.fn.crossplane.io/composition-resource-name: {{ $.observed.composite.resource.spec.id }}-{{ . }}:這行程式碼設定composition-resource-nameannotation,用於追蹤資源的來源。spec.forProvider.name: {{ . }}:這行程式碼設定Database資源的name欄位,用於指定資料函式庫的名稱。
玄貓(BlackCat)談 Crossplane Composition Functions:如何用 Go 範本靈活組態資源
在之前的文章中,玄貓(BlackCat)向大家介紹了 Crossplane Composition 的基本概念。今天,我們將更深入地探討 Composition Functions,特別是如何使用 Go 範本來靈活地組態資源。
從 Database 消失說起:Composition Function 的奧秘
讓我們回顧一下之前的 Composition 範例。如果你夠仔細,會發現原本在資源清單中的 Database 消失了!取而代之的是一個新的步驟,這個步驟使用 Go 範本函式。
34 name: {{ $.observed.composite.resource.spec.id }}
35 forProvider: {}
36 {{ end }}
這個範例展示了 Composition Function 的強大之處。它允許我們在 Composition 中呼叫函式,並將輸入內容傳遞給這些函式。更重要的是,它還會傳送 Composite Resource 的當前狀態。這就是 observed 欄位的作用。
observed 欄位包含了所有資訊,包括 Claim 建立的 Composite Resource 定義,以及先前步驟中執行的 Function 所組裝的所有資源。雖然 Function 的開發者可能更關心先前組裝的資源,但作為 Function 的使用者,我們更關注從 Composite Resource 中存取欄位的能力。
具體來說,我們關心 spec.parameters.databases 和 spec.id 欄位。這些欄位,以及所有其他 Composite Resource 欄位,都可以在 observed.composite.resource 欄位中找到。
Go 範本:靈活組態的根本
有了以上概念,讓我們快速瀏覽一下範本本身。
我們正在迭代 spec.parameters.databases 欄位的值,這是一個陣列,並為每個值產生一個 Database。
如果你熟悉 Helm 範本,應該對 Go 範本的語法不會陌生。關鍵在於理解所有內容都可以在 observed 欄位中存取。
值得一提的是 metadata.name 和 metadata.annotations.crossplane.io/external-name 欄位的值。
我們可以將 metadata.name 留空,讓 Crossplane 自動產生一個唯一的名稱。但玄貓(BlackCat)不喜歡隨機名稱,所以我們使用 spec.id 和資料函式庫名稱({{ . }},來自 range 迴圈)的組合。這樣,我們就能產生一個結合了這兩者的唯一資源名稱。
然而,雖然這適用於 Managed Resource 的名稱,但玄貓(BlackCat)不希望資料函式庫的「真實」名稱與 Claim 的 spec.parameters.databases 欄位中指定的名稱不同。
預設情況下,資源的「真實」名稱(在 Azure 中最終會變成某個東西的名稱)與 Kubernetes 資源名稱相同,除非名稱是自動產生的,就像 AWS VPC 的情況一樣。因此,有時它們相同,有時則不同。Crossplane 透過 crossplane.io/external-name annotation 來解決這個問題。它將資源的「真實」名稱儲存在這裡,無論它是否與 Managed Resource 的名稱比對。
但正如玄貓(BlackCat)之前提到的,我們不希望這兩者相同,因此我們自己指定 crossplane.io/external-name annotation,並將其設定為 spec.parameters.databases[] 的值({{ . }})。
剩下的部分都與 Go 範本的工作方式有關,這不是本文的重點。
實戰演練:新增和驗證資料函式庫
讓我們套用 Composition…
kubectl apply --filename compositions/sql-v9/$HYPERSCALER.yaml
…並檢索所有資料函式庫。
kubectl get databases.postgresql.sql.crossplane.io
如果沒有出現任何資源,別擔心,這並不是錯誤!
我們套用了一個範本,該範本迭代 spec.parameters.databases 欄位中的值,但我們沒有將該欄位新增到 Claim 中。因此,我們沒有任何資料函式庫。這也證明瞭其中一個要求:不指定任何資料函式庫的使用者將不會獲得任何資料函式庫。這是一種選擇加入的模型。
讓我們新增一些資料函式庫。
apiVersion: devopstoolkitseries.com/v1alpha1
kind: SQLClaim
metadata:
name: my-db
spec:
parameters:
databases:
- db-01
- db-02
更新後的 manifest 新增了兩個資料函式庫:db-01 和 db-02。
讓我們套用它…
kubectl --namespace a-team apply \
--filename examples/$HYPERSCALER-sql-v9.yaml
…並檢索所有資料函式庫。
kubectl get databases.postgresql.sql.crossplane.io
現在,我們可以看到兩個新的資料函式庫。
NAME READY SYNCED AGE
my-db-...-db-01 True True 7s
my-db-...-db-02 True True 7s
它們需要幾秒鐘才能在伺服器內部啟動並執行,所以讓我們利用這段時間來仔細檢查一切是否按預期工作。玄貓(BlackCat)不相信自己,所以你也不應該相信我。
因此,讓我們仔細檢查資料函式庫是否確實在伺服器內部建立。我們已經完成了取得身份驗證資料和執行帶有 psql 的容器的難關,因此我們可以不用太多解釋地做到這一點。
首先,檢索 a-team Namespace 中的 Secrets…
kubectl --namespace a-team get secrets
…如果它是 Azure(並且堅持使用唯一名稱),則將伺服器名稱(與 Secret 名稱相同)儲存在 DB_NAME 變數中。對於其他人,它將是 my-db。
export DB_NAME=my-db
接下來,我們將檢索使用者…
export PGUSER=$(kubectl --namespace a-team \
get secret $DB_NAME --output jsonpath="{.data.username}" \
| base64 -d)
…密碼…
export PGPASSWORD=$(kubectl --namespace a-team \
get secret $DB_NAME --output jsonpath="{.data.password}" \
| base64 -d)
…和主機。
export PGHOST=$(kubectl --namespace a-team \
get secret $DB_NAME --output jsonpath="{.data.endpoint}" \
| base64 -d)
現在我們可以執行一個帶有 psql 的 Pod…
kubectl run postgresql-client --rm -ti --restart='Never' \
--image docker.io/bitnami/postgresql:16 \
--env PGPASSWORD=$PGPASSWORD --env PGHOST=$PGHOST \
--env PGUSER=$PGUSER --command -- sh
…連線到伺服器…
psql --host $PGHOST -U $PGUSER -d postgres -p 5432
…並列出所有資料函式庫。
\l
輸出結果應該包含我們剛剛建立的 db-01 和 db-02 資料函式庫。