使用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