Master 核心组件详解

1. API Server

1.1. 概述

API Server 是 Kubernetes 控制平面的核心组件,作为整个集群的”大脑”,它负责暴露 Kubernetes API 并处理所有 REST 操作,是集群内所有组件交互的中枢。

基本架构

客户端(kubectl/Controller/Node等) 

API Server (kube-apiserver)

etcd (集群状态存储)
TeX

核心功能

  • API 暴露
    • 提供 Kubernetes 所有资源的 CRUD 接口
    • 支持 RESTful 风格的 API
    • 提供 Watch 机制监听资源变化
  • 请求处理
    • 认证(Authentication)
    • 授权(Authorization)
    • 准入控制(Admission Control)
    • 请求验证(Validation)
  • 集群状态管理
    • 与 etcd 交互,持久化集群状态
    • 保证数据的一致性和正确性
  • 通信枢纽
    • 所有控制平面组件(Controller Manager, Scheduler)都通过 API Server 工作
    • 所有节点上的 kubelet 都与 API Server 保持通信

工作流程:

  • 请求接收
    • 监听 HTTPS 端口(默认 6443)
    • 接收来自 kubectl、控制器、节点等的请求
  • 认证阶段
  • 授权阶段
  • 准入控制
    • 动态修改或验证请求
    • 常见准入控制器
      • MutatingAdmissionWebhook
      • ValidatingAdmissionWebhook
  • 资源验证
    • 验证资源对象的字段和规范
    • 确保符合 API 架构定义
  • etcd 交互
    • 将合法变更持久化到 etcd
    • 从 etcd 读取数据返回给客户端
  • 响应生成
    • 返回适当的 HTTP 状态码
    • 返回请求的资源或操作结果

1.2. API 组织形式

Kubernetes API 是一个 RESTful API,其组织形式遵循一种非常清晰和层级化的结构。可以将其理解为一个层层递进的路径。

核心组织形式

# 核心 API
/api/{version}/namespaces/{namespace}/{resource}/{name}

# 非核心 API
/apis/{apiGroup}/{version}/namespaces/{namespace}/{resource}/{name}
Bash

1.2.1. API 根路径 (/api 或 /apis)

这是所有 API 请求的起点。Kubernetes 有两类 API 组,它们的根路径不同:

  • /api:这是 核心(Core)API 组 的路径,也称为 Legacy Group。这里包含的是 Kubernetes 最早期、最核心的对象,例如 podsservicesnodesnamespaceseventsconfigmapssecrets 等。这些对象非常重要,以至于它们没有独立的 API 组名。
  • /apis:这是所有 命名 API 组(Named API Groups) 的路径。这是现代 Kubernetes API 的组织方式,它将功能相关的对象分类到不同的组中,更易于管理和扩展。例如,所有的 apps(Deployments, StatefulSets 等)都在 apps 组里。
  • 简单记忆:除了最核心的几个对象,其他所有 API 都通过 /apis 访问。

1.2.2. API 组 (apiGroup)

这一层用于对功能相关的 API 进行分类。这避免了所有资源都堆积在一个扁平的空间里,减少了命名冲突,并允许各组独立发展。

  • 核心组(在 /api 下): 这一层没有显式的组名。版本直接跟在 /api 后面。
    • 示例:/api/v1
  • 命名 API 组(在 /apis 下): 组名是路径的一部分。
    • apps:包含部署和状态化应用相关的资源,如 deploymentsstatefulsetsdaemonsetsreplicasets
      • 路径示例:/apis/apps/v1
    • networking.k8s.io:包含网络相关的资源,如 ingressesnetworkpolicies
      • 路径示例:/apis/networking.k8s.io/v1
    • storage.k8s.io:包含存储相关的资源,如 storageclasses
      • 路径示例:/apis/storage.k8s.io/v1
    • batch:包含批处理任务相关的资源,如 jobscronjobs
      • 路径示例:/apis/batch/v1
    • rbac.authorization.k8s.io:包含基于角色的访问控制资源,如 rolesrolebindings
      • 路径示例:/apis/rbac.authorization.k8s.io/v1
    • 还有很多其他组,如 autoscalingpolicycertificates.k8s.io 等。

1.2.3. API 版本 (version)

