kuberntes主节点控制平面由:API Server , controller manager和scheduler组成
kuberntes的核心是他的api服务器,但是api服务器是如何工作的呢,下面我们就仔细研究下他的HTTP接口
API服务器的HTTP接口
从客户机的角度来看, 由于性能原因, 用JSON 或者protocol buffer协议来暴露其RESTful HTTP API
API服务器的HTTP接口再处理请求的时候,使用HTTP verbs(或者说是http方法)来查询和操作kuberntes资源
- 使用HTTP GET 来检索具有特定资源类型或者资源集合或者资源列表 比如pod
- 使用HTTP POST 动词来更新现有资源,比如更改pod的容器镜像
- 使用HTTP PATCH 来对现有资源进行部分更新, 使用的参考, 参见kuberntes官网的文档 Use a JSON merge patch to update a Deployment
- 使用HTTP DELETE来删除资源
在实际使用中我们用kubectl来操作我们想要的资源, 用kubectl来充当http的发起者, 然后使用相关HTTP verbs来做相应的请求, 这是官网的kubectl操作手册kuberntes-api-v1.14 , 可以看到日常我们使用的kubectl get
, kubectl patch
等等
API相关概念
先让我们认识下kuberntes API的相关概念术语
Kind
每一个object都有一个kind字段, 在JSON中是用小写kind表示 , 在golang中是用大写Kind表示 , 以pod做例子, 对于pod这种类型的kind, 有三种类别
- 表示系统中持久化的实体(a persistent entity in the system) , 比如Pod 或者Endpoints, 他们是存在于多个namespaces当中
- 第二种就是: Lists 它表示一种或者多种kind的集合 , 比如PodLists 或者NodeLists , 当你使用
kubectl get pods
就能得到他们 - 第三种就是拥有特种用途的类型 , 他们用于特定操作和非持久化实体 , 比如/binding 或者/scale , 这2个子http path就是用于特定资源的特定操作的, scale就是扩缩容 , 还有比如对于kuberntes的服务发现来说 使用apigroup和apiresource 对于错误结果, 用status来表示
在kuberntes当中 , 一个gloang type对应一个kind类型 ,所以对于golang来说 , kind是用单数表示而且首字母大写
API group
API group就是 kinds的集合, 是一种类型的集合 比如在kuberntes当中 , Job和ScheduledJob这样的批处理对象就是同属于一个 API group: batch , 他们同属于离线业务
version
每个API group可以同时存在与多个versions当中 , 比如v1beta1 , v1alpha1 , v1中
Resource
通常来说, 一个小写的复数词 ,比如pods , 他标识一组HTTP端口 , 或者说是标识一组HTTP path , 他表示系统中某种对象类型的CRUD语义 ,比如
- …../pods , 他表示列出了该类型的所以实例,这种情况下它返回一个podLists
- ……/pods/nginxs 他表示列出了列表中某个具体 的 , 他返回一个pod
除了可以实现CRUD, 资源还可以有进一步的端点来执行特定操作 , 比如…/pod/nginx/port-forward, …/pod/nginx/exec, or …/pod/nginx/logs
resource和kind经常可能弄混,他们的区别是什么呢
- resource对应HTTP路径
- kind是这些端点返回和接受的对象类型,以及持久化到etcd里的对象类型
resource是API group和version的一部分, 统称为GroupVersionResource (or GVR) , GVR唯一的定义了一个HTTP路径, 例如 默认名称空间的路径是/apis/batch/v1/namespaces/default/jobs
.
每个kind都存在于一个api group和version中 , 并通过GroupVersionKind (GVK) 来标识
GVR和GVK是相关联的 , GVK通过GVR标识的HTTP路径来提供服务, 将GVK映射到GVR的过程就叫做REST mapping.
Kubernetes API Versioning
kuberntes可以在不同API路径上支持多个API版本 , 不同的API版本意味着不同的稳定性和支持
- Alpha level (e.g., v1alpha1) , 通常来说是禁用的, 它是用来测试的, 可以在下次版本更迭的时候不经过通知就删除
- Beta level (e.g., v2beta3) , 默认是启用的 , 意味着经过了良好的测试, 然而的beta或稳定版中,语义可能发生变化
- Stable 意味着GA, 比如v1 它将会出现在后续版本中
在kuberntes HTTP API空间中 我们也区分核心组和非核心组 , 核心组是指/api/v1
以下的所以内容 , 非核心组指的就是/apis/$NAME/$VERSION
的内容了 , 由于历史的原因核心组并不像规定的那样位于/apis/core/v1
下面 , 那是因为在API组这个概念引入之前,核心组就已经存在了
当然还有第三中的HTTP路径 , 即不与资源对齐的路径 , 比如/metrics, /logs, or /healthz , 他们是有特殊功用的一些路径
如何使用命令行来使用API
1 | $ kubectl proxy --port=8080 |
这个命令可以让kuberntes代理到我们本地计算机, 允许我们通过直接向HTTP发请求, 返回一个JSON
比如
1 | $ curl http://127.0.0.1:8080/apis/batch/v1 |
当然在最近版的版的kubectl上 可以用kubectl来代替curl
1 | kubectl get --raw /apis/batch/v1 |
你可以使用命令来看api-resource
1 | $ kubectl api-resourcesNAME |
API如何处理请求
如图
服务器处理请求流程
- HTTP请求先由DefaultBuildHandlerChain()注册的一系列过滤器处理 , 这个函数位于k8s.io/apiserver/pkg/server/config.go上 它对请求进行一系列过滤操作, 经过验证的就返回相应HTTP返回码
- 接下来, 看请求的路径, 通过handler路由到各种程序中, k8s.io/apiserver/pkg/server/handler.go
- 每个API group都注册了一个handler , 详情参见k8s.io/apiserver/pkg/endpoints/groupversion.go和k8s.io/apiserver/pkg/endpoints/installer.go 它接受HTTP请求和上下文 , 并从etcd储存中检索和传递请求的对象
那DefaultBuildHandlerChain()到底发生了什么呢, 让我们看下源代码
1 | //源代码位置:kuberntes/apiserver/pkg/server/config.go |
先来看第一个:
WithPanicRecovery() Takes care of recovery and log panics 位于server/filters/wrap.go
WithRequestInfo()
将RequestInfo附加在上下文 定义在endpoints/filters/requestinfo.go
WithWaitGroup()
将所以非长时间的请求添加到等待组, 用于优雅的关闭 ,定义在server/filters/waitgroup.go
WithTimeoutForNonLongRunningRequests()
超时非长时间运行的请求(如大多数 GET、 PUT、 POST 和 DELETE 请求) ,与长时间运行的请求(如 watches 和 proxy 请求)形成对比 定义在server/filters/timeout.go
WithCORS()
提供了一个 CORS 实现。 是跨区域资源共享的缩写,是一种允许 JavaScript 嵌入到 HTML 页面中,使 XMLHttpRequests 成为一个不同于 JavaScript 起源的域的机制 , 定义在server/filters/cors.go
WithAuthentication()
尝试验证人或机器用户的身份,并将用户信息存储在提供的上下文中。 一旦成功,就会从请求中删除 Authorization HTTP 标头。 如果身份验证失败,则返回 HTTP 401状态代码 定义在endpoints/filters/authentication.go
WithAudit()
用所有传入请求的审计日志信息处理程序。 审计日志条目包含诸如请求的源 IP、用户调用操作以及请求的命名空间等信息 定义在admission/audit.go
WithImpersonation()
通过检查试图更改用户的请求(类似于 sudo)来处理用户的模拟 定义在endpoints/filters/impersonation.go
WithMaxInFlightLimit()
限制in-flight请求的数量 定义在server/filters/maxinflight.go
WithAuthorization()
通过调用授权模块检查权限,并将所有授权请求传递给多路复用器,多路复用器将请求分发给正确的处理程序。 如果用户没有足够的权限,则返回 HTTP 403状态代码。 现在的 Kubernetes 使用的是以角色为基础的准入控制器(RBAC) 定义在endpoints/filters/authorization.go
在处理完这些过滤器之后 , 实际的请求开始处理, 即到了上图中的第一个框(API HTTP handler)
对于/, /version, /apis, /healthz和非RESTful APIs这些请求 , 立即开始处理
对 RESTful 资源的请求进入请求管道,包括
admission
传入的对象要通过一个admission chain , 这个chain上有20不同的admission plug-ins ,这些admission plug-ins可以是上图第三个框(Mutating admission)或者是第四个框(object schema vaildation)或者是二者兼具 .
在mutating阶段 , 传入的请求可以被更改 , 通过admission configuration来更改 , 比如镜像拉去策略可以设置成Always, IfNotPresent Never
在object schema validation阶段就是纯粹的验证了, 比如pod的字段有效性 ,或者创建namespace之前验证其存不存在
validation
对传入的对象进行更大逻辑的验证 , 比如字符串检查 , 验证pod的容器名称 , 它存在与系统中的每个对象中
etcd-backed CRUD logic
这里实现了“ The HTTP Interface of The API Server”中的不同动词; 例如,更新逻辑从 etcd 中读取对象,检查是否有其他用户在“ Optimistic Concurrency”意义上修改了对象,如果没有,则将请求对象写入 etcd
备注: admission chain里的20中plug-ins如下:
在kuberntes1.14中,他们的顺序是:AlwaysAdmit, NamespaceAutoProvision, NamespaceLifecycle, NamespaceExists, SecurityContextDeny, LimitPodHardAntiAffinityTopology, PodPreset, LimitRanger, ServiceAccount, NodeRestriction, TaintNodesByCondition, AlwaysPullImages, ImagePolicyWebhook, PodSecurityPolicy, PodNodeSelector, Priority, DefaultTolerationSeconds, PodTolerationRestriction, DenyEscalatingExec, DenyExecOnPrivileged, EventRateLimit, ExtendedResourceToleration, PersistentVolumeLabel, DefaultStorageClass, StorageObjectInUseProtection, OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota, and AlwaysDeny