k8s零基础入门运维课程
k8s纯源码解读教程(3个课程内容合成一个大课程)
k8s运维进阶调优课程
k8s管理运维平台实战
k8s二次开发课程
cicd 课程
prometheus全组件的教程
- 01_prometheus零基础入门,grafana基础操作,主流exporter采集配置
- 02_prometheus全组件配置使用、底层原理解析、高可用实战
- 03_prometheus-thanos使用和源码解读
- 04_kube-prometheus和prometheus-operator实战和原理介绍
- 05_prometheus源码讲解和二次开发
- 06_prometheus监控k8s的实战配置和原理讲解,写go项目暴露业务指标
go语言课程
- golang基础课程
- golang实战课,一天编写一个任务执行系统,客户端和服务端架构
- golang运维开发项目之k8s网络探测实战
- golang运维平台实战,服务树,日志监控,任务执行,分布式探测
- golang运维开发实战课程之k8s巡检平台
直播答疑sre职业发展规划
k8s支持多种限流配置
为了防止突发流量影响apiserver可用性,k8s支持多种限流配置,包括:
- MaxInFlightLimit,server级别整体限流
- Client限流
- EventRateLimit, 限制event
- APF,更细力度的限制配置
MaxInFlightLimit限流
- apiserver默认可设置最大并发量(集群级别,区分只读与修改操作)
- 通过参数--max-requests-inflight代表只读请求
- --max-mutating-requests-inflight代表修改请求
- 可以简单实现限流。
源码解读
- 入口 GenericAPIServer.New中的添加hook
位置 D:\go_path\src\github.com\kubernetes\kubernetes\staging\src\k8s.io\apiserver\pkg\server\config.go
if c.FlowControl != nil { const priorityAndFairnessFilterHookName = "priority-and-fairness-filter" if !s.isPostStartHookRegistered(priorityAndFairnessFilterHookName) { err := s.AddPostStartHook(priorityAndFairnessFilterHookName, func(context PostStartHookContext) error { genericfilters.StartPriorityAndFairnessWatermarkMaintenance(context.StopCh) return nil }) if err != nil { return nil, err } } } else { const maxInFlightFilterHookName = "max-in-flight-filter" if !s.isPostStartHookRegistered(maxInFlightFilterHookName) { err := s.AddPostStartHook(maxInFlightFilterHookName, func(context PostStartHookContext) error { genericfilters.StartMaxInFlightWatermarkMaintenance(context.StopCh) return nil }) if err != nil { return nil, err } } }
- 意思是FlowControl为nil ,代表未启用 APF,API 服务器中的整体并发量将受到 kube-apiserver 的参数 --max-requests-inflight 和 --max-mutating-requests-inflight 的限制。
启动metrics观测的函数
func startWatermarkMaintenance(watermark *requestWatermark, stopCh <-chan struct{}) { // Periodically update the inflight usage metric. go wait.Until(func() { watermark.lock.Lock() readOnlyWatermark := watermark.readOnlyWatermark mutatingWatermark := watermark.mutatingWatermark watermark.readOnlyWatermark = 0 watermark.mutatingWatermark = 0 watermark.lock.Unlock() metrics.UpdateInflightRequestMetrics(watermark.phase, readOnlyWatermark, mutatingWatermark) }, inflightUsageMetricUpdatePeriod, stopCh) // Periodically observe the watermarks. This is done to ensure that they do not fall too far behind. When they do // fall too far behind, then there is a long delay in responding to the next request received while the observer // catches back up. go wait.Until(func() { watermark.readOnlyObserver.Add(0) watermark.mutatingObserver.Add(0) }, observationMaintenancePeriod, stopCh) }
WithMaxInFlightLimit代表限流处理函数
- 调用的入口在 D:\go_path\src\github.com\kubernetes\kubernetes\staging\src\k8s.io\apiserver\pkg\server\config.go
DefaultBuildHandlerChain中,还是判断FlowControl为nil就开启WithMaxInFlightLimit
if c.FlowControl != nil { requestWorkEstimator := flowcontrolrequest.NewWorkEstimator(c.StorageObjectCountTracker.Get) handler = filterlatency.TrackCompleted(handler) handler = genericfilters.WithPriorityAndFairness(handler, c.LongRunningFunc, c.FlowControl, requestWorkEstimator) handler = filterlatency.TrackStarted(handler, "priorityandfairness") } else { handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.LongRunningFunc) }
解读
如果limit num为0就不开启限流了
if nonMutatingLimit == 0 && mutatingLimit == 0 { return handler }
构造限流的chan,类型为长度=limit的 bool chan
var nonMutatingChan chan bool var mutatingChan chan bool if nonMutatingLimit != 0 { nonMutatingChan = make(chan bool, nonMutatingLimit) watermark.readOnlyObserver.SetX1(float64(nonMutatingLimit)) } if mutatingLimit != 0 { mutatingChan = make(chan bool, mutatingLimit) watermark.mutatingObserver.SetX1(float64(mutatingLimit)) }
检查是否是长时间运行的请求
// Skip tracking long running events. if longRunningRequestCheck != nil && longRunningRequestCheck(r, requestInfo) { handler.ServeHTTP(w, r) return }
使用BasicLongRunningRequestCheck检查是否是watch或者pprof debug等长时间运行的请求,因为这些请求不受限制,位置 D:\go_path\src\github.com\kubernetes\kubernetes\staging\src\k8s.io\apiserver\pkg\server\filters\longrunning.go
// BasicLongRunningRequestCheck returns true if the given request has one of the specified verbs or one of the specified subresources, or is a profiler request. func BasicLongRunningRequestCheck(longRunningVerbs, longRunningSubresources sets.String) apirequest.LongRunningRequestCheck { return func(r *http.Request, requestInfo *apirequest.RequestInfo) bool { if longRunningVerbs.Has(requestInfo.Verb) { return true } if requestInfo.IsResourceRequest && longRunningSubresources.Has(requestInfo.Subresource) { return true } if !requestInfo.IsResourceRequest && strings.HasPrefix(requestInfo.Path, "/debug/pprof/") { return true } return false } }
检查是只读操作还是修改操作,决定使用哪个chan限制
var c chan bool
isMutatingRequest := !nonMutatingRequestVerbs.Has(requestInfo.Verb)
if isMutatingRequest {
c = mutatingChan
} else {
c = nonMutatingChan
}
如果队列未满,有空的位置,则更新下排队数字
- 使用select 向c中写入true,如果能写入到说明队列未满
记录下对应的指标
select { case c <- true: // We note the concurrency level both while the // request is being served and after it is done being // served, because both states contribute to the // sampled stats on concurrency. if isMutatingRequest { watermark.recordMutating(len(c)) } else { watermark.recordReadOnly(len(c)) } defer func() { <-c if isMutatingRequest { watermark.recordMutating(len(c)) } else { watermark.recordReadOnly(len(c)) } }() handler.ServeHTTP(w, r)
default代表队列已满,但是如果请求的group中含有 system:masters,则放行
因为apiserver认为这个组是很重要的请求,不能被限流
if currUser, ok := apirequest.UserFrom(ctx); ok { for _, group := range currUser.GetGroups() { if group == user.SystemPrivilegedGroup { handler.ServeHTTP(w, r) return } } }
- group=system:masters 对应的clusterRole 为cluster-admin
队列已满,如果请求的group中没有 system:masters,则返回http 429错误
- http 429 代表当前有太多请求了,请重试
并设置 response 的header Retry-After =1
if isMutatingRequest { metrics.DroppedRequests.WithContext(ctx).WithLabelValues(metrics.MutatingKind).Inc() } else { metrics.DroppedRequests.WithContext(ctx).WithLabelValues(metrics.ReadOnlyKind).Inc() } metrics.RecordRequestTermination(r, requestInfo, metrics.APIServerComponent, http.StatusTooManyRequests) tooManyRequests(r, w)
Client限流
- 例如client-go默认的qps为5,但是只支持客户端限流,只能由各个发起端限制
- 集群管理员无法控制用户行为。
EventRateLimit
- EventRateLimit在1.13之后支持,只限制event请求
- 集成在apiserver内部webhoook中
- 可配置某个用户、namespace、server等event操作限制,通过webhook形式实现。
和文档一起学习
原理
- 具体原理可以参考提案,每个eventratelimit 配置使用一个单独的令牌桶限速器
- 每次event操作,遍历每个匹配的限速器检查是否能获取令牌,如果可以允许请求,否则返回429。
优点
- 实现简单,允许一定量的并发
可支持server/namespace/user等级别的限流
缺点
- 仅支持event,通过webhook实现只能拦截修改类请求
- 所有namespace的限流相同,没有优先级
APF,更细力度的限制配置
- API 优先级和公平性(APF)是MaxInFlightLimit限流的一种替代方案,设计文档见提案。
- API 优先级和公平性(1.15以上,alpha版本), 以更细粒度(byUser,byNamespace)对请求进行分类和隔离。 支持突发流量,通过使用公平排队技术从队列中分发请求从而避免饥饿。
- APF限流通过两种资源,PriorityLevelConfigurations定义隔离类型和可处理的并发预算量,还可以调整排队行为。 FlowSchemas用于对每个入站请求进行分类,并与一个 PriorityLevelConfigurations相匹配。
- 可对用户或用户组或全局进行某些资源某些请求的限制,如限制default namespace写services put/patch请求。
优点
- 考虑情况较全面,支持优先级,白名单等
可支持server/namespace/user/resource等细粒度级别的限流
缺点
- 配置复杂,不直观,需要对APF原理深入了解
功能较新,缺少生产环境验证
文档地址
- https://kubernetes.io/zh/docs...