1. 简介
API Server提供k8s各类资源对象(pod、RC、Service等)的增删改查及watch等HTTP Rest接口,整个k8s系统的总线和数据中心。
1.1. 功能
kube-apiserver是 Kubernetes 最重要的核心组件之一,主要提供以下的功能:
- 提供集群管理的 REST API 接口,包括认证授权、数据校验以及集群状态变更等
- 提供其他模块之间的数据交互和通信的枢纽(其他模块通过 API Server 查询或修改数据,整个Kubernetes集群操作etcd的唯一入口,只有 API Server 才直接操作 etcd)
- 资源配额控制的入口,拥有完备的集群安全机制
1.2. 工作原理
API Server提供了k8s的rest api,实现了认证、授权和准入控制等安全功能,同时也负责了集群状态的存储操作。
- rest api
kube-apiserver支持同时提供https和http api,其中http api是非安全接口,不做任何认证授权机制,不建议生产环境启用,但两个接口提供的rest api格式相同。
- https api 默认监听在6443端口(–secure-port=6443);
- http api 默认监听在127.0.0.1的8080端口(使用参数 --insecure-port=8080);
- 访问控制说明
k8s api 每个请求都会经过多阶段的访问控制才会被接受,包括认证、授权及准入控制等。
- 认证
开启TLS情况下,所有请求都需要首先认证。k8s支持多种认证机制,并且支持同时开启多个认证插件(仅一个认证通过即可),如认证成功,则用户的username会传入授权模块做进一步的授权验证,而认证失败的请求则返回http 401 。
- 授权
认证之后的请求就到了授权模块,和认证类似,k8s也支持多种授权机制,并支持同时开启多个授权插件(仅一个验证通过即可)。如授权成功,则用户的请求会发送到准入控制模块做进一步的请求验证,失败的请求则返回http403。
- 准入控制
用来对请求做进一步的验证或添加默认参数,不同于认证和授权只关心请求的用户和操作,准入控制还会处理请求的内容,并且仅对创建、更新、删除或连接(如代理)等有效,而对读操作无效。准入控制也支持同时开启多个插件,但他们是依次调用的,只有全部插件都通过的请求才可以允许进入系统。
kube-apiserver工作原理图如下:
kube-apiserver包含三种APIServer:
- aggregatorServer:负责处理 apiregistration.k8s.io 组下的APIService资源请求,同时将来自用户的请求拦截转发给aggregated server(AA)
- kubeAPIServer:负责对请求的一些通用处理,包括:认证、鉴权以及各个内建资源(pod, deployment,service and etc)的REST服务等
- apiExtensionsServer:负责CustomResourceDefinition(CRD)apiResources以及apiVersions的注册,同时处理CRD以及相应CustomResource(CR)的REST请求(如果对应CR不能被处理的话则会返回404),也是apiserver Delegation的最后一环
另外还包括bootstrap-controller,主要负责Kubernetes default apiserver service的创建以及管理。
2. 组件相互关系
apiserver在k8s中也是一个Service对象,名字叫做kubernetes, 通过kubectl get svc命令可以查看:
Kubernetes为所有资源增删改查的唯一入口,各组件均以list-watch的方式向Apiserve发送请求。为减少Apiserver的压力,各组件都采用缓存来缓存数据。功能模块在某些情况下不直接访问Apiserver,而是通过访问缓存来间接访问Apiserver。
Kubelet&Apiserver
每个node上的kubelet每隔一个时间周期(通过参数--node-status-update-frequency指定上报频率,默认是 10s 上报一次),就会调用Apiserver的REST接口来报告自身状态。Kubelet通过watch接口,监听pod信息。监听创建、删除、修改事件。
Controller-manager&Apiserver
controler-manager中包含多个controller,举例: Node Controller模块通过API server提供的Watch接口,实现监控Node信息,并做相应处理。
Scheduler&Apiserver
Scheduler通过APl server的watch接口来监听,监听到新建pod副本后,检索所有符合该Pod要求的Node列表,开始执行Pod调度,调度成功后将pod绑定到具体节点。
3. 配置参数含义
api server的配置参数位置:
master节点的 /ect/kubernetes/kube-apiserver.yaml
cat kube-apiserver.yaml
参数配置表:
[root@master1 manifests]# cat kube-apiserver.yaml
apiVersion: v1 #版本号
kind: Pod #容器
metadata: #元数据
annotations: #插件
kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 10.16.0.45:6443
creationTimestamp: null
labels: #标签
component: kube-apiserver
tier: control-plane
name: kube-apiserver #组件名称
namespace: kube-system #组件所在命名空间
spec: #详细定义
containers: #容器列表
- command: #容器的启动命令列表
- kube-apiserver
- --advertise-address=10.16.0.45 #向集群成员发布apiserver的IP地址,该地址必须能够被集群的成员访问。如果为空,则使用--bind-address,如果--bind-address未指定,那么使用主机的默认接口。
- --allow-privileged=true #如果为true,允许特权模式的容器。默认值:false
- --anonymous-auth=True #允许匿名请求到API server的安全端口。未被其他身份验证方法拒绝的请求将被视为匿名请求。匿名请求的system username:anonymous,system group name:unauthenticated。默认值:true
- --apiserver-count=3 #集群中运行的apiserver的数量,必须为一个正数。默认值:1在使用时--endpoint-reconciler-type=master-count时启用的
- --authorization-mode=Node,RBAC #在安全端口上执行授权的有序的插件列表。默认值:AlwaysAllow以逗号分隔的列表:AlwaysAllow,AlwaysDeny,ABAC,Webhook,RBAC,Node.
- --bind-address=0.0.0.0 #监听安全端口的IP地址。关联的接口必须能被集群的其他部分以及CLI/web客户机访问。
- --client-ca-file=/etc/kubernetes/ssl/ca.crt #如果设置,任何提交的客户ca文件中由某个权威机构签名的客户机证书的请求都使用与客户机证书的公共名称相对应的身份进行身份验证。
- --default-not-ready-toleration-seconds=3153600000 #指示notReady:NoExecute的容忍秒数,默认情况下添加到没有这种容忍的每个pod中
- --default-unreachable-toleration-seconds=3153600000 #指示对不可达的NoExecute的容忍秒数
- --enable-admission-plugins=NodeRestriction #除了默认启用的插件,还应该额外启动的admission插件
- --enable-aggregator-routing=False #打开aggregator路由请求到endpoints IP,而不是集群IP
- --enable-bootstrap-token-auth=true #启用允许‘kube-system' namespace中的secrets类型的'bootstrap.kubernetes.io/token'用于TLS引导身份验证。
- --endpoint-reconciler-type=lease #使用endpoint协调器(master-count,lease(默认值),none)
- --etcd-cafile=/etc/ssl/etcd/ssl/ca.pem #用于保护etcd通信的SSL证书权威文件
- --etcd-certfile=/etc/ssl/etcd/ssl/node-master1.pem #用于保护etcd通信的SSL证书文件
- --etcd-keyfile=/etc/ssl/etcd/ssl/node-master1-key.pem #用来保护etcd通信的SSL key文件
- --etcd-servers=h体体ps://10.16.0.45:2379,h体体ps://10.16.0.43:2379,h体体ps://10.16.0.46:2379 #连接的etcd服务器列表(格式://ip:port),以逗号分隔
- --event-ttl=1h0m0s #保留时间的时间。默认值:1h0m0s
- --feature-gates=ServiceTopology=true #用于描述alpha/实验特性的功能的一组键值对
- --insecure-port=0 #insecure-port字段值为0,表示默认禁用了8080端口
- --kubelet-client-certificate=/etc/kubernetes/ssl/apiserver-kubelet-client.crt #用于TLS的客户端证书文件路径
- --kubelet-client-key=/etc/kubernetes/ssl/apiserver-kubelet-client.key #用于TLS的客户端私钥文件路径
- --kubelet-preferred-address-types=InternalDNS,InternalIP,Hostname,ExternalDNS,ExternalIP #为kubelet通信使用的首选NodeAddress 类型列表。默认值:[Hostname,InternalDNS,InternalIP,ExternalDNS,ExternalIP]
- --profiling=False #通过web接口host:port/debug/pprof/启用profiling,默认值 true
- --proxy-client-cert-file=/etc/kubernetes/ssl/front-proxy-client.crt #用于证明aggregator或kube-apiserver在请求期间发出呼叫的身份的客户端证书。
- --proxy-client-key-file=/etc/kubernetes/ssl/front-proxy-client.key #用于证明聚合器或kube-apiserver的身份的客户端证书的私钥,当它必须在请求期间调用时使用。包括将请求代理给用户api-server和调用webhook admission插件
- --request-timeout=1m0s #一个可选字段,指示处理程序必须在发出请求前保持打开状态时间。这是默认请求超时,但可能被其他参数覆盖,例如:特殊类型请求的--min-request-timeou
- --requestheader-allowed-names=front-proxy-client #客户端证书常用名称列表,允许在--requestheader-username-headers指定的标头中提供用户名,如果为空,则允许在--requestheader-client-ca文件中通过当局验证的任何客户端证书。
- --requestheader-client-ca-file=/etc/kubernetes/ssl/front-proxy-ca.crt #根证书绑定包用于在信任--requestheader-username-headers指定的标头中的用户名前,在传入请求上验证客户证书。
- --requestheader-extra-headers-prefix=X-Remote-Extra- #要检查的请求标头前缀列表
- --requestheader-group-headers=X-Remote-Group #要检查组的请求标头列表
- --requestheader-username-headers=X-Remote-User # 要检查用户名的请求标头列表
- --secure-port=6443 #为带有身份验证和授权的HTTPS提供服务的端口。不能设置0关闭它。默认值:6443
- --service-account-issuer=h体体ps://kubernetes.default.svc.cluster.net #Service account token发行者的标识符。发布者将在“iss”声明中断言该标识符。该值是一个字符串或者URL
- --service-account-key-file=/etc/kubernetes/ssl/sa.pub #包含PEM编码的x509 RSA或ECDSA私有或者公共密钥的文件。用于验证service account token。指定的文件可以包含多个值。该参数可以被指定多个不同的文件。如果没有指定,--tls-private-key-file将被使用。如果提供了--service-account-signing-key,则必须指定该参数。
- --service-account-signing-key-file=/etc/kubernetes/ssl/sa.key #指向包含service account token签发方当前私钥文件的路径。签发方将用这个私钥签署已发行的ID token。需要设置'TokenRequest' feature gate
- --service-cluster-ip-range=10.96.0.0/16 #CIDR IP范围,用于分配service 集群IP。不能与分配给节点pod的任何IP范围重叠。默认值:10.0.0.0/24
- --service-node-port-range=30000-32767 #为NodePort可视性服务保留的端口范围。默认值: 30000-32767
- --storage-backend=etcd3 #持久性存储后端。可选值:'etcd3' (默认), 'etcd2'.
- --tls-cert-file=/etc/kubernetes/ssl/apiserver.crt #包含HTTPS的默认x509证书的文件。如果启用HTTPS服务,切没有提供--tls-cert-file和--tls-private-key-file,则为公共地址生成自签名证书和密钥,并保存到--cert-dir指定的目录中。
- --tls-private-key-file=/etc/kubernetes/ssl/apiserver.key #包含和--tls-cert-file配对的默认x509私钥的文件
image: docker.ctyun.cn:60001/matrix/amd64-ctyunos/kube-apiserver:v1.20.2 #集群的镜像源
imagePullPolicy: IfNotPresent #镜像拉取策略,IfNotPresent表示镜像在宿主机上不存在时拉取
livenessProbe: #存活指针,判断Pod是否健康,定期检查如果健康可判定为runing,失败的话kubectl会根据Pod重启策略重启
failureThreshold: 8 #failureThreshold 表示探针的最大失败次数,如果达到了最大的失败次数,在存活性探针的情况,容器将重新启动。
httpGet:
host: 10.16.0.45
path: /livez
port: 6443
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
name: kube-apiserver
readinessProbe: #探测应用是否就绪并处于正常服务状态,不正常则不会收到Service的流量
failureThreshold: 3
httpGet:
host: 10.16.0.45
path: /readyz
port: 6443
scheme: HTTPS
periodSeconds: 1
timeoutSeconds: 15
resources:
requests:
cpu: 250m
startupProbe: #探测应用是否启动完成,如果在failureThreshold*periodSeconds周期内未就绪,应用进程会被重启
failureThreshold: 30
httpGet:
host: 10.16.0.45
path: /livez
port: 6443
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
volumeMounts: #挂载数据卷
- mountPath: /etc/ssl/certs #容器内的挂载目录
name: ca-certs
readOnly: true
- mountPath: /etc/pki
name: etc-pki
readOnly: true
- mountPath: /etc/pki/ca-trust
name: etc-pki-ca-trust
readOnly: true
- mountPath: /etc/pki/tls
name: etc-pki-tls
readOnly: true
- mountPath: /etc/ssl/etcd/ssl
name: etcd-certs-0
readOnly: true
- mountPath: /etc/kubernetes/ssl
name: k8s-certs
readOnly: true
hostNetwork: true #运行的应用程序可以直接看到宿主主机的网络接口,宿主机所在的局域网上所有网络接口都可以访问到该应用程序及端口。
priorityClassName: system-node-critical #system-cluster-critical、system-node-critical优先级别主要是给系统组件来使用的,用于确保重要的组件优先被调度。
volumes: #卷
- hostPath:
path: /etc/ssl/certs
type: DirectoryOrCreate
name: ca-certs
- hostPath:
path: /etc/pki
type: DirectoryOrCreate
name: etc-pki
- hostPath:
path: /etc/pki/ca-trust
type: ""
name: etc-pki-ca-trust
- hostPath:
path: /etc/pki/tls
type: ""
name: etc-pki-tls
- hostPath:
path: /etc/ssl/etcd/ssl
type: DirectoryOrCreate
name: etcd-certs-0
- hostPath:
path: /etc/kubernetes/ssl
type: DirectoryOrCreate
name: k8s-certs
status: {}
注意:
- k8s中的apiVersion版本,可以使用命令
kubectl api-versions
查看,常见的有以下三个:
- alpha:开发版,可能包含错误,随时可能会丢弃对该功能的支持
- beta:测试版,软件经过很好的测试,启用功能被认为是安全的,细节可能会改变,但功能在后续版本不会被删除
- stable:稳定版,将出现在后续发布的软件版本中
4. Q&A
注:上述配置参数仅解释了我们部署的apiserver的现有参数,并不是全部的apiserver参数,建议结合官网,梳理一下。 官网如下:
Q:这个默认接口指的是默认网络接口,如果有多网卡的话,怎么知道默认网络接口是哪个?
A:KubeAPIServer为Kubernetes中各API Resources注册路由信息,同时暴露RESTful API,使集群中以及集群外的服务都可以通过RESTful API操作Kubernetes中的资源,通过路由信息可以确定默认网络接口。
Q:如果关闭特权模式,能不能通过PSP策略或者pod的securityContext配置来开启特权容器?
A: 不可以,PSP策略和securityContext配置可以在allow-privileged=true的前提下开启特权容器
Q:节点NotReady的时候,这些节点上面的容器的容忍时间,超过这个时间之后再进行驱逐和重新调度
A: controller manager、api server以及kubelet的pod节点时间参数:
- --node-monitor-period="5s" . #在NodeController中同步节点状态的周期。默认5s
- --node-monitor-grace-period: "20s" #我们允许运行的节点在标记为不健康之前没有响应的时间。必须是kubelet的nodeStatusUpdateFrequency的N倍,其中N表示允许kubelet发布节点状态的重试次数默认40s。
- --node-startup-grace-period: "30s" #我们允许启动节点在标记为不健康之前没有响应的时间。,默认1m0s。
- --pod-eviction-timeout: "1m" #删除失败节点上的pods的宽限期。默认5m
- --default-not-ready-toleration-seconds=60 指示notReady:NoExecute的容忍秒数,默认情况下添加到没有这种容忍的每个pod中。
- --default-unreachable-toleration-seconds=60 指示对不可到达的:NoExecute的容忍秒数,默认情况下添加到没有这种容忍的每个pod中。
- --enable-admission-plugins=NodeRestriction,PodSecurityPolicy,DefaultTolerationSeconds 默认容忍的秒数开启
Q:这些插件有哪些,大体上有什么作用,比如PSP插件是干什么的?
A:
插件有:NodeRestriction, NamespaceLifecycle, LimitRanger, ServiceAccount, DefaultStorageClass, DefaultTolerationSeconds, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota
整体作用为:
作用:准入控制器是一段代码,它会在请求通过认证和鉴权之后、对象被持久化之前拦截到达 API 服务器的请求。
准入控制器可以执行验证(Validating)和/或变更(Mutating)操作。 变更(mutating)控制器可以根据被其接受的请求更改相关对象;验证(validating)控制器则不行。
准入控制器限制创建、删除、修改对象的请求。 准入控制器也可以阻止自定义动作,例如通过 API 服务器代理连接到 Pod 的请求。 准入控制器不会 (也不能)阻止读取(get、watch 或 list)对象的请求。
Kubernetes 1.27 中的准入控制器列表中,有两个特殊的控制器:MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook。 它们根据 API 中的配置,分别执行变更和验证准入控制。
以PSP插件为例:
Q:这个参数具体是什么意思,endpoint IP 和 集群IP 具体指的是什么IP?
A: API聚合机制是Kubernetes1.7引入的新特性,可以将用户扩展的API注册到apiserver上,仍然通过API Server的HTTP URL对新的API进行访问和操作。为了实现这个机制,Kubernetes在 kube-apiserver服务中引入了一个API聚合层(API AggregationLayer),用于将扩展API的访问请求转发到用户服务的功能。
设计API聚合机制的目标:
- 增加API的扩展性:使得开发人员可以编写自己的APl Server来发布他们的 API,而无需对Kubernetes核心代码进行任何修改。
- 无需等待Kubernetes核心团队的复杂审查:允许开发人员将其API作为单独的APl Server发布,使集群管理员不用对Kubernetes核心代码进行修改就能使用新的API,也就无需等待社区繁杂的审查了。
- 支持试验性新特性API开发:可以在独立的API聚合服务中开发新的API,不影响系统现有的功能。
- 确保新的API遵循Kubernetes的规范:如果没有API聚合机制,开发人员就可能会被迫推出自己的设计,可能不遵循Kubernetes规范。
总的来说,API聚合机制的目标是提供集中的API发现机制和安全的代理功能,将开发人员的新API动态地、无缝地注册到Kubernetes APl Server中进行测试和使用。
Endpoints IP : 服务和pod不是直接连接,而是通过Endpoint资源进行连通。endpoint资源是暴露一个服务的ip地址和port的列表。
集群IP:Service的IP地址,此为虚拟IP地址。外部网络无法ping通,只有kubernetes集群内部访问使用。
Pod IP:每个Pod的IP地址,他是Docker Engine根据docker网桥的IP地址段进行分配的,通常是一个虚拟的二层网络。同Service下的pod可以直接根据PodIP相互通信,不同Service下的pod在集群间pod通信要借助于 cluster ip,pod和集群外通信,要借助于node ip
NodeIP: 可以是物理机的IP(也可能是虚拟机IP)。每个Service都会在Node节点上开通一个端口,外部可以通过NodeIP:NodePort即可访问Service里的Pod,和我们访问服务器部署的项目一样,IP:端口/项目名
Q:这个参数具体是什么意思,endpoint协调器是干嘛的,lease是什么意思
A: 使用端点协调器(master-count, lease, none),
- 如果 kubeadm 被调用为 --feature-gates=HighAvailability,则标志 --endpoint-reconciler-type=lease 被设置,从而启用内部 API server VIP 的 endpoints 的自动协调
- 如果 kubeadm 被调用为 --feature-gates=DynamicKubeletConfig,则 API 服务器上的相应功能将通过 --feature-gates=DynamicKubeletConfig=true 标志激活
Lease机制:即租约机制(TTL,Time To Live),Etcd 可以为存储的 key-value 对设置租约,当租约到期,key-value 将失效删除;同时也支持续约,通过客户端可以在租约到期之前续约,以避免 key-value 对过期失效。Lease 机制可以保证分布式锁的安全性,为锁对应的 key 配置租约,即使锁的持有者因故障而不能主动释放锁,锁也会因租约到期而自动释放
Q:这里配置了3个ETCD的endpoint,但是etcd是有leader的,这里配置了3个,那请求是怎么代理到etcd leader上面的(很重要)?
A: etcd通过进行leader选举来实现高可用:
在一个 Raft 集群中只有 Leader 节点能够处理客户端的请求(如果客户端的请求发到了 Follower,Follower 将会把请求重定向到 Leader),客户端的每一个请求都包含一条被复制状态机执行的指令。Leader 把这条指令作为一条新的日志条目(entry)附加到日志中去,然后并行的将附加条目发送给 Followers,让它们复制这条日志条目。当这条日志条目被 Followers 安全的复制,Leader 会应用这条日志条目到它的状态机中,然后把执行的结果返回给客户端。如果 Follower 崩溃或者运行缓慢,再或者网络丢包,Leader 会不断的重复尝试附加日志条目(尽管已经回复了客户端)直到所有的 Follower 都最终存储了所有的日志条目,确保强一致性。