以 gpushare-device-plugin 为例,探究 Resource yaml 配置

1. 以 gpushare-device-plugin 为例,探究 Resource yaml 配置

我理解 k8s 中最核心的 resource 就是 Pod,创建 Pod 需要先生成 yaml 文件,然后通过 kubectl apply -f example-pod.yaml 来创建 pod。

k8s 中处理 pod 的内部流程应该来说会比较复杂,才搞两周的我肯定是没有这个实力;而熟悉创建 pod 的 yaml 配置相对比较容易,只有先会用,才能逐步的了解具体的实现细节,由浅入深由表及里这个方法论是不会错误的。

因此,本文主要分析 type Pod struct 的结构,晦涩的字段暂且不管,只抓核心字段,然后再分析 type DaemonSet struct,最后通过创建 gpushare-device-plugindevice-plugin-ds.yaml 来验证我们的学习效果

1.1. Pod 结构体分析

代码见:kubernetes/vendor/k8s.io/api/core/v1/types.go

type Pod struct {
    /* 注意,metav1.TypeMeta 是类型,但是成员变量却不存在?这不合常理
       查询了好久才发现,这种叫做 promoted field,还有一个孪生姐妹 anonymous field
       参见:https://stackoverflow.com/questions/28014591/nameless-fields-in-go-structs

       metav1.TypeMeta 在 kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go 中
       type TypeMeta struct {
           Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"`
           APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,2,opt,name=apiVersion"`
       }

       注意,`json:",inline"` 这个被称为 go struct field tag:
       参见:https://medium.com/rungo/structures-in-go-76377cc106a2

       json 代表对 json unpack/pack 处理的 metadata
       protobuf 是对 protobuf 处理的 metadata
       inline 比较特殊,https://github.com/isayme/blog/issues/15,主要是为了从 json 中读取数据后去掉当前层,直接内嵌进去

       注意,Pod 实例可以直接使用 metav1.TypeMeta 实例的成员:pod.Kind、pod.APIVersion,但是 yaml 的配置应该是:

       apiVersion: extensions/v1beta1
       kind: DaemonSet

       这是和 inline 以及 protobuf:"bytes,1,opt,name=kind" 相关的,请注意

    */
    metav1.TypeMeta `json:",inline"`

    /*
        注意,对于 metav1.ObjectMeta,代码中调用时 Pod.Name、Pod.Namespace,但是对于 yaml 文件却是:

        metadata:
          name: gpushare-device-plugin-ds
          namespace: kube-system

        这是因为有 protobuf tag 的指示,注意 metav1.ObjectMeta 不含 inline,因此和 metav1.TypeMeta 的 yaml 有不同,请一定注意

    */
    metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

    Spec PodSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`

    Status PodStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}

1.1.1. type TypeMeta struct

代码见:kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go

只有两个参数:

  • apiVersion
  • kind

1.1.2. type ObjectMeta struct

代码见:kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go

常用的字段如下:

  • name
  • labels
  • annotations
  • namespace

完整分析如下:

type ObjectMeta struct {
    // Pod 的名字(常用)
    /*
        metadata:
          name: gpushare-device-plugin-ds
    */
    Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`

    // 打标签(常用) https://www.jianshu.com/p/cd6b4b4caaab
    /*
        metadata:
          labels:
            app: nginx
            release: stable
    */
    Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`

    // annotations (常用)k8s 内部组件对这个会比较关心(偏系统),labels 是用户对其关心(偏用户),前者不需要自己设置,k8s 会自动设置,达到某种效果
    Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`


    // kube-system,不填就是 default(常用)
    /*
        metadata:
          name: gpushare-device-plugin-ds
          namespace: kube-system
    */
    Namespace string `json:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"`

    // 不常用
    GenerateName string `json:"generateName,omitempty" protobuf:"bytes,2,opt,name=generateName"`

    // 不常用
    SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,4,opt,name=selfLink"`

    // 不常用
    UID types.UID `json:"uid,omitempty" protobuf:"bytes,5,opt,name=uid,casttype=k8s.io/kubernetes/pkg/types.UID"`

    // 不常用,系统填写 https://k8smeetup.github.io/docs/reference/api-concepts/
    ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,6,opt,name=resourceVersion"`

    // 不常用
    Generation int64 `json:"generation,omitempty" protobuf:"varint,7,opt,name=generation"`

    // 不常用,系统填写
    CreationTimestamp Time `json:"creationTimestamp,omitempty" protobuf:"bytes,8,opt,name=creationTimestamp"`

    // 不常用,系统填写
    DeletionTimestamp *Time `json:"deletionTimestamp,omitempty" protobuf:"bytes,9,opt,name=deletionTimestamp"`

    // 优雅的被删除,不常用
    DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty" protobuf:"varint,10,opt,name=deletionGracePeriodSeconds"`

    // 不常用,垃圾收集相关:https://kubernetes.io/zh/docs/concepts/workloads/controllers/garbage-collection/
    OwnerReferences []OwnerReference `json:"ownerReferences,omitempty" patchStrategy:"merge" patchMergeKey:"uid" protobuf:"bytes,13,rep,name=ownerReferences"`

    // 不常用,1.15 将被废弃
    Initializers *Initializers `json:"initializers,omitempty" protobuf:"bytes,16,opt,name=initializers"`

    // 不常用,垃圾回收相关:https://draveness.me/kubernetes-garbage-collector
    Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"`

    // 不常用,有多 cluster 的时候,可能需要指定 cluster
    ClusterName string `json:"clusterName,omitempty" protobuf:"bytes,15,opt,name=clusterName"`

    // 不常用,不了解
    ManagedFields []ManagedFieldsEntry `json:"managedFields,omitempty" protobuf:"bytes,17,rep,name=managedFields"`
}

