本文已發布超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
client-go 第 6 版介紹
Kubernetes API 伺服器 公開任何用戶端皆可使用的 REST 介面。client-go 是 Go 程式語言的官方用戶端程式庫。Kubernetes 內部 (例如,kubectl 內部) 以及 眾多外部消費者 皆使用它:例如 etcd-operator 或 prometheus-operator 等運算子;KubeLess 和 OpenShift 等更高等級的架構;以及更多。
client-go 的版本 6 更新新增了對 Kubernetes 1.9 的支援,允許存取最新的 Kubernetes 功能。雖然 變更記錄 包含所有詳細資訊,但此部落格文章重點介紹最重要的變更,並旨在引導如何從版本 5 升級。
此部落格文章是讓協力廠商消費者更容易存取 client-go 的眾多努力之一。更輕鬆的存取是許多公司眾多人共同努力的成果,他們都在 Kubernetes Slack 的 #client-go-docs 頻道中會面。我們很樂意聽取意見和進一步改進的想法,當然也感謝任何想要貢獻的人。
API 群組變更
以下 API 群組升級是 Kubernetes 1.9 的一部分
- 工作負載物件 (Deployments、DaemonSets、ReplicaSets 和 StatefulSets) 已在 Kubernetes 1.9 中升級至 apps/v1 API 群組。client-go 遵循此轉換,並允許開發人員透過匯入 k8s.io/api/apps/v1 套件而非 k8s.io/api/apps/v1beta1,以及使用 Clientset.AppsV1() 來使用最新版本。
- Admission Webhook Registration 已在 Kubernetes 1.9 中升級至 admissionregistration.k8s.io/v1beta1 API 群組。先前的 ExternalAdmissionHookConfiguration 類型已被不相容的 ValidatingWebhookConfiguration 和 MutatingWebhookConfiguration 類型取代。此外,admission.k8s.io 中的 webhook admission 酬載類型 AdmissionReview 已升級至 v1beta1。請注意,版本化物件現在會傳遞至 webhook。如需詳細資訊,請參閱 admission webhook 文件。
CustomResources 的驗證
在 Kubernetes 1.8 中,我們引入了 CustomResourceDefinitions (CRD) 預先持久性結構描述驗證 作為 Alpha 功能。在 1.9 中,此功能已升級為 Beta 版,並且預設為啟用。作為 client-go 使用者,您可以在 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1 中找到 API 類型。
可以在 CRD 規格中將 OpenAPI v3 結構描述 定義為
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata: ...
spec:
...
validation:
openAPIV3Schema:
properties:
spec:
properties:
version:
type: string
enum:
- "v1.0.0"
- "v1.0.1"
replicas:
type: integer
minimum: 1
maximum: 10
上述 CRD 中的結構描述對執行個體套用以下驗證
- spec.version 必須是字串,且必須是「v1.0.0」或「v1.0.1」。
- spec.replicas 必須是整數,且最小值必須為 1,最大值必須為 10。spec.version (v1.0.2) 和 spec.replicas (15) 的值無效的 CustomResource 將會被拒絕
apiVersion: mygroup.example.com/v1
kind: App
metadata:
name: example-app
spec:
version: "v1.0.2"
replicas: 15
$ kubectl create -f app.yaml
The App "example-app" is invalid: []: Invalid value: map[string]interface {}{"apiVersion":"mygroup.example.com/v1", "kind":"App", "metadata":map[string]interface {}{"creationTimestamp":"2017-08-31T20:52:54Z", "uid":"5c674651-8e8e-11e7-86ad-f0761cb232d1", "clusterName":"", "name":"example-app", "namespace":"default", "deletionTimestamp":interface {}(nil), "deletionGracePeriodSeconds":(\*int64)(nil)}, "spec":map[string]interface {}{"replicas":15, "version":"v1.0.2"}}:
validation failure list:
spec.replicas in body should be less than or equal to 10
spec.version in body should be one of [v1.0.0 v1.0.1]
請注意,透過 Admission Webhooks,Kubernetes 1.9 提供了另一個 Beta 版功能,可在物件建立或更新之前驗證物件。從 1.9 開始,這些 webhook 也允許物件變更 (例如,設定預設值或注入值)。當然,webhook 也適用於 CRD。此外,webhook 可用於實作不容易使用 CRD 驗證表示的驗證。請注意,webhook 比 CRD 驗證更難實作,因此對於許多目的而言,CRD 驗證是正確的工具。
建立命名空間 Informer
通常,控制器中會處理一個命名空間中或僅具有特定標籤的物件。Informer 現在允許 您調整 ListOptions,用於查詢 API 伺服器以列出和監看物件。未初始化的物件 (供 初始化程式 使用) 可以透過將 IncludeUnitialized 設定為 true 來使其可見。所有這些都可以使用共用 Informer 的新 NewFilteredSharedInformerFactory 建構函式來完成
import “k8s.io/client-go/informers”
...
sharedInformers := informers.NewFilteredSharedInformerFactory(
client,
30\*time.Minute,
“some-namespace”,
func(opt \*metav1.ListOptions) {
opt.LabelSelector = “foo=bar”
},
)
請注意,對應的 Lister 將只知道符合命名空間和給定 ListOptions 的物件。請注意,相同的限制適用於用戶端上的 List 或 Watch 呼叫。
cert-manager 的此 生產程式碼範例 示範了如何在實際程式碼中使用命名空間 Informer。
多型縮放用戶端
從歷史上看,只有 extensions API 群組中的類型才能與自動產生的 Scale 用戶端搭配運作。此外,不同的 API 群組針對其 /scale 子資源使用不同的 Scale 類型。為了補救這些問題,k8s.io/client-go/scale 提供了一個 多型縮放用戶端,以一致的方式縮放不同 API 群組中的不同資源
import (
apimeta "k8s.io/apimachinery/pkg/api/meta"
discocache "k8s.io/client-go/discovery/cached"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
“k8s.io/client-go/scale”
)
...
cachedDiscovery := discocache.NewMemCacheClient(client.Discovery())
restMapper := discovery.NewDeferredDiscoveryRESTMapper(
cachedDiscovery,
apimeta.InterfacesForUnstructured,
)
scaleKindResolver := scale.NewDiscoveryScaleKindResolver(
client.Discovery(),
)
scaleClient, err := scale.NewForConfig(
client, restMapper,
dynamic.LegacyAPIPathResolverFunc,
scaleKindResolver,
)
scale, err := scaleClient.Scales("default").Get(groupResource, "foo")
傳回的縮放物件是通用的,並公開為 autoscaling/v1.Scale 物件。它由內部 Scale 類型支援,並定義了與支援縮放的 API 群組中所有特殊 Scale 類型之間的轉換。我們計畫在 1.10 中將其擴展到 CustomResources。
如果您正在實作對縮放子資源的支援,我們建議您公開 autoscaling/v1.Scale 物件。
類型安全 DeepCopy
深度複製物件以前需要呼叫 Scheme.Copy(Object),但顯著的缺點是會失去類型安全。來自 client-go 版本 5 的典型程式碼片段需要類型轉換
newObj, err := runtime.NewScheme().Copy(node)
if err != nil {
return fmt.Errorf("failed to copy node %v: %s”, node, err)
}
newNode, ok := newObj.(\*v1.Node)
if !ok {
return fmt.Errorf("failed to type-assert node %v", newObj)
}
感謝 k8s.io/code-generator,Copy 現在已被每個物件上的類型安全 DeepCopy 方法取代,讓您可以在數量和 API 錯誤介面方面顯著簡化程式碼
newNode := node.DeepCopy()
不需要錯誤處理:此呼叫永遠不會失敗。只有在節點為 nil 時,DeepCopy() 才會傳回 nil。
為了複製 runtime.Objects,runtime.Object 介面中有一個額外的 DeepCopyObject() 方法。
由於舊方法已不復存在,用戶端需要相應地更新其複製調用。
程式碼產生和 CustomResources
不建議使用 client-go 的動態用戶端來存取 CustomResources,並且已被使用 k8s.io/code-generator 中的產生器的類型安全程式碼取代。查看 Open Shift 部落格上的深度剖析,以了解如何將程式碼產生與 client-go 搭配使用。
註解區塊
您現在可以將標籤放在類型或函數正上方的註解區塊中,或放在上方的第二個區塊中。這兩個註解區塊之間不再有區別。這曾經是使用產生器時的細微錯誤來源
// second block above
// +k8s:some-tag
// first block above
// +k8s:another-tag
type Foo struct {}
自訂用戶端方法
您現在可以使用擴充標籤定義來建立自訂動詞。這讓您可以擴展超出 HTTP 定義的動詞。這為更高等級的自訂開啟了大門。
例如,此區塊會導致產生方法 UpdateScale(s *autoscaling.Scale) (*autoscaling.Scale, error)
// genclient:method=UpdateScale,verb=update,subresource=scale,input=k8s.io/kubernetes/pkg/apis/autoscaling.Scale,result=k8s.io/kubernetes/pkg/apis/autoscaling.Scale
解決 Golang 命名衝突
在更複雜的 API 群組中,Kind、群組名稱、Go 套件名稱和 Go 群組別名名稱可能會衝突。在 1.9 之前,這沒有得到正確處理。以下標籤解決了命名衝突,並使產生的程式碼更美觀
// +groupName=example2.example.com
// +groupGoName=SecondExample
這些通常位於 API 套件的 doc.go 檔案中。第一個用作 CustomResource 群組名稱,以 RESTful 方式透過 HTTP 與 API 伺服器對話時使用。第二個用於產生的 Golang 程式碼中 (例如,在 clientset 中) 以存取群組版本
clientset.SecondExampleV1()
最後,可以在 Go 套件名稱中使用點。在本節的範例中,您會將 groupName 片段放入專案的 pkg/apis/example2.example.com 目錄中。
範例專案
Kubernetes 1.9 包含許多範例專案,可以作為您自己專案的藍圖
- k8s.io/sample-apiserver 是一個簡單的使用者提供的 API 伺服器,透過 API 聚合 整合到叢集中。
- k8s.io/sample-controller 是一個全功能的 控制器 (也稱為運算子),具有共用 Informer 和工作佇列,用於處理已建立、已變更或已刪除的物件。它基於 CustomResourceDefinitions,並使用 k8s.io/code-generator 來產生 deepcopy 函數、類型化 clientset、Informer 和 Lister。
供應商化
為了從先前的版本 5 更新到 client-go 的版本 6,必須更新程式庫本身以及某些協力廠商相依性。先前,此過程很麻煩,因為在跨版本發布的現有套件佈局中,有許多程式碼被重構或重新定位。幸運的是,在最新版本中,需要移動的程式碼少得多,這應該可以簡化大多數使用者的升級程序。
已發布儲存庫的狀態
過去,k8s.io/client-go、k8s.io/api 和 k8s.io/apimachinery 不常更新。標籤 (例如,v4.0.0) 是在 Kubernetes 發布後相當長一段時間才建立的。隨著 1.9 版本發布,我們恢復執行每晚機器人,以更新所有儲存庫以供公開使用,甚至在手動標記之前也是如此。這包括分支
- master
- release-1.8 / release-5.0
- release-1.9 / release-6.0 Kubernetes 標籤 (例如,v1.9.1-beta1) 也會自動套用至已發布的儲存庫,並加上 kubernetes- 前綴 (例如,kubernetes-1.9.1-beta1)。
這些標籤的測試涵蓋範圍有限,但可以供 client-go 和其他程式庫的早期採用者使用。此外,它們有助於供應商化正確版本的 k8s.io/api 和 k8s.io/apimachinery。請注意,我們僅在 k8s.io/client-go 上建立類似 v6.0.3 的語意版本標籤。k8s.io/api 和 k8s.io/apimachinery 的對應標籤是 kubernetes-1.9.3。
另請注意,只有這些標籤對應於 Kubernetes 的已測試版本。如果您依賴發布分支,例如 release-1.9,則您的用戶端正在未發布的 Kubernetes 程式碼上執行。
client-go 供應商化的狀態
一般而言,要供應商化的相依性清單會自動產生並寫入檔案 Godeps/Godeps.json。只有其中列出的修訂版本經過測試。這尤其表示我們不會且無法根據相依性的 master 分支測試程式碼庫。這使我們處於以下情況,具體取決於使用的供應商化工具
- godep 透過在您的 GOPATH 中從 k8s.io/client-go 執行 godep restore 來讀取 Godeps/Godeps.json。然後使用 godep save 在您的專案中供應商化。godep 將從您的 GOPATH 中選擇正確的版本。
- glide 會從其相依性 (包括來自 k8s.io/client-go 的相依性) 自動讀取 Godeps/Godeps.json,無論是在 init 還是 update 時。因此,只要沒有衝突,glide 應該幾乎是自動的。
- dep 目前不會以一致的方式尊重 Godeps/Godeps.json,尤其是在更新時。手動將 client-go 相依性指定為限制或覆寫至關重要,對於非 k8s.io/* 相依性也是如此。如果沒有這些,dep 只會選擇相依性 master 分支,這可能會導致問題,因為它們會頻繁更新。
- Kubernetes 和 golang/dep 社群意識到這些問題 [issue #1124、issue #1236],並且 正在共同努力尋找解決方案。在此之前,必須特別注意。請參閱 client-go 的 INSTALL.md 以取得更多詳細資訊。
更新相依性 – golang/dep
即使 golang/dep 今天存在缺陷,dep 仍正慢慢成為 Go 生態系統中的事實標準。透過必要的注意和對遺失功能的了解,dep 可以 (並且正在!) 成功使用。以下是如何使用 dep 將具有 client-go 5 的專案更新到最新版本 6 的示範
(如果您仍在執行 client-go 版本 4,並且想要安全地進行而不跳過版本,現在是查看 這篇優秀的部落格文章 的好時機,該文章描述了如何升級到版本 5,由我們在 Heptio 的朋友彙編。)
在開始之前,務必了解 client-go 相依於另外兩個 Kubernetes 專案:k8s.io/apimachinery 和 k8s.io/api。此外,如果您正在使用 CRD,您可能也相依於 k8s.io/apiextensions-apiserver 以取得 CRD 用戶端。第一個公開了較低層級的 API 機制 (例如結構描述、序列化和類型轉換),第二個包含 API 定義,第三個提供與 CustomResourceDefinitions 相關的 API。為了使 client-go 能夠正確運作,它需要使其配套程式庫以相應的匹配版本供應商化。每個程式庫儲存庫都提供一個名為 release-<version> 的分支,其中 <version> 指的是特定的 Kubernetes 版本;對於 client-go 版本 6,必須參考每個儲存庫上的 release-1.9 分支。
假設透過 dep 供應商化了 client-go 最新版本 5 修補程式版本,則 Gopkg.toml 清單檔應如下所示 (可能使用分支而不是版本)
[[constraint]]
name = "k8s.io/api"
version = "kubernetes-1.8.1"
[[constraint]]
name = "k8s.io/apimachinery"
version = "kubernetes-1.8.1"
[[constraint]]
name = "k8s.io/apiextensions-apiserver"
version = "kubernetes-1.8.1"
[[constraint]]
name = "k8s.io/client-go"
version = "5.0.1"
請注意,如果用戶端實際上不需要某些程式庫,則這些程式庫可能會遺失。
升級到 client-go 版本 6 表示要提升版本和標籤識別碼,如下所示 (強調)。
[constraint]]
name = "k8s.io/api"
version = "kubernetes-1.9.0"
[[constraint]]
name = "k8s.io/apimachinery"
version = "kubernetes-1.9.0"
[[constraint]]
name = "k8s.io/apiextensions-apiserver"
version = "kubernetes-1.9.0"
[[constraint]]
name = "k8s.io/client-go"
version = "6.0.0"
可以在 此處 找到升級結果。
注意事項:如上所述,dep 無法以可靠且可重現的方式擷取完整的相依性集。這表示對於 100% 未來萬無一失的專案,您必須將限制 (甚至覆寫) 新增到 client-go 的 Godeps/Godeps.json 中列出的許多其他套件。如果發生任何問題,請準備好新增它們。我們正在與 golang/dep 社群合作,以使這變得更輕鬆、更順暢的體驗。
最後,我們需要告知 dep 透過執行 dep ensure 升級到指定的版本。如果一切順利,命令調用的輸出應為空,唯一的成功跡象是 vendor 資料夾內的一些已更新檔案。
如果您正在使用 CRD,您可能也會使用程式碼產生。以下 Gopkg.toml 區塊會將所需的程式碼產生套件新增到您的專案
required = [
"k8s.io/code-generator/cmd/client-gen",
"k8s.io/code-generator/cmd/conversion-gen",
"k8s.io/code-generator/cmd/deepcopy-gen",
"k8s.io/code-generator/cmd/defaulter-gen",
"k8s.io/code-generator/cmd/informer-gen",
"k8s.io/code-generator/cmd/lister-gen",
]
[[constraint]]
branch = "kubernetes-1.9.0"
name = "k8s.io/code-generator"
您是否也希望透過 dep 刪除不需要的套件 (例如測試檔案) 或在此時將變更提交到 VCS 取決於您 - 但從升級的角度來看,您現在應該已準備好透過 client-go 利用 Kubernetes 1.9 帶來的全部新功能。