kubernetes pod-name生成过程 源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。如需转载必须通知本人,微信号: qinzhao990570827 https://blog.csdn.net/qq_21816375/article/details/85810825

kubernetes 版本

[root@master-47-35 ~]# kubectl version
Client Version: version.Info{Major:"1", Minor:"11+", GitVersion:"v1.11.0-168+f47446a730ca03", GitCommit:"f47446a730ca037473fb3bf0c5abeea648c1ac12", GitTreeState:"clean", BuildDate:"2018-08-25T21:05:52Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"11+", GitVersion:"v1.11.0-168+f47446a730ca03", GitCommit:"f47446a730ca037473fb3bf0c5abeea648c1ac12", GitTreeState:"clean", BuildDate:"2018-08-25T21:05:52Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}
[root@master-47-35 ~]# 

pod name生成规则

kubernetes生成pod有三种方式,如下图
$GOPATH/src/k8s.io/kubernetes/pkg/kubelet/types/pod_update.go
ku-api
生成pod name的方式有以下几种方式:
1.静态podname的生成方式
2.kube-controller-manager生成方式
a. statefulset生成podName方式
b. deployment生成podName方式
c. job生成podName方式
d. daemonset生成podName方式
e. replicaset生成podName方式
f. cronjob生成podName方式
标准podName统一生成name的格式是controllerName-5个随机字符转,也有例外,比如statefulset,静态podname的生成方式就不一样了


下面一一讲解

1.静态podname的生成方式
这种方式一般是通过kubelet方式创建的,指定kubelet的指定方式--pod-manifest-path
例如:
–pod-manifest-path=/etc/kubernetes/manifests

kubelet
指定之后kubelet会拉取pod起来,并且监听文件是否变化,一有变化就马上重建pod

–pod-manifest-path 对应的kubelet的命令行启动参数接收值为 StaticPodPath,
源码
oo

dd
启动一个协程计时器去监听文件是否变化
ds
listConfig()函数的具体实现
listConfig
extractFromDir(path)函数实现
extract
extractFromFile实现
modify
node
podName-01
这样子就实现了修改podName的功能了:文件指定的文件name-主机名

2.kube-controller-manager生成方式
这一类podName的特点就是controllerName结合5个随机字符串组成,每一个资源对象都继承podControl的方法,podControl有增删改pod信息的接口
pods-name

a. statefulset生成podName方式
首先查看defaultStatefulSetControl对象的生成方法
demo-01

NewDefaultStatefulSetControl这个方法被NewStatefulSetController这个方法调用
NewStatefulSetController 方法调用了Informer实时监控内存的信息,Informer这个框架在这里就不多说了

podControl直接用的实现就是RealPodControl,
setstate
RealPodControl实现了PodControlInterface接口
接下来查看RealPodControl在statefulsetController中如何使用,大概流程图是这样子的
worker—>processNextWorkItem—>sync—>syncStatefulSet—>UpdateStatefulSet—>updateStatefulSet—>ssc.podControl.CreateStatefulPod—>identityMatches—>getPodName

其中worker就是消费ssc的队列,只要有事件发生,就会add事件到队列里
work-ssc
sync的具体实现
sync
syncStatefulSet具体实现
sync
UpdateStatefulSet
statt

如果是创建就执行CreateStatefulPod函数请求kube-apiserver创建pod,具体实现请看下图
create ssc

updateStatefulSet函数的部分实现

...
	// Enforce the StatefulSet invariants
		if identityMatches(set, replicas[i]) && storageMatches(set, replicas[i]) {
			continue
		}
...

在identityMatches函数实现了pod的name的修改
identityMatches
setname
这样子就实现statefulset的podname生成规则了
我们再回到函数CreateStatefulPod

func (spc *realStatefulPodControl) CreateStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error {
	// Create the Pod's PVCs prior to creating the Pod
	if err := spc.createPersistentVolumeClaims(set, pod); err != nil {
		spc.recordPodEvent("create", set, pod, err)
		return err
	}
	// If we created the PVCs attempt to create the Pod
	_, err := spc.client.CoreV1().Pods(set.Namespace).Create(pod)
	// sink already exists errors
	if apierrors.IsAlreadyExists(err) {
		return err
	}
	spc.recordPodEvent("create", set, pod, err)
	return err
}

该函数通过client-go向kube-apiserver发出创建pod对象的请求时,实际上已经有生成podName的规则了,也就是statefulset的name加上5个随机字符串,但是由于statefuleset的特殊性,因此把返回来的podname重新按照statefuleset的方式生成了

接下来我们看看生成podName的规则如何实现
追踪spc.client.CoreV1().Pods(set.Namespace).Create(pod)函数,实际上就是向kube-apiserver发出POST方法创建POD资源对象的请求
因此基于这个思路,我们来看POST请求的具体实现
$GOPATH/src/k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/installer.go

