关于 K8s中Kube-apiserver 的一些总结
写在前面
-
学习K8s,简单整理下 -
官网很详细,小伙伴系统学习可以到官网 -
https://kubernetes.io/zh/docs/tasks/administer-cluster/access-cluster-api/
「年轻游侠儿泪眼模糊,凄然一笑,站起身,拿木剑对准墙壁,狠狠折断。
此后江湖再无温华的消息,这名才出江湖便已名动天下的木剑游侠儿,一夜之间,以最决然的苍凉姿态,离开了江湖。
刺骨大雪中,他最后对自己说了一句。
“不练剑了。”」 ——烽火戏诸侯《雪中悍刀行》
Kubernetes API Server原理分析
「 Kubernetes API Server
的核心功能是提供了Kubernetes各类资源对象(如Pod,RC, Service等)的增、删、改、查及Watch等HTTP Rest接口,接受外部请求,并将信息写到ETCD中,成为集群内各个功能模块之间数据交互和通信的中心枢纽,是整个系统的数据总线和数据中心。除此之外,它还有以下一些功能特性。」
(1)是集群管理的API入口。
(2)是资源配额控制的入口。
(3)提供了完备的集群安全机制。
理论概述
「Kubernetes API Server
通过一个名为kube-apiserver
的进程提供服务,该进程运行在Master节点
上,如果小伙伴使用二进制方式安装k8s,会发现,kube-apiserver是docker之后第一个要启动的服务」
旧版本中kube-apiserver
进程在本机的8080
端口(对应参数-insecure-port
)提供REST服务。
新版本中启动HTTPS安全端口(--secure-port=6443
)来启动安全机制,加强REST API访问的安全性。这里需要说明的是,好像是从1.20开始就不支持了,在apiserver配置文件里添加 --insecure-port=8080会导致启动不了,所以不在支持直接http的方式访问(可以用代理)
在高版本的环境中,有时候环境起不来,会报错说6443端口没有开放,我们需要确认kube-apiserver
服务是否启动成功
通常我们可以通过命令行工具kubectl
来与Kubernetes API Server
交互,它们之间的接口是REST
调用。
使用 kubectl 代理
「如果我们只想对外暴露部分REST
服务,则可以在Master
或其他任何节点上通过运行kubect proxy
进程启动一个内部代理来实现。」
┌──[[email protected]]-[~]
└─$kubectl proxy --port=8080 &
[1] 43454
┌──[[email protected]]-[~]
└─$Starting to serve on 127.0.0.1:8080
┌──[[email protected]]-[~]
└─$curl http://localhost:8080/api/ > kubeapi
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 185 100 185 0 0 2863 0 --:--:-- --:--:-- --:--:-- 2936
┌──[[email protected]]-[~]
└─$head -20 kubeapi
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "192.168.26.81:6443"
}
]
}┌──[[email protected]]-[~]
└─$jobs
[1]+ Running kubectl proxy --port=8080 &
┌──[[email protected]]-[~]
└─$fg
kubectl proxy --port=8080
^C
「当然,我们也可以拒绝访问某一资源,比如pod」
┌──[[email protected]]-[~]
└─$kubectl proxy --reject-paths="^/api/v1/pod" --port=8080 --v=1 &
┌──[[email protected]]-[~]
└─$curl http://localhost:8080/api/v1/pods
Forbidden
┌──[[email protected]]-[~]
└─$curl http://localhost:8080/api/v1/configmaps > $(mktemp kube.XXXXXX)
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 25687 0 25687 0 0 644k 0 --:--:-- --:--:-- --:--:-- 660k
┌──[[email protected]]-[~]
└─$head -5 kube.zYxKiH
{
"kind": "ConfigMapList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "81890"
┌──[[email protected]]-[~]
└─$curl http://localhost:8080/api/v1/secrets | head -5
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0{
"kind": "SecretList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "82039"
100 65536 0 65536 0 0 1977k 0 --:--:-- --:--:-- --:--:-- 2064k
curl: (23) Failed writing body (0 != 16378)
┌──[[email protected]]-[~]
└─$
「kubectl proxy具有很多特性,最实用的一个特性是提供简单有效的安全机制,比如采用白名单来限制非法客户端访问时,只要增加下面这个参数即可:」
--accept-hosts="^localhost$, ^127\\.0\\.0\\.1$,^\\[::1\\]$"
不使用 kubectl 代理
「通过将身份认证令牌直接传给 API 服务器,可以避免使用 kubectl 代理,像这样:」 使用 grep/cut 方式:
# 查看所有的集群,因为你的 .kubeconfig 文件中可能包含多个上下文
┌──[[email protected]]-[~]
└─$kubectl config view -o jsonpath='{"Cluster name\tServer\n"}{range .clusters[*]}{.name}{"\t"}{.cluster.server}{"\n"}{end}'
Cluster name Server
kubernetes https://192.168.26.81:6443
# 从上述命令输出中选择你要与之交互的集群的名称
┌──[[email protected]]-[~]
└─$export CLUSTER_NAME=kubernetes
# 指向引用该集群名称的 API 服务器
┌──[[email protected]]-[~]
└─$APISERVER=$(kubectl config view -o jsonpath="{.clusters[?(@.name==\"$CLUSTER_NAME\")].cluster.server}")
┌──[[email protected]]-[~]
└─$echo $APISERVER
https://192.168.26.81:6443
# 获得令牌
┌──[[email protected]]-[~]
└─$TOKEN=$(kubectl get secret default-token-xg77h -o jsonpath='{.data.token}' -n kube-system | base64 -d)
# 使用令牌玩转 API
┌──[[email protected]]-[~]
└─$curl -X GET $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "192.168.26.81:6443"
}
]
}
编程方式访问 API
python
要使用 Python 客户端,运行下列命令:pip install kubernete
PS E:\docker> pip install kubernetes
Collecting kubernetes
Using cached kubernetes-21.7.0-py2.py3-none-any.whl (1.8 MB)
............
「将 ~/.kube 的config文件的内容复制到本地目录,保存为文件kubeconfig.yaml」
┌──[[email protected]]-[~]
└─$cp .kube/config kubeconfig.yaml
python
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File : k8s_api.py
@Time : 2021/12/16 23:05:02
@Author : Li Ruilong
@Version : 1.0
@Contact : [email protected]
@Desc : K8s Demo
'''
# here put the import lib
from kubernetes import client, config
config.kube_config.load_kube_config(config_file="kubeconfig.yaml")
v1=client.CoreV1Api()
print("Listing pods with their IPs:")
ret = v1.list_pod_for_all_namespaces(watch=False)
for i in ret.items:
print("%s\t%s\t%s" % (i.status.pod_ip, i.metadata.namespace, i.metadata.name))
「输出所有的pod和对应的node IP」
PS D:\code\blogger\blogger\资源> python .\k8s_api.py
Listing pods with their IPs:
10.244.88.67 kube-system calico-kube-controllers-78d6f96c7b-85rv9
192.168.26.81 kube-system calico-node-6nfqv
192.168.26.83 kube-system calico-node-fv458
192.168.26.82 kube-system calico-node-h5lsq
.................
Java
# 克隆 Java 库
git clone --recursive https://github.com/kubernetes-client/java
java的客户端
package io.kubernetes.client.examples;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1Pod;
import io.kubernetes.client.openapi.models.V1PodList;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.KubeConfig;
import java.io.FileReader;
import java.io.IOException;
/**
* A simple example of how to use the Java API from an application outside a kubernetes cluster
*
* <p>Easiest way to run this: mvn exec:java
* -Dexec.mainClass="io.kubernetes.client.examples.KubeConfigFileClientExample"
*
* <p>From inside $REPO_DIR/examples
*/
public class KubeConfigFileClientExample {
public static void main(String[] args) throws IOException, ApiException {
// file path to your KubeConfig
String kubeConfigPath = "D:\\code\\k8s\\java\\examples\\examples-release-10\\src\\main\\java\\io\\kubernetes\\client\\examples\\config";
// loading the out-of-cluster config, a kubeconfig from file-system
ApiClient client =
ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build();
// set the global default api-client to the in-cluster one from above
Configuration.setDefaultApiClient(client);
// the CoreV1Api loads default api-client from global configuration.
CoreV1Api api = new CoreV1Api();
// invokes the CoreV1Api client
V1PodList list =
api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null);
for (V1Pod item : list.getItems()) {
System.out.println(item.getMetadata().getName());
}
}
}
「输出所有pod」
D:\Java\jdk1.8.0_251\bin\java.exe 。。。
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Bad level value for property: .level
Bad level value for property: java.util.logging.ConsoleHandler.level
calico-kube-controllers-78d6f96c7b-85rv9
calico-node-6nfqv
calico-node-fv458
calico-node-h5lsq
coredns-7f6cbbb7b8-ncd2s
coredns-7f6cbbb7b8-pjnct
etcd-vms81.liruilongs.github.io
。。。。。。。。。
独特的Kubernetes Proxy API接口
「Kubernetes Proxy API
接口,作用是代理REST请求,即Kubernetes API Server
把收到的REST
请求转发到某个Node
上的kubelet·
守护进程的REST
端口上,由该kubelet
进程负责响应。」
┌──[[email protected]]-[~]
└─$kubectl proxy 8080 &
[1] 76543
┌──[[email protected]]-[~]
└─$Starting to serve on 127.0.0.1:8001
┌──[[email protected]]-[~]
└─$kubectl get nodes
NAME STATUS ROLES AGE VERSION
vms81.liruilongs.github.io Ready control-plane,master 4d v1.22.2
vms82.liruilongs.github.io Ready <none> 4d v1.22.2
vms83.liruilongs.github.io NotReady <none> 4d v1.22.2
REST接口方式调用
┌──[[email protected]]-[~]
└─$curl http://localhost:8001/api/
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "192.168.26.81:6443"
}
]
}┌──[[email protected]]-[~]
└─$
┌──[[email protected]]-[~]
└─$curl http://localhost:8001/api/v1/namespaces/default/pods
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "92086"
},
"items": []
}
┌──[[email protected]]-[~]
└─$curl http://localhost:8080/api/v1/nodes/vms82.liruilongs.github.io/
「需要说明的是:这里获取的Pod的信息数据来自Node而非etcd数据库,所以两者可能在·某些时间点会有偏差。此外,如果kubelet进程在启动时包含-enable-debugging-handlers=true
参数,那么Kubernetes Proxy API
还会增加其他的接口信息 」
集群功能模块之间的通信
「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信息
,如果监听到新的Pod副本被调度绑定到本节点,则执行Pod对应的容器的创建和启动逻辑;如果监听到Pod对象被删除,则删除本节点上的相应的Pod容器;如果监听到修改Pod信息,则kubelet监听到变化后,会相应地修改本节点的Pod容器。」
「kube-controller-manager
进程与API Server
的交互。」 「kube-controller-manager
中的Node Controller
模块通过API Sever
提供的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.」