Kubernetes 支持 API 版本化,这是为了在引入新功能和改进时能平滑升级和向后兼容。常见的版本有:

  • v1:稳定的正式发布版本。功能和特性已经确定,会长期支持。
  • v1beta1v2alpha1 等:测试版(Beta) 或 实验版(Alpha)。Beta 版本通常功能比较稳定,但细节可能还会变化;Alpha 版本是实验性的,可能包含错误,默认可能被禁用,并且随时可能被删除而不另行通知。

1.2.4. 命名空间 (namespaces/{namespace})

这一层将资源隔离到不同的虚拟集群中。并非所有资源都是命名空间级的

  • 命名空间级资源(Namespaced Resources):如 Pods, Services, Deployments。它们的 API 路径中包含命名空间。
    • 示例:/api/v1/namespaces/default/pods/my-pod
    • 这表示获取 default 命名空间中名为 my-pod 的 Pod。
  • 集群级资源(Cluster-Scoped Resources):如 Nodes, PersistentVolumes, Namespaces 本身。它们的 API 路径中没有 namespaces/{namespace} 这部分。
    • 示例:/api/v1/nodes/my-node
    • 示例:/apis/storage.k8s.io/v1/storageclasses/standard

1.2.5. 资源类型 (resource)

这一层指定你要操作的具体对象类型,例如 podsservicesdeploymentsconfigmaps。资源名称通常是复数形式。

1.2.6. 资源名称 (name)

这一层指定你要操作的特定对象的名称。如果省略名称(只到 resource 层),则表示操作该类型的所有资源集合(例如,列出所有 Pod)。

1.2.7. 完整示例

假设我们有一个如下定义的 Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx-deployment
  namespace: web-app
spec:
  ...
Bash

要通过 HTTP REST 访问这个特定的 Deployment 对象,其 API 路径将是:/apis/apps/v1/namespaces/web-app/deployments/my-nginx-deployment

  • /apis:使用命名 API 组(非核心组)。
  • apps:API 组名。
  • v1:API 版本。
  • namespaces/web-app:该资源所在的命名空间。
  • deployments:资源类型(复数)。
  • my-nginx-deployment:资源的具体名称。

1.3. 访问 API

最简单的方法是使用 kubectl proxy 来代理 API Server,然后用 curl 或浏览器访问。

# 启动代理,这会在你的本地 localhost:8080 创建一个到 API Server 的代理,并自动处理身份认证。(使用你当前的 kubeconfig 文件,如 ~/.kube/config 中的证书或令牌)
kubectl proxy --port=8080

# 访问 API
# 访问根路径,查看所有可用的 API
$ curl http://localhost:8080/api/
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "192.168.2.153:6443"
    }
  ]
}


