Kubernetes学习笔记
- API
- 格式
- 前缀
- API组
- API版本
- Pod
- 概念
- 优势
- 局限性
- 创建Pod
- ReplicationController
- 概念
- 配置
- Pod模板
- Kubernetes架构
- 概述
- 节点
- 定义
- 管理
- 节点名称唯一性
- 节点自注册
- 手动节点管理
- 节点状态
- 节点心跳
- 节点控制器
- 逐出速率限制
- 资源容量跟踪
API
Kubernetes把其管理的资源均视为API对象,并对外提供REST风格的API来操纵这些对象。Kubernetes API由kube-apiserver组件提供,Kubernetes内部各组件与kube-apiserver通信也是使用API来驱动的,命令行工具比如kubectl以及各种Kubernetes客户端程序均是使用API与Kubernetes通信。
格式
Kubernetes API格式为prefix/group/version/resource,比如表示Deployment资源列表的API为/apis/apps/v1/deployments,其中apis表示前缀,apps表示API组,v1表示API组的版本,deployments表示资源名称,如下图所示:
前缀
API英文全称是Application Programming Interface,即应用程序编程接口。那么从广义上说,Kubernetes 的kube-apiserver组件对外暴露的所有端点都可以称作API,应用程序可以使用这些接口来实现特定的功能,比如使用/api/v1或apis/apps接口来创建资源,使用/healthz来查询集群健康状态,所以这些接口都是API。
常说的Kubernetes API是狭义上的概念,即专指那些表示Kubernetes资源的API,为了与其他API有所区分,Kubernetes特意加了api前缀,该前缀表示这些API用于管理Kubernetes资源。用于管理Kubernetes资源的API前缀除了api外,还有apis,在Kubernetes早期的设计中,只有api前缀,后来为了引入API组的设计,又添加了apis前缀,简单地说,使用api前缀的API是Kubernetes最核心的一组API,而使用apis前缀的API是Kubernetes发展过程中引入的分组API。
API组
一方面,随着Kubernetes提供的功能增多,Kubernetes自身开发和维护难度越来越大,另一方面,用户往往只需要Kubernetes提供的部分功能。所以为了使Kubernetes更容易扩展和演进,同时可以让用户有选择性地开启和关闭非核心功能,Kubernetes设计者们提出了API分组的理念。
API分组理念是指把Kubernetes的API按照功能分组,该理念被提出时Kubernetes已经有了以api为前缀的一组核心API,考虑到兼容策略,这组API不适宜修改,所以API分组实际上针对非核心的扩展API,后续新加的功能统一使用apis为前缀,并把API按组区分,部分API组如下所示:
"/apis/apps"
"/apis/autoscaling"
"/apis/rbac.authorization.k8s.io"
...
出现在前缀apis后面的就是API组,比如apps表示一组用于应用管理的API,autoscaling表示一组用于应用自动扩展的API,rbac.authorization.k8s.io表示一组用于基于角色控制的API。
把API分组最大的好处在于用户可以自由地开启和关闭相应的非核心功能。用户可以使用kube-apiserver组件提供的**–runtime-config**参数来显式地控制某项功能是否开启。比如:
--runtime-config=autoscaling/v1=false,rbac.authorization.k8s.io/v1=true
上面的配置显式地将autoscaling功能关闭,同时把rbac.authorization.k8s.io功能开启。需要说明的是,相当大一部分API组默认是开启的,比如默认情况下rbac.authorization.k8s.io这组API是开启的,这意味着上面配置中rbac.authorization.k8s.io/v1=true是多余的,出现在本例中仅用于说明API组可以显式地控制开启和关闭。
把API分组另一非常重要的好处在于,它可以给每组API按照功能成熟度划分成不同的版本,比如v1alpha1,v1beta1,v1等。
API版本
每组API都有相应的版本表示其成熟度,比如autoscaling就有多个版本:
"/apis/autoscaling/v1",
"/apis/autoscaling/v2beta1",
"/apis/autoscaling/v2beta2",
为API提供版本并且多版本共存的意义在于为用户提供清晰的成熟度参考,比如:
- 版本名包含alpha表示该功能正在实验过程中,不推荐应用在生产环境中,因此Kubernetes默认不会开启这些功能;
- 版本名包含beta表示该功能基本可用,希望用户尝试并提供反馈,因此Kubernetes往往默认启用这些功能;
- 版本名为vx表示功能已固定,相应的API也不会再修改,用户可以放心使用。
为API分组同时为每个API提供多个版本,这允许每组功能可以不同的速度演进,而不必互相影响。
Pod
概念
Kubernetes不直接管理容器,它管理的是名为Pod的对象。Pod是对容器的高级抽像,Pod单词英文含义为豆荚,非常形象地揭示了其于容器的关系,就像一个豆荚中可以含一个或多个豆子一样,一个Pod也可以包含一个或多个容器。在Kubernetes中,Pod是最基础的对象,不管Pod中包含多少容器,Pod的创建和销毁对应的是其包含的所有容器一并创建和销毁。
优势
在部署容器化应用时,有时我们会希望多个密切相关的容器能够部署在同一节点,这样这些容器之间可以方便地共享本地存储,也可以方便地互相通信,甚至我们也希望这些容器能够一并创建和销毁。
局限性
尽管Pod可以封装容器,借此我们可以在一定程度上完成容器的批量管理,但实际上直接创建Pod的场景非常罕见。
Kubernetes将Pod视为一种不可靠的资源,它没有自愈能力,当遇到node异常或因资源不足而被驱逐时,Pod将会被删除。
为了满足各种场景下管理Pod的诉求,Kubernetes在Pod之上又提供了多种控制器资源,比如Deployment、StatefulSet和DaemonSet等,这些控制器可以帮助我们更好的管理Pod,确保Pod总是按照我们预期的行为在运行。
创建Pod
apiVersion: v1
kind: Pod
metadata:
name: pod-runs-nginx
spec:
containers:
- name: nginx
image: nginx:latest
配置将创建一个类型为Pod的资源,资源的版本为v1,Pod名称为pod-runs-nginx,Pod中容器名字为nginx,容器镜像为nginx:latest。
ReplicationController
概念
ReplicationController用于定义指定Pod的副本数,与创建多个Pod相比,它可以保证Pod意外终止后,集群中仍会有指定个数的Pod副本在运行。运行于kube-controller-manager组件中的ReplicationController控制器(控制器和资源名相同)会监控集群中Pod的副本数:
- 如果Pod数量已经超出预期,那么ReplicationController将会删除部分Pod,使Pod数量符合预期。
- 如果Pod数量低于预期,那么ReplicationController将会创建新的Pod,使用Pod数量符合预期。
ReplicationController控制器会时刻监控Pod的副本数量,一旦发现Pod数量不符合预期(Pod数量过多或过少),均会通过增加或删除Pod的手段来让Pod维持在预期数量。
ReplicationController控制器监管的是整个集群范围的Pod。如图所示,如果使用ReplicationController创建两个Pod的副本,当其中一个Pod意外终止后,新的Pod会被创建出来,从而保证集群中仍有两个副本在运行。当Node2被关闭后,运行于其上的Pod被重新调度到Node1中运行,集群中总的Pod数始终保持在2个。
配置
apiVersion: v1
kind: ReplicationController
metadata:
name: replication-controller-runs-pod
spec:
replicas: 3
selector:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.19.0
其中有三个关键的项:
- spec.replicas指定了期望的Pod副本数;
- spec.selector指定了Selector,ReplicationController正是通过该Selector来查找Pod对象;
- spec.template指定了Pod的模版,当ReplicationController发现Pod数量低于预期时将使用该模版创建新的Pod。
Pod模板
Pod模版用于Kubernetes内部动态地创建Pod,它广泛应用于各种控制器中,包括ReplicationController、Deployments、Jobs和DaemonSets等等。
从数据结构上看,Pod模版(PodTemplateSpec)可以理解为简化版的Pod,它只保留了Pod的Metadata和Spec,如下所示:
type PodTemplateSpec struct {
// Metadata of the pods created from this template.
// +optional
metav1.ObjectMeta
// Spec defines the behavior of a pod.
// +optional
Spec PodSpec
}
Kubernetes架构
概述
Kubernetes 集群由一个控制面和一组用于运行容器化应用的工作机器组成,这些工作机器称作节点(Node)。 每个集群至少需要一个工作节点来运行 Pod。工作节点托管着组成应用负载的 Pod。控制面管理集群中的工作节点和 Pod。 在生产环境中,控制面通常跨多台计算机运行,而一个集群通常运行多个节点,以提供容错和高可用。
控制面组件:控制面组件会为集群做出全局决策,比如资源的调度。 以及检测和响应集群事件,例如当不满足 Deployment 的 replicas 字段时,要启动新的 Pod)。
- kube-apiserver:API 服务器是 Kubernetes 控制面的组件, 该组件负责公开了 Kubernetes API,负责处理接受请求的工作。 API 服务器是 Kubernetes 控制面的前端。Kubernetes API 服务器的主要实现是
kube-apiserver。 kube-apiserver 设计上考虑了水平扩缩,它可通过部署多个实例来进行扩缩。 你可以运行
kube-apiserver 的多个实例,并在这些实例之间平衡流量。 - etcd:一致且高可用的键值存储,用作 Kubernetes 所有集群数据的后台数据库。
- kube-scheduler:kube-scheduler 是控制面的组件, 负责监视新创建的、未指定运行节点(node)的 Pods, 并选择节点来让 Pod 在上面运行。调度决策考虑的因素包括单个 Pod 及 Pods 集合的资源需求、软硬件及策略约束、
亲和性及反亲和性规范、数据位置、工作负载间的干扰及最后时限。 - kube-controller-manager:kube-controller-manager 是控制面的组件, 负责运行控制器进程。从逻辑上讲, 每个控制器都是一个单独的进程, 但是为了降低复杂性,它们都被编译到同一个可执行文件,并在同一个进程中运行。
- Node 控制器:负责在节点出现故障时进行通知和响应
- Job 控制器:监测代表一次性任务的 Job 对象,然后创建 Pod 来运行这些任务直至完成
- EndpointSlice 控制器:填充 EndpointSlice 对象(以提供 Service 和 Pod 之间的链接)。
- ServiceAccount 控制器:为新的命名空间创建默认的 ServiceAccount。
节点组件 :节点组件会在每个节点上运行,负责维护运行的 Pod 并提供 Kubernetes 运行时环境。
- kubelet:kubelet 会在集群中每个节点(node)上运行。 它保证容器(containers)都运行在 Pod 中。kubelet 接收一组通过各类机制提供给它的 PodSpec,确保这些 PodSpec 中描述的容器处于运行状态且健康。
- kube-proxy(可选):kube-proxy 是集群中每个节点(node)上所运行的网络代理, 实现 Kubernetes 服务(Service) 概念的一部分。kube-proxy 维护节点上的一些网络规则, 这些网络规则会允许从集群内部或外部的网络会话与 Pod 进行网络通信。如果操作系统提供了可用的数据包过滤层,则 kube-proxy 会通过它来实现网络规则。 否则,kube-proxy 仅做流量转发。如果你使用网络插件为 Service 实现本身的数据包转发, 并提供与 kube-proxy 等效的行为,那么你不需要在集群中的节点上运行 kube-proxy。
- 容器运行时:这个基础组件使 Kubernetes 能够有效运行容器。 它负责管理 Kubernetes 环境中容器的执行和生命周期。
插件(Addons):插件使用 Kubernetes 资源(DaemonSet、 Deployment 等)实现集群功能。 插件中命名空间域的资源属于 kube-system 命名空间。
- DNS:几乎所有 Kubernetes 集群都应该有集群 DNS。集群 DNS 是一个 DNS 服务器,和环境中的其他 DNS 服务器一起工作,它为 Kubernetes 服务提供 DNS 记录。 Kubernetes 启动的容器自动将此 DNS 服务器包含在其 DNS 搜索列表中。
- Web 界面(仪表盘):Dashboard 是 Kubernetes 集群的通用的、基于 Web 的用户界面。 它使用户可以管理集群中运行的应用程序以及集群本身,并进行故障排除。
- 容器资源监控:容器资源监控 将关于容器的一些常见的时序度量值保存到一个集中的数据库中,并提供浏览这些数据的界面。
- 集群层面日志:集群层面日志机制负责将容器的日志数据保存到一个集中的日志存储中, 这种集中日志存储提供搜索和浏览接口。
- 网络插件:网络插件 是实现容器网络接口(CNI)规范的软件组件。它们负责为 Pod 分配 IP 地址,并使这些 Pod 能在集群内部相互通信。
Kubernetes 架构允许大幅度的定制:你可以部署自定义的调度器与默认的 Kubernetes 调度器协同工作,也可以完全替换掉默认的调度器。API 服务器可以通过 CustomResourceDefinition 和 API 聚合进行扩展。
节点
定义
Kubernetes 通过将容器放入在节点(Node)上运行的 Pod 中来执行工作负载。 节点可以是一个虚拟机或者物理机器,取决于所在的集群配置。 每个节点包含运行 Pod 所需的服务; 这些节点由控制面负责管理。节点上的组件包括 kubelet、 容器运行时以及 kube-proxy
管理
向 API 服务器添加节点的方式主要有两种:
- 节点上的 kubelet 向控制面执行自注册;
- 手动添加一个 Node 对象
Node 对象的名称必须是合法的 DNS 子域名。
节点名称唯一性
节点名称用来标识 Node 对象。 Kubernetes 假定名字相同的资源是同一个对象。 **就 Node 而言,隐式假定使用相同名称的实例会具有相同的状态(例如网络配置、根磁盘内容) 和类似节点标签这类属性。**这可能在节点被更改但其名称未变时导致系统状态不一致。 如果某个 Node 需要被替换或者大量变更,需要从 API 服务器移除现有的 Node 对象, 之后再在更新之后重新将其加入。
节点自注册
当 kubelet 标志 --register-node 为 true(默认)时,它会尝试向 API 服务注册自己。
kubelet 使用下列参数启动:
- –kubeconfig - 用于向 API 服务器执行身份认证所用的凭据的路径。
- –cloud-provider - 与某云驱动 进行通信以读取与自身相关的元数据的方式。
- –register-node - 自动向 API 服务器注册。
- –register-with-taints - 使用所给的污点列表 (逗号分隔的 =:)注册节点。当 register-node 为 false 时无效。
- –node-ip - 可选的以英文逗号隔开的节点 IP 地址列表。你只能为每个地址簇指定一个地址。 例如在单协议栈 IPv4 集群中,需要将此值设置为 kubelet 应使用的节点 IPv4 地址。 如果你未提供这个参数,kubelet 将使用节点默认的 IPv4 地址(如果有); 如果节点没有 IPv4 地址,则 kubelet 使用节点的默认 IPv6 地址。
- –node-labels - 在集群中注册节点时要添加的标签。 (参见 NodeRestriction 准入控制插件所实施的标签限制)。
- –node-status-update-frequency - 指定 kubelet 向 API 服务器发送其节点状态的频率。
当 Node 鉴权模式和 NodeRestriction 准入插件被启用后, 仅授权 kubelet 创建/修改自己的 Node 资源。
手动节点管理
- 可以使用 kubectl 来创建和修改 Node 对象。
- 如果希望手动创建节点对象,请设置 kubelet 标志 --register-node=false。
- 可以修改 Node 对象(忽略 --register-node 设置)。 例如,你可以修改节点上的标签或并标记其为不可调度。
- 可以结合使用 Node 上的标签和 Pod 上的选择算符来控制调度。 例如,你可以限制某 Pod 只能在符合要求的节点子集上运行。
- 如果标记节点为不可调度(unschedulable),将阻止新 Pod 调度到该 Node 之上, 但不会影响任何已经在其上的 Pod。 这是重启节点或者执行其他维护操作之前的一个有用的准备步骤。
- 要标记一个 Node 为不可调度,执行以下命令:
kubectl cordon $NODENAME
被 DaemonSet 控制器创建的 Pod 能够容忍节点的不可调度属性。 DaemonSet 通常提供节点本地的服务,即使节点上的负载应用已经被腾空, 这些服务也仍需运行在节点之上。
节点状态
一个节点的状态包含以下信息:
- 地址(Addresses)
- 状况(Condition)
- 容量与可分配(Capacity)
- 信息(Info)
可以使用 kubectl 来查看节点状态和其他细节信息:
kubectl describe node <节点名称>
节点心跳
Kubernetes 节点发送的心跳帮助你的集群确定每个节点的可用性,并在检测到故障时采取行动。
对于节点,有两种形式的心跳:
- 更新节点的 .status
- kube-node-lease 名字空间中的 Lease(租约)对象。 每个节点都有一个关联的 Lease 对象。
节点控制器
节点控制器在节点的生命周期中扮演多个角色:
第一个是当节点注册时为它分配一个 CIDR 区段(如果启用了 CIDR 分配)。
第二个是保持节点控制器内的节点列表与云服务商所提供的可用机器列表同步。 如果在云环境下运行,只要某节点不健康,节点控制器就会询问云服务是否节点的虚拟机仍可用。 如果不可用,节点控制器会将该节点从它的节点列表删除。
第三个是监控节点的健康状况。节点控制器负责:
在节点不可达的情况下,在 Node 的 .status 中更新 Ready 状况。 在这种情况下,节点控制器将 NodeReady 状况更新为 Unknown。
如果节点仍然无法访问:对于不可达节点上的所有 Pod 触发 API 发起的逐出操作。 默认情况下,节点控制器在将节点标记为 Unknown 后等待 5 分钟提交第一个驱逐请求。
默认情况下,节点控制器每 5 秒检查一次节点状态,可以使用 kube-controller-manager 组件上的 --node-monitor-period 参数来配置周期。
逐出速率限制
大部分情况下,节点控制器把逐出速率限制在每秒 --node-eviction-rate 个(默认为 0.1)。 这表示它每 10 秒钟内至多从一个节点驱逐 Pod。
当一个可用区域(Availability Zone)中的节点变为不健康时,节点的驱逐行为将发生改变。 节点控制器会同时检查可用区域中不健康(Ready 状况为 Unknown 或 False) 的节点的百分比:
- 如果不健康节点的比例超过 --unhealthy-zone-threshold(默认为 0.55), 驱逐速率将会降低。
- 如果集群较小(意即小于等于 --large-cluster-size-threshold 个节点 - 默认为 50), 驱逐操作将会停止。
- 否则驱逐速率将降为每秒 --secondary-node-eviction-rate 个(默认为 0.01)。
在逐个可用区域中实施这些策略的原因是, 当一个可用区域可能从控制面脱离时其它可用区域可能仍然保持连接。 如果你的集群没有跨越云服务商的多个可用区域,那(整个集群)就只有一个可用区域。
跨多个可用区域部署你的节点的一个关键原因是当某个可用区域整体出现故障时, 工作负载可以转移到健康的可用区域。 因此,如果一个可用区域中的所有节点都不健康时,节点控制器会以正常的速率 --node-eviction-rate 进行驱逐操作。 在所有的可用区域都不健康(也即集群中没有健康节点)的极端情况下, 节点控制器将假设控制面与节点间的连接出了某些问题,它将停止所有驱逐动作 (如果故障后部分节点重新连接,节点控制器会从剩下不健康或者不可达节点中驱逐 Pod)。
节点控制器还负责驱逐运行在拥有 NoExecute 污点的节点上的 Pod, 除非这些 Pod 能够容忍此污点。 节点控制器还负责根据节点故障(例如节点不可访问或没有就绪) 为其添加污点。 这意味着调度器不会将 Pod 调度到不健康的节点上。
资源容量跟踪
Node 对象会跟踪节点上资源的容量(例如可用内存和 CPU 数量)。 通过自注册机制生成的 Node 对象会在注册期间报告自身容量。 如果你手动添加了 Node, 你就需要在添加节点时手动设置节点容量。
Kubernetes 调度器 保证节点上有足够的资源供其上的所有 Pod 使用。 它会检查节点上所有容器的请求的总和不会超过节点的容量。 总的请求包括由 kubelet 启动的所有容器,但不包括由容器运行时直接启动的容器, 也不包括不受 kubelet 控制的其他进程。
未完待续……