S_lion's Studio

k8s-apiserver原理

字数统计: 3.5k阅读时长: 12 min
2021/09/26 Share

apiserver作用

其对外暴露了Kubernetes API。它是的 Kubernetes 核心控制层。它被设计为水平扩展,即通过部署更多实例来横向扩展。API Server 负责和 etcd 交互(其他组件不会直接操作 etcd,只有 API Server 这么做),是整个 kubernetes 集群的数据中心,所有的交互都是以 API Server 为核心的。API Server 提供了以下的功能:

  • 提供了Kubemetes 各类资源对象的增、删、改、查及watch等的http rest接口。是资源配额的入口。
  • 提供了完备的集群安全控制:API Server 提供的验证和授权保证了整个集群的安全。
  • 集群内部各个模块之间通信的枢纽:所有模块之间并不会互相调用,而是通过和 API Server 打交道来完成各自的工作。

kubernetes API server通过一个名为kube-apiserver的进程提供服务(运行在master节点),默认情况下kube-apiserver进程通过本机的8080端口(对应参数–insecure-port)提供REST服务。默认安装后会启动HTTPS安全端口(–secure-port=6443)来启动安全机制,加强REST API的安全性。

可以通过curl 127.0.0.1:8080/api/v1查看目前支持的资源对象种类。

apiserver之restful接口

在Kubernetes系统中,大多数情况下,API定义和实现都符合标准的HTTP REST格式, 比如通过标准的HTTP动词(POST、PUT、GET、DELETE)来完成对相关资源对象的查询、创建、修改、删除等操作。但同时Kubernetes 也为某些非标准的REST行为实现了附加的API接口,例如Watch某个资源的变化、进入容器执行某个操作等。另外,某些API接口可能违背严格的REST模式,因为接口不是返回单一的JSON对象,而是返回其他类型的数据,比如JSON对象流(Stream)或非结构化的文本日志数据等。

使用REST接口

  • GET /<资源名的复数格式>:获得某一类型的资源列表,例如GET /pods 返回一个Pod资源列表。

  • POST /<资源名的复数格式>:创建一个资源,该资源来自用户提供的JSON对象。

  • GET /<资源名复数格式>/<名字>:通过给出的名称(Name)获得单个资源,例如GET /pods/first 返回一个名称为“first”的Pod。

  • DELETE /<资源名复数格式>/<名字>:通过给出的名字删除单个资源,删除选项(DeleteOptions)中可以指定的优雅删除(Grace Deletion)的时间(GracePeriodSeconds),该可选项表明了从服务端接收到删除请求到资源被删除的时间间隔(单位为秒)。不同的类别(Kind)可能为优雅删除时间(Grace Period)申明默认值。用户提交的优雅删除时间将覆盖该默认值,包括值为0的优雅删除时间。

  • PUT /<资源名复数格式>/<名字>:通过给出的资源名和客户端提供的JSON对象来更新或创建资源。

  • PATCH /<资源名复数格式>/<名字>:选择修改资源详细指定的域。

    对于PATCH操作,目前Kubernetes API通过相应的HTTP首部“Content-Type”对其进行识别。

  • GET /watch/<资源名复数格式>:随时间变化,不断接收一连串的JSON对象,这些JSON对象记录了给定资源类别内所有资源对象的变化情况。

  • GET /watch/<资源名复数格式>/:随时间变化,不断接收一连串的JSON对象,这些JSON对象记录了某个给定资源对象的变化情况。

特殊的REST接口

k8s Proxy API接口,这类接口的作用是代理REST请求,即kubernetes API Server把收到的REST请求转发到某个Node上的kubelet守护进程的REST端口上,由该kubelet进程负责响应。

最实用的一个特性是可以实现一个简单有效的安全机制,如果只想对外暴露部分REST服务,则可以在Master或者其他任何节点上通过运行kubectl proxy进程启动一个内部代理来实现。

1
kubectl proxy --address=192.168.191.15 --accept-hosts='^*$'

以上的例子是我们安装好kube-dashboard后登录ui所用的启动代理命令,其中的–accept-hosts就是允许访问的机器。

apiserver之安全机制

