简介:云原生社区活动---Kubernetes源码剖析第一期
有幸参与云原生社区举办的Kubernetes源码剖析活动,活动主要以书籍《Kubernetes源码剖析》为主要思路进行展开,提出在看书过程当中遇到的问题,和社区成员一块儿讨论,最后会将结果总结到云原生社区的知识星球或Github。node
第一期活动主要以书本第五章<Client-go编程式交互>为主题进行学习,计划共三周半。git
计划以下:github
学习总得有个重要的优先级,我我的的优先级是这样的,仅供参考:编程
学习环境相关:api
本文主题缓存
本文是第一周,课题有两个:app
Client-go源码目录结构框架
[root@normal11 k8s-client-go]# tree . -L 1
.
├── CHANGELOG.md
├── code-of-conduct.md
├── CONTRIBUTING.md
├── discovery
├── dynamic
├── examples
├── Godeps
├── go.mod
├── go.sum
├── informers
├── INSTALL.md
├── kubernetes
├── kubernetes_test
├── LICENSE
├── listers
├── metadata
├── OWNERS
├── pkg
├── plugin
├── README.md
├── rest
├── restmapper
├── scale
├── SECURITY_CONTACTS
├── testing
├── third_party
├── tools
├── transport
└── util 学习
client-go代码库已经集成到了Kubernetes源码中,因此书本中展现的内容是在Kubernetes源码中源码结构,而这里展现的是Client-go代码库中原始的内容,因此多了一些源码以外的内容,例如README、example、go.mod等。下面讲一下各个目录的做用,内容引自书本:测试
几种Client-go客户端
下图是一个简单的总结,其中ClientSet、DynamicClient、DiscoveryClient都是基于RESTClient封装的。
RESTClient
最基础的客户端,对HTTP Request进行了封装,实现了RESTFul风格的API。
案例代码:
package main
import (
"fmt" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd"
)
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "/root/.kube/config") if err != nil { panic(err.Error()) } config.APIPath = "api" config.GroupVersion = &corev1.SchemeGroupVersion config.NegotiatedSerializer = scheme.Codecs restClient, err := rest.RESTClientFor(config) if err != nil { panic(err.Error()) } result := &corev1.NodeList{} err = restClient.Get().Namespace("").Resource("nodes").VersionedParams(&metav1.ListOptions{Limit: 100}, scheme.ParameterCodec).Do().Into(result) if err != nil { panic(err) } for _, d := range result.Items { fmt.Printf("Node Name %v n", d.Name) }
}
预期运行结果将会打印K8S集群中的node
ClientSet
对RESTClient进行了对象分类方式的封装,能够实例化特定资源的客户端,
以Resource和Version的方式暴露。例如实例化一个只操做appsv1版本的Deploy客户端,
ClientSet能够认为是一系列资源的集合客户端。缺点是不能直接访问CRD。
经过client-gen代码生成器生成带有CRD资源的ClientSet后能够访问CRD资源。(未测试)
案例代码:
package main
import (
apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd"
)
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "/root/.kube/config") if err != nil { panic(err) } clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err) } podClient := clientset.CoreV1().Pods(apiv1.NamespaceDefault) list, err := podClient.List(metav1.ListOptions{Limit: 500}) if err != nil { panic(err) } for _, d := range list.Items { if d.Name == "" { } // fmt.Printf("NAME:%v t NAME:%v t STATUS: %+vn ", d.Namespace, d.Name, d.Status) } //请求namespace为default下的deploy deploymentClient := clientset.AppsV1().Deployments(apiv1.NamespaceDefault) deployList, err2 := deploymentClient.List(metav1.ListOptions{Limit: 500}) if err2 != nil { panic(err2) } for _, d := range deployList.Items { if d.Name == "" { } // fmt.Printf("NAME:%v t NAME:%v t STATUS: %+vn ", d.Namespace, d.Name, d.Status) } // 请求ds资源 todo 有兴趣能够尝试下 // clientset.AppsV1().DaemonSets()
}
代码中分别打印了获取到K8S集群中的500个Pod和500个deploy,目前打印语句是注释了,若是要看效果须要先删掉注释。
案例代码中还留了一个小内容,请求获取daemonset资源,感兴趣的能够试一试。
DynamicClient
这是一种动态客户端,对K8S任意资源进行操做,包括CRD。
请求返回的结果是map[string]interface{}
代码案例:
package main
import (
"fmt" apiv1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/tools/clientcmd"
)
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "/root/.kube/config") if err != nil { panic(err) } dymaicClient, err := dynamic.NewForConfig(config) checkErr(err) //map[string]interface{} //TODO 获取CRD资源 这里是获取了TIDB的CRD资源 // gvr := schema.GroupVersionResource{Version: "v1alpha1", Resource: "tidbclusters", Group: "pingcap.com"} // unstructObj, err := dymaicClient.Resource(gvr).Namespace("tidb-cluster").List(metav1.ListOptions{Limit: 500}) // checkErr(err) // fmt.Println(unstructObj) gvr := schema.GroupVersionResource{Version: "v1", Resource: "pods"} unstructObj, err := dymaicClient.Resource(gvr).Namespace(apiv1.NamespaceDefault).List(metav1.ListOptions{Limit: 500}) checkErr(err) // fmt.Println(unstructObj) podList := &corev1.PodList{} err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructObj.UnstructuredContent(), podList) checkErr(err) for _, d := range podList.Items { fmt.Printf("NAME:%v t NAME:%v t STATUS: %+vn ", d.Namespace, d.Name, d.Status) }
}
func checkErr(err error) {
if err != nil { panic(err) }
}
这个案例是打印了namespace为default下的500个pod,一样的,在案例中也有一个todo,获取CRD资源,感兴趣的能够尝试一下。若是K8S集群中没有TIDB的资源能够自行换成本身想要的CRD资源。
代码中已经有获取v1alpha1版本的tidbclusters资源。若是你不知道CRD相关的信息,能够按照下面的步骤来找出对应的信息:
这样 资源的GVR(Group、Version、Resource)都有了
DiscoveryClient
这是一种发现客户端,在前面的客户端中须要知道资源的Resource和Version才能找到你想要的,
这些信息太多很难所有记住,这个客户端用于获取资源组、版本等信息。
前面用到的api-resources和api-versions都是经过discoveryClient客户端实现的, 源码在Kubernetes源码库中 pkg/kubectl/cmd/apiresources/apiresources.go pkg/kubectl/cmd/apiresources/apiversions.go
// RunAPIResources does the work
func (o APIResourceOptions) RunAPIResources(cmd cobra.Command, f cmdutil.Factory) error {
w := printers.GetNewTabWriter(o.Out) defer w.Flush() //拿到一个DiscoveryClient客户端 discoveryclient, err := f.ToDiscoveryClient() if err != nil { return err }
// RunAPIVersions does the work
func (o *APIVersionsOptions) RunAPIVersions() error {
// Always request fresh data from the server o.discoveryClient.Invalidate() //经过discoveryClient获取group相关信息 groupList, err := o.discoveryClient.ServerGroups() if err != nil { return fmt.Errorf("couldn't get available api versions from server: %v", err) }
案例代码:
获取集群中的GVR
package main
import (
"fmt" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/discovery" "k8s.io/client-go/tools/clientcmd"
)
func main() {
config, err := clientcmd.BuildConfigFromFlags("","/root/.kube/config") if err != nil { panic(err.Error()) } discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) if err != nil { panic(err.Error()) } _, APIResourceList, err := discoveryClient.ServerGroupsAndResources() if err != nil { panic(err.Error()) } for _, list := range APIResourceList { gv, err := schema.ParseGroupVersion(list.GroupVersion) if err != nil { panic(err.Error()) } for _, resource := range list.APIResources { fmt.Printf("name: %v, group: %v, version %vn", resource.Name, gv.Group, gv.Version) } }
}
预期效果:打印集群中的GVR
[root@normal11 discoveryclient]# go run main.go name: bindings, group: , version v1 name: componentstatuses, group: , version v1 name: configmaps, group: , version v1 name: endpoints, group: , version v1 ...
DiscoveryClient在请求到数据以后会缓存到本地,默认存储位置是~/.kube/cache和~/.kube/http-cache,默认是每10分钟会和API Server同步一次。
总结
第一周主要是了解下各类客户端的使用以及不一样,有时间的能够再进行一些拓展试验,研究对象能够选择一些主流的框架或官方示例,例如:
延伸阅读:
始发于 四颗咖啡豆,转载请声明出处. 关注公粽号->[四颗咖啡豆] 获取最新内容