控制平面组件api-server

## API Server kube-apiserver是Kubernetes最重要的核心组件之一,主要提供以下的功能: ●提供集群管理的REST API接口,包括认证授权、数据校验以及集群状态变更等; ●提供其他模块之间的数据交互和通信的枢纽(`其他模块通过API Server查询或修改数据,只有API Server才直接操作etcd,其次作为用户,不应该直接连etcd,在kubernetes的技术栈里面应该只有API Server自己去连etcd,因为API Server这边可以对etcd做一些保护操作,比如限流,认证鉴权,它是挡在etcd之前的守护者,API server本身有缓存的机制,所以很多的读操作直接在API server这边直接处理掉了,他不会再把这个请求转到etcd里面去,这样的话其实有效的减少了整个集群对etcd的并发请求`) ## 高可用的API server `高可用 = 冗余部署 + 负载均衡` **构建高可用的多副本apiserver** apiserver是无状态的Rest Server,无状态所以方便Scale Up / down。 **负载均衡** 1. 在多个apiserver实例之.上,配置负载均衡 2. 证书可能需要加上LoadbalancerVIP重新生成 ### 设置合适的缓存大小 ### 客户端尽量使用长连接 ### 预留充足的CPU,内存资源 随着集群中节点数量不断增多, API Server对CPU和内存的开销也不断增大。过少的CPU资源会降低其处理效率,过少的内存资源会导致Pod被0OMKilled,直接导致服务不可用。在规划API Server资源时,不能仅看当下需求,也要为未来预留充分。 ![image.png](https://cos.easydoc.net/97954506/files/l180030l.png) ![image.png](https://cos.easydoc.net/97954506/files/l1805eup.png) ## 访问控制概览 Kubernetes API的每个请求都会经过多阶段的访问控制之后才会被接受,这包括认证、授权以及准入控制(Admission Control)等。 ![image.png](https://cos.easydoc.net/97954506/files/l17uwis2.png) 1. 一个request发送到api 2. handler接收到请求以后, 3. 请求会被发送到认证和鉴权两个模块 4. Mutating(变形)支持webhook,可以通过一些webhook针对对象做一些修改 5. 走入kubernetes自定义的对象,检验变形后的对象是不是符合kubernetes的规范 6. 通过Validating的插件调用webhook添加校验的逻辑 7. 存入etcd,完成持久化 ### 访问控制细节 ![image.png](https://cos.easydoc.net/97954506/files/l17v9cn0.png) # 认证 开启TLS时,所有的请求都需要首先认证。Kubernetes 支持多种认证机制,并支持同时开启多 个认证插件( 只要有一一个认证通过即可)。如果认证成功,则用户的username会传入授权模块 做进一步授权验证;而对于认证失败的请求则返回HTTP 401。 ## 构建符合kubernetes规范的认证服务 需要依照Kubernetes规范,构建认证服务,用来认证tokenreview request。 **构建认证服务。** ●认证服务需要满足如下Kubernetes的规范: - URL: https://authn.example.com/authenticate - Method: POST - Input: - Output: ![image.png](https://cos.easydoc.net/97954506/files/l17w0675.png) ```golang package main import ( "context" "encoding/json" "log" "net/http" "github.com/google/go-github/github" "golang.org/x/oauth2" authentication "k8s.io/api/authentication/v1beta1" ) func main() { http.HandleFunc("/authenticate", func(w http.ResponseWriter, r *http.Request) { //解码认证请求 decoder := json.NewDecoder(r.Body) var tr authentication.TokenReview err := decoder.Decode(&tr) if err != nil { log.Println("[Error]", err.Error()) w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]interface{}{ "apiVersion": "authentication.k8s.io/v1beta1", "kind": "TokenReview", "status": authentication.TokenReviewStatus{ Authenticated: false, }, }) return } log.Print("receving request") // Check User //转发认证请求到认证服务器 ts := oauth2.StaticTokenSource( &oauth2.Token{AccessToken: tr.Spec.Token}, ) tc := oauth2.NewClient(context.Background(), ts) client := github.NewClient(tc) user, _, err := client.Users.Get(context.Background(), "") if err != nil { log.Println("[Error]", err.Error()) w.WriteHeader(http.StatusUnauthorized) json.NewEncoder(w).Encode(map[string]interface{}{ "apiVersion": "authentication.k8s.io/v1beta1", "kind": "TokenReview", "status": authentication.TokenReviewStatus{ Authenticated: false, }, }) return } log.Printf("[Success] login as %s", *user.Login) //认证结果并返回给API Server w.WriteHeader(http.StatusOK) trs := authentication.TokenReviewStatus{ Authenticated: true, User: authentication.UserInfo{ Username: *user.Login, UID: *user.Login, }, } json.NewEncoder(w).Encode(map[string]interface{}{ "apiVersion": "authentication.k8s.io/v1beta1", "kind": "TokenReview", "status": trs, }) }) log.Println(http.ListenAndServe(":3000", nil)) } ``` 具体使用配置 `https://github.com/cncamp/101/tree/master/module6/authn-webhook` # 鉴权 授权主要是用于对集群资源的访问控制,通过检查请求包含的相关属性值,与相对应的访问策略 相比较,API请求必须满足某些策略才能被处理。 跟认证类似,Kubernetes 也支持多种授权机制,并支持同时开启多个授权插件(只要有一个验 证通过即可)。 如果授权成功,则用户的请求会发送到准入控制模块做进一步的请 求验证;对于授权失败的请求 则返回HTTP 403。 **Kubernetes授权仅处理以下的请求属性:** ●user, group, extra ●API、请求方法(如get、post、 update、patch和delete) 和请求路径( 如/api) ●请求资源和子资源 ●API Group ●Namespace **目前,Kubernetes支持以下授权插件:** ●ABAC ●RBAC ``` Rule:规则,规则是一组属于不同 API Group 资源上的一组操作的集合 可能存在的操作有:【create get delete list update edit watch exec patch】 Role 和 ClusterRole:角色和集群角色,这两个对象都包含上面的 Rules 元素,二者的区别在于,在 Role 中, 定义的规则只适用于单个命名空间,也就是和 namespace 关联的,而 ClusterRole 是集群范围内的,因此定义的 规则不受命名空间的约束。另外 Role 和 ClusterRole 在Kubernetes 中都被定义为集群内部的 API 资源,和我 们前面学习过的 Pod、Deployment 这些对象类似,都是我们集群的资源对象,所以同样的可以使用 YAML 文件来描 述,用 kubectl 工具来管理 Subject:主体 User Account:用户 Group:组 关联多个账户的 Service Account:服务帐号 通过 Kubernetes API 来管理的一些用户帐号,和 namespace 进行关联 RoleBinding 和 ClusterRoleBinding:角色绑定和集群角色绑定 简单来说就是把声明的 Subject (主体) 和我们的 Role 进行绑定的过程(给某个用户绑定上操作的权限),二者的 区别也是作用范围的区别:RoleBinding 只会影响到当前 namespace 下面的资源操作权限,而 ClusterRoleBinding 会影响到所有的 namespace。 ``` ●Webhook ●Node ### ABAC VS RBAC - ABAC (Attribute Based Access Control)本来是不错的概念,但是在Kubernetes中的实现比较难于管理和理解,而且需要对Master所在节点的SSH和文件系统权限,要使得对授权的变更成功生效,还需要重新启动API Server。 - 而RBAC的授权策略可以利用kubectl或者Kubernetes API直接进行配置。RBAC可以授权给用户,让用户有权进行授权管理,这样就可以无需接触节点,直接进行授权管理。RBAC在Kubernetes中被映射为API资源和操作。 #### RBAC老图 ![image.png](https://cos.easydoc.net/97954506/files/l17wvad3.png) #### RBAC新图 ![image.png](https://cos.easydoc.net/97954506/files/l17wwj6z.png) ### role 和 clusterrole ![image.png](https://cos.easydoc.net/97954506/files/l17x6qz6.png) ### Rolebinding ![image.png](https://cos.easydoc.net/97954506/files/l17x8i7u.png) ## 账号/组的管理 角色绑定(Role Binding)是将角色中定义的权限赋予一个或者一用户。它包含若干主体(用户、组或服务账户)的列表和对这些主体所获得的角色的引用。 组的概念: ●当与外部认证系统对接时,用户信息(Userlnfo) 可包含Group信息,授权可针对用户群组; ●当对ServiceAccount授权时,Group代表某个namespace下的所有ServiceAccount。 ![image.png](https://cos.easydoc.net/97954506/files/l17xbirz.png) ## 针对群组授权 ![image.png](https://cos.easydoc.net/97954506/files/l17xf34p.png) ## 划分系统角色 **User** ●管理员 - 所有资源的所有权限? ●普通用户 - 是否有该用户创建的namespace下的所有object的操作权限? - 对其他用户的namespace资源是否可读,是否可写? **SystemAccount** - SystemAccount 是开发者(kubernetes developer或者domain developer)创建应用后,应用于apiserver通讯需要的身份。 - 用户可以创建自定的ServiceAccount, Kubernetes也为每个namespace创建default ServiceAccount。 - Default ServiceAccount通常需要给定权限以后才能对apiserver做写操作。 实际上,Role 本身就是一个 Kubernetes 的 API 对象,定义如下所示: ``` kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: mynamespace name: example-role rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"] ``` 首先,这个 Role 对象指定了它能产生作用的 Namepace 是:mynamespace。 Namespace 是 Kubernetes 项目里的一个逻辑管理单位。不同 Namespace 的 API 对象,在通过 kubectl 命令进行操作的时候,是互相隔离开的。 比如,kubectl get pods -n mynamespace。 当然,这仅限于逻辑上的“隔离”,Namespace 并不会提供任何实际的隔离或者多租户能力。而在前面文章中用到的大多数例子里,我都没有指定 Namespace,那就是使用的是默认 Namespace:default。 然后,这个 Role 对象的 rules 字段,就是它所定义的权限规则。在上面的例子里,这条规则的含义就是:允许“被作用者”,对 mynamespace 下面的 Pod 对象,进行 GET、WATCH 和 LIST 操作。 那么,这个具体的“被作用者”又是如何指定的呢?这就需要通过 RoleBinding 来实现了。当然,RoleBinding 本身也是一个 Kubernetes 的 API 对象。它的定义如下所示: ``` kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: example-rolebinding namespace: mynamespace subjects: - kind: User name: example-user apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: example-role apiGroup: rbac.authorization.k8s.io ``` 可以看到,这个 RoleBinding 对象里定义了一个 subjects 字段,即“被作用者”。它的类型是 User,即 Kubernetes 里的用户。这个用户的名字是 example-user。 可是,在 Kubernetes 中,其实并没有一个叫作“User”的 API 对象。而且,我们在前面和部署使用 Kubernetes 的流程里,既不需要 User,也没有创建过 User。 实际上,Kubernetes 里的“User”,也就是“用户”,只是一个授权系统里的逻辑概念。它需要通过外部认证服务,比如 Keystone,来提供。或者,你也可以直接给 APIServer 指定一个用户名、密码文件。那么 Kubernetes 的授权系统,就能够从这个文件里找到对应的“用户”了。当然,在大多数私有的使用环境中,我们只要使用 Kubernetes 提供的内置“用户”,就足够了。 接下来,我们会看到一个 roleRef 字段。正是通过这个字段,RoleBinding 对象就可以直接通过名字,来引用我们前面定义的 Role 对象(example-role),从而定义了“被作用者(Subject)”和“角色(Role)”之间的绑定关系。 需要再次提醒的是,Role 和 RoleBinding 对象都是 Namespaced 对象(Namespaced Object),它们对权限的限制规则仅在它们自己的 Namespace 内有效,roleRef 也只能引用当前 Namespace 里的 Role 对象。 那么,对于非 Namespaced(Non-namespaced)对象(比如:Node),或者,某一个 Role 想要作用于所有的 Namespace 的时候,我们又该如何去做授权呢? 这时候,我们就必须要使用 ClusterRole 和 ClusterRoleBinding 这两个组合了。这两个 API 对象的用法跟 Role 和 RoleBinding 完全一样。只不过,它们的定义里,没有了 Namespace 字段,如下所示: ``` kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: example-clusterrole rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"] ``` ``` kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: example-clusterrolebinding subjects: - kind: User name: example-user apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: example-clusterrole apiGroup: rbac.authorization.k8s.io ``` 上面的例子里的 ClusterRole 和 ClusterRoleBinding 的组合,意味着名叫 example-user 的用户,拥有对所有 Namespace 里的 Pod 进行 GET、WATCH 和 LIST 操作的权限。 更进一步地,在 Role 或者 ClusterRole 里面,如果要赋予用户 example-user 所有权限,那你就可以给它指定一个 verbs 字段的全集,如下所示: ``` verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] ``` 这些就是当前 Kubernetes(v1.11)里能够对 API 对象进行的所有操作了。 类似地,Role 对象的 rules 字段也可以进一步细化。比如,你可以只针对某一个具体的对象进行权限设置,如下所示: ``` rules: - apiGroups: [""] resources: ["configmaps"] resourceNames: ["my-config"] verbs: ["get"] ``` 这个例子就表示,这条规则的“被作用者”,只对名叫“my-config”的 ConfigMap 对象,有进行 GET 操作的权限。 而正如我前面介绍过的,在大多数时候,我们其实都不太使用“用户”这个功能,而是直接使用 Kubernetes 里的“内置用户”。 这个由 Kubernetes 负责管理的“内置用户”,正是我们前面曾经提到过的:ServiceAccount。接下来,我通过一个具体的实例来为你讲解一下为 ServiceAccount 分配权限的过程。首先,我们要定义一个 ServiceAccount。它的 API 对象非常简单,如下所示: ``` apiVersion: v1 kind: ServiceAccount metadata: namespace: mynamespace name: example-sa ``` 可以看到,一个最简单的 ServiceAccount 对象只需要 Name 和 Namespace 这两个最基本的字段。 然后,我们通过编写 RoleBinding 的 YAML 文件,来为这个 ServiceAccount 分配权限: ``` kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: example-rolebinding namespace: mynamespace subjects: - kind: ServiceAccount name: example-sa namespace: mynamespace roleRef: kind: Role name: example-role apiGroup: rbac.authorization.k8s.io ``` 可以看到,在这个 RoleBinding 对象里,subjects 字段的类型(kind),不再是一个 User,而是一个名叫 example-sa 的 ServiceAccount。而 roleRef 引用的 Role 对象,依然名叫 example-role,也就是我在这篇文章一开始定义的 Role 对象。 这时候,用户的 Pod,就可以声明使用这个 ServiceAccount 了,比如下面这个例子: ``` apiVersion: v1 kind: Pod metadata: namespace: mynamespace name: sa-token-test spec: containers: - name: nginx image: nginx:1.7.9 serviceAccountName: example-sa ``` 在这个例子里,我定义了 Pod 要使用的要使用的 ServiceAccount 的名字是:example-sa。 等这个 Pod 运行起来之后,我们就可以看到,该 ServiceAccount 的 token,也就是一个 Secret 对象,被 Kubernetes 自动挂载到了容器的 /var/run/secrets/kubernetes.io/serviceaccount 目录下,如下所示: ``` $ kubectl describe pod sa-token-test -n mynamespace Name: sa-token-test Namespace: mynamespace ... Containers: nginx: ... Mounts: /var/run/secrets/kubernetes.io/serviceaccount from example-sa-token-vmfg6 (ro) ``` 这时候,我们可以通过 kubectl exec 查看到这个目录里的文件: ``` $ kubectl exec -it sa-token-test -n mynamespace -- /bin/bash root@sa-token-test:/# ls /var/run/secrets/kubernetes.io/serviceaccount ca.crt namespace token ``` 如上所示,容器里的应用,就可以使用这个 ca.crt 来访问 APIServer 了。更重要的是,此时它只能够做 GET、WATCH 和 LIST 操作。因为 example-sa 这个 ServiceAccount 的权限,已经被我们绑定了 Role 做了限制。 如果一个 Pod 没有声明 serviceAccountName,Kubernetes 会自动在它的 Namespace 下创建一个名叫 default 的默认 ServiceAccount,然后分配给这个 Pod。 但在这种情况下,这个默认 ServiceAccount 并没有关联任何 Role。也就是说,此时它有访问 APIServer 的绝大多数权限。当然,这个访问所需要的 Token,还是默认 ServiceAccount 对应的 Secret 对象为它提供的,如下所示。 ``` $kubectl describe sa default Name: default Namespace: default Labels: <none> Annotations: <none> Image pull secrets: <none> Mountable secrets: default-token-s8rbq Tokens: default-token-s8rbq Events: <none> $ kubectl get secret NAME TYPE DATA AGE kubernetes.io/service-account-token 3 82d $ kubectl describe secret default-token-s8rbq Name: default-token-s8rbq Namespace: default Labels: <none> Annotations: kubernetes.io/service-account.name=default kubernetes.io/service-account.uid=ffcb12b2-917f-11e8-abde-42010aa80002 Type: kubernetes.io/service-account-token Data ==== ca.crt: 1025 bytes namespace: 7 bytes token: <TOKEN数据> ``` 可以看到,Kubernetes 会自动为默认 ServiceAccount 创建并绑定一个特殊的 Secret:它的类型是kubernetes.io/service-account-token;它的 Annotation 字段,声明了kubernetes.io/service-account.name=default,即这个 Secret 会跟同一 Namespace 下名叫 default 的 ServiceAccount 进行绑定。 除了前面使用的“用户”(User),Kubernetes 还拥有“用户组”(Group)的概念,也就是一组“用户”的意思。如果你为 Kubernetes 配置了外部认证服务的话,这个“用户组”的概念就会由外部认证服务提供。 而对于 Kubernetes 的内置“用户”ServiceAccount 来说,上述“用户组”的概念也同样适用。 实际上,一个 ServiceAccount,在 Kubernetes 里对应的“用户”的名字是: ``` system:serviceaccount:<Namespace名字>:<ServiceAccount名字> ``` 而它对应的内置“用户组”的名字,就是: ``` system:serviceaccounts:<Namespace名字> ``` 这两个对应关系,请你一定要牢记。比如,现在我们可以在 RoleBinding 里定义如下的 subjects: ``` subjects: - kind: Group name: system:serviceaccounts:mynamespace apiGroup: rbac.authorization.k8s.io ``` 这就意味着这个 Role 的权限规则,作用于 mynamespace 里的所有 ServiceAccount。这就用到了“用户组”的概念。而下面这个例子: ``` subjects: - kind: Group name: system:serviceaccounts apiGroup: rbac.authorization.k8s.io ``` 就意味着这个 Role 的权限规则,作用于整个系统里的所有 ServiceAccount。最后,值得一提的是,在 Kubernetes 中已经内置了很多个为系统保留的 ClusterRole,它们的名字都以 system: 开头。你可以通过 kubectl get clusterroles 查看到它们。 一般来说,这些系统 ClusterRole,是绑定给 Kubernetes 系统组件对应的 ServiceAccount 使用的。 比如,其中一个名叫 system:kube-scheduler 的 ClusterRole,定义的权限规则是 kube-scheduler(Kubernetes 的调度器组件)运行所需要的必要权限。你可以通过如下指令查看这些权限的列表: ``` $ kubectl describe clusterrole system:kube-scheduler Name: system:kube-scheduler ... PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- ... services [] [] [get list watch] replicasets.apps [] [] [get list watch] statefulsets.apps [] [] [get list watch] replicasets.extensions [] [] [get list watch] poddisruptionbudgets.policy [] [] [get list watch] pods/status [] [] [patch update] ``` 这个 system:kube-scheduler 的 ClusterRole,就会被绑定给 kube-system Namesapce 下名叫 kube-scheduler 的 ServiceAccount,它正是 Kubernetes 调度器的 Pod 声明使用的 ServiceAccount。 除此之外,Kubernetes 还提供了四个预先定义好的 ClusterRole 来供用户直接使用: 1. cluster-admin; 2. admin; 3. edit; 4. view。 通过它们的名字,你应该能大致猜出它们都定义了哪些权限。比如,这个名叫 view 的 ClusterRole,就规定了被作用者只有 Kubernetes API 的只读权限。 而我还要提醒你的是,上面这个 cluster-admin 角色,对应的是整个 Kubernetes 项目中的最高权限(verbs=*),如下所示: ``` $ kubectl describe clusterrole cluster-admin -n kube-system Name: cluster-admin Labels: kubernetes.io/bootstrapping=rbac-defaults Annotations: rbac.authorization.kubernetes.io/autoupdate=true PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- *.* [] [] [*] [*] [] [*] ``` 所以,请你务必要谨慎而小心地使用 cluster-admin。 ## 实现方案 在cluster创建时,创建自定义的role,比如namespace-creator。 Namespace-creator role定义用户可操作的对象和对应的读写操作。 #### 创建自定义的namespace admission controller: ●当namespace创建请求被处理时,获取当前用户信息并annotate到namespace。 #### 创建RBAC controller: ●Watch namespace的创建事件; ●获取当前namespace的创建者信息; ●在当前namespace创建rolebinding对象,并将namespace-creator角色和用户绑定。 ## 与权限相关的其他最佳实践 ClusterRole是非namespace绑定的,针对整个集群生效。 通常需要创建一个管 理员角色,并且绑定给开发运营团队成员。 ThirdPartyResource和CustomResourceDefinition是全局资源,普通用户创建ThirdPartyResource以后,需要管理员授予相应权限后才能真正操作该对象。 针对所有的角色管理,建议创建spec,用源代码驱动: - 虽然可以通过edit操作来修改权限,但后期会导致权限管理混乱,可能会有很多临时创建出 来的角色和角色绑定对象,重复绑定某一个资源权限。 - 权限是可以传递的,用户A可以将其对某对象的某操作,抽取成一个权限,并赋给用户B;防止海量的角色和角色绑定对象,因为大量的对象会导致鉴权效率低,同时给apiserver增加负担。 ServiceAccount也需要授权的,否则你的component可能无法操作某对象。 `Tips: SSH到master节点通过insecure port访问apiserver可绕过鉴权,当需要做管理操作又没有权限时可以使用(不推荐)。` ![image.png](https://cos.easydoc.net/97954506/files/l1w9t266.png) ## 运营过程中出现过的陷阱 案例1: 1. 研发人员为提高系统效率,将update方法修改为patch; 2. 研发人员本地非安全测试环境测试通过; 3. 上生产,发现不work; 4. 原因:忘记更新rolebinding,对应的serviceaccount没有patch权限。 案例2: 1. 研发人员创建CRD,并针对该CRD编程; 2. 上生产后不工作; 3. 原因,该CRD未授权,对应的组件get不到对应的CRD资源。 # 准入 ### 准入控制 **为资源增加自定义属性** ●作为多租户集群方案中的一环,我们需要在namespace的准入控制中,获取用户信息,并将用户信息更新的namespace的annotation。 ●只有当namespace中有有效用户信息时,我们才可以在namespace创建时,自动绑定用户权限,namespace才可用。 **配额管理** ●原因:资源有限,如何限定某个用户有多少资源? . 方案: ●预定义每个namespace的ResourceQuota, 并把spec保存为configmap; ``` 1. 用户可以创建多少个Pod: BestEffortPod; QoSPod。 2. 用户可以创建多少个service; 3. 用户可以创建多 少个ingress; . 4. 用户可以创建多少个service VIP; ``` ●创建ResourceQuota Controller: `监控namespace创建事件,当namespace创建时,在该namespace创建对应的ResourceQuota对象。` ●apiserver中开启ResourceQuota的admission plugin。 ### 准入控制 - 准入控制(Admission Control)在授权后对请求做进一步的验证或添加默认参数。不同于授权和认证只关心请求的用户和操作,准入控制还处理请求的内容,并且仅对创建、更新、删除或连接(如代理)等有效,而对读操作无效。 - 准入控制支持同时开启多个插件,它们依次调用,只有全部插件都通过的请求才可以放过进入系统。 # 限流 ### 计数器固定窗口算法 ![image.png](https://cos.easydoc.net/97954506/files/l17z1ifi.png) ### 计数器滑动窗口算法 ![image.png](https://cos.easydoc.net/97954506/files/l17z51md.png) ### 漏洞算法 ![image.png](https://cos.easydoc.net/97954506/files/l17z5rbs.png) ### 令牌桶算法 ![image.png](https://cos.easydoc.net/97954506/files/l17z6ayb.png) ## API server中的限流 ![image.png](https://cos.easydoc.net/97954506/files/l17z8m6b.png) ## 传统限流方法的局限性 ●粒度粗 `无法为不同用户,不同场景设置不通的限流.` ●单队列 `共享限流窗口/桶,一个坏用户可能会将整个系统堵塞,其他正常用户的请求无法被及时处理。` ●不公平 `正常用户的请求会被排到队尾,无法及时处理而饿死。` ●无优先级 `重要的系统指令一并被限流,系统故障难以恢复。` ![image.png](https://cos.easydoc.net/97954506/files/l17ze9cs.png) ## 搭建多租户的kubernetes集群 ![image.png](https://cos.easydoc.net/97954506/files/l180654z.png) ![image.png](https://cos.easydoc.net/97954506/files/l1806gi7.png) ![image.png](https://cos.easydoc.net/97954506/files/l1806pbi.png) ![image.png](https://cos.easydoc.net/97954506/files/l1807004.png) ![image.png](https://cos.easydoc.net/97954506/files/l18077cv.png) ![image.png](https://cos.easydoc.net/97954506/files/l1807gh8.png) # API server代码走读 `https://cncamp.notion.site/kube-apiserver-10d5695cbbb14387b60c6d622005583d` ```golang cmd/kube-apiserver/app/server.go:NewAPIServerCommand()--> completedOptions, err := Complete(s)--> s.Etcd.WatchCacheSizes, err = serveroptions.WriteWatchCacheSizes(sizes) Run(completedOptions, genericapiserver.SetupSignalHandler())-->CreateServerChain()--> CreateServerChain()--> CreateKubeAPIServerConfig--> buildGenericConfig(s.ServerRunOptions, proxyTransport)--> genericapiserver.NewConfig(legacyscheme.Codecs) // create codec factory for encoding/decoding controlplane.DefaultAPIResourceConfigSource() // group version: enabled/disabled storageFactoryConfig.Complete(s.Etcd) completedStorageFactoryConfig.New()--> // register access path in etcd for all k8s objects storageFactory.AddCohabitatingResources(networking.Resource("networkpolicies"), extensions.Resource("networkpolicies")) s.Etcd.ApplyWithStorageFactoryTo(storageFactory, genericConfig)--> c.AddHealthChecks() c.RESTOptionsGetter = &StorageFactoryRestOptionsFactory{Options: *s, StorageFactory: factory} // 认证 s.Authentication.ApplyTo()--> // clientcert, serviceaccount, bootstrap token, authenticatorConfig.New()--> newWebhookTokenAuthenticator(config) // webhook // 鉴权 BuildAuthorizer(s, genericConfig.EgressSelector, versionedInformers)--> authorizationConfig.New()--> rbacAuthorizer := rbac.New()--> // if authorizer type is rbac // 准入 buildServiceResolver(s.EnableAggregatorRouting, genericConfig.LoopbackClientConfig.Host, versionedInformers) admissionConfig.New(proxyTransport, genericConfig.EgressSelector, serviceResolver)--> admission.PluginInitializer{webhookPluginInitializer, kubePluginInitializer} net.SplitHostPort(s.Etcd.StorageConfig.Transport.ServerList[0]) utilwait.PollImmediate(etcdRetryInterval, etcdRetryLimit*etcdRetryInterval, preflight.EtcdConnection{ServerList: s.Etcd.StorageConfig.Transport.ServerList}.CheckEtcdServers) capabilities.Initialize() // allow privillage? config := &controlplane.Config{} createAPIExtensionsConfig() createAPIExtensionsServer()--> apiextensionsConfig.Complete().New(delegateAPIServer)--> s.AddHealthChecks(delegateCheck) // 注册通用handler installAPI(s, c.Config) // register generic api handler e.g. index, profiling, metrics, flow control CreateKubeAPIServer(kubeAPIServerConfig, apiExtensionsServer.GenericAPIServer) kubeAPIServerConfig.Complete().New(delegateAPIServer) m.InstallLegacyAPI(&c, c.GenericConfig.RESTOptionsGetter, legacyRESTStorageProvider)--> m.GenericAPIServer.AddPostStartHookOrDie(controllerName, bootstrapController.PostStartHook)--> controlplane.controller.Start()--> async.NewRunner(c.RunKubernetesNamespaces, c.RunKubernetesService, repairClusterIPs.RunUntil, repairNodePorts.RunUntil) m.GenericAPIServer.AddPreShutdownHookOrDie(controllerName, bootstrapController.PreShutdownHook) // 注册core group API handler m.GenericAPIServer.InstallLegacyAPIGroup() // register handler for /api restStorageProviders := []RESTStorageProvider{appsrest.StorageProvider{}} m.InstallAPIs(c.ExtraConfig.APIResourceConfigSource, c.GenericConfig.RESTOptionsGetter, restStorageProviders...)--> // 初始化对应group中对象的watch cache restStorageBuilder.NewRESTStorage(apiResourceConfigSource, restOptionsGetter)--> // trigger appsrest.StorageProvider p.v1Storage(apiResourceConfigSource, restOptionsGetter)--> daemonsetstore.NewREST(restOptionsGetter)--> store.CompleteWithOptions(options)--> opts, err := options.RESTOptions.GetRESTOptions(e.DefaultQualifiedResource)--> // etcd.go ret.Decorator = genericregistry.StorageWithCacher()--> cacherstorage.NewCacherFromConfig(cacherConfig)--> watchCache := newWatchCache()--> // 注册API handler m.GenericAPIServer.InstallAPIGroups(apiGroupsInfo...)--> // register handler for /apis s.installAPIResources(APIGroupPrefix, apiGroupInfo, openAPIModels)--> apiGroupVersion.InstallREST(s.Handler.GoRestfulContainer)--> discovery.NewAPIVersionHandler(g.Serializer, g.GroupVersion, staticLister{apiResources}) createAggregatorServer(aggregatorConfig, kubeAPIServer.GenericAPIServer, apiExtensionsServer.Informers)--> apiServices := apiServicesToRegister(delegateAPIServer, autoRegistrationController) server.PrepareRun()--> s.GenericAPIServer.PrepareRun()--> s.installHealthz() s.installLivez() s.installReadyz() prepared.Run(stopCh)--> s.runnable.Run(stopCh)--> // preparedGenericAPIServer.Run() s.NonBlockingRun(delayedStopCh)--> s.SecureServingInfo.Serve(s.Handler, s.ShutdownTimeout, internalStopCh)--> RunServer(secureServer, s.Listener, shutdownTimeout, stopCh) ```