kubectl 在干什么

1,251 阅读6分钟

本文预设了几个前提,会关注到kubectl在干什么的同学,基本上已经对k8s有了一定的了解,至少也是有使用kubectl探索过k8s的一些基础的功能。

img

上面是一张k8s核心组件的架构图,由图可知 kubectl只与apiserver打交道。探索kubectl在干什么约等于探索apiserver提供了什么。

那么我们先来看一眼apiserver大致提供了什么。

kubectl proxy
Starting to serve on 127.0.0.1:8001

上面这一条命令是在kubectl可执行的node节点,开启一个 apiserver的proxy,可以免鉴权直接通过curl方式访问apiserver。

 curl  http://127.0.0.1:8001/
{
  "paths": [
    "/api",
    "/api/v1",
    "/apis",
    "...",
    "/apis/apiextensions.k8s.io/v1",
    "/healthz",
    "/...",
    "/healthz/poststarthook/apiservice-openapi-controller",
    "...",
    "/healthz/poststarthook/start-kube-apiserver-admission-initializer",
    "/livez",
    "/...",
    "/livez/poststarthook/start-kube-apiserver-admission-initializer",
    "/logs",
    "/metrics",
    "/openapi/v2",
    "/readyz",
    "....",
    "/readyz/shutdown",
    "/version"
  ]
}