func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService) (*metav1.APIResource, error) {
	admit := a.group.Admit

	optionsExternalVersion := a.group.GroupVersion
	if a.group.OptionsExternalVersion != nil {
		optionsExternalVersion = *a.group.OptionsExternalVersion
	}
	...
	case "POST": // Create a resource.
			var handler restful.RouteFunction
			if isNamedCreater {
				handler = restfulCreateNamedResource(namedCreater, reqScope, admit)
			} else {
				handler = restfulCreateResource(creater, reqScope, admit)
			}
			handler = metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, handler)
			article := getArticleForNoun(kind, " ")
			doc := "create" + article + kind
			if isSubresource {
				doc = "create " + subresource + " of" + article + kind
			}
			route := ws.POST(action.Path).To(handler).
				Doc(doc).
				Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
				Operation("create" + namespaced + kind + strings.Title(subresource) + operationSuffix).
				Produces(append(storageMeta.ProducesMIMETypes(action.Verb), mediaTypes...)...).
				Returns(http.StatusOK, "OK", producedObject).
			// TODO: in some cases, the API may return a v1.Status instead of the versioned object
			// but currently go-restful can't handle multiple different objects being returned.
				Returns(http.StatusCreated, "Created", producedObject).
				Returns(http.StatusAccepted, "Accepted", producedObject).
				Reads(defaultVersionedObject).
				Writes(producedObject)
			addParams(route, action.Params)
			routes = append(routes, route)
			...

查看restfulCreateResource的实现

