zoukankan      html  css  js  c++  java
  • 以 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
    
  • 相关阅读:
    盒子垂直水平居中
    Sahi (2) —— https/SSL配置(102 Tutorial)
    Sahi (1) —— 快速入门(101 Tutorial)
    组织分析(1)——介绍
    Java Servlet (1) —— Filter过滤请求与响应
    CAS (8) —— Mac下配置CAS到JBoss EAP 6.4(6.x)的Standalone模式(服务端)
    JBoss Wildfly (1) —— 7.2.0.Final编译
    CAS (7) —— Mac下配置CAS 4.x的JPATicketRegistry(服务端)
    CAS (6) —— Nginx代理模式下浏览器访问CAS服务器网络顺序图详解
    CAS (5) —— Nginx代理模式下浏览器访问CAS服务器配置详解
  • 原文地址:https://www.cnblogs.com/oolo/p/11694401.html
Copyright © 2011-2022 走看看