Kubernetes基础概念介绍

  1. Kubernetes
    1. What
    2. Why
    3. How
  2. Kubernetes集群架构
    1. 控制平面组件
      1. kube-apiserver
      2. kube-scheduler
      3. kube-controller-manager
      4. cloud-controller-manager
      5. etcd
    2. Node组件
      1. kubelet
      2. kube-proxy
      3. Container Runtime
  3. Kubernetes对象
    1. 对象结构
    2. 服务端字段校验
    3. 对象管理
      1. 指令式命令
      2. 指令式对象配置
      3. 声明式对象配置
    4. 对象规约和状态
    5. 对象的名字和ID
    6. 对象的标签和选择算符
      1. 标签选择符
      2. API对象的选择应用
    7. Namespace
    8. 对象的Annotation
    9. Finalizers
    10. Owners(属主) and Dependents(附属)
  4. Kubernetes API
    1. API groups
    2. Discovery API & OpenAPI
    3. Persistence
  • 参考
  • Kubernetes

    我们用经典的What? Why? How?三段式分析框架来快速的了解和学习Kubernetes。

    What

    Kubernetes是一个可移植,可扩展的开源平台,用于管理容器化的工作负载和服务,支持声明式的配置和自动化。

    Kubernetes名字来源于希腊语,以为舵手或飞行员,由于名字过长,经常缩写为K8s,是将”K”和”s”之间的八个字母缩写而成的简称。

    Kubernetes的前身是Google内部的Borg,Borg最初是为了管理Google内部的大规模分布式系统而开发的,其中包括了大量的容器。Google 在 2014 年开源了 Kubernetes 项目。Kubernetes 融合了 Google 超过 15 年的大规模生产环境工作负载管理经验,以及社区中最佳的理念和实践。

    Why

    我们先从服务部署和交付的技术发展历史来看一下,为什么会产生Kubernetes,如下是程序部署交付经历的三个时代:

    1. 传统部署时代

    我也称之为物理机部署时代,早期,我们直接在物理服务器上运行应用程序。由于无法在物理服务器上定义应用程序的资源边界,导致资源分配问题频发。例如,若多个应用程序运行在同一台物理服务器上,可能出现某个应用占用大部分资源的情况,导致其他应用性能下降。解决方案是将每个应用部署在独立的物理服务器上。然而,这种方式资源利用率低下且维护成本高昂,难以扩展。

    当然,你如果了解Linux,就会知道Linux内核引入了cgroup来对进程的资源使用进行的限制,这也是虚拟化技术进行资源隔离的基础。

    1. 虚拟化部署时代

    虚拟化技术应运而生。它允许在单个物理服务器的CPU上运行多个虚拟机(VM)。虚拟化实现了应用程序在虚拟机之间的隔离,提升了安全性。

    虚拟化显著提高了物理服务器的资源利用率,并增强了扩展性:应用可快速添加或更新,硬件成本降低。通过虚拟化,可将一组物理资源抽象为可动态分配的虚拟机集群。每台虚拟机均是一台完整机器,在虚拟化硬件上运行包括操作系统在内的所有组件。

    这个时代也称之为:云计算的时代,也就是现在各大云厂商所提供的基础的IaaS服务,可以提供,VM级别的灵活调度,例如:超卖,冷热迁移,机器硬件的自动扩容。Iaas基础设施带来了更多的容错性和可用性,例如可以冷热迁移,可以自动扩容

    1. 容器部署时代

    传统部署时代和虚拟化部署时代,对于业务开发者来说,没有什么大的区别,应用程序都是基于OS部署,都是直接和OS打交道。

    随着容器化技术的发展,来到了容器部署时代,容器与虚拟机类似,但隔离性限制较少,允许多个容器共享操作系统内核,因此更为轻量。与虚拟机类似,容器拥有独立的文件系统、CPU份额、内存、进程空间等资源。由于与底层基础设施解耦,容器可跨云平台及操作系统发行版无缝迁移。

    容器流行的核心优势包括:

    • 敏捷应用创建与部署:相比虚拟机镜像,容器镜像的创建更高效。
    • 持续开发、集成与部署:得益于镜像的不可变性,可快速构建、部署容器镜像并实现可靠回滚。
    • 开发与运维的关注点分离:在构建/发布阶段创建应用容器镜像,而非部署阶段,从而将应用与基础设施解耦。
    • 可观测性:不仅提供操作系统级指标,还能监控应用健康状态等信号。
    • 环境一致性:在开发、测试和生产环境中表现统一(如笔记本电脑与云端行为一致)。
    • 云与操作系统兼容性:支持Ubuntu、RHEL、CoreOS、本地环境及主流公有云平台。
    • 以应用为中心的管理:从虚拟硬件上的操作系统运行,升级为基于逻辑资源的操作系统级应用运行。
    • 松耦合、分布式、弹性、独立的微服务:应用拆分为小型独立组件,可动态部署和管理,而非运行在单一用途的巨型单体架构上。
    • 资源隔离:确保应用性能的可预测性。
    • 资源高效利用:提升资源利用率与密度。

    上面列出了容器流行的很多原因,最直观的来说:容器是打包和运行应用程序的好方法。随着云原生技术的发展,容器化越来越成为行业首选的技术方案,我觉得有一个很好的对容器的概括:

    Build Once, Run anywhere.

    容器让交付更简单,一次构建,可以在任何地方部署,依托registery,可以方便的进行发布和回滚,基于虚拟化环境,可以做到更好的环境隔离和高度的可移植性。如下:一个简单容器的构建和交付流程图:

    那么随之而来的就是,在生产环境中,我们需要管理运行应用程序的容器并确保不会出现停机。例如,如果一个容器发生故障,需要启动另一个容器。如果由系统来处理这种行为,会不会更容易?

    这就是Kubernetes的用武之地!Kubernetes为您提供了一个弹性运行分布式系统的框架。它会处理应用程序的扩展和故障转移,提供部署模式等功能。例如:Kubernetes可以轻松管理系统的金丝雀部署。

    Kubernetes为您提供以下核心能力:

    • Service discovery and load balancing服务发现与负载均衡:Kubernetes可以通过DNS名称或容器IP地址暴露容器。当容器流量激增时,Kubernetes能够进行负载均衡并分配网络流量,确保部署稳定。

    • Storage orchestration存储编排:支持自动挂载您选择的存储系统(如本地存储、公有云存储等)。

    • Automated rollouts and rollbacks自动化部署与回滚:可以通过Kubernetes声明容器的期望状态,系统将以受控速率将实际状态调整至目标状态。例如,可自动化创建新容器、回收旧容器资源。

      • Automatic bin packing自动完成装箱计算:向Kubernetes提供节点集群后,您只需声明容器所需的CPU和内存资源,Kubernetes会自动优化容器在节点间的分布以最大化资源利用率。
    • Self-healing自我修复:自动重启故障容器,替换不可用容器,并屏蔽未通过健康检查的容器,直到其恢复服务能力。

    • Secret and configuration management密钥与配置管理:安全存储和管理密码、OAuth令牌、SSH密钥等敏感信息,支持不重建镜像即可更新密钥和应用配置。

    • Batch execution批量任务执行:除常驻服务外,还能管理批处理和CI工作负载,按需替换故障容器。

    • Horizontal scaling水平扩缩容:通过命令行、UI或基于CPU使用率自动横向扩展应用规模。

    • IPv4/IPv6 dual-stackIPv4/IPv6双栈支持:为Pod和服务分配双协议栈地址

    • Designed for extensibility可扩展架构:无需修改上游源码即可为集群添加新功能。

    How

    这里先跳过How,本文主要是Kubernetes的基础知识学习和汇总,不讨论How的问题。

    Kubernetes集群架构

    Kubernetes集群由一个控制平面Control Plane一组运行容器化应用程序的工作机器,称为Nodes组成。每个集群至少需要一个工作节点来运行 Pod。

    Node托管了作为应用程序工作负载组件的 Pod,Control Plane负责管理集群中Node和Pod。在生产环境中,Control Plane通常跨多台计算机运行,而集群通常会运行多个Node,以提供容错能力并实现高可用性。

    如下是Kubernetes集群架构图,列出了一个完整且可运行的 Kubernetes 集群所需的各种组件:

    下面的Kubernets集群架构图概括性的阐述了构建集群必不可少的Control Plane组件,相对上图,更简洁。

    Kubernetes集群组件主要分为两个部分:

    • Master节点:集群的控制节点,对集群进行调度管理,包含组件:
      • kube-apiserver:集群的统一入口,提供REST API接口,所有对集群的操作都通过这个组件进行;
      • kube-scheduler:负责监听新创建的但未调度的 Pod,根据资源需求(CPU、内存)、策略(如亲和性)和节点状态,选择合适的节点部署 Pod。
      • kube-controller-manager:负责运行和管理所有的Controller进程,如节点控制器、副本控制器、端点控制器等,它们负责监控集群状态并使其与期望状态保持一致。
      • cloud-controller-manager:负责运行与云服务提供商交互的控制器,允许集群连接到云提供商的API。
      • etcd:一个高可用的键值存储,用于保存集群的所有配置数据。
    • Node节点:集群的工作节点,运行业务应用,包含组件:
      • kubelet:在每个节点上运行的代理,确保容器在Pod中运行。
      • kube-proxy:网络代理,维护节点上的网络规则,实现Kubernetes服务的抽象。
      • 容器运行时(Container Runtime):如Docker、containerd或CRI-O,负责运行容器。

    如下Kubernetes Architecture文中的动态的集群架构图,可以更形象和详细的展示了集群核心组件间的交互:

    控制平面组件

    Master节点的控制平面组件会为整个集群做出全局决策,比如资源调度,集群事件的检测和响应,例如当对象的spec.replicas没有达到预期申明的数量,会启动新的Pod;控制平面的组件如下:

    kube-apiserver

    APIserver是 Kubernetes 集群的核心组件,可以被视为控制平面(Control Plane)的中枢。它通过暴露一个 REST API 接口提供前端服务,允许终端用户、Kubernetes 的其他内部组件(如控制器、调度器)以及外部系统进行通信。

    Kubernetes API 接口主要提供了以下功能:

    1. 查询与请求信息:用于检索 Kubernetes 对象(如 Pod、Deployment、ConfigMap、Secret、命名空间等)的状态和元数据。
    2. 修改对象状态:作为修改 Kubernetes API 对象状态的唯一网关(例如创建、更新或删除资源)。

    所有与 Kubernetes 集群的交互(如通过 kubectl 发送的命令)最终都会通过 API 服务器进行,确保操作的安全性、一致性和可审计性。

    下图引用自文章,这张图可以很好的展示apiserver作为控制面的核心组件,在集群内的关键作用。

    kube-apiserver 设计上考虑了水平扩缩,如下是一个Kubernetes集群的kube-apiserver的部署情况:

    1
    2
    3
    4
    $ kubectl -n kube-system get pods -o wide|grep kube-apiserver
    kube-apiserver-30.49.40.140 1/1 Running 3 630d 30.49.40.140 30.49.40.140 <none> <none>
    kube-apiserver-30.49.40.198 1/1 Running 1 630d 30.49.40.198 30.49.40.198 <none> <none>
    kube-apiserver-30.49.40.201 1/1 Running 1 630d 30.49.40.201 30.49.40.201 <none> <none>

    kube-scheduler

    kube-scheduler负责监听新创建的,且未指定Node的Pods,并为其选择合适的Node来运行;

    调度决策考虑的因素包括单个 Pod 及 Pods 集合的资源需求、软硬件及策略约束、 亲和性及反亲和性规范、数据位置、工作负载间的干扰及最后时限。

    下图引用自Kubernetes Scheduler如何工作一文,很好的描述了kube-scheduler的基本职责。

    kube-controller-manager

    kube-controller-manager相当于所有Controller的大脑,负责运行和管理所有的Controller进程,如节点控制器、副本控制器、端点控制器等,它们负责监控集群状态并使其与期望状态保持一致。

    kube-controller-manager是控制平面的核心组件之一,它的的实现方式是:将内置的Controller打包成一个二进制文件,并在一个进程中运行它们。以下是kube-controller-manager包含的主要控制器及其职责:

    • **节点控制器(Node Controller)**:监控节点状态,当节点不可用时采取响应措施,例如更新节点状态并从节点移除Pod。
    • **副本控制器(Replication Controller)**:确保指定数量的Pod副本在运行。
    • **部署控制器(Deployment Controller)**:管理Deployment资源,实现应用的声明式更新。
    • **状态集控制器(StatefulSet Controller)**:管理StatefulSet资源,为有状态应用提供保障。
    • **服务控制器(Service Controller)**:创建和更新服务相关的资源,如LoadBalancer。
    • **端点控制器(Endpoints Controller)**:填充Endpoints对象(即连接Services和Pods)。
    • **服务账号控制器(ServiceAccount Controller)**:为新的命名空间创建默认的服务账号。
    • **持久卷控制器(PersistentVolume Controller)**:管理持久卷的绑定、回收和动态供应。
    • **命名空间控制器(Namespace Controller)**:确保对已删除的命名空间进行清理。
    • **作业控制器(Job Controller)**:监控代表一次性任务的Job对象,然后创建Pods来运行这些任务。
    • **定时作业控制器(CronJob Controller)**:管理定时任务的执行。
    • **水平Pod自动扩缩器(HorizontalPodAutoscaler)**:根据CPU使用率或其他指标自动调整Pod数量。
    • **证书签名控制器(Certificate Controller)**:处理证书请求并生成证书。

    下图引用自Kubernetes Controller Manager 如何工作一文:

    cloud-controller-manager

    云基础设施技术能够让我们在公有云、私有云和混合云环境中运行 Kubernetes。Kubernetes 秉承自动化、API 驱动的基础设施理念,强调各组件间的松散耦合。

    cloud-controller-manager 是 Kubernetes 控制平面的核心组件,内嵌了云厂商定制化控制逻辑。它的主要功能包括:

    1. 云平台集成:将 Kubernetes 集群与云服务商的 API 深度集成;
    2. 职责分离:将与云平台交互的组件(如节点控制器、路由控制器)与集群内部组件(如调度器、控制器管理器)解耦;

    通过将 Kubernetes 与底层云基础设施的互操作逻辑解耦,cloud-controller-manager 使云厂商能够以不同于 Kubernetes 主项目的节奏发布特性更新。

    该组件采用插件机制构建,允许不同云服务商通过标准化接口将其平台与 Kubernetes 集成。

    cloud-controller-manager内置的核心控制器主要包括:

    • 节点控制器(Node Controller):监控节点状态,同步云厂商的实例元数据(如实例类型、区域信息)。若节点与云实例失联,自动标记节点为 NotReady 并触发剔除(Eviction)。

    • 服务控制器(Service Controller):管理 Service 类型为 LoadBalancer 的资源,自动在云平台创建/删除负载均衡器(如 AWS ELB、GCP LB)。同步负载均衡器的状态到 Kubernetes Service 对象。

    • 路由控制器(Route Controller):在云厂商网络中自动配置路由表,确保 Pod 间跨节点通信(如 AWS VPC 路由、GCP VPC 路由)。处理 Flannel 等 CNI 插件的网络路由需求。

    • 持久卷标签控制器(PersistentVolumeLabel Controller):为动态创建的 PersistentVolume(PV)自动附加云厂商的标签(如 AWS EBS 的 failure-domain.beta.kubernetes.io/zone)。确保 PV 的拓扑信息与云存储区域匹配。

    • 存储控制器(StorageController):管理云存储资源(如 AWS EBS、GCP Persistent Disk)的附加/分离操作。
      自动处理存储卷的生命周期(如扩容、快照)。

    下图引用自Kubernetes Cloud Controller Manager 一文,展示了Kubernets如何通过cloud-controller-manager来使用云厂商的Load Balancer来暴露集群中的Service的,以及管理存储。

    etcd

    etcd 是兼顾一致性与高可用性的nosql db,作为保存 Kubernetes 所有集群数据的后台数据库。可以称之为Kubernetes的大脑, etcd 在 Kubernetes 中的核心的功能包括:

    • 集中式存储:etcd 存储所有 Kubernetes 对象的配置、状态和元数据,包括 Pod、Secret、DaemonSet、Deployment、ConfigMap 和 StatefulSet 等。

    • 实时追踪:Kubernetes 利用 etcd 的监听功能(通过 Watch() API)监控对象状态的变化,从而实现实时跟踪和响应更新。

    • API 可访问性:etcd 通过 gRPC 暴露键值 API。它还包含一个 gRPC 网关(RESTful 代理),可将 HTTP API 调用转换为 gRPC 消息。这使得 etcd 成为 Kubernetes 的理想数据库。

    • 数据存储结构:所有 Kubernetes 对象以键值对形式存储在 etcd 的 /registry 目录下,称为 etcd 键空间(etcd key space)。例如:默认命名空间中名为 nginx 的 Pod 信息存储在 /registry/pods/default/nginx 路径下。

    如下图,引用etcd in Kubernetes: A Quick Guide,解释了etcd在Kubernetes如何使用的过程:

    Node组件

    kubelet

    kubelet 是 Kubernetes 集群中 每个节点(Node)上运行的核心代理组件。 它保证 Pod及其Container的运行,kubelet 接收一组通过各类机制提供给它的 PodSpecs, 确保这些 PodSpecs 中描述的容器处于运行状态且健康。kubelet直接与容器运行时(如 Docker、containerd)交互,但 kubelet 不会管理不是由 Kubernetes 创建的容器。

    kubelet核心职责包括:

    1. Pod 生命周期管理

      • 根据 API Server 下发的 Pod 配置,创建、启动、停止容器
      • 监控容器状态,若容器异常退出,按重启策略(如 AlwaysOnFailure)重新启动。
      • 执行 Pod 的生命周期钩子(Lifecycle Hooks),如 PostStartPreStop
    2. 节点的管理

      • 将节点注册到 Kubernetes 集群中,使 API 服务器能够感知到该节点的存在。

      • 定期向 API Server 提交节点状态(如 CPU/内存容量、就绪状态)。

      • 若节点故障(如磁盘满、网络断开),主动标记节点为 NotReady 并触发驱逐逻辑。

    3. 资源管理

      • 通过 cgroups 限制容器的 CPU、内存、磁盘 I/O 等资源使用。
      • 监控资源使用情况,执行 OOM(内存溢出)终止或资源不足时的 Pod 驱逐。
      • 镜像和容器垃圾回收 - 清理未使用的容器和镜像以释放节点资源

    kubelet 作为每个节点上的“Kubernetes代理”,不仅管理该节点上的 Pod 和容器,还负责整个节点的管理和维护,确保节点正常运行并能够被 Kubernetes 控制平面有效管理。但完整的节点管理(如操作系统更新、硬件维护)需依赖外部工具(如云平台、运维系统)。

    下图引用自Kubelet Deep Dive 一文,描述了kubelet是如何进行Pod管理的。

    kube-proxy

    kube-proxy是集群中每个Node上运行的网络代理, 实现 Kubernetes 服务(Service) 概念的一部分。

    kube-proxy 维护节点上的一些网络规则, 这些网络规则会允许从集群内部或外部的网络会话与 Pod 进行网络通信。

    如果操作系统提供了可用的数据包过滤层(如 iptables 或 ipvs),则 kube-proxy 会通过它来实现网络规则。 否则,kube-proxy 仅做流量转发。

    如果您使用的网络插件(如 Cilium 或 Calico)已自行实现了 Service 的流量转发功能,且其行为与 kube-proxy 等效,则无需在集群节点上运行 kube-proxy。

    kube-proxy 当前支持以下几种实现

    • userspace:最早的负载均衡方案,它在用户空间监听一个端口,所有服务通过 iptables 转发到这个端口,然后在其内部负载均衡到实际的 Pod。该方式最主要的问题是效率低,有明显的性能瓶颈。
    • iptables:目前推荐的方案,完全以 iptables 规则的方式来实现 service 负载均衡。该方式最主要的问题是在服务多的时候产生太多的 iptables 规则,非增量式更新会引入一定的时延,大规模情况下有明显的性能问题。
    • ipvs:为解决 iptables 模式的性能问题,v1.11 新增了 ipvs 模式,采用增量式更新,并可以保证 service 更新期间连接保持不断开。
    • winuserspace:同 userspace,但仅工作在 windows 节点上。

    如下是引用cilium中kube-proxy在iptables模式下如何通过iptables来进行流量管理的,和Docker Engine的网络管理基本一致。

    kube-proxy运行在kube-system namespace中,每个Node上都有一个kube-proxy,具体查看哪个kube-proxy运行在哪个Node上,可以通过下面的命令

    1
    2
    3
    4
    5
    root@VM-131-137-centos ~# kubectl -n kube-system get pods -o wide|grep kube-proxy
    kube-proxy-456j9 1/1 Running 0 289d 30.49.40.6 30.49.40.6 <none> <none>
    kube-proxy-4mx8t 1/1 Running 0 289d 30.49.40.201 30.49.40.201 <none> <none>
    kube-proxy-vbvxd 1/1 Running 0 289d 30.49.40.198 30.49.40.198 <none> <none>
    kube-proxy-zs2vt 1/1 Running 0 289d 30.49.40.140 30.49.40.140 <none> <none>

    Container Runtime

    容器运行时环境是负责容器的运行,它是 Kubernetes 节点上的一个关键组件,负责管理容器的生命周期、镜像和执行环境。容器运行时的主要功能

    1. 容器生命周期管理:创建、启动、停止和删除容器
    2. 镜像管理:下载、解压和存储容器镜像
    3. 网络管理:为容器设置网络命名空间和网络接口
    4. 存储管理:为容器挂载存储卷
    5. 资源隔离:使用 Linux 内核特性(如 cgroups、namespaces)实现容器隔离

    如下是CNCF 生态全景图列出的目前支持CRI的Container Runtime列表:

    目前主要流行的是:

    • containerd:目前最广泛采用的容器运行时,最初是从 Docker 中提取的核心组件,现在是 CNCF 项目,功能全面,性能优良,安全性好

    • CRI-O:专为 Kubernetes 设计的轻量级运行时,只实现了 CRI 所需的功能,没有额外功能,由 Red Hat 主导开发,OpenShift 默认使用

    CRI Intro一文中介绍了,在Kubernetes中如何通过CRI来调用不同的Container Runtime来实现容器的管理,如下图:

    Kubernetes对象

    在Kubernetes系统中,所有资源都视为对象,Kubernetes对象是持久化的实体,Kubernetes 使用这些实体去表示整个集群的状态。例如,这些对象描述了:

    • 哪些容器化应用正在运行(以及在哪些节点上运行);
    • 可以被应用使用的资源;
    • 关于应用运行时行为的策略,比如重启策略、升级策略以及容错策略;

    Kubernetes的对象是一种“意向表达(Record of Intent)”,什么意思呢?就是Kubernetes对象一旦创建之后,Kubernetes 系统将持续工作以确保对象存在。通过创建对象,本质上是告诉Kubernetes系统,你所期望的工作负载的样子,这就是Kubernetes集群所谓的期望状态desired state

    当操作Kubernetes的对象的时候,无论是创建,修改,或者删除,都需要通过Kubernetes API.,比如,当使用 kubectl 命令的时候, kubectl 命令会调用对应的Kubernetes API,我们也可以直接在程序中通过Client Libraries来调用Kubernetes API。

    下面我们先看一下Kubernetes的对象的结构的组成和定义:

    对象结构

    在想要创建的 Kubernetes 对象对应的 manifest中(YAML or JSON文件)中,需要配置如下的字段:

    • apiVersion :创建该对象所使用的 Kubernetes API 的版本
    • kind :想要创建的对象的类型
    • metadata:帮助识别对象唯一性的数据,包括一个 name 字符串、UID 和可选的 namespace
    • spec:期望对象的状态,spec 的精确格式对每个 Kubernetes 对象来说是不同的,包含了特定于该对象的嵌套字段。Kubernetes API 参考能够帮助我们找到任何我们想创建的对象的 spec 格式。

    如下是Deployment对象的结构定义:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/api/apps/v1/types.go
    type Deployment struct {
    metav1.TypeMeta `json:",inline"`

    // +optional
    metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

    // Specification of the desired behavior of the Deployment.
    // +optional
    Spec DeploymentSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`

    // Most recently observed status of the Deployment.
    // +optional
    Status DeploymentStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
    }

    Deployment对象结构中的结构成员中:

    • TypeMeta,标识对象的API版本和对象的类型,所有对象都包含此子结构,此子结构的定义使用了inline内敛标签,在序列化/反序列化的时候会平铺在父结构体中。
    • ObjectMeta,标识对象的metadata,里面主要包含识别对象唯一性的数据,以及一些元数据。

    TypeMetaObjectMeta的基础定义如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    //https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go
    type TypeMeta struct {
    // +optional
    Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"`

    // +optional
    APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,2,opt,name=apiVersion"`
    }

    type ObjectMeta struct {
    // +optional
    Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`

    // +optional
    GenerateName string `json:"generateName,omitempty" protobuf:"bytes,2,opt,name=generateName"`

    // +optional
    Namespace string `json:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"`

    // +optional
    SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,4,opt,name=selfLink"`

    // UID is the unique in time and space value for this object. It is typically generated by
    // the server on successful creation of a resource and is not allowed to change on PUT
    // operations.
    //
    // Populated by the system.
    // Read-only.
    // More info: http://kubernetes.io/docs/user-guide/identifiers#uids
    // +optional
    UID types.UID `json:"uid,omitempty" protobuf:"bytes,5,opt,name=uid,casttype=k8s.io/kubernetes/pkg/types.UID"`

    // +optional
    ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,6,opt,name=resourceVersion"`

    // +optional
    Generation int64 `json:"generation,omitempty" protobuf:"varint,7,opt,name=generation"`

    // +optional
    CreationTimestamp Time `json:"creationTimestamp,omitempty" protobuf:"bytes,8,opt,name=creationTimestamp"`

    // +optional
    DeletionTimestamp *Time `json:"deletionTimestamp,omitempty" protobuf:"bytes,9,opt,name=deletionTimestamp"`

    // +optional
    DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty" protobuf:"varint,10,opt,name=deletionGracePeriodSeconds"`

    // +optional
    Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`

    // +optional
    Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`

    // +optional
    // +patchMergeKey=uid
    // +patchStrategy=merge
    OwnerReferences []OwnerReference `json:"ownerReferences,omitempty" patchStrategy:"merge" patchMergeKey:"uid" protobuf:"bytes,13,rep,name=ownerReferences"`

    // +optional
    // +patchStrategy=merge
    Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"`

    // +optional
    ManagedFields []ManagedFieldsEntry `json:"managedFields,omitempty" protobuf:"bytes,17,rep,name=managedFields"`
    }

    从上面Deployment对象结构的定义我们可以看到对应着Kubernetes 对象对应的 manifest中结构定义要求,包含了了基本的:apiVersionkindmetadataspec等,如下官方示例,是创建Deployment对象的manifest:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: nginx-deployment
    spec:
    selector:
    matchLabels:
    app: nginx
    replicas: 2 # tells deployment to run 2 pods matching the template
    template:
    metadata:
    labels:
    app: nginx
    spec:
    containers:
    - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

    通过kubectl apply命令可以在Kubernetes集群中部署上面的Deployment对象:

    1
    2
    3
    4
    5
    6
    $ kubectl apply -f https://k8s.io/examples/application/deployment.yaml
    deployment.apps/nginx-deployment created
    $ kubectl get pods
    NAME READY STATUS RESTARTS AGE
    nginx-deployment-77d8468669-2npx8 0/1 ContainerCreating 0 7s
    nginx-deployment-77d8468669-469nx 0/1 ContainerCreating 0 7s

    服务端字段校验

    Kubernetes从v1.25版本开始,API server提供了服务器端的对象字段的校验,针对操作时传入的对象的manifest,检测其中是否存在未知,重复的对象的字段。它在服务器端提供了 kubectl --validate 的所有功能。

    kubectl --validate 接受值如下:

    • strict:严格的字段验证,验证失败时会报错;
    • warn:执行字段验证,但错误会以警告形式提供而不是拒绝请求;
    • ignore:不执行服务器端字段验证;
    • true:等价于strict默认值
    • false:等价于ignore

    kubectl 无法连接到支持字段验证的 API 服务器时,它将回退为使用客户端验证。

    特性 客户端验证(kubectl) 服务器端验证(API Server)
    触发时机 在发送请求前由 kubectl 本地检查 请求到达 API 服务器后强制执行
    可靠性 依赖 kubectl 版本和本地 schema 的准确性 始终基于集群当前 API 的权威 schema
    验证范围 可能因版本差异遗漏新字段 检查所有字段,包括未知或重复字段
    绕过方式 可通过 --validate=false 跳过 无法绕过,强制执行

    相对于validate的字段验证,其实Kubernetes很早就支持了--dry-run来模拟执行指令,可以验证server-side default 值注入、mutating webhook 或者 CRD 的复杂校验,但不会真正的改变集群中的资源,在Kubernetes v1.18以后,提供了--dry-run=client|server|none

    • client:只在本地做验证和模拟(不与 API Server 通讯)。
    • server:将请求发送到服务器,由服务器模拟执行但不真正持久化资源。
    • none:正常执行(等价于不 dry-run)。

    对象管理

    kubectl提供了三种方式来创建和管理Kubernetes的对象,如下:

    管理方式 操作对象 推荐使用环境 支持的编写者 学习曲线
    指令式命令 活动对象 开发项目 1+ 最低
    指令式对象配置 单个文件 生产项目 1 中等
    声明式对象配置 文件目录 生产项目 1+ 最高

    指令式命令

    通过指令式命令,我们可以直接操作集群中的活动对象,通过调用kubectl传入操作的命令参数来进行对象的操作。

    指令式命令只推荐在:开始学习Kubernetes时或者执行一次性的Task的情况下使用。因为:该方式是直接操作活动对象,不提供以前配置的历史记录

    如下是一些指令式命令:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # 创建一个 Deployment
    kubectl create deployment nginx --image=nginx

    # 创建一个 Pod
    kubectl run nginx --image=nginx

    # 创建一个 Service
    kubectl expose deployment nginx --port=80 --type=NodePort
    # 创建一个 Service
    kubectl create service nodeport nginx --tcp=80:80

    # 扩展 Deployment
    kubectl scale deployment nginx --replicas=3

    # 添加/覆盖标签
    kubectl label pods/nginx app=web --overwrite

    # 删除 Deployment 及其关联 Pod
    kubectl delete deployment/nginx

    # 列出指定命名空间的 Pod
    kubectl get pods -n kube-system

    指令式对象配置

    通过指令式对象配置来操作对象,kubectl需要指定操作的命令(例如createreplace等)+可选参数+至少一个文件名。指定的文件必须包含 YAML 或 JSON 格式的对象的完整定义。

    Warningreplace命令会用文件中最新的Spec替换对象已经存在规约Spec,所以它会删除不存在最终配置中所有的已经发生的对象状态。此方式不能用在资源类型是独立于配置文件更新的,例如类型为 LoadBalancer 的服务,它的 externalIPs 字段就是独立于集群配置进行更新

    如下示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 从文件创建对象(若对象已存在则报错)
    kubectl create -f nginx.yaml

    # 替换现有对象的完整配置(需提供完整 YAML)
    kubectl replace -f nginx.yaml

    # 从文件删除资源
    kubectl delete -f nginx.yaml -f redis.yaml

    # 应用配置变更(推荐)
    kubectl apply -f nginx.yaml

    # 直接编辑活动对象的实时配置(生成临时 YAML)
    kubectl edit deployment/nginx

    声明式对象配置

    用户可以通过本地的对象配置文件(YAML/JSON)来进行对象的操作,但用户不需要定义操作的类型,kubectl 会自动检测每个文件的创建、更新和删除操作。 这使得配置可以在目录上工作,根据目录中配置文件对不同的对象执行不同的操作。

    Note:声明式对象配置会保留其他操作者所做的修改,即使这些修改没有被合入最新对象的配置文件。这是通过patch API的只写入新增的变化值来实现的,而不是通过replace API来全量覆盖对象的配置。

    例如如下:通过目录中的所有对象配置文件,创建或者更新活跃对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 对比配置文件与活动对象状态
    kubectl diff -f configs/

    # 创建和更新目录中的所有资源
    kubectl apply -f configs/

    # 递归处理目录
    kubectl apply -f configs/ --recursive

    # 删除目录中定义的所有资源
    kubectl delete -f configs/

    下面是Kubernetes中三种对象的管理方式的优缺点的对比,可以对他们有个概括性的了解:

    操作方式 优点 缺点
    指令式命令
    Imperative commands
    • 简单直接,易于学习和使用
    • 只需一条命令即可完成操作
    • 适合快速测试和学习
    • 不需要预先定义完整的YAML/JSON文件
    • 无法实现版本控制
    • 难以自动化和重复操作
    • 不适合复杂应用部署
    • 无法方便地记录配置历史
    指令式对象配置
    Imperative object configuration
    • 对象配置可以进行版本控制
    • 可以与Git等版本控制系统集成
    • 操作意图明确
    • 可以审查配置文件后再执行
    • 需要理解对象的完整定义
    • replace操作会覆盖其他方式做出的变更
    • 不适合对象有独立更新字段的情况
    • 每次更改都需要完整的配置文件
    声明式对象配置
    Declarative object configuration
    • 可以处理目录中的多个文件
    • 自动检测每个对象需要的操作
    • 保留其他方式所做的更改
    • 最适合生产环境
    • 通过patch仅更新差异部分
    • 较难调试和理解
    • 部分更新可能导致意外的配置
    • 学习曲线较陡
    • 操作预览较复杂

    对象规约和状态

    几乎每个 Kubernetes 对象都包含两个嵌套的对象字段来管理对象的配置:对象规约 spec 和对象状态**status**。

    • 对象的 **spec**字段,在对象创建的时候设置,描述你希望对象所具有的特征:期望状态desired state

    • 对象的 **status**字段,描述了对象的当前状态,它是由 Kubernetes 系统和组件设置并更新的。在任何时刻,Kubernete控制平面都一直在积极地管理着对象的实际状态,以使之达成期望状态。

    如上面提到的Deployment对象中的定义,其中就有对象规约 spec 和对象状态**status**两个对象字段。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/apps/types.go
    type Deployment struct {
    metav1.TypeMeta `json:",inline"`

    // +optional
    metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

    // Specification of the desired behavior of the Deployment.
    // +optional
    Spec DeploymentSpec

    // Most recently observed status of the Deployment.
    // +optional
    Status DeploymentStatus
    }

    Kubernetes 中的 Deployment 对象能够表示运行在集群中的应用。 当创建 Deployment 时,你可能会设置 Deployment 的 spec对象来指定该应用要有 3 个副本运行。 Kubernetes 系统读取 Deployment 的 spec, 并启动我们所期望的应用的 3 个实例,更新状态以与规约相匹配。 如果这些实例中有的失败了(一种状态变更),Kubernetes 系统会通过执行修正操作来响应 specstatus 间的不一致 ,即再启动一个新的实例来替换。

    对象的名字和ID

    每个对象都有一个该类型的资源内的唯一的Name字段。同样,每个对象还有一个全局唯一UID,此UID集群中唯一,不管资源类型。

    例如一个Pod和一个Deployment对象的名字都可以是myapp-1234,但是他们在集群中的UID是唯一的。如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $kubectl run my-app-666 --image=nginx
    pod/my-app-666 created

    $kubectl create deployment my-app-666 --image=nginx
    deployment.apps/my-app-666 created

    $kubectl get pod my-app-666 -o=custom-columns=NAME:.metadata.name,UID:.metadata.uid
    NAME UID
    my-app-666 f2eac56e-2513-4eaf-a055-2fdb33dab543

    $kubectl get deployment my-app-666 -o=custom-columns=NAME:.metadata.name,UID:.metadata.uid
    NAME UID
    my-app-666 c73d263a-6018-4c7a-aa24-48e66b648d69

    在「对象结构」一节我们知道,对象的唯一性标识,包括Name字段和UID字段都在对象的metadata中,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    type ObjectMeta struct {
    // +optional
    Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`

    // UID is the unique in time and space value for this object. It is typically generated by
    // the server on successful creation of a resource and is not allowed to change on PUT
    // +optional
    UID types.UID `json:"uid,omitempty" protobuf:"bytes,5,opt,name=uid,casttype=k8s.io/kubernetes/pkg/types.UID"`

    ...
    }

    对象的标签和选择算符

    标签(Labels)是一组附加到Kubernetes 对象的key/value对,标签旨在用于指定对于用户有意义的对象的标识属性,但不直接对核心系统有语义含义。标签设计目的:可以被用来组织和选择对象的子集。标签可以在创建时附加到对象,随后可以随时添加和修改。 每个对象都可以定义一组键/值标签。每个键对于给定对象必须是唯一的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go
    type ObjectMeta struct {
    // +optional
    Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`

    // +optional
    UID types.UID `json:"uid,omitempty" protobuf:"bytes,5,opt,name=uid,casttype=k8s.io/kubernetes/pkg/types.UID"`

    // +optional
    Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`

    // +optional
    Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`
    }

    Kubernetes中的每个对象的元数据都可以包含一组键值对,称之为标签,标签的设计目的:为用户提供,在对象中添加有意义的标识属性,但是对整个核心系统没有直接语义含义

    有效的标签键:

    • 可选的前缀:用斜杠(/)和名称分隔。如果指定,前缀必须是 DNS 子域:由点(.)分隔的一系列 DNS 标签,总共不超过 253 个字符, 后跟斜杠(/)。
    • 名称:名称段是必需的,必须小于等于 63 个字符,以字母数字字符([a-z0-9A-Z])开头和结尾, 带有破折号(-),下划线(_),点( .)和之间的字母数字。

    如果省略前缀,则假定标签键对用户是私有的。 向最终用户对象添加标签的自动系统组件(例如 kube-schedulerkube-controller-managerkube-apiserverkubectl 或其他第三方自动化工具)必须指定前缀。

    kubernetes.io/k8s.io/ 前缀是为 Kubernetes 核心组件保留的

    有效标签值:

    • 必须为 63 个字符或更少(可以为空)
    • 除非标签值为空,必须以字母数字字符([a-z0-9A-Z])开头和结尾
    • 包含破折号(-)、下划线(_)、点(.)和字母或数字

    标签选择符

    names and UIDs不同的是,labels不具有唯一性,通常我们会设置多个对象含有相同的标签,通过标签选择算符,客户端/用户可以识别一组对象。标签选择算符是 Kubernetes 中的核心分组原语

    API 目前支持两种类型的选择算符:基于等值的基于集合的。 标签选择算符可以由逗号分隔的多个需求组成。 在多个需求的情况下,必须满足所有要求,因此逗号分隔符充当逻辑&&)运算符。

    • 基于等值的需求

    基于等值基于不等值的需求允许按标签键和值进行过滤。 匹配对象必须满足所有指定的标签约束,尽管它们也可能具有其他标签。 可接受的运算符有 ===!= 三种。 前两个表示相等(并且是同义词),而后者表示不相等。例如:

    1
    2
    environment = production
    tier != frontend
    • 基于集合的需求

    基于集合的标签需求允许你通过一组值来过滤键。 支持三种操作符:innotinexists(只可以用在键标识符上)。例如:

    1
    2
    3
    4
    environment in (production, qa)
    tier notin (frontend, backend)
    partition
    !partition
    • 第一个示例选择了所有键等于 environment 并且值等于 production 或者 qa 的资源。
    • 第二个示例选择了所有键等于 tier 并且值不等于 frontend 或者 backend 的资源,以及所有没有 tier 键标签的资源。
    • 第三个示例选择了所有包含了有 partition 标签的资源;没有校验它的值。
    • 第四个示例选择了所有没有 partition 标签的资源;没有校验它的值。

    类似地,逗号分隔符充当运算符。因此,使用 partition 键(无论为何值)和 environment 不同于 qa 来过滤资源可以使用 partition, environment notin (qa) 来实现。

    基于集合的标签选择算符是相等标签选择算符的一般形式,因为 environment=production 等同于 environment in (production)!=notin 也是类似的。

    基于集合的要求可以与基于相等的要求混合使用。例如:partition in (customerA, customerB),environment!=qa

    如下示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $ kubectl get pods -l key1=value1,key2=value2
    NAME READY STATUS RESTARTS AGE
    nginx 1/1 Running 0 7d22h

    $ kubectl label pods -lkey2 key3=value3
    pod/nginx labeled

    $ kubectl get pods -lkey3=value3
    NAME READY STATUS RESTARTS AGE
    nginx 1/1 Running 0 7d23h

    $ kubectl get pods -l 'key1 in (value1), key3=value3'
    NAME READY STATUS RESTARTS AGE
    nginx 1/1 Running 0 7d23h

    有时需要要在创建新资源之前对现有的 Pod 和其它资源重新打标签。 这可以用 kubectl label 完成:如下是匹配标签key1=value1的Pod,然后进行打标签key4=value4

    1
    2
    3
    $ kubectl label pods -l key1=value1 key4=value4
    NAME READY STATUS RESTARTS AGE
    nginx 1/1 Running 0 7d22h

    API对象的选择应用

    一些 Kubernetes 对象,例如 servicesreplicationcontrollers, 也使用了标签选择算符去指定了其他资源的集合,例如 pods

    一个 Service 指向的一组 Pod 是由标签选择算符定义的。同样,一个 ReplicationController 应该管理的 Pod 的数量也是由标签选择算符定义的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    apiVersion: v1
    kind: Service
    metadata:
    ...
    spec:
    clusterIP: None
    ...
    selector:
    app.kubernetes.io/instance: gitbee
    app.kubernetes.io/name: gitbee
    ...
    status:
    loadBalancer: {}

    Namespace

    在 Kubernetes 中,名字空间(Namespace) 提供一种机制,将同一Cluster中的资源划分为相互隔离的组。 同一Namespace内的资源名称要唯一(不同资源的Name前缀不同),但跨名字空间时没有这个要求。 Namespace的作用域仅针对名字空间的对象: (e.g. Deployments, Services, etc.) ,针对**集群作用域的对象 (e.g. StorageClass, Nodes, PersistentVolumes, etc.)并不适用

    Namespace适用于存在很多跨多个团队或项目的用户的场景。对于只有几到几十个用户的集群,根本不需要创建或考虑名字空间。

    Namespace为资源提供了一个范围。资源的名称需要在名字空间内是唯一的,但不能跨名字空间。 名字空间不能相互嵌套,每个 Kubernetes 资源只能在一个名字空间中

    Namespace是在多个用户之间划分集群资源的一种方法(通过资源配额)。不必使用多个名字空间来分隔仅仅轻微不同的资源,例如同一软件的不同版本: 应该使用标签来区分同一名字空间中的不同资源。

    Kubernetes集群初始化会默认创建以下4个Namespace:

    Namespace 描述
    default Kubernetes 包含这个名字空间,以便于你无需创建新的名字空间即可开始使用新集群。
    kube-node-lease 该名字空间包含用于与各个Node关联的 Lease(租约)对象。Node租约允许 kubelet 发送心跳,由此控制面能够检测到节点故障。
    kube-public 所有的客户端(包括未经身份验证的客户端)都可以读取该名字空间。该名字空间主要预留为集群使用,以便某些资源需要在整个集群中可见可读。该名字空间的公共属性只是一种约定而非要求。
    kube-system 该名字空间用于 Kubernetes 系统创建的对象。

    对象的Annotation

    你可以使用 Kubernetes 注解为对象附加任意的非标识的元数据。 客户端程序(例如工具和库)能够获取这些元数据信息。

    你可以使用标签或注解将元数据附加到 Kubernetes 对象。 标签可以用来选择对象和查找满足某些条件的对象集合。 相反,注解不用于标识和选择对象。 注解中的元数据,可以很小,也可以很大,可以是结构化的,也可以是非结构化的,能够包含标签不允许的字符。

    注解的设计主要是存储非识别性的元数据,为对象提供附加信息。供工具、库、系统(如 CI/CD、监控、运营商)或用户查看,例如:构建信息、Git 哈希、配置说明、监控链接、许可证信息。

    注解(Annotations) 存储的形式也是键/值对。

    有效的注解键分为两部分:

    • 可选的前缀:以斜杠(/)和名称分隔。如果指定,则前缀必须是 DNS 子域:一系列由点(.)分隔的 DNS 标签, 总计不超过 253 个字符,后跟斜杠(/)。
    • 名称, 名称段是必需项,并且必须在 63 个字符以内,以字母数字字符([a-z0-9A-Z])开头和结尾, 并允许使用破折号(-),下划线(_),点(.)和字母数字。

    前缀是可选的。 如果省略前缀,则假定注解键对用户是私有的。 由系统组件添加的注解 (例如,kube-schedulerkube-controller-managerkube-apiserverkubectl 或其他第三方组件),必须为终端用户添加注解前缀。

    kubernetes.io/k8s.io/ 前缀是为 Kubernetes 核心组件保留的。如下是简单的annotation操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    $ kubectl annotate pod nginx walkerdu.com/key1=value1
    pod/nginx annotated
    $ kubectl annotate pod nginx walkerdu.com/key1=value1-1 --overwrite
    pod/nginx annotated

    $kubectl get pod nginx -o yaml
    apiVersion: v1
    kind: Pod
    metadata:
    annotations:
    tke.cloud.tencent.com/networks-status: "..."
    walkerdu.com/key1: value1-1
    ...

    # 删除annotation,通过在key后面加'-'
    $ kubectl annotate pod nginx walkerdu.com/key1-
    pod/nginx annotated

    Finalizers

    Finalizers 是 Kubernetes对象元数据(metadata)中的一个可选字段(可见定义:type ObjectMeta struct),用于控制对象的删除流程,确保在对象真正被删除前执行某些“清理”或“预删除”操作。它本质上是一种删除阻塞机制(deletion blocker)。

    例如:

    1
    2
    3
    4
    metadata:
    finalizers:
    - kubernetes.io/pv-protection
    - mycontroller.example.com/cleanup

    当一个对象(如 Pod、Namespace、CustomResource 等)设置了 Finalizers,当你试图删除该资源时,处理删除请求的 API 服务器会注意到 finalizers 字段中的值, 并进行以下操作:

    1. 将对象的 metadata.deletionTimestamp 设为当前时间(标记为“终止中”)。
    2. 等待metadata.finalizers 字段内的所有项被删除。
    3. 只有当 finalizers 列表为空时,才真正删除对象。返回 202 状态码(HTTP “Accepted”)

    自定义 Finalizer(CRD 控制器常用)常使用 finalizer 实现资源清理,如删除外部数据库、云资源等。示例:自定义控制器逻辑

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 伪代码
    if obj.DeletionTimestamp != nil {
    if contains(obj.Finalizers, "mycontroller.example.com/cleanup") {
    // 执行清理:删除云资源、关闭连接等
    cleanupExternalResources(obj)

    // 移除 finalizer
    removeFinalizer(obj, "mycontroller.example.com/cleanup")
    updateObj(obj)
    }
    return
    }

    // 正常 reconcile 逻辑...

    Owners(属主) and Dependents(附属)

    在 Kubernetes中,一些对象是另一些对象的所有者,例如一个ReplicaSet对象是一组Pod对象的所有者。

    Owners 和 Dependents 是对象间所有者-从属关系的核心概念,主要用于垃圾回收(Garbage Collection)机制,帮助自动管理资源的生命周期。这与 Finalizers 密切相关,因为删除过程会涉及这些关系。

    Owners 和 Dependents 的关系通过 metadata.ownerReferences 字段定义,对象元数据(metadata)中的一个可选字段(可见定义:type ObjectMeta struct),表示这个对象被哪些更高层的对象控制或创建。

    Kubernetes 使用 Owners 和 Dependents 来实现级联删除(Cascading Deletion):

    1. 设置 Owner Reference:控制器(如 Deployment Controller)在创建子对象时,自动添加 ownerReferences。
    2. 垃圾回收:当 Owner 被删除(设置 deletionTimestamp)时:
      • Kubernetes 的 Garbage Collector 检查所有 Dependents。
      • 如果 Dependent 有匹配的 Owner Reference,且没有 finalizers 阻塞,则自动删除 Dependent。
    3. 删除类型:
      • Orphaned Deletion:不级联删除 Dependents(使用 –cascade=orphan)。
      • Foreground Deletion:默认,Owner 先删除 Dependents,再删除自己。
      • Background Deletion:Owner 立即删除,Dependents 在后台删除。

    与 Finalizers 的交互:

    • 如果 Dependent 有 Finalizers(如 kubernetes.io/pv-protection),删除会被阻塞,直到控制器移除 Finalizer。
    • 这确保了清理逻辑(如释放持久卷)在删除前执行。

    常见应用场景

    场景 Owners 示例 Dependents 示例 作用
    Deployment Deployment ReplicaSet Deployment 删除时,级联删除 ReplicaSet 和其 Pod。
    StatefulSet StatefulSet Pod、PVC 确保有序删除 Pod,并释放 PVC(如果有 finalizer 保护)。
    Custom Resources (CRD) Custom Controller Custom Resource Instances Operator 删除时,清理外部资源(如数据库)。
    Namespace Namespace Controller 所有 Namespace 内对象 Namespace 删除时,级联删除内部资源,除非有 finalizers。

    如下我通过deployment创建的一个Pod的过程中,相关对象中的属主的配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    $ kubectl get deployment.apps/wecom-read-it-later -o yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    annotations:
    deployment.kubernetes.io/revision: "3"
    meta.helm.sh/release-name: wecom-read-it-later
    ...
    namespace: default
    resourceVersion: "12480254788"
    uid: eb579845-53c6-4176-9bfe-05fa50041315

    $ kubectl get replicaset.apps/wecom-read-it-later-7c58678d5b -o yaml
    apiVersion: apps/v1
    kind: ReplicaSet
    metadata:
    annotations:
    deployment.kubernetes.io/desired-replicas: "1"
    deployment.kubernetes.io/max-replicas: "2"
    deployment.kubernetes.io/revision: "3"
    ...
    resourceVersion: "12480254772"
    uid: b92aba2f-1af4-4529-8a31-59d249d0fd15
    ownerReferences:
    - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: Deployment
    name: wecom-read-it-later
    uid: eb579845-53c6-4176-9bfe-05fa50041315

    $ kubectl get pod wecom-read-it-later-7c58678d5b-r6n9w -o yaml
    apiVersion: v1
    kind: Pod
    metadata:
    annotations:
    ...
    labels:
    app.kubernetes.io/instance: wecom-read-it-later
    app.kubernetes.io/name: wecom-read-it-later
    pod-template-hash: 7c58678d5b
    ...
    resourceVersion: "12480254767"
    uid: bd2deef0-e12f-4883-a47c-f05854f35aa4
    ownerReferences:
    - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: wecom-read-it-later-7c58678d5b
    uid: b92aba2f-1af4-4529-8a31-59d249d0fd15

    例如上面Pod的metadata.ownerReferences中标记了属主ReplicaSet对象的uid: b92aba2f-1af4-4529-8a31-59d249d0fd15,然后ReplicaSet对象的metadata.ownerReferences中标记了属主Deployment对象的uid: eb579845-53c6-4176-9bfe-05fa50041315

    Kubernetes API

    Kubernetes API 使我们可以查询和操纵 Kubernetes 中对象的状态。API server作为Kubernetes控制面的核心,负责提供 HTTP API,以供用户、集群中的不同部分和集群外部组件相互通信。

    REST API是Kubernetes架构的基础,组件之间所有的操作以及外部用户命令都是通过REST API调用API server进行处理

    API groups

    API Groups 是 Kubernetes 中用于对 API 资源进行逻辑分组和版本管理的核心概念。它是 Kubernetes 从 v1.2 开始引入的机制,目的是解决早期 API(如 v1)过于庞大、难以演进的问题。

    为什么需要 API 组?在 Kubernetes 早期:

    • 所有资源(Pod、Service、ReplicationController 等)都放在一个扁平的 v1 API 中。
    • 随着功能增加,API 越来越臃肿,无法独立演进(比如网络策略和批处理作业不该和核心 Pod 绑定同一版本)。

    解决方案:按功能划分 API 组,每个组独立版本控制。API组从结构上分为两类:

    • 核心组(core/legacy)组, REST 路径为 /api/v1。 核心组并不作为 apiVersion 字段的一部分,例如, apiVersion: v1。核心组只有v1版本,包括了最古老的核心资源:Pod、Service、Node,ConfigMap,Secret。
    • 命名组(Named groups),有明确的Group名字,每个组可有多个版本(v1, v1beta1, v1alpha1),指定的组位于 REST 路径 /apis/$GROUP_NAME/$VERSION, 并且使用 apiVersion: $GROUP_NAME/$VERSION (例如,apiVersion: batch/v1)。
    REST API Path apiVersion 写法 API Group(组名) 版本 说明
    /api/v1 v1 (空组,也叫 core 组) v1 最古老的核心资源:Pod、Service、Node…
    /apis/apps/v1 apps/v1 apps v1 Deployment、StatefulSet、DaemonSet
    /apis/batch/v1 batch/v1 batch v1 Job、CronJob
    /apis/networking.k8s.io/v1 networking.k8s.io/v1 networking.k8s.io v1 Ingress、NetworkPolicy
    /apis/discovery.k8s.io/v1 discovery.k8s.io/v1 discovery.k8s.io v1 EndpointSlice

    版本控制是在 API 级别而不是在资源或字段级别完成的,以确保 API 呈现出清晰、一致的系统资源和行为视图, 并能够控制对生命结束和/或实验性 API 的访问。

    每个 API 组可独立演进版本,遵循以下阶段:

    版本后缀 含义 是否稳定 可否用于生产
    v1 稳定版(GA) ✅ 是 ✅ 推荐
    v1beta1 Beta 版 ⚠️ 可能有 breaking change ⚠️ 谨慎使用
    v1alpha1 Alpha 版 ❌ 不稳定,可能删除 ❌ 仅测试

    例如:Ingress 最初在 extensions/v1beta1,后来迁移到 networking.k8s.io/v1

    Discovery API & OpenAPI

    Kubernetes 集群里有很多 API(比如 Pod、Deployment、Service 这些资源都是通过 API 来操作的)。不同的集群版本、不同的厂商(比如原生 Kubernetes、OpenShift、各种云厂商的 K8s)支持的 API 可能有点差别。为了让各种工具(kubectl、helm、CI/CD、监控、第三方客户端等)能自动适配任何一个 Kubernetes 集群,集群就必须把自己“我到底支持哪些 API、支持哪些版本、每个资源长什么样”这些信息告诉外面。

    为了解决这个问题,每个 Kubernetes 集群都会发布集群所使用的 API 规范。 Kubernetes 使用两种机制来发布这些 API 规范;这两种机制可以相互结合来提供操作。 所支持的两种机制如下:

    • The Discovery API :提供有关 Kubernetes API 的信息:API 名称、资源、版本和支持的操作。它是一个独立于 Kubernetes OpenAPI 的 API。 其目的是为可用的资源提供简要总结,不详细说明资源的具体模式。有关资源模式的参考,请参阅 OpenAPI 文档。
    • Kubernetes OpenAPI Document :所有 Kubernetes API 端点提供(完整的)OpenAPI v2.0 and 3.0 schemas。OpenAPI v3 是访问 OpenAPI 的首选方法, 因为它提供了更全面和准确的 API 视图。其中包括所有可用的 API 路径,以及每个端点上每个操作所接收和生成的所有资源。 它还包括集群支持的所有可扩展组件。这些数据是完整的规范,比 Discovery API 提供的规范要大得多。

    每个Kubernetes版本默认支持的上述两种机制在Kubernetes源码都是可以看到的(所有API的描述都是由Go源码自动生成json文件),包含了所有的API和资源,这些json文件是“快照副本”,仅用于客户端代码生成、文档生成、离线查看。

    两种API暴露机制对应的结构分为如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // https://github.com/kubernetes/kubernetes/tree/master/api
    api
    ├── OWNERS
    ├── api-rules
    ├── discovery
    │ ├── aggregated_v2.json
    │ ├── api.json
    │ ├── api__v1.json
    │ ├── apis.json
    │ ├── ...
    │ ├── apis__apps.json
    │ ├── apis__apps__v1.json
    │ ├── apis__authentication.k8s.io.json
    │ ├── apis__authentication.k8s.io__v1.json
    └── openapi-spec
    ├── README.md
    ├── swagger.json
    └── v3
    ├── api__v1_openapi.json
    ├── api_openapi.json
    ├── apis__apps__v1_openapi.json
    ├── apis__apps_openapi.json
    ├── apis__authentication.k8s.io__v1_openapi.json
    ├── apis__authentication.k8s.io_openapi.json
    ├── ...

    如下是openapi中关于Pod对象的结构描述,可以看得出完全是由Go的Pod结构生成出来的API资源描述。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    // https://github.com/kubernetes/kubernetes/blob/master/api/openapi-spec/v3/api__v1_openapi.json
    {
    "components": {
    "schemas": {
    // ...
    "io.k8s.api.core.v1.Pod": {
    "description": "Pod is a collection of containers that can run on a host. This resource is created by clients and scheduled onto hosts.",
    "properties": {
    "apiVersion": {
    "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
    "type": "string"
    },
    "kind": {
    "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
    "type": "string"
    },
    "metadata": {
    "allOf": [
    {
    "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
    }
    ],
    "default": {},
    "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata"
    },
    "spec": {
    "allOf": [
    {
    "$ref": "#/components/schemas/io.k8s.api.core.v1.PodSpec"
    }
    ],
    "default": {},
    "description": "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status"
    },
    "status": {
    "allOf": [
    {
    "$ref": "#/components/schemas/io.k8s.api.core.v1.PodStatus"
    }
    ],
    "default": {},
    "description": "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status"
    }
    },
    "type": "object",
    "x-kubernetes-group-version-kind": [
    {
    "group": "",
    "kind": "Pod",
    "version": "v1"
    }
    ]
    },
    // ...
    }
    }

    我们可以通过kubectl来动态的查看Kubernetes集群所有的API资源的详细信息:

    1
    2
    3
    kubectl api-versions
    kubectl api-resources
    kubectl get --raw /api | jq .

    如下是查看API资源的简要信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    $ kubectl api-resources --api-group=''
    NAME SHORTNAMES APIVERSION NAMESPACED KIND
    bindings v1 true Binding
    componentstatuses cs v1 false ComponentStatus
    configmaps cm v1 true ConfigMap
    endpoints ep v1 true Endpoints
    events ev v1 true Event
    limitranges limits v1 true LimitRange
    namespaces ns v1 false Namespace
    nodes no v1 false Node
    persistentvolumeclaims pvc v1 true PersistentVolumeClaim
    persistentvolumes pv v1 false PersistentVolume
    pods po v1 true Pod
    podtemplates v1 true PodTemplate
    replicationcontrollers rc v1 true ReplicationController
    resourcequotas quota v1 true ResourceQuota
    secrets v1 true Secret
    serviceaccounts sa v1 true ServiceAccount
    services svc v1 true Service
    $ kubectl api-resources --api-group='apps'
    NAME SHORTNAMES APIVERSION NAMESPACED KIND
    controllerrevisions apps/v1 true ControllerRevision
    daemonsets ds apps/v1 true DaemonSet
    deployments deploy apps/v1 true Deployment
    replicasets rs apps/v1 true ReplicaSet
    statefulsets sts apps/v1 true StatefulSet

    如下是查看API的名称,分组,版本信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $ kubectl api-versions 
    admissionregistration.k8s.io/v1
    apiextensions.k8s.io/v1
    apiregistration.k8s.io/v1
    apps/v1
    authentication.k8s.io/v1
    authorization.k8s.io/v1
    autoscaling/v1
    autoscaling/v2
    batch/v1

    kubetctl api-versionskubetctl api-resources都是通过Dicovery API来获取集群的API简要信息,我们可以通过kubetctl explain来访问集群暴露的OpenAPI 详细资源的文档说明:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    $ kubectl explain pod
    KIND: Pod
    VERSION: v1

    DESCRIPTION:
    Pod is a collection of containers that can run on a host. This resource is
    created by clients and scheduled onto hosts.

    FIELDS:
    apiVersion <string>
    APIVersion defines the versioned schema of this representation of an object.
    Servers should convert recognized schemas to the latest internal value, and
    may reject unrecognized values. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

    kind <string>
    Kind is a string value representing the REST resource this object
    represents. Servers may infer this from the endpoint the client submits
    requests to. Cannot be updated. In CamelCase. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

    metadata <ObjectMeta>
    Standard object's metadata. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

    spec <PodSpec>
    Specification of the desired behavior of the pod. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

    status <PodStatus>
    Most recently observed status of the pod. This data may not be up to date.
    Populated by the system. Read-only. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

    也可以通过--raw 参数来获取OpenAPI暴露的资源详细信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    $ kubectl get --raw /openapi/v3/api/v1
    {
    "openapi": "3.0.0",
    "info": {
    "title": "Kubernetes",
    "version": "v1.30.0"
    },
    "paths": {
    "/api/v1/": {
    "get": {
    "tags": [
    "core_v1"
    ],
    "description": "get available resources",
    "operationId": "getCoreV1APIResources",
    "responses": {
    "200": {
    "description": "OK",
    "content": {
    "application/json": {
    "schema": {
    "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList"
    }
    },
    "application/vnd.kubernetes.protobuf": {
    "schema": {
    "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList"
    }
    },
    ...

    API Server 维护 OpenAPI Schema,kubectl api-versions / api-resources / openapi/v3 全部是实时从内存动态生成的。

    • API Server 启动时会为所有注册的 API 资源维护 OpenAPI/Swagger 规范
    • 这些规范在资源注册时就已经定义好了(可以理解为”静态”的元数据)
    • CRD(自定义资源)也会在安装时向 API Server 注册其 schema

    Persistence

    Kubernetes 会将集群内所有的API资源对象进行序列化(protobuf encoding)的写到 etcd 中存储

    参考

    Scheduler in Kubernetes: A Quick Guide

    How to Monitor Kubernetes API Server

    How to Monitor kube-controller-manager

    Kube Controller Manager: A Quick Guide

    What is Kube-Proxy and why move from iptables to eBPF?