<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用 Clientset 獲取 Kubernetes 資源對象

          共 10500字,需瀏覽 21分鐘

           ·

          2020-07-28 17:27

          ?

          本節(jié)主要講解 Kubernetes 核心的資源類型 Scheme 的定義以及如何使用 Clientset 來獲取集群資源對象。

          介紹

          當(dāng)我們操作資源和 apiserver 進行通信的時候,需要根據(jù)資源對象類型的 Group、Version、Kind 以及規(guī)范定義、編解碼等內(nèi)容構(gòu)成 Scheme 類型,然后 Clientset 對象就可以來訪問和操作這些資源類型了,Scheme 的定義主要在 api 子項目之中,源碼倉庫地址: https://github.com/kubernetes/api ,被同步到 Kubernetes 源碼的 staging/src/k8s.io/api 之下。

          主要就是各種資源對象的原始結(jié)構(gòu)體定義,比如查看 apps/v1 目錄下面的定義:

          $ tree staging/src/k8s.io/api/apps/v1staging/src/k8s.io/api/apps/v1├── BUILD├── doc.go├── generated.pb.go├── generated.proto├── register.go├── types.go├── types_swagger_doc_generated.go└── zz_generated.deepcopy.go
          0 directories, 8 files

          types.go 文件

          其中 types.go 文件里面就是 apps/v1 這個 GroupVersion 下面所有的資源對象的定義,有 Deployment、DaemonSet、StatefulSet、ReplicaSet 等幾個資源對象,比如 Deployment 的結(jié)構(gòu)體定義如下所示:

          20d5834ae60f358826ed93dde3814fde.webp

          TypeMetaObjectMeta、DeploymentSpec 以及 DeploymentStatus 4個屬性組成,和我們使用 YAML 文件定義的 Deployment 資源對象也是對應(yīng)的。

          apiVersion: apps/v1kind: Deploymentmetadata:  name:  nginx-deploy  namespace: defaultspec:  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - name: nginx        image: nginx        ports:        - containerPort: 80

          其中 apiVersionkind 就是 TypeMeta 屬性,metadata 屬性就是 ObjectMeta,spec 屬性就是 DeploymentSpec,當(dāng)資源部署過后也會包含一個 status 的屬性,也就是 DeploymentStatus ,這樣就完整的描述了一個資源對象的模型。

          zz_generated.deepcopy.go 文件

          上面定義的規(guī)范在 Kubernetes 中稱為資源類型 Scheme,此外zz_generated.deepcopy.go 文件是由 deepcopy-gen 工具創(chuàng)建的定義各資源類型 DeepCopyObject() 方法的文件,所有注冊到 Scheme 的資源類型都要實現(xiàn) runtime.Object 接口:

          // staging/src/k8s.io/apimachinery/pkg/runtime/interface.gotype Object interface {  GetObjectKind() schema.ObjectKind  DeepCopyObject() Object}

          而所有的資源類型都包含一個 TypeMeta 類型,而該類型實現(xiàn)了 GetObjectKind() 方法,所以各資源類型只需要實現(xiàn) DeepCopyObject() 方法即可:

          // staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/meta.gofunc (obj *TypeMeta) GetObjectKind() schema.ObjectKind { return obj }

          各個資源類型的 DeepCopyObject() 方法也不是手動定義,而是使用 deepcopy-gen 工具命令統(tǒng)一自動生成的,該工具會讀取 types.go 文件中的 +k8s:deepcopy-gen 注釋,以 Deployment 為例:

          // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
          // Deployment enables declarative updates for Pods and ReplicaSets.type Deployment struct {......}

          然后將自動生成的代碼保存到 zz_generated.deepcopy.go 文件中。

          register.go 文件

          register.go 文件的主要作用是定義 AddToScheme 函數(shù),將各種資源類型注冊到 Clientset 使用的 ?Scheme 對象中去,由于每個資源自動生成了 DeepCopyObject() 方法,這樣資源就實現(xiàn)了 runtime.Object 接口,所以可以注冊到 Scheme 中去了。

          // staging/src/k8s.io/api/apps/v1/register.govar (  // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.  // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.  SchemeBuilder      = runtime.NewSchemeBuilder(addKnownTypes)  localSchemeBuilder = &SchemeBuilder  // 對外暴露的 AddToScheme 方法用于注冊該 Group/Verion 下的所有資源類型  AddToScheme        = localSchemeBuilder.AddToScheme)
          // staging/src/k8s.io/client-go/kubernetes/scheme/register.go// 新建一個 Scheme,將各類資源對象都添加到該 Schemevar Scheme = runtime.NewScheme()// 為 Scheme 中的所有類型創(chuàng)建一個編解碼工廠var Codecs = serializer.NewCodecFactory(Scheme)// 為 Scheme 中的所有類型創(chuàng)建一個參數(shù)編解碼工廠var ParameterCodec = runtime.NewParameterCodec(Scheme)// 將各 k8s.io/api// 目錄下資源類型的 AddToScheme() 方法注冊到 SchemeBuilder 中var localSchemeBuilder = runtime.SchemeBuilder{ ...... appsv1.AddToScheme, appsv1beta1.AddToScheme, appsv1beta2.AddToScheme, ......}
          var AddToScheme = localSchemeBuilder.AddToScheme
          func init() { v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) // 調(diào)用 SchemeBuilder 中各資源對象的 AddToScheme() 方法,將它們注冊到到 Scheme 對象 utilruntime.Must(AddToScheme(Scheme))}

          將各類資源類型注冊到全局的 Scheme 對象中,這樣 Clientset 就可以識別和使用它們了,那么我們應(yīng)該如何使用 Clientset 呢?

          示例

          首先我們來看下如何通過 Clientset 來獲取資源對象,我們這里來創(chuàng)建一個 Clientset 對象,然后通過該對象來獲取默認命名空間之下的 Deployments 列表,代碼如下所示:

          package main
          import ( "flag" "fmt" "os" "path/filepath"
          metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd")
          func main() { var err error var config *rest.Config var kubeconfig *string
          if home := homeDir(); home != "" { kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file") } else { kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file") } flag.Parse()
          // 使用 ServiceAccount 創(chuàng)建集群配置(InCluster模式) if config, err = rest.InClusterConfig(); err != nil { // 使用 KubeConfig 文件創(chuàng)建集群配置 if config, err = clientcmd.BuildConfigFromFlags("", *kubeconfig); err != nil { panic(err.Error()) } }
          // 創(chuàng)建 clientset clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err.Error()) }
          // 使用 clientsent 獲取 Deployments deployments, err := clientset.AppsV1().Deployments("default").List(metav1.ListOptions{}) if err != nil { panic(err) } for idx, deploy := range deployments.Items { fmt.Printf("%d -> %s\n", idx+1, deploy.Name) }
          }
          func homeDir() string { if h := os.Getenv("HOME"); h != "" { return h } return os.Getenv("USERPROFILE") // windows}

          上面的代碼運行可以獲得 default 命名空間之下的 Deployments:

          $ go run main.go1 -> details-v12 -> el-gitlab-listener3 -> nginx4 -> productpage-v15 -> ratings-v16 -> reviews-v17 -> reviews-v28 -> reviews-v3

          這是一個非常典型的訪問 Kubernetes 集群資源的方式,通過 client-go 提供的 Clientset 對象來獲取資源數(shù)據(jù),主要有以下三個步驟:

          1. 使用 kubeconfig 文件或者 ServiceAccount(InCluster 模式)來創(chuàng)建訪問 Kubernetes API 的 Restful 配置參數(shù),也就是代碼中的 rest.Config 對象

          1. 使用 rest.Config 參數(shù)創(chuàng)建 Clientset 對象,這一步非常簡單,直接調(diào)用 kubernetes.NewForConfig(config) 即可初始化

          1. 然后是 Clientset 對象的方法去獲取各個 Group 下面的對應(yīng)資源對象進行 CRUD 操作

          Clientset 對象

          上面我們了解了如何使用 Clientset 對象來獲取集群資源,接下來我們來分析下 Clientset 對象的實現(xiàn)。

          上面我們使用的 Clientset 實際上是對各種資源類型的 Clientset 的一次封裝:

          // staging/src/k8s.io/client-go/kubernetes/clientset.go
          // NewForConfig 使用給定的 config 創(chuàng)建一個新的 Clientsetfunc NewForConfig(c *rest.Config) (*Clientset, error) { configShallowCopy := *c if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) } var cs Clientset var err error cs.admissionregistrationV1beta1, err = admissionregistrationv1beta1.NewForConfig(&configShallowCopy) if err != nil { return nil, err } // 將其他 Group 和版本的資源的 RESTClient 封裝到全局的 Clientset 對象中 cs.appsV1, err = appsv1.NewForConfig(&configShallowCopy) if err != nil { return nil, err } ...... cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) if err != nil { return nil, err } return &cs, nil}

          上面的 NewForConfig 函數(shù)里面就是將其他的各種資源的 RESTClient 封裝到了全局的 Clientset 中,這樣當(dāng)我們需要訪問某個資源的時候只需要使用 Clientset 里面包裝的屬性即可,比如 clientset.CoreV1() 就是訪問 Core 這個 Group 下面 v1 這個版本的 RESTClient。這些局部的 RESTClient 都定義在 staging/src/k8s.io/client-go/typed///_client.go 文件中,比如 staging/src/k8s.io/client-go/kubernetes/typed/apps/v1/apps_client.go 這個文件中就是定義的 apps 這個 Group 下面的 v1 版本的 RESTClient,這里同樣以 Deployment 為例:

          // staging/src/k8s.io/client-go/kubernetes/typed/apps/v1/apps_client.go// NewForConfig 根據(jù) rest.Config 創(chuàng)建一個 AppsV1Clientfunc NewForConfig(c *rest.Config) (*AppsV1Client, error) {  config := *c  // 為 rest.Config 設(shè)置資源對象默認的參數(shù)  if err := setConfigDefaults(&config); err != nil {    return nil, err  }  // 實例化 AppsV1Client 的 RestClient  client, err := rest.RESTClientFor(&config)  if err != nil {    return nil, err  }  return &AppsV1Client{client}, nil}
          func setConfigDefaults(config *rest.Config) error { // 資源對象的 GroupVersion gv := v1.SchemeGroupVersion config.GroupVersion = &gv // 資源對象的 root path config.APIPath = "/apis" // 使用注冊的資源類型 Scheme 對請求和響應(yīng)進行編解碼,Scheme 就是前文中分析的資源類型的規(guī)范 config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
          if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() }
          return nil}
          func (c *AppsV1Client) Deployments(namespace string) DeploymentInterface { return newDeployments(c, namespace)}
          // staging/src/k8s.io/client-go/kubernetes/typed/apps/v1/deployment.go// deployments 實現(xiàn)了 DeploymentInterface 接口type deployments struct { client rest.Interface ns string}
          // newDeployments 實例化 deployments 對象func newDeployments(c *AppsV1Client, namespace string) *deployments { return &deployments{ client: c.RESTClient(), ns: namespace, }}

          通過上面代碼我們就可以很清晰的知道可以通過 clientset.AppsV1().Deployments("default")來獲取一個 deployments 對象,然后該對象下面定義了 deployments 對象的 CRUD 操作,比如我們調(diào)用的 List() 函數(shù):

          // staging/src/k8s.io/client-go/kubernetes/typed/apps/v1/deployment.go
          func (c *deployments) List(opts metav1.ListOptions) (result *v1.DeploymentList, err error) { var timeout time.Duration if opts.TimeoutSeconds != nil { timeout = time.Duration(*opts.TimeoutSeconds) * time.Second } result = &v1.DeploymentList{} err = c.client.Get(). Namespace(c.ns). Resource("deployments"). VersionedParams(&opts, scheme.ParameterCodec). Timeout(timeout). Do(). Into(result) return}

          從上面代碼可以看出最終是通過 c.client 去發(fā)起的請求,也就是局部的 restClient 初始化的函數(shù)中通過 rest.RESTClientFor(&config) 創(chuàng)建的對象,也就是將 rest.Config 對象轉(zhuǎn)換成一個 Restful 的 Client 對象用于網(wǎng)絡(luò)操作:

          // staging/src/k8s.io/client-go/rest/config.go
          // RESTClientFor 返回一個滿足客戶端 Config 對象上的屬性的 RESTClient 對象。// 注意在初始化客戶端的時候,RESTClient 可能需要一些可選的屬性。func RESTClientFor(config *Config) (*RESTClient, error) { if config.GroupVersion == nil { return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient") } if config.NegotiatedSerializer == nil { return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient") } qps := config.QPS if config.QPS == 0.0 { qps = DefaultQPS } burst := config.Burst if config.Burst == 0 { burst = DefaultBurst }
          baseURL, versionedAPIPath, err := defaultServerUrlFor(config) if err != nil { return nil, err }
          transport, err := TransportFor(config) if err != nil { return nil, err } // 初始化一個 HTTP Client 對象 var httpClient *http.Client if transport != http.DefaultTransport { httpClient = &http.Client{Transport: transport} if config.Timeout > 0 { httpClient.Timeout = config.Timeout } }
          return NewRESTClient(baseURL, versionedAPIPath, config.ContentConfig, qps, burst, config.RateLimiter, httpClient)}

          到這里我們就知道了 Clientset 是基于 RESTClient 的,RESTClient 是底層的用于網(wǎng)絡(luò)請求的對象,可以直接通過 RESTClient 提供的 RESTful 方法如 Get()、Put()、Post()、Delete() 等和 APIServer 進行交互:

          • 同時支持 JSON 和 protobuf 兩種序列化方式

          • 支持所有原生資源

          但實際上除了常用的 CRUD 操作之外,我們還可以進行 Watch 操作,可以監(jiān)聽資源對象的增、刪、改、查操作,這樣我們就可以根據(jù)自己的業(yè)務(wù)邏輯去處理這些數(shù)據(jù)了,但是實際上也并不建議這樣使用,因為往往由于集群中的資源較多,我們需要自己在客戶端去維護一套緩存,而這個維護成本也是非常大的,為此 client-go 也提供了自己的實現(xiàn)機制,那就是 Informers。Informers 是這個事件接口和帶索引查找功能的內(nèi)存緩存的組合,這樣也是目前最常用的用法。Informers 第一次被調(diào)用的時候會首先在客戶端調(diào)用 List 來獲取全量的對象集合,然后通過 Watch 來獲取增量的對象更新緩存,這個我們后續(xù)在講解。




          K8S進階訓(xùn)練營,點擊下方圖片了解詳情

          1a0458e81038f61a90d0d612383eadf2.webp

          瀏覽 27
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲日韩免费视频 | 男女操逼视频免费看 | 日韩精品一区二区三区在线观看 | 亚洲无码专区区免费 | 大香蕉婷婷丁香五月 |