func restfulCreateResource(r rest.Creater, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction {
	return func(req *restful.Request, res *restful.Response) {
		handlers.CreateResource(r, scope, admit)(res.ResponseWriter, req.Request)
	}
}
// CreateResource returns a function that will handle a resource creation.
func CreateResource(r rest.Creater, scope RequestScope, admission admission.Interface) http.HandlerFunc {
	return createHandler(&namedCreaterAdapter{r}, scope, admission, false)
}

$GOPATH/src/k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/handlers/create.go

func createHandler(r rest.NamedCreater, scope RequestScope, admit admission.Interface, includeName bool) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		// For performance tracking purposes.
		trace := utiltrace.New("Create " + req.URL.Path)
		defer trace.LogIfLong(500 * time.Millisecond)

		if isDryRun(req.URL) {
			scope.err(errors.NewBadRequest("dryRun is not supported yet"), w, req)
			return
		}

		// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
		timeout := parseTimeout(req.URL.Query().Get("timeout"))

		var (
			namespace, name string
			err             error
		)
		if includeName {
			namespace, name, err = scope.Namer.Name(req)
		} else {
			namespace, err = scope.Namer.Namespace(req)
		}
		if err != nil {
			scope.err(err, w, req)
			return
		}
			...
				trace.Step("About to store object in database")
		result, err := finishRequest(timeout, func() (runtime.Object, error) {
			return r.Create(
				ctx,
				name,
				obj,
				rest.AdmissionToValidateObjectFunc(admit, admissionAttributes),
				includeUninitialized,
			)
		})
		...

finishRequest该函数存储对象到etcd里
这时我们得了解$GOPATH/src/k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/installer.go文件的registerResourceHandlers函数storage rest.Storage入参的含义,

// Install handlers for API resources.
func (a *APIInstaller) Install() ([]metav1.APIResource, *restful.WebService, []error) {
	var apiResources []metav1.APIResource
	var errors []error
	ws := a.newWebService()
	glog.Infof("a.group.Storage===== : %s \n", a.group.Storage)
	// Register the paths in a deterministic (sorted) order to get a deterministic swagger spec.
	paths := make([]string, len(a.group.Storage))
	var i int = 0
	for path := range a.group.Storage {
		paths[i] = path
		glog.Infof("a.group.Storage[%s]=%s \n", path,a.group.Storage[path])
		i++
	}
	sort.Strings(paths)
	for _, path := range paths {
		apiResource, err := a.registerResourceHandlers(path, a.group.Storage[path], ws)
		if err != nil {
			errors = append(errors, fmt.Errorf("error in registering resource: %s, %v", path, err))
		}
		if apiResource != nil {
			apiResources = append(apiResources, *apiResource)
		}
	}
	return apiResources, ws, errors
}

追踪发现它就是k8s的资源对象,竟然是资源对象,那就要查看这个资源对象的实现这个接口的具体实现在哪里
查看具体实现形式
$GOPATH/src/k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/registry/rest/rest.go

// that objects may implement any of the below interfaces.
type Storage interface {
	// New returns an empty object that can be used with Create and Update after request data has been put into it.
	// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
	New() runtime.Object
}

查看Storage这个接口以及所有的接口实现形式,猜测是每个k8s资源对象都实现了这些接口
顺着这个猜测去查看Storage这个接口所在的目录发现了RESTCreateStrategy,而这里有个函数BeforeCreate就是生成资源对象的名字的,而且调用了随机生成5个字符串的包k8s.io/apiserver/pkg/storage/names,那就基本上可以知道BeforeCreate这个函数是生成podname的方法,那BeforeCreate就是关键我们就从BeforeCreate调用的地方入手
ddsee

追踪发现有以下这几个地方使用到
use
接着跟踪
$GOPATH/src/k8s.io/kubernetes/pkg/registry/core/service/storage/rest.go 158行使用到了,但是这个是服务对象,不是我们分析的POD的对象,这个文件猜测是针对service对象的restful api,没有完全实现Interface这个接口$GOPATH/src/k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/storage/interfaces.go

这里是调用BeforeCreate的方法的地方

// Create inserts a new item according to the unique key from the object.
func (e *Store) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
	if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil {
		return nil, err
	}
	// at this point we have a fully formed object.  It is time to call the validators that the apiserver
	// handling chain wants to enforce.
	if createValidation != nil {
		if err := createValidation(obj.DeepCopyObject()); err != nil {
			return nil, err
		}
	}

接着查看发现
$GOPATH/src/k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/registry/generic/registry/store.go的对象继承Interface这个接口以及实现了以下接口
storage
接下来查看Interface这个接口到底是谁实现了,顾名思义,这个就是etcdv2以及etcdv3实现了具体实现代码在/src/k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/storage这个路径下的etcd etcd3两个文件夹,这里就不去详细讲解了
讲到这里,基本上流程走完了,BeforeCreate这个函数里的
ddsesd
方法就是生成podName的具体实现过程

...
if len(objectMeta.GetGenerateName()) > 0 && len(objectMeta.GetName()) == 0 {
		objectMeta.SetName(strategy.GenerateName(objectMeta.GetGenerateName()))
	}
...

实现
再分析发现每个k8s资源对象都实现了Store这个对象的方法

store
基本上都有rest storage 目录以及策略的方法方便操作以及存储数据到etcd
$GOPATH/src/k8s.io/kubernetes/pkg/registry/core/rest/storage_core.go
这里有各种资源汇集生成NewLegacyRESTStorage
在注册路由的时候使用到
$GOPATH/src/k8s.io/kubernetes/pkg/master/master.go

func (m *Master) InstallLegacyAPI(c *completedConfig, restOptionsGetter generic.RESTOptionsGetter, legacyRESTStorageProvider corerest.LegacyRESTStorageProvider) {
	legacyRESTStorage, apiGroupInfo, err := legacyRESTStorageProvider.NewLegacyRESTStorage(restOptionsGetter)
	if err != nil {
		glog.Fatalf("Error building core storage: %v", err)
	}

	controllerName := "bootstrap-controller"
	coreClient := coreclient.NewForConfigOrDie(c.GenericConfig.LoopbackClientConfig)
	bootstrapController := c.NewBootstrapController(legacyRESTStorage, coreClient, coreClient, coreClient)
	m.GenericAPIServer.AddPostStartHookOrDie(controllerName, bootstrapController.PostStartHook)
	m.GenericAPIServer.AddPreShutdownHookOrDie(controllerName, bootstrapController.PreShutdownHook)

	if err := m.GenericAPIServer.InstallLegacyAPIGroup(genericapiserver.DefaultLegacyAPIPrefix, &apiGroupInfo); err != nil {
		glog.Fatalf("Error in registering group versions: %v", err)
	}
}

同时每个k8s资源都实现RESTStorageProvider该接口
jiekou
同时每个k8s资源都有Strategy策略方法,是为了存储到etcd的时候选择存储策略
storage
到此为止,分析了statefuleset的生成podName的方法已经搞定,接下来继续
b. deployment生成podName方式
deployment继承的接口是controller.RSControlInterface,原因是需要扩容缩容,滚动升级,回滚等操作,具体生成podName的流程如下,ReplicaSetController实现了controller.PodControlInterface,基本都一样
DeploymentController.syncHandler —>syncDeployment–>getReplicaSetsForDeployment—> NewReplicaSetControllerRefManager—> NewBaseController —>syncHandler(属性值)—>syncReplicaSet—>manageReplicas —>CreatePodsWithControllerRef—>createPods
getNewReplicaSet函数
dsa

CreatePodsWithControllerRef
CreatePodsWithControllerRef

createPods 原理跟statefulset一样,都是在BreforeCreate方法中添加5个随机字符串,这里就不多说了
pods

原理和statefulset一样,都是增加队列,消费队列,逻辑,流程都是差不多的,接下来的 c. job生成podName方式 d. daemonset生成podName方式 e. replicaset生成podName方式 f. cronjob生成podName方式也差不多,这里就不再详细分析了

容器名字生成规则

源代码
$GOPATH/src/k8s.io/kubernetes/pkg/kubelet/dockershim/naming.go
demo
dd-demo
SanboxName生成规则
demd

ContainerName生成规则
con
例子
docker-ps

容器名字生成规则具体请参考:
容器生成规则

猜你喜欢

转载自blog.csdn.net/qq_21816375/article/details/85810825
今日推荐