1.1.3. type PodSpec struct

代码:kubernetes/vendor/k8s.io/api/core/v1/types.go

我们提取几个比较重要的,核心的,容易搞不清楚的:

  • volumes
  • InitContainers
  • Containers

其他的详细分析见下文,在这里提供一个非常棒的网站Kubernetes 指南,非常全面,对我帮助很大

// PodSpec is a description of a pod.
type PodSpec struct {
    // 常用,volumes,下文详细分析
    Volumes []Volume `json:"volumes,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name" protobuf:"bytes,1,rep,name=volumes"`

    // 常用,initContainers,下文详细分析
    InitContainers []Container `json:"initContainers,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,20,rep,name=initContainers"`

    // 常用,containers,下文详细分析
    Containers []Container `json:"containers" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=containers"`

    // 常用,重启策略 Always、OnFailure、Never
    RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" protobuf:"bytes,3,opt,name=restartPolicy,casttype=RestartPolicy"`

    // 不常用,退出等待多少时间~
    TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty" protobuf:"varint,4,opt,name=terminationGracePeriodSeconds"`

    // 不常用,和重试有关,也许是标志失败Pod的重试最大时间,超过这个时间不会继续重试
    ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty" protobuf:"varint,5,opt,name=activeDeadlineSeconds"`

    // 不常用,DNS 配置,ClusterFirstWithHostNet、ClusterFirst、Default、None
    DNSPolicy DNSPolicy `json:"dnsPolicy,omitempty" protobuf:"bytes,6,opt,name=dnsPolicy,casttype=DNSPolicy"`

    // 常用,是一个供用户将 Pod 与 Node 进行绑定的字段
    /*
        spec:
          nodeSelector:
          disktype: ssd
    */
    NodeSelector map[string]string `json:"nodeSelector,omitempty" protobuf:"bytes,7,rep,name=nodeSelector"`

    /*
        常用,pod 也可以访问 apiserver,但是用什么权限呢?ServiceAccountName 就是这个东西

        首先需要创建一个 ServiceAccount:
        apiVersion: v1
        kind: ServiceAccount
        metadata:
          name: gpushare-device-plugin
          namespace: kube-system

        然后,可以直接使用:
        spec:
          serviceAccount: gpushare-device-plugin

    */
    ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"bytes,8,opt,name=serviceAccountName"`

    // 不常用,废弃
    DeprecatedServiceAccount string `json:"serviceAccount,omitempty" protobuf:"bytes,9,opt,name=serviceAccount"`

    // 不常用,serviceAccount 是否自动挂载??
    AutomountServiceAccountToken *bool `json:"automountServiceAccountToken,omitempty" protobuf:"varint,21,opt,name=automountServiceAccountToken"`

    // 常用,但不太应该用!指定调度到哪个 node 上,跳过了调度器!
    NodeName string `json:"nodeName,omitempty" protobuf:"bytes,10,opt,name=nodeName"`

    // 常用,isolation,是否用宿主机的网络(namespace)一般 false
    HostNetwork bool `json:"hostNetwork,omitempty" protobuf:"varint,11,opt,name=hostNetwork"`

    // 常用,isolation,是否能看到宿主机的进程 (pid namespace)一般 false
    HostPID bool `json:"hostPID,omitempty" protobuf:"varint,12,opt,name=hostPID"`

    // 常用,isolation,是否能看到宿主机 IPC (ipc namespace)一般 false
    HostIPC bool `json:"hostIPC,omitempty" protobuf:"varint,13,opt,name=hostIPC"`

    // 常用,是否 pod 下面多个 container 共享一个 PID namespace
    // 置为 true 后,container 能互相看到对方的进程
    ShareProcessNamespace *bool `json:"shareProcessNamespace,omitempty" protobuf:"varint,27,opt,name=shareProcessNamespace"`

    // 常用,参见 https://feisky.gitbooks.io/kubernetes/concepts/security-context.html
    // 简单说,启用 selinux?限制端口,总之,限制不可信容器的使用
    // 暂时不用关注
    SecurityContext *PodSecurityContext `json:"securityContext,omitempty" protobuf:"bytes,14,opt,name=securityContext"`

    // 常用,下载镜像也得有权限吧?不过默认是用 default serviceAccount 的 ImagePullSecrets
    ImagePullSecrets []LocalObjectReference `json:"imagePullSecrets,omitempty" patchStrategy:"merge" patchMergeKey:"name"

    // 常用,hostname
    // If specified, the fully qualified Pod hostname will be "<hostname>.<subdomain>.<pod namespace>.svc.<cluster domain>".
    Hostname string `json:"hostname,omitempty" protobuf:"bytes,16,opt,name=hostname"`

    // 同上
    Subdomain string `json:"subdomain,omitempty" protobuf:"bytes,17,opt,name=subdomain"`

    // 常用,调度相关,共分 3 级,NodeAffinity、PodAffinity、PodAntiAffinity
    // 详见:https://feisky.gitbooks.io/kubernetes/components/scheduler.html
    Affinity *Affinity `json:"affinity,omitempty" protobuf:"bytes,18,opt,name=affinity"`

    // 不常用,调度相关,指定调度器的名字
    SchedulerName string `json:"schedulerName,omitempty" protobuf:"bytes,19,opt,name=schedulerName"`

    // 常用,https://feisky.gitbooks.io/kubernetes/components/scheduler.html#taints-%E5%92%8C-tolerations
    // 不调度到哪台机器
    Tolerations []Toleration `json:"tolerations,omitempty" protobuf:"bytes,22,opt,name=tolerations"`

    // 常用,指定 /etc/hosts,非常重要
    /*
        spec:
            hostAliases:
            - ip: "10.1.2.3"
            hostnames:
            - "foo.remote"
            - "bar.remote"


        cat /etc/hosts
        # Kubernetes-managed hosts file.
        127.0.0.1 localhost
        ...
        10.244.135.10 hostaliases-pod
        10.1.2.3 foo.remote
        10.1.2.3 bar.remote

    */
    HostAliases []HostAlias `json:"hostAliases,omitempty" patchStrategy:"merge" patchMergeKey:"ip" protobuf:"bytes,23,rep,name=hostAliases"`

    // 不重要,调度相关,指定被调度的优先级
    // PriorityClass 是一个 resource 需要自己去创建,创建后在这里指定
    // https://feisky.gitbooks.io/kubernetes/concepts/pod.html#%E4%BC%98%E5%85%88%E7%BA%A7
    /*
        apiVersion: scheduling.k8s.io/v1alpha1
        kind: PriorityClass
        metadata:
          name: high-priority
        value: 1000000
        globalDefault: false
        description: "This priority class should be used for XYZ service pods only."
    */
    PriorityClassName string `json:"priorityClassName,omitempty" protobuf:"bytes,24,opt,name=priorityClassName"`

    // 不常用,调度,Priority Admission Controller is enabled 后失效(读 PriorityClassName 中的 value),否则生效。越大越优先
    Priority *int32 `json:"priority,omitempty" protobuf:"bytes,25,opt,name=priority"`

    // 常用,写死 resolve.conf,我觉得 DNSConfig 和 DNSPolicy 类似,前者是用户写死,后者是根据用户选的策略系统自己填写
    DNSConfig *PodDNSConfig `json:"dnsConfig,omitempty" protobuf:"bytes,26,opt,name=dnsConfig"`

    // 不常用,pod 启动后额外的检测,只有通过才是 ready
    // v1.11 引入:https://godleon.github.io/blog/Kubernetes/k8s-Pod-Overview/
    ReadinessGates []PodReadinessGate `json:"readinessGates,omitempty" protobuf:"bytes,28,opt,name=readinessGates"`

    // 不常用,支持多 CRI,v1.12 引入,比如 Pod 包含 Kata Containers/gVisor + runc 的多个容器
    RuntimeClassName *string `json:"runtimeClassName,omitempty" protobuf:"bytes,29,opt,name=runtimeClassName"`

    // 不常用,不详
    EnableServiceLinks *bool `json:"enableServiceLinks,omitempty" protobuf:"varint,30,opt,name=enableServiceLinks"`

    // 不常用,新搞出来的一个资源抢占的内容???
    PreemptionPolicy *PreemptionPolicy `json:"preemptionPolicy,omitempty" protobuf:"bytes,31,opt,name=preemptionPolicy"`
}

