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

以 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 来验证我们的学习效果

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"`
}

type TypeMeta struct

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

只有两个参数:

  • apiVersion
  • kind

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"`
}

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"`
}

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"`
}

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"`
        
        // 使用 secret
        /*
              spec: 
                volumes:
                - name: foo
                   secret:
                     secretName: mysecret
        */
        Secret *SecretVolumeSource `json:"secret,omitempty" protobuf:"bytes,6,opt,name=secret"`
	// 重点在 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 数据源
	...

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"`
}

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"`
}

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"`
}

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"`
}

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"`
}

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>

分析 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
posted on 2019-10-17 19:50  silenceli  阅读(1139)  评论(0编辑  收藏  举报