控制平面组件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资源时,不能仅看当下需求,也要为未来预留充分。


## 访问控制概览
Kubernetes API的每个请求都会经过多阶段的访问控制之后才会被接受,这包括认证、授权以及准入控制(Admission Control)等。

1. 一个request发送到api
2. handler接收到请求以后,
3. 请求会被发送到认证和鉴权两个模块
4. Mutating(变形)支持webhook,可以通过一些webhook针对对象做一些修改
5. 走入kubernetes自定义的对象,检验变形后的对象是不是符合kubernetes的规范
6. 通过Validating的插件调用webhook添加校验的逻辑
7. 存入etcd,完成持久化
### 访问控制细节

# 认证
开启TLS时,所有的请求都需要首先认证。Kubernetes 支持多种认证机制,并支持同时开启多
个认证插件( 只要有一一个认证通过即可)。如果认证成功,则用户的username会传入授权模块
做进一步授权验证;而对于认证失败的请求则返回HTTP 401。
## 构建符合kubernetes规范的认证服务
需要依照Kubernetes规范,构建认证服务,用来认证tokenreview request。
**构建认证服务。**
●认证服务需要满足如下Kubernetes的规范:
- URL: https://authn.example.com/authenticate
- Method: POST
- Input:
- Output:

```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老图

#### RBAC新图

### role 和 clusterrole

### Rolebinding

## 账号/组的管理
角色绑定(Role Binding)是将角色中定义的权限赋予一个或者一用户。它包含若干主体(用户、组或服务账户)的列表和对这些主体所获得的角色的引用。
组的概念:
●当与外部认证系统对接时,用户信息(Userlnfo) 可包含Group信息,授权可针对用户群组;
●当对ServiceAccount授权时,Group代表某个namespace下的所有ServiceAccount。

## 针对群组授权

## 划分系统角色
**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可绕过鉴权,当需要做管理操作又没有权限时可以使用(不推荐)。`

## 运营过程中出现过的陷阱
案例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)在授权后对请求做进一步的验证或添加默认参数。不同于授权和认证只关心请求的用户和操作,准入控制还处理请求的内容,并且仅对创建、更新、删除或连接(如代理)等有效,而对读操作无效。
- 准入控制支持同时开启多个插件,它们依次调用,只有全部插件都通过的请求才可以放过进入系统。
# 限流
### 计数器固定窗口算法

### 计数器滑动窗口算法

### 漏洞算法

### 令牌桶算法

## API server中的限流

## 传统限流方法的局限性
●粒度粗
`无法为不同用户,不同场景设置不通的限流.`
●单队列
`共享限流窗口/桶,一个坏用户可能会将整个系统堵塞,其他正常用户的请求无法被及时处理。`
●不公平
`正常用户的请求会被排到队尾,无法及时处理而饿死。`
●无优先级
`重要的系统指令一并被限流,系统故障难以恢复。`

## 搭建多租户的kubernetes集群






# 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)
```