我们都知道 apiserver 是 kubernetes 里一个组件,可以简单地认为其是一个 web 应用,提供 http 接口(亦称 restful)服务,如同 CRUD 程序员所熟知的 tomcat,同样地 CRUD 程序员使用 MySQL 存储业务数据,而 apiserver 则使用 etcd 存储数据。
从使用角度看,web 服务器要做到:1. 监听端口,2. 映射 url 和处理方法,我们来看一看 apiserver 是如何做的?
调试启动 apiserver(别问我是怎么启动的,那是另外一个话题,欢迎关注留言)。
1 // cmd/kube-apiserver/apiserver.go:32 2 func main() { 3 command := app.NewAPIServerCommand() 4 code := cli.Run(command) 5 os.Exit(code) 6 }
在 secure_serving 文件的 Serve 方法中断点,结合注释,能清晰地看到启动了 http(s) server。
以 "kubectl get namespaces" 为例探究 url 的映射过程,该条命令对应的rest 请求是:
http get /api/v1/namespaces
接着看 url 是如何映射的,经过一番排查,发现 2 处关键调用点:
pkg/controlplane/instance.go:524 InstallLegacyAPI() vendor/k8s.io/apiserver/pkg/endpoints/installer.go:98 Install()
跟踪下来的调用栈如下:
url 和处理函数的映射关系保存在了 WebService 类中,类的定义如下:
1 // WebService holds a collection of Route values that bind a Http Method + URL Path to a function. 2 type WebService struct { 3 rootPath string 4 pathExpr *pathExpression // cached compilation of rootPath as RegExp 5 routes []Route 6 produces []string 7 consumes []string 8 pathParameters []*Parameter 9 filters []FilterFunction 10 documentation string 11 apiVersion string 12 13 typeNameHandleFunc TypeNameHandleFunction 14 15 dynamicRoutes bool 16 17 // protects 'routes' if dynamic routes are enabled 18 routesLock sync.RWMutex 19 }
这是 go-restful 包(https://github.com/emicklei/go-restful),apiserver 依赖它实现 url 和处理函数的映射,进一步查看 Route 的数据内容,包括了 url 和处理函数:
命令行执行 kubectl get namespaces,apiserver 处理请求的调用栈如下,所以确定了 api/v1/namespaces 对应的处理函数(即pkg/registry/core/namespace/storage/storage.go 中 List 方法),继续深挖这一函数映射的时机点。
继续 debug,发现具体的代码调用细节:
1 func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService) (*metav1.APIResource, *storageversion.ResourceInfo, error) { 2 // 省略大段代码 3 lister, isLister := storage.(rest.Lister) 4 5 case "LIST": // List all resources of a kind. 6 doc := "list objects of kind " + kind 7 if isSubresource { 8 doc = "list " + subresource + " of objects of kind " + kind 9 } 10 handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, deprecated, removedRelease, 11 restfulListResource(lister, watcher, reqScope, false, a.minRequestTimeout)) 12 handler = utilwarning.AddWarningsHandler(handler, warnings) 13 route := ws.GET(action.Path).To(handler). 14 Doc(doc). 15 Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). 16 Operation("list"+namespaced+kind+strings.Title(subresource)+operationSuffix). 17 Produces(append(storageMeta.ProducesMIMETypes(action.Verb), allMediaTypes...)...). 18 Returns(http.StatusOK, "OK", versionedList). 19 Writes(versionedList) 20 if err := AddObjectParams(ws, route, versionedListOptions); err != nil { 21 return nil, nil, err 22 } 23 switch { 24 case isLister && isWatcher: 25 doc := "list or watch objects of kind " + kind 26 if isSubresource { 27 doc = "list or watch " + subresource + " of objects of kind " + kind 28 } 29 route.Doc(doc) 30 case isWatcher: 31 doc := "watch objects of kind " + kind 32 if isSubresource { 33 doc = "watch " + subresource + "of objects of kind " + kind 34 } 35 route.Doc(doc) 36 } 37 addParams(route, action.Params) 38 routes = append(routes, route) 39 }
对于 namespace 而言,storage 是 pkg/registry/core/namespace/storage/storage.go 中的 REST 类,因为 REST 类实现了 rest.Lister 接口,所以可以强转。代码第 13 行中,action.path 和 handler 就是我们苦苦追寻的 url 和函数了。
1 // pkg/registry/core/namespace/storage/storage.go:44 2 // rest implements a RESTStorage for namespaces 3 type REST struct { 4 store *genericregistry.Store 5 status *genericregistry.Store 6 } 7 8 func (r *REST) NewList() runtime.Object { 9 return r.store.NewList() 10 } 11 12 func (r *REST) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) { 13 return r.store.List(ctx, options) 14 } 15 16 func (e *REST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { 17 return e.store.ConvertToTable(ctx, object, tableOptions) 18 } 19 20 // staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go:103 21 // Lister is an object that can retrieve resources that match the provided field and label criteria. 22 type Lister interface { 23 // NewList returns an empty object that can be used with the List call. 24 // This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object) 25 NewList() runtime.Object 26 // List selects resources in the storage which match to the selector. 'options' can be nil. 27 List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) 28 // TableConvertor ensures all list implementers also implement table conversion 29 TableConvertor 30 }
接下来的代码就不跟了,最终是映射到了 namespace storage 的 List 方法中,前面已经验证过了。这一系列操作基本是 apiserver 大部分资源 crud 的套路。
- apiserver urlapiserver url apiserver kube-apiserver kube-apiserver kubernetes apiserver kube kube-apiserver apiserver kube kube-apiserver kubernetes apiserver流程 kube-apiserver kubernetes apiserver scheme kube-apiserver apiserver namespace版本 kube-apiserver apiserver内存 工具 kube-apiserver apiserver接口 证书