• 以 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
    
  • 相关阅读:
    就业指导【黄春霞】
    百度面试题
    面试题08-多线程网络
    面试题07-内存管理
    面试题06-Foundation
    面试题05-UI控件
    面试题04-应用程序
    面试题03-第三方框架
    面试题02-客户端安全性和框架设计
    面试题01-数据存储
  • 原文地址:https://www.cnblogs.com/oolo/p/11694401.html
Copyright © 2020-2023  润新知