本节重点介绍 :
-
k8s代码库和模块地址
- 下载 apiserver源码
-
apiserver中监控源码阅读
k8s源码地址分布
k8s代码库
- 访问github上k8s仓库,readme中给出了k8s 模块的代码地址
- 举例图片
组件仓库列表 地址
Repositories currently staged here:
k8s.io/api
k8s.io/apiextensions-apiserver
k8s.io/apimachinery
k8s.io/apiserver
k8s.io/cli-runtime
k8s.io/client-go
k8s.io/cloud-provider
k8s.io/cluster-bootstrap
k8s.io/code-generator
k8s.io/component-base
k8s.io/controller-manager
k8s.io/cri-api
k8s.io/csi-api
k8s.io/csi-translation-lib
k8s.io/kube-aggregator
k8s.io/kube-controller-manager
k8s.io/kube-proxy
k8s.io/kube-scheduler
k8s.io/kubectl
k8s.io/kubelet
k8s.io/legacy-cloud-providers
k8s.io/metrics
k8s.io/mount-utils
k8s.io/pod-security-admission
k8s.io/sample-apiserver
k8s.io/sample-cli-plugin
k8s.io/sample-controller
下载 apiserver 源码
go get -d k8s.io/apiserver
分析apiserver 监控源码
以qps指标 apiserver_request_total为例
定位源码位置
- 在源码目录全文搜索
apiserver_request_total
,选择.go文件 - 举例图片
- 发现位于 D:\go_path\pkg\mod\k8s.io\apiserver@v0.22.1\pkg\endpoints\metrics\metrics.go
requestCounter = compbasemetrics.NewCounterVec(&compbasemetrics.CounterOpts{Name: "apiserver_request_total",Help: "Counter of apiserver requests broken out for each verb, dry run value, group, version, resource, scope, component, and HTTP response code.",StabilityLevel: compbasemetrics.STABLE,},[]string{"verb", "dry_run", "group", "version", "resource", "subresource", "scope", "component", "code"},)
- 分析,在这里定义了指标,并且指定了相关的标签,可以和我们在prometheus中查询到的结果匹配上
apiserver_request_total{code="0", component="apiserver", contentType="application/json", group="apiregistration.k8s.io", instance="172.20.70.205:6443", job="kubernetes-apiservers", resource="apiservices", scope="cluster", verb="WATCH", version="v1beta1"}
9014
apiserver_request_total{code="0", component="apiserver", contentType="application/json", group="apps", instance="172.20.70.205:6443", job="kubernetes-apiservers", resource="daemonsets", scope="cluster", verb="WATCH", version="v1"}
8863
apiserver_request_total{code="0", component="apiserver", contentType="application/json", group="apps", instance="172.20.70.205:6443", job="kubernetes-apiservers", resource="deployments", scope="cluster", verb="WATCH", version="v1"}
8830
apiserver_request_total{code="0", component="apiserver", contentType="application/json", group="crd.projectcalico.org", instance="172.20.70.205:6443", job="kubernetes-apiservers", resource="bgpconfigurations", scope="cluster", verb="WATCH", version="v1"}
5575
apiserver_request_total{code="0", component="apiserver", contentType="application/json", group="crd.projectcalico.org", instance="172.20.70.205:6443", job="kubernetes-apiservers", resource="bgppeers", scope="cluster", verb="WATCH", version="v1"}
2791
apiserver_request_total{code="0", component="apiserver", contentType="application/json", group="crd.projectcalico.org", instance="172.20.70.205:6443", job="kubernetes-apiservers", resource="blockaffinities", scope="cluster", verb="WATCH", version="v1"}
2942
apiserver_request_total{code="0", component="apiserver", contentType="application/json", group="crd.projectcalico.org", instance="172.20.70.205:6443", job="kubernetes-apiservers", resource="clusterinformations", scope="cluster", verb="WATCH", version="v1"}
2770
处理请求的metric 函数 MonitorRequest
- 代码位置 D:\go_path\pkg\mod\k8s.io\apiserver@v0.22.1\pkg\endpoints\metrics\metrics.go
- 源码如下
// MonitorRequest handles standard transformations for client and the reported verb and then invokes Monitor to record
// a request. verb must be uppercase to be backwards compatible with existing monitoring tooling.
func MonitorRequest(req *http.Request, verb, group, version, resource, subresource, scope, component string, deprecated bool, removedRelease string, httpCode, respSize int, elapsed time.Duration) {// We don't use verb from <requestInfo>, as this may be propagated from// InstrumentRouteFunc which is registered in installer.go with predefined// list of verbs (different than those translated to RequestInfo).// However, we need to tweak it e.g. to differentiate GET from LIST.reportedVerb := cleanVerb(CanonicalVerb(strings.ToUpper(req.Method), scope), verb, req)dryRun := cleanDryRun(req.URL)elapsedSeconds := elapsed.Seconds()requestCounter.WithContext(req.Context()).WithLabelValues(reportedVerb, dryRun, group, version, resource, subresource, scope, component, codeToString(httpCode)).Inc()// MonitorRequest happens after authentication, so we can trust the username given by the requestinfo, ok := request.UserFrom(req.Context())if ok && info.GetName() == user.APIServerUser {apiSelfRequestCounter.WithContext(req.Context()).WithLabelValues(reportedVerb, resource, subresource).Inc()}if deprecated {deprecatedRequestGauge.WithContext(req.Context()).WithLabelValues(group, version, resource, subresource, removedRelease).Set(1)audit.AddAuditAnnotation(req.Context(), deprecatedAnnotationKey, "true")if len(removedRelease) > 0 {audit.AddAuditAnnotation(req.Context(), removedReleaseAnnotationKey, removedRelease)}}requestLatencies.WithContext(req.Context()).WithLabelValues(reportedVerb, dryRun, group, version, resource, subresource, scope, component).Observe(elapsedSeconds)// We are only interested in response sizes of read requests.if verb == "GET" || verb == "LIST" {responseSizes.WithContext(req.Context()).WithLabelValues(reportedVerb, group, version, resource, subresource, scope, component).Observe(float64(respSize))}
}
- 其中 WithLabelValues设置metric的值 ,Inc()代表counter +1
requestCounter.WithContext(req.Context()).WithLabelValues(reportedVerb, dryRun, group, version, resource, subresource, scope, component, codeToString(httpCode)).Inc()
- 这个是k8s在prometheus sdk上做的封装,位置 D:\go_path\pkg\mod\k8s.io\component-base@v0.22.1\metrics\counter.go
func (v *CounterVec) WithLabelValues(lvs ...string) CounterMetric {if !v.IsCreated() {return noop // return no-op counter}if v.LabelValueAllowLists != nil {v.LabelValueAllowLists.ConstrainToAllowedList(v.originalLabels, lvs)}return v.CounterVec.WithLabelValues(lvs...)
}
自己请求的计数
- 如果userName是 system:apiserver ,那么把代表自身请求的metric apiserver_selfrequest_total +1
info, ok := request.UserFrom(req.Context())if ok && info.GetName() == user.APIServerUser {apiSelfRequestCounter.WithContext(req.Context()).WithLabelValues(reportedVerb, resource, subresource).Inc()}
要被废弃的api被请求
- metric丢弃的设置,apiserver_requested_deprecated_apis代表要被废弃的api被请求了,打印信息
if deprecated {deprecatedRequestGauge.WithContext(req.Context()).WithLabelValues(group, version, resource, subresource, removedRelease).Set(1)audit.AddAuditAnnotation(req.Context(), deprecatedAnnotationKey, "true")if len(removedRelease) > 0 {audit.AddAuditAnnotation(req.Context(), removedReleaseAnnotationKey, removedRelease)}}
- prometheus查询结果
apiserver_requested_deprecated_apis{group="apiregistration.k8s.io", instance="172.20.70.205:6443", job="kubernetes-apiservers", removed_release="1.22", resource="apiservices", version="v1beta1"}
1
apiserver_requested_deprecated_apis{group="authorization.k8s.io", instance="172.20.70.205:6443", job="kubernetes-apiservers", removed_release="1.22", resource="subjectaccessreviews", version="v1beta1"}
1
apiserver_requested_deprecated_apis{group="certificates.k8s.io", instance="172.20.70.205:6443", job="kubernetes-apiservers", removed_release="1.22", resource="certificatesigningrequests", version="v1beta1"}
1
apiserver_requested_deprecated_apis{group="extensions", instance="172.20.70.205:6443", job="kubernetes-apiservers", removed_release="1.22", resource="ingresses", version="v1beta1"}
1
apiserver_requested_deprecated_apis{group="networking.k8s.io", instance="172.20.70.205:6443", job="kubernetes-apiservers", removed_release="1.22", resource="ingressclasses", version="v1beta1"}
1
apiserver_requested_deprecated_apis{group="networking.k8s.io", instance="172.20.70.205:6443", job="kubernetes-apiservers", removed_release="1.22", resource="ingresses", version="v1beta1"}
1
apiserver_requested_deprecated_apis{group="scheduling.k8s.io", instance="172.20.70.205:6443", job="kubernetes-apiservers", removed_release="1.22", resource="priorityclasses", version="v1beta1"}
1
设置请求延迟值
- 代码如下
requestLatencies.WithContext(req.Context()).WithLabelValues(reportedVerb, dryRun, group, version, resource, subresource, scope, component).Observe(elapsedSeconds)
- requestLatencies定义了从0.05到60秒的bucket
requestLatencies = compbasemetrics.NewHistogramVec(&compbasemetrics.HistogramOpts{Name: "apiserver_request_duration_seconds",Help: "Response latency distribution in seconds for each verb, dry run value, group, version, resource, subresource, scope and component.",// This metric is used for verifying api call latencies SLO,// as well as tracking regressions in this aspects.// Thus we customize buckets significantly, to empower both usecases.Buckets: []float64{0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 40, 50, 60},StabilityLevel: compbasemetrics.STABLE,},[]string{"verb", "dry_run", "group", "version", "resource", "subresource", "scope", "component"},)
- 分位值查询
histogram_quantile(0.99, sum(rate(apiserver_request_duration_seconds_bucket{job="kubernetes-apiservers"}[5m])) by (verb, le))
- 结果图片
设置 http响应的大小histogram
- 设置代码
if verb == "GET" || verb == "LIST" {responseSizes.WithContext(req.Context()).WithLabelValues(reportedVerb, group, version, resource, subresource, scope, component).Observe(float64(respSize))}
- responseSizes定义了 1KB 到1GB的大小的bucket
responseSizes = compbasemetrics.NewHistogramVec(&compbasemetrics.HistogramOpts{Name: "apiserver_response_sizes",Help: "Response size distribution in bytes for each group, version, verb, resource, subresource, scope and component.",// Use buckets ranging from 1000 bytes (1KB) to 10^9 bytes (1GB).Buckets: compbasemetrics.ExponentialBuckets(1000, 10.0, 7),StabilityLevel: compbasemetrics.ALPHA,},[]string{"verb", "group", "version", "resource", "subresource", "scope", "component"},)
追踪MonitorRequest调用链
InstrumentRouteFunc在prometheus的HandlerFunc基础上封装了k8s的go-restful
// InstrumentRouteFunc works like Prometheus' InstrumentHandlerFunc but wraps
// the go-restful RouteFunction instead of a HandlerFunc plus some Kubernetes endpoint specific information.
func InstrumentRouteFunc(verb, group, version, resource, subresource, scope, component string, deprecated bool, removedRelease string, routeFunc restful.RouteFunction) restful.RouteFunction {return restful.RouteFunction(func(req *restful.Request, response *restful.Response) {requestReceivedTimestamp, ok := request.ReceivedTimestampFrom(req.Request.Context())if !ok {requestReceivedTimestamp = time.Now()}delegate := &ResponseWriterDelegator{ResponseWriter: response.ResponseWriter}//lint:file-ignore SA1019 Keep supporting deprecated http.CloseNotifier_, cn := response.ResponseWriter.(http.CloseNotifier)_, fl := response.ResponseWriter.(http.Flusher)_, hj := response.ResponseWriter.(http.Hijacker)var rw http.ResponseWriterif cn && fl && hj {rw = &fancyResponseWriterDelegator{delegate}} else {rw = delegate}response.ResponseWriter = rwrouteFunc(req, response)MonitorRequest(req.Request, verb, group, version, resource, subresource, scope, component, deprecated, removedRelease, delegate.Status(), delegate.ContentLength(), time.Since(requestReceivedTimestamp))})
}
registerResourceHandlers 中根据不同的verb处理
- 位置 D:\go_path\pkg\mod\k8s.io\apiserver@v0.22.1\pkg\endpoints\installer.go
- LIST的verb代码如下
case "LIST": // List all resources of a kind.doc := "list objects of kind " + kindif isSubresource {doc = "list " + subresource + " of objects of kind " + kind}handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, deprecated, removedRelease, restfulListResource(lister, watcher, reqScope, false, a.minRequestTimeout))handler = utilwarning.AddWarningsHandler(handler, warnings)route := ws.GET(action.Path).To(handler).Doc(doc).Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).Operation("list"+namespaced+kind+strings.Title(subresource)+operationSuffix).Produces(append(storageMeta.ProducesMIMETypes(action.Verb), allMediaTypes...)...).Returns(http.StatusOK, "OK", versionedList).Writes(versionedList)
- 上层被APIInstaller调用
// Install handlers for API resources.
func (a *APIInstaller) Install() ([]metav1.APIResource, []*storageversion.ResourceInfo, *restful.WebService, []error) {var apiResources []metav1.APIResourcevar resourceInfos []*storageversion.ResourceInfovar errors []errorws := a.newWebService()// Register the paths in a deterministic (sorted) order to get a deterministic swagger spec.paths := make([]string, len(a.group.Storage))var i int = 0for path := range a.group.Storage {paths[i] = pathi++}sort.Strings(paths)for _, path := range paths {apiResource, resourceInfo, 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)}if resourceInfo != nil {resourceInfos = append(resourceInfos, resourceInfo)}}return apiResources, resourceInfos, ws, errors
}
本节重点总结 :
-
k8s代码库和模块地址
- 下载 apiserver源码
-
apiserver中监控源码阅读