1.1.3.1. type Volume struct

代码见:kubernetes/vendor/k8s.io/api/core/v1/types.go

这里的 Volume 我理解是创建 volume,因此需要 volume name + volume source。

我们常用的是使用宿主机的地址映射到 container 中,作为卷,或者是映射宿主机的设备进入 container。

除此之外,volumes 更多的 source 是 ceph、cinder、nfs、iscsi 等,这样才具有持久化的能力。这部分十分复杂,我们简单分析下

核心参数:

  • name
  • volumeSource
type Volume struct {
    // volume 名字
    Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
    // Volume 的资源来源于哪里?
    VolumeSource `json:",inline" protobuf:"bytes,2,opt,name=volumeSource"`
}

1.1.3.2. type VolumeSource struct

代码见:kubernetes/vendor/k8s.io/api/core/v1/types.go

VolumeSource 我理解就是 k8s 支持的后端存储有哪些,最常见的就是把宿主机磁盘挂载到容器中,作为 volume

type VolumeSource struct {
    // 映射宿主机目录到 container 中,注意,这里仅仅是创建资源,映射在 container 的 spec 中的
    /*
    type HostPathVolumeSource struct {
        // 宿主机路径,可以是 device、dir、socket 等
        Path string `json:"path" protobuf:"bytes,1,opt,name=path"`
        // 支持很多类型,File、socket、Device,最保险的使用默认值 "",一般来说兼容一切
        Type *HostPathType `json:"type,omitempty" protobuf:"bytes,2,opt,name=type"`
    }
    */

    /*
    举个例子:
    spec:
      volumes:
        - name: device-plugin
          hostPath:
            path: /var/lib/kubelet/device-plugins

    */
    HostPath *HostPathVolumeSource `json:"hostPath,omitempty" protobuf:"bytes,1,opt,name=hostPath"`

    // 创建一个宿主机临时目录,给 container 用,这可能涉及到多个 container 之间的数据交互的配合,也可能是 container 本身受到 10GB 大小的限制,可能日志会写不下
    /*
    例子:
        spec:
          volumes:
          - name: nginx-vol
            emptyDir: {}
    */
    EmptyDir *EmptyDirVolumeSource `json:"emptyDir,omitempty" protobuf:"bytes,2,opt,name=emptyDir"`

    // 重点在 ceph 上,但是目前没有这个实力
    NFS *NFSVolumeSource `json:"nfs,omitempty" protobuf:"bytes,7,opt,name=nfs"`
    RBD *RBDVolumeSource `json:"rbd,omitempty" protobuf:"bytes,11,opt,name=rbd"`
    PersistentVolumeClaim *PersistentVolumeClaimVolumeSource `json:"persistentVolumeClaim,omitempty" protobuf:"bytes,10,opt,name=persistentVolumeClaim"`

    // 很多其他的 volume 数据源
    ...

1.1.4. type Container struct

initcontainer 和 container 都是 struct []Container 类型的,因此只需要分析 Container 即可,Container 是核心中的核心!

我们总结了常用的参数:

  • name
  • image
  • command
  • args
  • envs
  • ports
  • resources
  • volumeMounts
  • lifecycle
    • preStop
    • postStart
  • devicePath
  • imagePullPolicy
  • stdin
  • tty

详解如下:

type Container struct {
    // 常用,容器得有名字吧?
    Name string `json:"name" protobuf:"bytes,1,opt,name=name"`

    // 常用,容器得用镜像吧?
    /* 例子
      spec:
        containers:
        - name: nginx
          image: nginx:1.8
    */
    Image string `json:"image,omitempty" protobuf:"bytes,2,opt,name=image"`

    // 常用,启动容器的命令行,会覆盖 docker 本身的 entrypoint
    /*
    spec:
        containers:
        - command:
            - gpushare-device-plugin-v2
            - -logtostderr
            - --v=5
            - --memory-unit=GiB
    */
    Command []string `json:"command,omitempty" protobuf:"bytes,3,rep,name=command"`
    // 常用,命令行参数
    /*
    spec:
      containers:
      - name: command-demo-container
        image: debian
        command: ["printenv"]
        args: ["HOSTNAME", "KUBERNETES_PORT"]
    */
    Args []string `json:"args,omitempty" protobuf:"bytes,4,rep,name=args"`

    // 常用,工作目录
    WorkingDir string `json:"workingDir,omitempty" protobuf:"bytes,5,opt,name=workingDir"`

    // 常用,pod 对外的 port
    /*
    spec:
      containers:
      - ports:
        - containerPort: 80
    */
    Ports []ContainerPort `json:"ports,omitempty" patchStrategy:"merge" patchMergeKey:"containerPort" protobuf:"bytes,6,rep,name=ports"`

    // 不常用,可能是 env 存放在 configmap resource 中,这里引用一下
    EnvFrom []EnvFromSource `json:"envFrom,omitempty" protobuf:"bytes,19,rep,name=envFrom"`

    // 传入容器的环境变量,比 EnvFrom 直接
    Env []EnvVar `json:"env,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,7,rep,name=env"`

    // 常用,需要的资源
    /*
    spec:
      containers:
      - resources:
          limits:
            memory: "300Mi"
            cpu: "1"
          requests:
            memory: "300Mi"
            cpu: "1"
    */
    Resources ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,8,opt,name=resources"`

    // 常用,volume 映射,type VolumeMount struct 还需要仔细研究下
    /*
    spec:
      containers:
      - volumeMounts:
        - name: device-plugin
          mountPath: /var/lib/kubelet/device-plugins
    */
    VolumeMounts []VolumeMount `json:"volumeMounts,omitempty" patchStrategy:"merge" patchMergeKey:"mountPath" protobuf:"bytes,9,rep,name=volumeMounts"`

    // 常用,但需要研究,貌似是 blockdevice,需要先创建 PVC
    /*
        apiVersion: v1
        kind: PersistentVolumeClaim
        metadata:
          name: my-pvc
        spec:
          accessModes:
            - ReadWriteMany
          volumeMode: Block
          storageClassName: my-sc
          resources:
            requests:
            storage: 1Gi
    */

    /*
    apiVersion: v1
    kind: Pod
    metadata:
      name: my-pod
    spec:
      containers:
        - name: my-container
          image: busybox
          command:
            - sleep
            - “3600”
          volumeDevices:
            - devicePath: /dev/block
              name: my-volume
          imagePullPolicy: IfNotPresent
      volumes:
        - name: my-volume
          persistentVolumeClaim:
            claimName: my-pvc
    */
    VolumeDevices []VolumeDevice `json:"volumeDevices,omitempty" patchStrategy:"merge" patchMergeKey:"devicePath"

    // 重要,但暂时不看,健康检查
    // https://feisky.gitbooks.io/kubernetes/introduction/201.html#%E5%81%A5%E5%BA%B7%E6%A3%80%E6%9F%A5
    LivenessProbe *Probe `json:"livenessProbe,omitempty" protobuf:"bytes,10,opt,name=livenessProbe"`

    // 重要,但暂时不看,监控检查
    // https://feisky.gitbooks.io/kubernetes/introduction/201.html#%E5%81%A5%E5%BA%B7%E6%A3%80%E6%9F%A5
    ReadinessProbe *Probe `json:"readinessProbe,omitempty" protobuf:"bytes,11,opt,name=readinessProbe"`

    // 在 poststart 和 prestop 的时候可以插入执行的内容
    /*
    spec:
        containers:
        - name: lifecycle-demo-container
        image: nginx
        lifecycle:
          postStart:
            exec:
              command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"
          preStop:
            exec:
              command: ["/usr/sbin/nginx","-s","quit"]
    */
    Lifecycle *Lifecycle `json:"lifecycle,omitempty" protobuf:"bytes,12,opt,name=lifecycle"`

    // 详见:https://k8smeetup.github.io/docs/tasks/debug-application-cluster/determine-reason-pod-failure/
    // 貌似不用特别管
    TerminationMessagePath string `json:"terminationMessagePath,omitempty" protobuf:"bytes,13,opt,name=terminationMessagePath"`

    // 同上,注意
    TerminationMessagePolicy TerminationMessagePolicy `json:"terminationMessagePolicy,omitempty"

    // 常用,类型 Always、Never、IfNotPresent
    ImagePullPolicy PullPolicy `json:"imagePullPolicy,omitempty" protobuf:"bytes,14,opt,name=imagePullPolicy,casttype=PullPolicy"`

    // 同 Pod 中的 SecurityContext
    SecurityContext *SecurityContext `json:"securityContext,omitempty" protobuf:"bytes,15,opt,name=securityContext"`

    // 是否开启 stdin
    Stdin bool `json:"stdin,omitempty" protobuf:"varint,16,opt,name=stdin"`

    // 不常用,估计只能同时允许一个 stdin 的连接
    StdinOnce bool `json:"stdinOnce,omitempty" protobuf:"varint,17,opt,name=stdinOnce"`

    // 是否开启 tty
    TTY bool `json:"tty,omitempty" protobuf:"varint,18,opt,name=tty"`
}

1.1.5. type PodStatus struct

PodStatus 全部由系统来填写,和创建无关,我们仅仅需要了解即可

总体来说

  • phase (Pod 状态)
  • 有 Pod 下面所有 container 的状态,有原因,有一些奇怪的字段。
type PodStatus struct {
    // 系统填写,Pending、Running、Succeeded、Failed、Unknown 就五种状态
    Phase PodPhase `json:"phase,omitempty" protobuf:"bytes,1,opt,name=phase,casttype=PodPhase"`

    // 系统填写,Pod 下多个 container 的状态,包括
    /*
        type PodCondition struct {
            // ContainersReady,Initialized,Ready,PodScheduled
            Type PodConditionType `json:"type" protobuf:"bytes,1,opt,name=type,casttype=PodConditionType"`
            // True, False, Unknown. 不知道什么意思
            Status ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status,casttype=ConditionStatus"`
            // 上一次提交状态的时间?
            LastProbeTime metav1.Time `json:"lastProbeTime,omitempty" protobuf:"bytes,3,opt,name=lastProbeTime"`
            // 上一次提交状态变化的时间?
            LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" protobuf:"bytes,4,opt,name=lastTransitionTime"`
            // 为啥会状态会变化
            Reason string `json:"reason,omitempty" protobuf:"bytes,5,opt,name=reason"`
            // 为啥状态会变化给用户看的
            Message string `json:"message,omitempty" protobuf:"bytes,6,opt,name=message"`
        }
    */
    Conditions []PodCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,2,rep,name=conditions"`

    // 人能看懂的为啥处于这个状态的原因
    Message string `json:"message,omitempty" protobuf:"bytes,3,opt,name=message"`

    // 工程师能看懂的为啥处于这个状态的原因
    Reason string `json:"reason,omitempty" protobuf:"bytes,4,opt,name=reason"`

    // 和抢占有关系,不懂 https://www.jianshu.com/p/bdcb9528a8b1
    NominatedNodeName string `json:"nominatedNodeName,omitempty" protobuf:"bytes,11,opt,name=nominatedNodeName"`

    // 调度该 pod 的 scheduler 的 IP
    HostIP string `json:"hostIP,omitempty" protobuf:"bytes,5,opt,name=hostIP"`

    // Pod 的 IP 地址?
    PodIP string `json:"podIP,omitempty" protobuf:"bytes,6,opt,name=podIP"`

    // 启动时间?,具体再议
    StartTime *metav1.Time `json:"startTime,omitempty" protobuf:"bytes,7,opt,name=startTime"`

    // initcontainer 的状态,因为它是最先启动的,它启动成功后,container 才能启动
    InitContainerStatuses []ContainerStatus `json:"initContainerStatuses,omitempty" protobuf:"bytes,10,rep,name=initContainerStatuses"`

    // container 状态
    ContainerStatuses []ContainerStatus `json:"containerStatuses,omitempty" protobuf:"bytes,8,rep,name=containerStatuses"`

    // QoS 相关的,可选 Guaranteed、Burstable、BestEffort
    QOSClass PodQOSClass `json:"qosClass,omitempty" protobuf:"bytes,9,rep,name=qosClass"`
}

1.2. DaemonSet 结构体

代码:kubernetes/vendor/k8s.io/api/apps/v1/types.go

分析的过程中,一定要注意与 Pod 对比

type DaemonSet struct {
    // 同 Pod 第一个字段
    metav1.TypeMeta `json:",inline"`
    // 同 Pod 第二个字段
    metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

    // 需要仔细分析,类似于 PodSpec
    Spec DaemonSetSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`

    // 需要仔细分析,类似于 PodStatus
    Status DaemonSetStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}

1.2.1. type DaemonSetSpec struct

代码:kubernetes/vendor/k8s.io/api/apps/v1/types.go

type DaemonSetSpec struct {
    // 该 DaemonSet 部署在哪些机器上?用 selector 来过滤
    Selector *metav1.LabelSelector `json:"selector" protobuf:"bytes,1,opt,name=selector"`

    // 需要仔细分析
    Template v1.PodTemplateSpec `json:"template" protobuf:"bytes,2,opt,name=template"`

    // update 的策略,默认是 RollingUpdate
    /*
    type DaemonSetUpdateStrategy struct {
        // 两种升级策略 RollingUpdate 和 OnDelete
        Type DaemonSetUpdateStrategyType `json:"type,omitempty" protobuf:"bytes,1,opt,name=type"`

        // 如果是 RollingUpdate,才生效,如果填数字 5,代表 5 个 5 个逐步升级,如果填 5%,则5% 5% 逐步升级,默认是 1
        RollingUpdate *RollingUpdateDaemonSet `json:"rollingUpdate,omitempty" protobuf:"bytes,2,opt,name=rollingUpdate"`
    }

    */
    UpdateStrategy DaemonSetUpdateStrategy `json:"updateStrategy,omitempty" protobuf:"bytes,3,opt,name=updateStrategy"`

    // 当 ready 后多少分钟,才认为该 DaemonSet 是 avaliable?
    MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,4,opt,name=minReadySeconds"`

    // 保存的历史的 checkpoint 有多少个,默认值是 10 个
    RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty" protobuf:"varint,6,opt,name=revisionHistoryLimit"`
}

1.2.2. type PodTemplateSpec struct

代码:kubernetes/vendor/k8s.io/api/core/v1/types.go

type PodTemplateSpec struct {
    // 同 Pod/DaemonSet 中第二个字段,不知道这是何意?
    metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

    // 同 Pod 中的 PodSpec
    Spec PodSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
}

1.2.3. type DaemonSetStatus struct

代码:kubernetes/vendor/k8s.io/api/apps/v1/types.go

DaemonSetStatus 比 PodStatus 简单很多,毕竟它的定义就是仅仅在每个符合条件的 host 上部署一个 Pod。

type DaemonSetStatus struct {
    // 正在运行这个 daemonset 的 node 个数
    CurrentNumberScheduled int32 `json:"currentNumberScheduled" protobuf:"varint,1,opt,name=currentNumberScheduled"`

    // 不该运行这个 daemonset 的 node 个数
    NumberMisscheduled int32 `json:"numberMisscheduled" protobuf:"varint,2,opt,name=numberMisscheduled"`

    // 应当运行这个 daemonset 的 node 个数
    DesiredNumberScheduled int32 `json:"desiredNumberScheduled" protobuf:"varint,3,opt,name=desiredNumberScheduled"`

    // 准备好运行这个 daemonset 的 node 个数
    NumberReady int32 `json:"numberReady" protobuf:"varint,4,opt,name=numberReady"`

    // The most recent generation observed by the daemon set controller.
    // +optional
    ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,5,opt,name=observedGeneration"`

    // 运行最新的 daemonset 的 node 个数
    UpdatedNumberScheduled int32 `json:"updatedNumberScheduled,omitempty" protobuf:"varint,6,opt,name=updatedNumberScheduled"`

    // 好烦
    NumberAvailable int32 `json:"numberAvailable,omitempty" protobuf:"varint,7,opt,name=numberAvailable"`

    NumberUnavailable int32 `json:"numberUnavailable,omitempty" protobuf:"varint,8,opt,name=numberUnavailable"`

    CollisionCount *int32 `json:"collisionCount,omitempty" protobuf:"varint,9,opt,name=collisionCount"`

    // 同 PodCondition
    Conditions []DaemonSetCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,10,rep,name=conditions"`
}

看一看这个 DaemonSet 的实际状态,和定义的 DaemonSetStatus 还真挺类似:

[root@k8s-master kubernetes]# kubectl describe daemonset gpushare-device-plugin-ds -n kube-system
Name:           gpushare-device-plugin-ds
Selector:       app=gpushare,component=gpushare-device-plugin,name=gpushare-device-plugin-ds
Node-Selector:  gpushare=true
Labels:         app=gpushare
                component=gpushare-device-plugin
                name=gpushare-device-plugin-ds
Annotations:    deprecated.daemonset.template.generation: 2
                kubectl.kubernetes.io/last-applied-configuration:
                  {"apiVersion":"extensions/v1beta1","kind":"DaemonSet","metadata":{"annotations":{},"name":"gpushare-device-plugin-ds","namespace":"kube-sy...
Desired Number of Nodes Scheduled: 1
Current Number of Nodes Scheduled: 1
Number of Nodes Scheduled with Up-to-date Pods: 1
Number of Nodes Scheduled with Available Pods: 1
Number of Nodes Misscheduled: 0
Pods Status:  1 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:           app=gpushare
                    component=gpushare-device-plugin
                    name=gpushare-device-plugin-ds
  Annotations:      scheduler.alpha.kubernetes.io/critical-pod:
  Service Account:  gpushare-device-plugin
  Containers:
   gpushare:
    Image:      registry.cn-hangzhou.aliyuncs.com/acs/k8s-gpushare-plugin:v2-1.12-lihao-test
    Port:       <none>
    Host Port:  <none>
    Command:
      gpushare-device-plugin-v2
      -logtostderr
      --v=5
      --memory-unit=GiB
    Limits:
      cpu:     1
      memory:  300Mi
    Requests:
      cpu:     1
      memory:  300Mi
    Environment:
      KUBECONFIG:  /etc/kubernetes/kubelet.conf
      NODE_NAME:    (v1:spec.nodeName)
    Mounts:
      /var/lib/kubelet/device-plugins from device-plugin (rw)
  Volumes:
   device-plugin:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/kubelet/device-plugins
    HostPathType:
Events:            <none>

1.3. 分析 gpushare-device-plugin 的 daemon 配置

按照其文档,gpushare-device-plugin 的 daemonset 配置是 device-plugin-ds.yaml

[root@k8s-master kubernetes]# cat device-plugin-ds.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: gpushare-device-plugin-ds
  namespace: kube-system
spec:
  template:
    metadata:
      annotations:
        # 调度相关
        scheduler.alpha.kubernetes.io/critical-pod: ""
      labels:
        # 调度相关
        component: gpushare-device-plugin
        app: gpushare
        name: gpushare-device-plugin-ds
    # 就是 PodSpec
    spec:
      # 在 gpushare-device-plugin 的 device-plugin-rbac.yaml 中创建了该 serviceAccount
      serviceAccount: gpushare-device-plugin
      # 使用宿主机网络
      hostNetwork: true
      # 其实我觉得最好在最外层 spec 下用 selector
      # 用 nodeSelector 也行,有 gpushare: true 的 node 才启动该 device plugin
      nodeSelector:
        gpushare: "true"
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/acs/k8s-gpushare-plugin:v2-1.12-lihao-test
        name: gpushare
        # 用 args 装 -logtostderr 没准更好
        command:
          - gpushare-device-plugin-v2
          - -logtostderr
          - --v=5
          - --memory-unit=GiB
        # 资源用不多
        resources:
          limits:
            memory: "300Mi"
            cpu: "1"
          requests:
            memory: "300Mi"
            cpu: "1"
        # 环境变量有这么几个
        env:
        - name: KUBECONFIG
          value: /etc/kubernetes/kubelet.conf
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        # 权限限制
        securityContext:
          # 不用放大权利
          allowPrivilegeEscalation: false
          capabilities:
            # 不用任何额外的权限
            drop: ["ALL"]
        # 把 grpc unix socket(device plugin)映射到容器中
        volumeMounts:
          - name: device-plugin
            mountPath: /var/lib/kubelet/device-plugins
      # 把 grpc 的 Unix socket 所在本地文件夹作为卷
      volumes:
        - name: device-plugin
          hostPath:
            path: /var/lib/kubelet/device-plugins

猜你喜欢

转载自www.cnblogs.com/oolo/p/11694401.html
今日推荐