什么是registry
在Kubernetes中,registry可以对ETCD中的Kubernetes各类型进行增删查改操作,并对外提供了生成apiGroupInfo的方法。这里的registry包含了两层含义,Storage和Registry,如NodeStorage和NodeRegistry。Storage提供了通用的增删查改方法,Regsitry是对Storage的封装,提供了具体类型的增删查改方法。registry定义在/pkg/registry目录下,主要包含:
- apps: 定义statefulsets相关的registry信息;
- authentication: 定义tokenreview相关的registry信息;
- authorization: 定义localsubjectaccessreview, selfsubjectaccessreview, subjectaccessreview相关的registry信息;
- autoscaling: 定义horizontalpodautoscaler相关的registry信息;
- batch: 定义scheduledjobs相关的registry信息;
- certificates: 定义certificatesigningrequests相关的registry信息;
- core: 定义componentstatus, configmap, controller, endpoint, event, limitrange, namespace, node, persistentvolume, persistentvolumeclaim, pod, podtemplate, rangeallocation, resourcequota, secret, service, serviceaccount的registry信息;
- extensions: 定义replicationcontrollers, daemonset, deployment, ingress, networkpolicy, podsecuritypolicy, replicaset等相关的registry信息;
- generic: 公共模块,对storage模块进行封装;
- policy: 定义poddisruptionbudgets相关的registry信息;
- rbac: 定义cluserrole, role等相关的registry信息;
- storage: 定义storageclass相关的registry信息。
以上就是/pkg/registry目录下的主要目录,每个子目录都对应一个APIGroup,如core对应的是v1,每个子目录下都有个rest的目录,其中core目录实现了NewLegacyRESTStorage()方法生成对应的apiGroupInfo,其他目录实现了NewRESTStorage()方法生成对应的apiGroupInfo。关于apiGroupInfo将在以后分析,现在只需要知道apiGroupInfo是联系registry和apiserver的通道。
在NewLegacyRESTStorage()方法中,会调用NewStorage()生成storage,如nodeStorage等,进一步地生成restStorageMap,然后生成apiGroupInfo。
其他APIGroup使用NewRESTStorage(),会调用NewREST()生成storage,如rolesStorage。
我们以core包下的node为例来看下NewStorage()的实现,以rbac包下的role为例来看下NewREST()的实现。
nodeStorage
node定义在/pkg/registry/core/node目录下。我们先来看下/pkg/registry/core/rest/storage_core.go中的NewLegacyRESTStorage()是如何生成nodeStorage和NodeRegistry的,代码如下:
1 2 3
| nodeStorage, err := nodeetcd.NewStorage(restOptionsGetter(api.Resource("nodes")), c.KubeletClientConfig, c.ProxyTransport) ...... restStorage.NodeRegistry = node.NewRegistry(nodeStorage.Node)
|
restOptionsGetter()
我们遇到的第一个问题就是restOptionsGetter()函数定义在哪。在NewLegacyRESTStorage()中,restOptionsGetter是以参数的形式传入的; NewLegacyRESTStorage()会被/pkg/master/master.go中的InstallLegacyAPI()方法调用,restOptionsGetter仍是以参数的形式传入;InstallLegacyAPI()会被/pkg/master/master.go中的New()调用,在New()上有restOptionsFactory的定义,可以看出,storageDecorator为registry.StorageWithCacher或generic.UndecoratedStorage。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| restOptionsFactory := restOptionsFactory{ deleteCollectionWorkers: c.DeleteCollectionWorkers, enableGarbageCollect ion: c.GenericConfig.EnableGarbageCollection, storageFactory: c.StorageFactory, } if c.EnableWatchCache { restOptionsFactory.storageDecorator = registry.StorageWithCacher } else { restOptionsFactory.storageDecorator = generic.UndecoratedStorage } ...... m.InstallLegacyAPI(c.Config, restOptionsFactory.NewFor, legacyRESTStorageProvider)
|
所以restOptionsGetter就是restOptionsFactory的NewFor()方法,定义在/pkg/master/master.go中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func (f restOptionsFactory) NewFor(resource unversioned.GroupResource) generic.RESTOptions { storageConfig, err := f.storageFactory.NewConfig(resource) if err != nil { glog.Fatalf("Unable to find storage destination for %v, due to %v", resource, err.Error()) } return generic.RESTOptions{ StorageConfig: storageConfig, Decorator: f.storageDecorator, DeleteCollectionWorkers: f.deleteCollectionWorkers, EnableGarbageCollection: f.enableGarbageCollection, ResourcePrefix: f.storageFactory.ResourcePrefix(resource), } }
|
NewFor()会生成一个generic.RESTOptions。这里还需要注意下,generic.RESTOptions中的Decorator一般情况下就是generic.UndercoratedStorage()方法。
NewStorage()
node的NewStorage()定义在/pkg/registry/core/node/etcd/etcd.go中,主要流程如下:
- 生成ETCD helper(现在只需知道ETCD helper是对ETCD client的封装即可),使用opts.Decorator()生成,即registry.StorageWithCacher()或generic.UndercoratedStorage()方法,这些将在以后分析;
- 生成store,store为genericStore,store中包含ETCD helper;
- 生成nodeREST;
- 生成NodeStorage。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| func NewStorage(opts generic.RESTOptions, kubeletClientConfig client.KubeletClientConfig, proxyTransport http.RoundTripper) (*NodeStorage, error) { prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.NodeList{} } storageInterface, dFunc := opts.Decorator( opts.StorageConfig, cachesize.GetWatchCacheSizeByResource(cachesize.Nodes), &api.Node{}, prefix, node.Strategy, newListFunc, node.NodeNameTriggerFunc) store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.Node{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { return prefix }, KeyFunc: func(ctx api.Context, name string) (string, error) { return registry.NoNamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Node).Name, nil }, PredicateFunc: node.MatchNode, QualifiedResource: api.Resource("nodes"), EnableGarbageCollection: opts.EnableGarbageCollection, DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: node.Strategy, UpdateStrategy: node.Strategy, DeleteStrategy: node.Strategy, ExportStrategy: node.Strategy, Storage: storageInterface, DestroyFunc: dFunc, } statusStore := *store statusStore.UpdateStrategy = node.StatusStrategy nodeREST := &REST{Store: store, proxyTransport: proxyTransport} statusREST := &StatusREST{store: &statusStore} proxyREST := &noderest.ProxyREST{Store: store, ProxyTransport: proxyTransport} nodeGetter := client.NodeGetterFunc(func(nodeName string) (*api.Node, error) { obj, err := nodeREST.Get(api.NewContext(), nodeName) if err != nil { return nil, err } node, ok := obj.(*api.Node) if !ok { return nil, fmt.Errorf("unexpected type %T", obj) } return node, nil }) connectionInfoGetter, err := client.NewNodeConnectionInfoGetter(nodeGetter, kubeletClientConfig) if err != nil { return nil, err } nodeREST.connection = connectionInfoGetter proxyREST.Connection = connectionInfoGetter return &NodeStorage{ Node: nodeREST, Status: statusREST, Proxy: proxyREST, KubeletConnectionInfo: connec tionInfoGetter, }, nil }
|
可以看出,NodeStorage本质是一个genericStore,关于Store,将在以后分析,现在只需知道Store中定义有Create(), Delete(), Get(), List(), Watch()等方法。
NewRegistry()
现在再来看来registry。registry是对storage的封装,可以更方便的增删查改ETCD中的对象。APIServer中的restHandler使用人是更通用的Storage中的方法对对象进行增删查改,感觉只有ApiServer中会使用registry,特别是在APIServer的controller(实现创建default和kube-system空间及default下kubernetes服务)中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| type Registry interface { ListNodes(ctx api.Context, options *api.ListOptions) (*api.NodeList, error) CreateNode(ctx api.Context, node *api.Node) error UpdateNode(ctx api.Context, node *api.Node) error GetNode(ctx api.Context, nodeID string) (*api.Node, error) DeleteNode(ctx api.Context, nodeID string) error WatchNodes(ctx api.Context, options *api.ListOptions) (watch.Interface, error) } type storage struct { rest.StandardStorage } func NewRegistry(s rest.StandardStorage) Registry { return &storage{s} }
|
NewRegistry()可以生成一个nodeRegistry。nodeRegistry就实现了node的增删查改的方法,如ListNodes(), CreateNode(), UpdateNode(), GetNode(), DeleteNode()和WatchNodes()等方法。
rolesStorage
role定义在/pkg/registry/rbac/role目录下,我们先来看/pkg/registry/rbac/rest/storage_rbac.go,storage_rabc.go中定义有NewRESTStorage()方法生成apiGroupInfo,NewRESTStorage()会调用v1alpha1Storage()方法,在v1alpha1Storage()中有:
1
| rolesStorage := roleetcd.NewREST(restOptionsGetter(rbac.Resource("roles")))
|
restOptionsGetter已经分析过。
NewREST()
rolesStorage的NewREST()定义在/pkg/registry/rbac/role/etcd/etcd.go中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| func NewREST(opts generic.RESTOptions) *REST { prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &rbac.RoleList{} } storageInterface, dFunc := opts.Decorator( opts.StorageConfig, cachesize.GetWatchCacheSizeByResource(cachesize.Roles), &rbac.Role{}, prefix, role.Strategy, newListFunc, storage.NoTriggerPublisher, ) store := ®istry.Store{ NewFunc: func() runtime.Object { return &rbac.Role{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { return registry.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, id string) (string, error) { return registry.NamespaceKeyFunc(ctx, prefix, id) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*rbac.Role).Name, nil }, PredicateFunc: role.Matcher, QualifiedResource: rbac.Resource("roles"), EnableGarbageCollection: opts.EnableGarbageCollection, DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: role.Strategy, UpdateStrategy: role.Strategy, DeleteStrategy: role.Strategy, Storage: storageInterface, DestroyFunc: dFunc, } return &REST{store} }
|
可以看出,roleStorage本质是一个genericStore。
NewRegistry()
NewRegistry()可以生成一个rolesRegistry,rolesRegistry定义有ListRoles(), CreateRole(), UpdateRole(), GetRole(), DeleteRole(), WatchRoles()等方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| type Registry interface { ListRoles(ctx api.Context, options *api.ListOptions) (*rbac.RoleList, error) CreateRole(ctx api.Context, role *rbac.Role) error UpdateRole(ctx api.Context, role *rbac.Role) error GetRole(ctx api.Context, name string) (*rbac.Role, error) DeleteRole(ctx api.Context, name string) error WatchRoles(ctx api.Context, options *api.ListOptions) (watch.Interface, error) } type storage struct { rest.StandardStorage } func NewRegistry(s rest.StandardStorage) Registry { return &storage{s} }
|
总结
本次分析介绍了Storage和Registry,主要分析了nodeStorage和nodeRegistry,rolesStorage和rolesRegsitry。其中NodeStorage是对generic.Storage的封装,generic.Storage中定义了对ETCD中对象的通用操作;NodeRegistry是对NodeStorage的封装,NodeRegistry定义了关于node的增删查改操作;rolesStorage和rolesRegistry也一样,可以用来操作role。