用户可通过客户端kubectl命令行工具或其它方式访问Kubernetes的API资源,每个访问API的请求,都要经过三个步骤校验:Authentication、Authorization、Admission Control,总体如下图所示:

  • Authentication(认证),对用户身份进行验证。认证方式现共有8种,可以启用一种或多种认证方式,只要有一种认证方式通过,就不再进行其它方式的认证。通常启用X509 Client Certs和Service Accout Tokens两种认证方式。
  • Authorization(授权),验证用户是否拥有访问权限。授权方式现共有6种,可以启用一种或多种授权方式,启用的任一种方式依据授权策略明确允许或拒绝请求,则立即返回,不再进行其它方式的授权。通常启用RBAC和Node授权方式。
  • Admission Control(准入控制),对API资源对象修改、校验。它有一个插件列表,所有请求需要经过这个列表中的每个准入控制插件修改、校验,如果某一个准入控制插件验证失败,就拒绝请求。

用户访问 Kubernetes API时,apiserver认证授权模块从HTTPS请求中获取用户的身份信息、请求资源路径和业务对象参数。身份信息是用来确认用户的身份是否合法,资源路径是用于判定用户是否拥有访问操作的权限,业务对象参数则在准入控制中接受修改或校验参数设置是否符合业务要求。

  1. 请求到达后,apiserver从请求的证书、Header中获取用户信息:Name(用户名)、UID(用户唯一标识)、Groups(用户分组)、Extra(用户额外信息)。认证通过后,通过Context将用户信息向下传播。
  2. 授权模块从Context、请求的Method及URI提取用户信息、请求资源属性。使用请求资源属性与授权策略匹配或向外部服务验证判断是否准予访问。
  3. 准入控制接收到HTTPS请求提交的Body内容、用户信息、资源信息,根据不同准入控制插件的功能对上述信息做修改或校验。

认证

认证即身份验证,认证关注的是谁发送的请求,也就是说用户必须用某种方式证明自己的身份信息。

Kubernetes集群有两种类型的用户:普通用户(normal users)、服务账户(service accounts),普通用户并不被Kubernetes管理和保存;而服务账户由Kubernetes存储在etcd中,并可通过apiserver API管理。

1.14版本中已支持8种认证方式,从身份验证的方法上可分为4种类型:Token、证书、代理、密码。Kubernetes可以同时启用多个认证方式,在这种情况下,每个认证模块都按顺序尝试验证用户身份,直到其中一个成功就认证通过。

启用多个认证方式时,验证顺序如下:

kubernetes 系统的各组件需要使用 TLS 证书对通信进行加密,需要通过一个权威的机构进行签名许可,也就是ca,ca证书可以用购买的,可以自己通过cfssl来手动创建,也可以使用kubeadm生成的。

以下是kubeadm自己生成的证书。

Kubernetes把证书放在了两个文件夹中

  • /etc/kubernetes/pki
  • /etc/kubernetes/pki/etcd

其中可以看到并没有kubelet,kube-scheduler,kube-controller-manager,kube-proxy的证书,是因为kubeadm初始化时已经把证书生成到了/etc/kubernetes下,叫它kubeconfig,就是一个集成了各种安全授权信息的配置文件。

里面不仅有证书,还有所属的用户(RBAC)对应的权限。

再研究下node节点怎么认证的:

当想要加入一个新node节点到集群中时会通过master节点生成一条命令,并到新节点执行,

比如:

会由master节点生成一个token再加上经过hash算法生成的ca证书来供节点连接集群。

这时新节点执行命令后会自动生成kubeconfig文件。

授权

授权包括六种方式:AlwaysDeny、AlwaysAllow、ABAC、RBAC、Webhook、Node。配置多个授权时,将按顺序检查每个授权,任何一个匹配的授权策略允许或拒绝请求时,则立即返回该决定,并且不会再检查其他授权策略;所有授权策略都没有允许或拒绝时,最终则拒绝该请求。

通过设置apiserver配置参数(–authorization-mode)启用授权插件。

授权只依据以下的属性进行判断:

其中API、Resource、Subresource、Namespace、APIGroup是从请求的URI中解析获取得到。

当前平台的授权方式是RBAC和NODE,机制为:

RBAC

RBAC包括四种类型:Role、ClusterRole、RoleBinding、ClusterRoleBinding。Role、ClusterRole包含一组权限规则,其中ClusterRole能应用于所有的命名空间(配置中没有命名空间选项),常用于没有命名空间的资源,如nodes;而Role只能包含单个命名空间的权限规则。RoleBinding、ClusterRoleBinding是将角色中定义的权限授予用户、用户组或服务账户。

NODE

1
kubectl get clusterrole system:node

准入控制

准入控制(Admission Control)是Kubernetes apiserver用于拦截请求的一种方式,运行在认证、授权之后,是权限认证链上的最后一环,对请求API资源对象进行修改(Mutation)和校验(Validating)。