直接通过 curl 访问 apiserver的根路径,可以看到打印了一个可访问的path路径列表。大致归类可以分为:

  1. api/v1
  2. apis/*
  3. healthz/*
  4. livez/*
  5. logs
  6. metrics
  7. Openapi/v2
  8. readyz
  9. Version

再次归类整理为:

  1. api resource 相关接口
    1. api/*
    2. apis/*
  2. openapi
  3. 检查类接口
    1. 健康检查 /healthz/*
    2. 存活检查 /livez/*
    3. 就绪检查 /readyz/*
  4. 其他
    1. 日志
    2. 指标
    3. 版本

根据上述的接口归类来看,主要提供业务逻辑相处理的接口是 api resource 相关的接口。

根据 kubernetes/staging/src/k8s.io/client-go/discovery/discovery-cient.go:482

// NewDiscoveryClientForConfig creates a new DiscoveryClient for the given config. This client
// can be used to discover supported resources in the API server.
func NewDiscoveryClientForConfig(c *restclient.Config) (*DiscoveryClient, error) {
	config := *c
	if err := setDiscoveryDefaults(&config); err != nil {
		return nil, err
	}
	client, err := restclient.UnversionedRESTClientFor(&config)
	return &DiscoveryClient{restClient: client, LegacyPrefix: "/api"}, err
}

kubernetes/staging/src/k8s.io/client-go/discovery/discovery-cient.go:192

// ServerResourcesForGroupVersion returns the supported resources for a group and version.
func (d *DiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (resources *metav1.APIResourceList, err error) {
	url := url.URL{}
	if len(groupVersion) == 0 {
		return nil, fmt.Errorf("groupVersion shouldn't be empty")
	}
	if len(d.LegacyPrefix) > 0 && groupVersion == "v1" {
		url.Path = d.LegacyPrefix + "/" + groupVersion
	} else {
		url.Path = "/apis/" + groupVersion
	}
	resources = &metav1.APIResourceList{
		GroupVersion: groupVersion,
	}
	err = d.restClient.Get().AbsPath(url.String()).Do(context.TODO()).Into(resources)
	if err != nil {
		// ignore 403 or 404 error to be compatible with an v1.0 server.
		if groupVersion == "v1" && (errors.IsNotFound(err) || errors.IsForbidden(err)) {
			return resources, nil
		}
		return nil, err
	}
	return resources, nil
}

的代码逻辑来看,api/v1这样的 api resource list 属于历史遗留问题。

/api/v1/apis/apiextensions.k8s.io/v1 这样的接口返回的都是APIResourceList类型的对象。

namespaced的resource基本上都是有这样的 路径规范组成。而api/v1属于缺少了group字段的情形。

以/api/v1/pod接口为例,分析针对单个资源的接口组成

    {
      "name": "pods",
      "singularName": "",
      "namespaced": true,
      "kind": "Pod",
      "verbs": [
        "create",
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "update",
        "watch"
      ],
      "shortNames": [
        "po"
      ],
      "categories": [
        "all"
      ],
      "storageVersionHash": "xPOwRZ+Yhw8="
    },
    ...
      "name": "pods/status",
      "singularName": "",
      "namespaced": true,
      "kind": "Pod",
      "verbs": [
        "get",
        "patch",
        "update"
      ]
    },

一个重要的字段就是namespaced,当这个字段为true时,说明这个资源对象是受namespace隔离的资源,大多数操作都需要指定namespace才可以执行。

针对某一类资源的动作列表可以在 verbs 字段中获取

  1. create // 创建
  2. delete // 删除
  3. deletecollection // 删除集合(?)
  4. get // 获取单个资源
  5. list // 获取资源列表
  6. patch // 以补丁数据形式修改数据
  7. update // 以覆盖形式修改数据
  8. watch // 订阅资源列表数据变化

上述针对pods资源的verbs对应在openapi中的描述则为

以及针对单个资源除了 增删改查以外的其他动作可由 `pods/status这样的接口获取。对应的openapi中的描述则为

另外,通常还会有一个跨ns查询的一个列表接口

kubectl

执行 kubectl options可以看到有一个参数-v,可以设置日志打印的级别

-v, --v=0: number for the log level verbosity

-v 7 可以看到所有请求apiserver的http相关日志,如

I0221 12:40:50.992381   34765 cached_discovery.go:78] skipped caching discovery info due to the server is currently unable to handle the request
I0221 12:40:50.993222   34765 round_trippers.go:420] GET http://localhost:8666/cluster/preee/api/v1/namespaces/default/services?limit=500
I0221 12:40:50.993232   34765 round_trippers.go:427] Request Headers:
I0221 12:40:50.993236   34765 round_trippers.go:431]     Accept: application/json;as=Table;v=v1;g=meta.k8s.io,application/json;as=Table;v=v1beta1;g=meta.k8s.io,application/json
I0221 12:40:50.993240   34765 round_trippers.go:431]     User-Agent: kubectl/v1.18.2 (darwin/amd64) kubernetes/52c56ce
I0221 12:40:51.048394   34765 round_trippers.go:446] Response Status: 200 OK in 55 milliseconds
NAME         TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)         AGE
kube-user    NodePort    192.168.255.192   <none>        443:31524/TCP   324d
kubernetes   ClusterIP   192.168.255.1     <none>        443/TCP         338d

第一次请求或者删除掉 ~/.kube/cache 目录后执行 kubectl get ns

INFO[0014] GET/api                                      
INFO[0015] GET/apis                                     
INFO[0015] GET/apis/events.k8s.io/v1beta1               
INFO[0015] GET/apis/scheduling.k8s.io/v1                
INFO[0015] GET/apis/coordination.k8s.io/v1              
...
INFO[0015] GET/api/v1                                   
INFO[0015] GET/apis/extensions/v1beta1                  
...           
INFO[0018] GET/api/v1/namespaces 

第二次执行 kubectl get ns

INFO[0085] GET/api/v1/namespaces 

可以得出第一个结论, kubectl 在执行第一次请求时,会缓存所有的api-resource 字段以便后续命令执行时进行相关请求拼装等

kubectl get po

/api/v1/namespaces/default/pods

kubectl get po -A

/api/v1/pods

/apis/apps/v1/namespaces/kube-system/deployments?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue&limit=500

INFO[0007] GET/apis/apps/v1/namespaces/kube-system/deployments?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue&limit=500

get all 命令会分别获取

  1. pods
  2. replicationcontrollers
  3. services
  4. daemonsets
  5. deployments
  6. replicasets
  7. statefulsets
  8. horizontalpodautoscalers
  9. jobs
  10. cronjobs

kubectl get all -A -l kubernetes.io/cluster-service=true

INFO[0064] GET/api/v1/pods?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue&limit=500 
INFO[0064] GET/api/v1/replicationcontrollers?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue&limit=500 
INFO[0064] GET/api/v1/services?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue&limit=500 
INFO[0064] GET/apis/apps/v1/daemonsets?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue&limit=500 
INFO[0064] GET/apis/apps/v1/deployments?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue&limit=500 
INFO[0064] GET/apis/apps/v1/replicasets?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue&limit=500 
INFO[0065] GET/apis/apps/v1/statefulsets?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue&limit=500 
INFO[0065] GET/apis/autoscaling/v1/horizontalpodautoscalers?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue&limit=500 
INFO[0065] GET/apis/batch/v1/jobs?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue&limit=500 
INFO[0065] GET/apis/batch/v1beta1/cronjobs?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue&limit=500

describe deploy

  1. deployments
  2. events
  3. replicasets

kubectl -nkube-system describe deploy coredns

INFO[0651] GET/apis/apps/v1/namespaces/kube-system/deployments/coredns? 
INFO[0651] GET/api/v1/namespaces/kube-system/events?fieldSelector=involvedObject.name%3Dcoredns%2CinvolvedObject.namespace%3Dkube-system%2CinvolvedObject.kind%3DDeployment%2CinvolvedObject.uid%3D1aa51196-7468-4aba-b26f-5908a50c31a7 
INFO[0651] GET/apis/apps/v1/namespaces/kube-system/replicasets?labelSelector=k8s-app%3Dkube-dns 

describe po

  1. pods
  2. Events

kubectl -nkube-system describe po coredns coredns-5488fc95f4-5jr6l

INFO[0840] GET/api/v1/namespaces/kube-system/pods/coredns-5488fc95f4-5jr6l? 
INFO[0840] GET/api/v1/namespaces/kube-system/pods/coredns-5488fc95f4-5jr6l? 
INFO[0840] GET/api/v1/namespaces/kube-system/events?fieldSelector=involvedObject.name%3Dcoredns-5488fc95f4-5jr6l%2CinvolvedObject.namespace%3Dkube-system%2CinvolvedObject.uid%3D3f0b3909-35ae-4c22-bfdc-5f5ddd84a9b8

describe svc

  1. services
  2. endpoints
  3. events

kubectl -nkube-system describe svc prometheus

INFO[0886] GET/api/v1/namespaces/kube-system/services/prometheus? 
INFO[0886] GET/api/v1/namespaces/kube-system/services/prometheus? 
INFO[0886] GET/api/v1/namespaces/kube-system/endpoints/prometheus? 
INFO[0886] GET/api/v1/namespaces/kube-system/events?fieldSelector=involvedObject.name%3Dprometheus%2CinvolvedObject.namespace%3Dkube-system%2CinvolvedObject.kind%3DService%2CinvolvedObject.uid%3D1c27a873-4c53-42d6-ab86-f7050a35fd6c

describe node

  1. nodes
  2. leases
  3. pods
  4. events

kubectl -nkube-system describe no vm-2-177-centos

INFO[1055] GET/api/v1/nodes/vm-2-177-centos?            
INFO[1056] GET/api/v1/nodes/vm-2-177-centos?            
INFO[1056] GET/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/vm-2-177-centos? 
INFO[1056] GET/api/v1/pods?fieldSelector=spec.nodeName%3Dvm-2-177-centos%2Cstatus.phase%21%3DFailed%2Cstatus.phase%21%3DSucceeded 
INFO[1056] GET/api/v1/events?fieldSelector=involvedObject.kind%3DNode%2CinvolvedObject.uid%3Dvm-2-177-centos%2CinvolvedObject.name%3Dvm-2-177-centos%2CinvolvedObject.namespace%3D

kubectl auth can-i get po

INFO[1516] GET/api/v1?timeout=32s                       
INFO[1516] POST/apis/authorization.k8s.io/v1/selfsubjectaccessreviews?