本文已發布超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

client-go 第 6 版介紹

Kubernetes API 伺服器 公開任何用戶端皆可使用的 REST 介面client-go 是 Go 程式語言的官方用戶端程式庫。Kubernetes 內部 (例如,kubectl 內部) 以及 眾多外部消費者 皆使用它:例如 etcd-operatorprometheus-operator 等運算子;KubeLessOpenShift 等更高等級的架構;以及更多。

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 中的結構描述對執行個體套用以下驗證

  1. spec.version 必須是字串,且必須是「v1.0.0」或「v1.0.1」。
  2. 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-gok8s.io/apik8s.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/apik8s.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 #1124issue #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/apimachineryk8s.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 帶來的全部新功能。