在Kubernetes 1.10+之后,用户在使用参数–enable-admission-plugins配置启用准入控制时不需要关注它们的排列顺序,apiserver在注册准入控制插件时,已经定义好了所有插件的执行顺序。

Admission主要插件列表:

集群功能模块间的通信

kubernetes API Server作为集群的核心,负责集群各功能模块之间的通信,集群内各个功能模块通过API Server将信息存入etcd,当需要获取和操作这些数据时,通过API Server提供的REST接口(GET\LIST\WATCH方法)来实现,从而实现各模块之间的信息交互。

kubelet与API Server交互

每个Node节点上的kubelet定期就会调用API Server的REST接口报告自身状态,API Server接收这些信息后,将节点状态信息更新到etcd中。kubelet也通过API Server的Watch接口监听Pod信息,从而对Node机器上的POD进行管理。

监听信息与kubelet执行动作,当新的POD副本被调度绑定到本节点,则kubelet执行POD对应的容器的创建和启动逻辑。
当POD对象被删除,则kubelet删除本节点上相应的POD容器。
当修改Pod信息,则kubelet修改本节点的POD容器。

kube-controller-manager与API Server交互

kube-controller-manager中的Node Controller模块通过API Server提供的Watch接口,实时监控Node的信息,并做相应处理。

kube-scheduler与API Server交互

Scheduler通过API Server的Watch接口监听到新建Pod副本的信息后,它会检索所有符合该Pod要求的Node列表,开始执行Pod调度逻辑。调度成功后将Pod绑定到目标节点上。

缓存

为了缓解各模块对API Server的访问压力,各功能模块都采用缓存机制来缓存数据,各功能模块定时从API Server获取指定的资源对象信息(LIST/WATCH方法),然后将信息保存到本地缓存,功能模块在某些情况下不直接访问API Server,而是通过访问缓存数据来间接访问API Server。

apiserver的核心机制

list-watch

List-Watch是kubernetes的核心机制。组件kubelet、kube-controller-manager、kube-scheduler需要监控各种资源(pod、service等)的变化,当这些对象发生变化时(add、delete、update),kube-apiserver会主动通知这些组件。这个过程类似一个发布-订阅系统。

下图是一个典型的Pod创建过程,在这个过程中,每次当kubectl创建了ReplicaSet对象后,controller-manager都是通过list-watch这种方式得到了最新的ReplicaSet对象,并执行自己的逻辑来创建Pod对象。其他的几个组件,Scheduler/Kubelet也是一样,通过list-watch得知变化并进行处理。

kube-apiserver对etcd的List-watch并提供watch restful API给其他组件(kubelet、kube-controller-manager、kube-scheduler、kube-proxy)。

  • 由组件向apiserver而不是etcd发起watch请求,在组件启动时就进行订阅,告诉apiserver需要知道什么数据发生变化。Watch是一个典型的发布-订阅模式。
  • 组件向apiserver发起的watch请求是可以带条件的,例如,scheduler想要watch的是所有未被调度的Pod,也就是满足Pod.destNode=””的Pod来进行调度操作;而kubelet只关心自己节点上的Pod列表。apiserver向etcd发起的watch是没有条件的,只能知道某个数据发生了变化或创建、删除,但不能过滤具体的值。也就是说对象数据的条件过滤必须在apiserver端而不是etcd端完成。
  • list是watch失败,数据太过陈旧后的弥补手段,list本身是一个简单的列表操作,和其它apiserver的增删改操作一样。

高可用实现

它被设计为水平扩展,即通过部署更多实例来横向扩展。

常见的高可用方案是

  • keepalived + haproxy
  • keepalived + nginx
  • 公有云LSB

原理就是利用软件对k8s的6443端口进行负载,再对每台master节点做IP高可用,通过脚本检测后端服务的健康状态。

参考文档

Kubernetes API详解

apiserver之list-watch篇

Kubernetes API 安全机制详解

《kubernetes权威指南》

CATALOG
  1. 1. apiserver作用
    1. 1.1. apiserver之restful接口
      1. 1.1.1. 使用REST接口
      2. 1.1.2. 特殊的REST接口
    2. 1.2. apiserver之安全机制
      1. 1.2.1. 认证
      2. 1.2.2. 授权
      3. 1.2.3. 准入控制
    3. 1.3. 集群功能模块间的通信
      1. 1.3.1. kubelet与API Server交互
      2. 1.3.2. kube-controller-manager与API Server交互
      3. 1.3.3. kube-scheduler与API Server交互
  2. 2. 缓存
  3. 3. apiserver的核心机制
    1. 3.1. list-watch
  4. 4. 高可用实现
  5. 5. 参考文档