使用client-go来获取kube config进行尝试

最近我开始阅读k8s的代码,所以首先从看起来简单的内容开始。我会参考in-cluster-client-configuration来进行。

主要.go

阅读 README 中关于在集群内进行身份验证的部分,可以看到在 main.go 的开头有一个 rest.InClusterConfig() 的说明。
我觉得没有必要认真阅读链接。Service Account Tokens.

rest.InClusterConfig()的实现

通过查看KUBERNETES_SERVICE_HOST和KUBERNETES_SERVICE_PORT环境变量,如果当前不在集群内,则抛出错误。然后,从相关路径获取令牌并返回。使用该令牌创建client-set。

func InClusterConfig() (*Config, error) {
	const (
		tokenFile  = "/var/run/secrets/kubernetes.io/serviceaccount/token"
		rootCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
	)
	host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
	if len(host) == 0 || len(port) == 0 {
		return nil, ErrNotInCluster
	}

	token, err := os.ReadFile(tokenFile)
	if err != nil {
		return nil, err
	}
// ~~~~省略
	return &Config{
		// TODO: switch to using cluster DNS.
		Host:            "https://" + net.JoinHostPort(host, port),
		TLSClientConfig: tlsClientConfig,
		BearerToken:     string(token),
		BearerTokenFile: tokenFile,
	}, nil
}

client-setの作成

使用client-go的kubernetes.NewForConfig()函数和之前提到的令牌,创建一个对象。

NewForConfig()的实现

使用NewForConfigAndClient函数创建了Clientset结构体(省略)。
Clientset结构体中定义了一些常见的类型,如corev1,在k8s.io/client-go/kubernetes/typed一带进行了定义。

このディレクトリのcorev1の下にはpod.go, service.goなどがあり、このClientset構造体から各種APIを叩くことになりそう。

func NewForConfig(c *rest.Config) (*Clientset, error) {
	configShallowCopy := *c

	// share the transport between all clients
	httpClient, err := rest.HTTPClientFor(&configShallowCopy)
	if err != nil {
		return nil, err
	}

	return NewForConfigAndClient(&configShallowCopy, httpClient)
}

Use a “for” loop

主要是列出所有命名空间的Pod的示例和错误处理的示例。

Podのリスト

clientset.CoreV1()が呼ばれると、core_client.goのCoreV1Client構造体が返される。
Pods(“”)でrest clientやnamespaceの情報が保持される。
List()は以下のような実装。
PodListに定義されたItemsに実際のPodが入っている。

func (c *pods) List(ctx context.Context, opts metav1.ListOptions) (result *v1.PodList, err error) {
	var timeout time.Duration
	if opts.TimeoutSeconds != nil {
		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
	}
	result = &v1.PodList{}
	err = c.client.Get().
		Namespace(c.ns).
		Resource("pods").
		VersionedParams(&opts, scheme.ParameterCodec).
		Timeout(timeout).
		Do(ctx).
		Into(result)
	return
}

错误处理

如果尝试获取一个在Get中不存在的Pod,可以在apimachinery的errors.go中编写一个if语句。

_, err = clientset.CoreV1().Pods("default").Get(context.TODO(), "example-xxxxx", metav1.GetOptions{})
if errors.IsNotFound(err) {
  fmt.Printf("Pod example-xxxxx not found in default namespace\n")
} else if statusError, isStatus := err.(*errors.StatusError); isStatus {
  fmt.Printf("Error getting pod %v\n", statusError.ErrStatus.Message)
} else if err != nil {
  panic(err.Error())
} else {
  fmt.Printf("Found example-xxxxx pod in default namespace\n")
}

个人感受

serviceaccountのtoken辺りをふんわり知れた。
clientsetを作らないことには始まらない印象を持った。
apimachinery, api, client-goを行ったり来たりして迷った。
両者の違いはKubernetes APIをGoから使う最初の一歩。書かれてるような実体と補助という関係らしいがイメージはついていない。

実装を追うと大変だが、最後にmain.goを見てみるとすごくシンプルでスッキリしていると感じる。

请看以下内容。

    • https://github.com/kubernetes/client-go/tree/master/examples/in-cluster-client-configuration

 

    • https://github.com/kubernetes/kubernetes

 

    • https://github.com/kubernetes/api/tree/master

 

    https://github.com/kubernetes/apimachinery/tree/master
bannerAds