$ curl http://localhost:8080/apis/
{
  "kind": "APIGroupList",
  "apiVersion": "v1",
  "groups": [
    {
      "name": "apiregistration.k8s.io",
      "versions": [
        {
          "groupVersion": "apiregistration.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "apiregistration.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "apps",
      "versions": [
        {
          "groupVersion": "apps/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "apps/v1",
        "version": "v1"
      }
    },
    {
      "name": "events.k8s.io",
      "versions": [
        {
          "groupVersion": "events.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "events.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "authentication.k8s.io",
      "versions": [
        {
          "groupVersion": "authentication.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "authentication.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "authorization.k8s.io",
      "versions": [
        {
          "groupVersion": "authorization.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "authorization.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "autoscaling",
      "versions": [
        {
          "groupVersion": "autoscaling/v2",
          "version": "v2"
        },
        {
          "groupVersion": "autoscaling/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "autoscaling/v2",
        "version": "v2"
      }
    },
    {
      "name": "batch",
      "versions": [
        {
          "groupVersion": "batch/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "batch/v1",
        "version": "v1"
      }
    },
    {
      "name": "certificates.k8s.io",
      "versions": [
        {
          "groupVersion": "certificates.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "certificates.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "networking.k8s.io",
      "versions": [
        {
          "groupVersion": "networking.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "networking.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "policy",
      "versions": [
        {
          "groupVersion": "policy/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "policy/v1",
        "version": "v1"
      }
    },
    {
      "name": "rbac.authorization.k8s.io",
      "versions": [
        {
          "groupVersion": "rbac.authorization.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "rbac.authorization.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "storage.k8s.io",
      "versions": [
        {
          "groupVersion": "storage.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "storage.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "admissionregistration.k8s.io",
      "versions": [
        {
          "groupVersion": "admissionregistration.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "admissionregistration.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "apiextensions.k8s.io",
      "versions": [
        {
          "groupVersion": "apiextensions.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "apiextensions.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "scheduling.k8s.io",
      "versions": [
        {
          "groupVersion": "scheduling.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "scheduling.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "coordination.k8s.io",
      "versions": [
        {
          "groupVersion": "coordination.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "coordination.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "node.k8s.io",
      "versions": [
        {
          "groupVersion": "node.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "node.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "discovery.k8s.io",
      "versions": [
        {
          "groupVersion": "discovery.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "discovery.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "flowcontrol.apiserver.k8s.io",
      "versions": [
        {
          "groupVersion": "flowcontrol.apiserver.k8s.io/v1",
          "version": "v1"
        },
        {
          "groupVersion": "flowcontrol.apiserver.k8s.io/v1beta3",
          "version": "v1beta3"
        }
      ],
      "preferredVersion": {
        "groupVersion": "flowcontrol.apiserver.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "metrics.k8s.io",
      "versions": [
        {
          "groupVersion": "metrics.k8s.io/v1beta1",
          "version": "v1beta1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "metrics.k8s.io/v1beta1",
        "version": "v1beta1"
      }
    }
  ]
}

# 访问特定组和版本
curl http://localhost:8080/apis/apps/v1
Bash

如果不使用代理,则需要自行处理证书身份认证,详细的身份认证,安全机制见后面的文章。

使用 kubectl get 查看 API 路径

kubectl get 命令的 -v(详细)选项可以显示它实际发起的 API 请求,这对于调试和理解非常有帮助。

$ kubectl get deployments.apps -v=6

I0820 11:04:52.110022    8450 loader.go:395] Config loaded from file:  /home/admin/.kube/config
I0820 11:04:52.124037    8450 round_trippers.go:553] GET https://api-server:8443/apis/apps/v1/namespaces/default/deployments?limit=500 200 OK in 10 milliseconds
No resources found in default namespace.
Bash

1.4. Pod 调度过程中的 List-Watch 机制

1.4.1. 概念

List-Watch 是 Kubernetes 中一种客户端与 API Server 之间保持数据同步的机制。它由两个基本操作组成:

  • List: 调用资源的 LIST API,一次性获取指定资源的所有实例(例如,所有 Pod)。
  • Watch: 调用资源的 WATCH API,建立一个长连接,持续监听该资源所有后续的变更事件(例如,Pod 的创建、更新、删除)。

核心思想:客户端首先通过 List 获取全量数据,然后通过 Watch 接收后续的增量变更,从而在本地维护一个与 API Server 完全一致的、最新的资源状态缓存。

1.4.2. List-Watch 作用

在分布式系统中,组件需要感知集群状态的变化。最朴素的方法是轮询(Polling),即客户端定期向服务器发送请求询问:“有变化吗?”。

轮询的缺点非常明显:

  • 高延迟: 变化发生和客户端感知到变化之间存在一个轮询间隔,实时性差。
  • 高开销: 即使没有任何变化,也会产生大量无用的请求和响应,浪费 CPU、网络带宽,给 API Server 带来巨大压力。

List-Watch 采用了一种 “订阅-推送” 模型,完美解决了轮询的问题:

  • 低延迟: 变更几乎可以立即推送给客户端。
  • 高效率: 只有在变化发生时才会产生网络流量。

2. Controller Manager

核心职责:实时监听集群状态(通过 API Server List-Watch 机制),一旦检测到当前状态(Current State)与期望状态(Desired State)不一致,就发起必要的调协(Reconciliation)操作,驱动集群向期望状态收敛。

Controller Manager 是 k8s 集群中的资源控制器集合,集群中的资源对象基本都是被对应的 Controller 自动管理着的。

核心架构与工作模式

  • 核心组件:Informer (Reflector)
    • Controller Manager 并不直接轮询 API Server。它使用我们之前讨论的 List-Watch 机制
    • 每个控制器都通过一个叫 Informer 的组件来订阅它关心的资源(如 Pod、Node、Service)。
    • Informer 通过 Watch API Server,将其关注的资源对象全量缓存(Store) 在本地内存中,并持续接收变更事件。
    • 当事件发生时(如 Pod 被删除),Informer 会调用控制器注册的回调函数(EventHandler),将事件放入一个工作队列(Work Queue) 中。
  • 控制循环 (Control Loop):每个控制器都是一个独立的循环,其工作流程可以概括为以下步骤。
    • 读取期望状态: 从 API Server 中读取用户通过 YAML 文件定义的“期望状态”。(例如,Deployment 期望有 3 个副本的 Nginx Pod)。
    • 观察当前状态: 从本地的 Informer 缓存中读取“当前状态”。(例如,当前只有 2 个 Nginx Pod 在运行)。
    • 比较与调协: 比较“当前状态”和“期望状态”。
      • 状态一致: 什么都不做,继续循环。
      • 状态不一致: 执行“调协操作(Reconcile)”,使当前状态向期望状态靠拢。
      • 循环: 不断重复上述过程。

3. Scheduler

如果说 Controller Manager 是负责“决策”的大脑,那么 Scheduler 就是负责“派工”的调度中心。它的任务非常简单明确,但却至关重要:为新创建的 Pod 选择一个最适合的 Node 来运行它

  • 它的核心职责: 监视未被调度的 Pod(即 spec.nodeName 为空的 Pod),通过一系列复杂的筛选和评分算法,为每个 Pod 分配一个最优的 Node,并将这个分配决定写回 API Server。
  • 它不负责启动 Pod: Scheduler 只做决定,不执行。它通过更新 Pod 的 spec.nodeName 字段来做出调度决策。随后,该 Node 上的 kubelet 监听到这个变化,才会真正去创建和运行 Pod 的容器。

调度流程详解

Scheduler 为一个 Pod 选择 Node 的过程是一个多阶段的流水线,主要分为两个核心步骤:过滤(Filtering) 和 评分(Scoring)

  • 阶段一:过滤 (Filtering) / 预选 (Predicates)
    • 目标: 从所有可用的 Node 中,筛掉那些不满足 Pod 要求的 Node。
    • 过程: Scheduler 会依次运行一系列的过滤策略。如果一个 Node 无法通过其中任何一个策略,它就会被排除。这个过程结束后,会得到一个候选 Node 列表
    • 常见的过滤策略
      • PodFitsResources:检查 Node 的可用资源(CPU、内存)是否满足 Pod 的请求(requests)。
      • PodFitsHostPorts:检查 Pod 申请的宿主机端口(hostPort)在 Node 上是否已经被占用。
      • MatchNodeSelector:检查 Node 的标签(labels)是否匹配 Pod 的 nodeSelector 或 nodeAffinity 规则。
      • Volume 相关:检查 Node 是否能挂载 Pod 声明的卷(例如,检查 AzureDisk 是否在正确的可用区)。
      • Taint 和 Toleration: 如果 Node 上有 Pod 不能容忍的污点(Taint),则会被过滤掉。
    • 如果过滤后没有候选节点,Pod 将一直处于 Pending 状态,直到有符合条件的 Node 出现(例如,集群扩容了)。
  • 阶段二:评分 (Scoring) / 优选 (Priorities)
    • 目标: 在通过过滤的候选 Node 列表中,为每个 Node 打分,选择一个最优的 Node。
    • 过程: Scheduler 会依次运行一系列的评分策略。每个策略都会给 Node 打一个分数(通常 0-10 分)。最后,将所有策略的分数加权求和,得到每个 Node 的最终得分。得分最高的 Node 被选中
    • 常见的评分策略
      • LeastRequested优先选择资源请求更少的 Node。其计算公式类似于 (Node可用CPU / Node总CPU) + (Node可用Memory / Node总Memory)) / 2 * 10。这有助于将负载分散到不同的 Node 上。
      • BalancedResourceAllocation优先选择CPU和内存资源使用率更均衡的 Node。例如,一个 Node 的 CPU 用了 80%,内存用了 20%,不如一个 CPU 和内存都用了 50% 的 Node 均衡。这与 LeastRequested 配合,能更好地利用资源。
      • ImageLocality优先选择已经存在了 Pod 所需容器镜像的 Node。这可以避免从镜像仓库拉取镜像的开销,加速 Pod 启动。
      • InterPodAffinity:根据 Pod 间亲和性(podAffinity/podAntiAffinity)规则进行评分,尽可能将 Pod 部署到满足亲和性或反亲和性要求的 Node 上。
      • NodeAffinity:根据节点亲和性规则进行评分。

上一篇
下一篇