kubernetes笔记

Posted by [Kohn] on Tuesday, May 9, 2023
Last Modified on Tuesday, April 23, 2024
本文阅读量

1 Kubectl

custom-columns

 kubectl get application -A -o "custom-columns=PRODUCT:.metadata.labels.infra\.tce\.io/oam-product"

通过curl命令访问k8s apiserver

有时候没有kubectl程序, 需要用curl来获取

TOKEN=$(kubectl get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='default')].data.token}"|base64 -d)
APISERVER=$(kubectl config view -o jsonpath="{.clusters[?(@.name==\"$CLUSTER_NAME\")].cluster.server}")
curl -X GET $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

在pod内访问k8s apiserver时, TOKEN位于 var/run/secrets/kubernetes.io/serviceaccount/token, APISERVER就是 KUBERNETES_SERVICE_HOST:KUBERNETES_SERVICE_PORT, 注意要加https:/

通过raw url来watch某个resource的事件

kubectl proxy &
curl -L -s -X GET -H "Content-Type: application/json" -H "Accept: application/json, */*"  127.0.0.1:8001/apis/k8s.test.io/v1alpha1/watch/namespaces/default/greetings

通过raw url来patch status

curl -k -s -X PATCH -H "Accept: application/json, */*" -H "Content-Type: application/merge-patch+json" 127.0.0.1:8001/apis/k8s.test.io/v1alpha1/namespaces/default/greetings/demo/status --data '{"status":{"ready":true}}'

获取某个obj的event

kubectl get event --namespace abc-namespace --field-selector involvedObject.name=my-pod-zl6m6

直接指定url查询

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/nginx_vts_server_requests_per_second"

kubectl apply

https://kubernetes.io/zh/docs/tasks/manage-kubernetes-objects/declarative-config/

kubectl apply的时候, 会参考对象的 kubectl.kubernetes.io/last-applied-configuration的值, 并且仅对上次 apply的值进行修改. 例如某字段, 创建时通过kubectl apply创建, 那下次 apply时, 不管是修改还是删除, 都会修改该字段. 而如果某字段不是kubectl apply创建, 那下次kubectl apply时, 如果apply的内容不包含这个字段, 那么不会修改该字段的值

rollout

restart

kubectl rollout restart的原理是为deployment的.Spec.Template.ObjectMeta.Annotations加上注解kubectl.kubernetes.io/restartedAt

进而触发deployment的自动重建pod的机制

2 Service

通过kubectl proxy在集群外访问service

参考: https://kubectl.docs.kubernetes.io/pages/container_debugging/proxying_traffic_to_services.html

例如:

curl "localhost:8099/api/v1/namespaces/tke/services/https:tke-registry-api:443/proxy/apis/registry.tkestack.io/v1/namespaces"

访问的是tke namespace下的tke-registry-api这个service, 使用https协议和443端口, URL为/apis/registry.tkestack.io/v1/namespaces

3 约束

长度字符约束

  1. Name: 长度<=253, [a-z0-9.-], 数字字母结尾及开头, 且.后面那一段也必须以数字字母结尾及开头, 例如a.a-a合法, 但是a.-a不合法.
  2. Label Key: 长度<=63, [a-z0-9-], 数字字母开头及结尾. Key可以有一个域名作为前缀, 域名最长可以253字符
  3. Label value: 长度<=63, [a-zA-Z0-9.-_], 数字字母结尾及开头.

4 Pod

驱逐单个Pod

see:

https://kubernetes.io/zh/docs/tasks/administer-cluster/safely-drain-node/
curl -v -H 'Content-type: application/json' http://127.0.0.1:8080/api/v1/namespaces/default/pods/quux/eviction -d @eviction.json
{
    "apiVersion": "policy/v1beta1",
    "kind": "Eviction",
    "metadata": {
        "name": "quux",
        "namespace": "default"
    }
}

注意URL里的名字/namespace要和json里的名字/namespace对应上

5 operator

为crd增加自定义输出

增加注释: +//+kubebuilder:printcolumn:JSONPath=".status.status",name=Status,type=integer

6 GVK&GVR

例如一个daemonset:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter

apps就是Group, v1就是Version, DaemonSet就是Kind, 加起来就是GVK

GVR则是GVK对应的url, 用于发送到apiserver. 通过REST Mapping完成GVK和GVR的映射

7 dynamic client

通过CRD的定义可以生成针对这个类型的一个clientset, 调用这个clientset可 以方便的处理CR的CRUD. 但是这也意味着使用方需要import生成的代码, 如果某 个需求是通用的调用一些没有提前知道的资源的CRUD就无法实现了, 这时候就需 要dynamic client.

dynamic client在使用时需要指定GVR, 得到的资源以unstructure.Unstructure 表示, 所有的读取编辑操作需要调用unstructure库来完成

dynamic client内部其实包装了rest client, 最终是调用rest client来进行http请求

// c.client是dynamic client
// c.client.client是rest client
result := c.client.client.
        Post().
        AbsPath(append(c.makeURLSegments(name), subresources...)...).
        Body(outBytes).
        SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
        Do(ctx)

controller-runtime中的client

controller-runtime提供的client封装了unstructuredClient和typedClient, 其本质都是调用client-go的RestClient

当上层业务通过以下代码读数据时:

// Get returns the flow instance with given name.
func (nc *flowController) Get(namespace, name string) (*nonv1.TFlow, error) {
        key := types.NamespacedName{
                Namespace: namespace,
                Name:      name,
        }

        var flow nonv1.TFlow
        if err := nc.client.Get(context.TODO(), key, &flow); err != nil {
                return nil, err
        }

        return &flow, nil
}

controller-runtime的client会检查对象时unstructure还是typed, 然后调用不同的client

// Get implements client.Client
func (c *client) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
        _, ok := obj.(*unstructured.Unstructured)
        if ok {
                return c.unstructuredClient.Get(ctx, key, obj)
        }
        return c.typedClient.Get(ctx, key, obj)
}

但是这两个client最终都是调用rest client, 只不过如果obj是明确了类型, 可以通过提前注册的scheme查到gvk, 否则就得用使用方自行提供gvk

func init() {
        _ = nonv1.AddToScheme(scheme)
}

func New(){
        // 创建controller-runtime中的client时需要提供Scheme, 其中就包含了GVK与某个类型的映射关系
        cli, err := client.New(config, client.Options{Scheme: scheme})
}