K3s、Kubernets基础

总结摘要
K3s、Kubernets基础

核心概念

K3s 是什么?和标准 K8s 的区别?(轻量级、简化部署、适合边缘)

一句话原理 K3s 是由 Rancher Labs 开发的轻量级 Kubernetes 发行版,它删除了 K8s 中非必需的遗留代码、Alpha/Beta 特性和内置插件(如默认 Storage Driver),将所有组件打包进一个小于 100MB 的单一二进制文件中。

一句话源码 在 Linux 服务器上只需执行 curl -sfL https://get.k3s.io | sh - 即可完成 K8s 集群的安装,无需复杂的 kubeadm init 和证书配置。

一句话项目/场景 在“物联网边缘计算”项目中,将 K3s 部署在 ARM 架构的树莓派或工业网关上,用于管理运行在边缘侧的容器应用,实现离线数据处理和低延迟响应。


详细区别:K3s vs 标准 K8s

维度标准 K8s (Upstream)K3s
二进制大小1.5 GB(需多个组件二进制)< 100 MB(单一二进制文件)
内存占用高(控制平面至少需要 2GB+)极低(最低 512MB 即可运行)
安装复杂度。需安装 kubeadm, kubelet, etcd 等,配置证书、RBAC。极低。一条命令自动安装,自带 SQLite/Traefik。
依赖项依赖外部 etcd、CNI 插件、容器运行时。去除外设依赖。内置 SQLite(可选 etcd)、Flannel、Traefik、CoreDNS。
数据库后端强依赖 etcd(运维门槛高)。支持 SQLite、MySQL、Postgres、etcd(灵活性极高)。
适用架构x86_64 服务器集群。x86_64 & ARM/ARM64(完美支持树莓派)。
适用场景云数据中心、大规模生产集群。边缘计算、IoT、CI/CD 流水线、嵌入式设备、开发测试环境。

为什么叫 K3s?

名字的由来是一个梗:K8s 是 10 个字母的缩写(K + 8 个字母 + s),K3s 意味着它是 K8s 的“一半”大小(5 个字母的概念),寓意更轻量、更精简。

K3s 做了哪些“减法”?

为了实现轻量级,K3s 剔除了以下内容,但完全保留了 K8s 的核心 API

  1. 废弃功能:移除了所有 Alpha/Beta 阶段的 API。
  2. 内置插件:去掉了默认的云厂商存储插件(AWS EBS, GCE PD 等),改用 CSI。
  3. Legacy 功能:移除了 docker 作为容器运行时的支持(仅支持 containerd,这也是 K8s 的趋势)。
  4. 重命名:相关二进制文件从 kube-xxx 改名为 k3s-xxx

K3s 的核心优势

  1. 简化运维: 原生 K8s 集群维护需要专业的 SRE 团队处理证书轮转、etcd 备份。K3s 大幅降低了运维门槛,甚至支持单节点运行完整的 K8s 功能。

  2. 边缘友好: 在网络不稳定或带宽受限的边缘环境,K3s 可以离线运行,并在网络恢复后自动同步数据。

  3. 适合开发: 开发者在本地笔记本电脑上运行 K3s,可以获得与生产环境一致的 K8s API 体验,比 Minikube 更省资源。

Kubernetes 的核心组件有哪些?(API Server、etcd、Scheduler、Controller Manager、Kubelet)

一句话原理 Kubernetes 采用控制平面工作节点分离的架构。控制平面负责“决策与管理”(大脑),工作节点负责“执行与负载”(手脚)。

一句话源码 用户通过 kubectlAPI Server 提交请求,数据存入 etcdScheduler 决定 Pod 去哪,Controller Manager 确保 Pod 数量正常,Kubelet 在节点上负责让容器跑起来。

一句话项目/场景 在“电商平台高可用部署”中,Scheduler 将订单服务 Pod 调度到空闲节点,Controller Manager 监控到订单服务 Pod 挂掉后立即重建,etcd 记录了整个集群的实时状态。


详细组件解析

一、 控制平面组件

这些组件负责管理集群状态,通常运行在 Master 节点上。

API Server(入口与网关)
  • 角色:集群的统一入口,唯一的“前台接待员”。
  • 作用
    • 提供RESTful API接口(如 kubectl 的所有命令最终都打到这)。
    • 负责认证、授权、准入控制(谁能进?能干什么?数据合不合法?)。
    • 是唯一能直接连接 etcd 的组件,所有状态变更都要经过它。
etcd(数据库)
  • 角色:集群的唯一记忆库
  • 作用
    • 高可用的键值对数据库,存储集群所有的配置信息和状态数据(如“现在应该有几个Pod”、“实际有几个Pod”)。
    • 注意:etcd 的数据丢失等于集群彻底崩溃,必须做好备份。
Scheduler(调度器)
  • 角色:集群的人力资源经理
  • 作用
    • 监听 API Server,发现“新来的 Pod 还没分配节点”。
    • 根据预设策略(资源余量、亲和性、污点容忍等)进行打分,选择一个最优节点绑定给 Pod。
    • 它只负责决策(把Pod安排到哪),不负责执行(真正去启动Pod)。
Controller Manager(控制器管理器)
  • 角色:集群的大管家,负责维持状态。
  • 作用
    • 包含多个控制器(如 Node Controller、Replication Controller)。
    • 核心逻辑:死循环执行“调谐循环”。不断对比“期望状态”(YAML定义)和“实际状态”(etcd记录)。
    • 例子:定义了 replicas: 3,如果实际只有 2 个 Pod 运行,它会通知 API Server 创建新的 Pod。

二、 工作节点组件

这些组件在每个工作节点上都必须运行,负责维护运行的 Pod。

Kubelet(节点代理)
  • 角色:Master 派驻在节点上的厂长
  • 作用
    • 监听 API Server,发现有 Pod 被调度到自己的节点上。
    • 调用底层的容器运行时(如 Containerd 或 Docker)来启动、停止容器
    • 定期向 Master 汇报节点健康状况(心跳)。
Kube-proxy(网络代理)
  • 角色:网络交警。
  • 作用
    • 维护节点上的网络规则(通常操作 iptables 或 IPVS)。
    • 确保 Service(虚拟IP)的流量能正确转发到后端的 Pod 上。

组件协作流程图解

假设用户执行 kubectl run nginx --image=nginx --replicas=3

  1. API Server:接收请求,验证权限,将请求写入 etcd
  2. Controller Manager:发现“期望 3 个 Nginx,实际 0 个”,创建 3 个 Pod 对象写入 etcd。
  3. Scheduler:发现 3 个新 Pod 处于 Pending 状态,根据资源情况分配节点(如 Node A, Node B, Node C),并更新 Pod 信息到 etcd。
  4. Kubelet(在 Node A/B/C 上):监听到有新 Pod 分配给自己,调用容器运行时下载镜像并启动容器。

总结记忆口诀

  • API Server:管入口,一切由此入。
  • etcd:管数据,一切存于此。
  • Scheduler:管调度,决定谁去哪。
  • Controller:管状态,少了补多了删。
  • Kubelet:管执行,负责干苦力。

什么是 Pod?和容器的关系?

一句话原理 Pod 是 Kubernetes 中最小的可部署单元,它是一组(一个或多个)紧密关联的容器的集合,这些容器共享网络命名空间(IP地址)和存储卷,始终“同生共死”。

一句话源码 Pod 的本质是一个逻辑概念,实际上是一组共享 Namespace 和 Cgroups 的容器进程

一句话项目/场景 在“内容管理系统”中,一个 Pod 包含两个容器:Nginx 容器(提供前端访问)和 File Puller 容器(定期拉取更新内容),两者通过共享的 Volume 交换文件,构成一个完整的服务单元。


详细解析:Pod 与容器的关系

类比理解

  • 容器:像是“独立运行的程序”。
  • Pod:像是“虚拟机”或“进程组”。
    • 在传统虚拟机中,几个程序跑在同一台机器上,共享 IP 和磁盘。
    • 在 K8s 中,Pod 模拟了这种环境。Pod 内的所有容器共享同一个网络栈( localhost 互相通信)和存储。

核心特性:为什么需要 Pod?

A. 共享网络

  • 机制:Pod 里的所有容器通过 Pause 容器(根容器)共享 Network Namespace。
  • 结果
    • 所有容器共享同一个 IP 地址。
    • 容器之间可以通过 localhost 直接通信(例如:Nginx 容器可以通过 localhost:8080 访问同一 Pod 内的 Tomcat 容器)。
    • 容器之间不存在端口冲突(不能同时监听同一端口)。

B. 共享存储

  • 机制:Pod 可以挂载 Volume,Pod 内的所有容器都可以挂载这个 Volume。
  • 结果:容器 A 写入文件,容器 B 可以直接读取,实现了数据共享。

C. 原子性管理

  • Pod 是 K8s 调度的原子单位。
  • K8s 不会调度“容器”,只会调度“Pod”。
  • 同一个 Pod 内的容器绝对不会被拆分到不同的机器上,它们总是同生共死。

容器设计模式:Sidecar(边车模式)

这是 Pod 最经典的设计模式,体现了 Pod 存在的价值。

  • 场景:一个主容器运行业务代码(如 Java 应用),但它需要日志收集功能。
  • 解耦方案:不要把日志收集逻辑写进 Java 代码里。
    • 容器 1(主容器):运行 Java 应用,把日志写到共享 Volume。
    • 容器 2(Sidecar 容器):运行 Logstash 或 Filebeat,读取共享 Volume 并上传到日志中心。
  • 优势:职责单一,容器独立,组合灵活。

总结对比

维度容器Pod
定义镜像的运行实例K8s 最小调度单元
包含关系包含在 Pod 中包含一个或多个容器
网络独立 IP内部容器共享 IP,对外统一 IP
生命周期独立运行内部容器同时创建、同时销毁
调度单位被 K8s 调度被 K8s 调度

核心记忆点Pod = 一组共享网络和存储的容器进程组。

什么是 Deployment、StatefulSet、DaemonSet?各自的应用场景?

一句话原理 这三者是 Kubernetes 的工作负载控制器,决定了 Pod 的创建方式、运行拓扑和生命周期

  • Deployment:管理无状态应用,像牧场主管理羊群(数量对就行,个体不重要)。
  • StatefulSet:管理有状态应用,像管理运动员队伍(每个人都有固定号码和角色)。
  • DaemonSet:管理守护进程,像每个房间的巡逻保安(每个节点必须有一个)。

一句话源码 Deployment 通过 replicas 控制副本数量;StatefulSet 通过 headless service 维持固定网络标识;DaemonSet 通过 nodeSelector 确保节点覆盖。

一句话项目/场景

  • Deployment:部署 Nginx 前端、Spring Boot API 服务。
  • StatefulSet:部署 MySQL 主从集群、Redis 集群、ZooKeeper。
  • DaemonSet:部署日志采集 Agent(Fluentd)、监控 Agent(Prometheus Node Exporter)。

详细解析与对比

Deployment(无状态应用)

这是最常用的控制器。

  • 核心特性
    • 无序:Pod 名称包含随机哈希(如 nginx-6d4cf5644-x7v2a),IP 也是动态的。
    • 弹缩:随意扩容缩容,新的 Pod 随时可以替代旧的 Pod,对业务无感知。
    • 滚动更新:支持自动滚动更新和回滚。
  • 应用场景
    • Web 服务器、API 微服务。
    • 任何不需要保存本地数据、不依赖固定网络身份的应用。

StatefulSet(有状态应用)

专为数据库和中间件设计。

  • 核心特性
    • 有序标识:Pod 名称固定且有序(如 mysql-0, mysql-1)。
    • 稳定存储:每个 Pod 绑定独立的持久化存储(PVC),Pod 重建后数据不丢失,且挂载回原来的存储。
    • 稳定网络:Pod 拥有固定的主机名,可以通过 DNS 解析(如 mysql-0.service.ns)。
    • 有序部署/缩容:必须按顺序启动(0->1->2),缩容时逆序(2->1->0)。
  • 应用场景
    • 关系型数据库。
    • 分布式存储。
    • 需要集群选举的应用。

DaemonSet(守护进程)

确保每个节点都跑一份副本。

  • 核心特性
    • 节点全覆盖:当新节点加入集群时,自动在该节点上创建 Pod;节点移除时,Pod 自动回收。
    • 无需指定副本数:副本数等于集群节点数。
  • 应用场景
    • 日志采集。
    • 监控代理。
    • 网络插件(Calico, Weave)。

总结对比表

维度DeploymentStatefulSetDaemonSet
Pod 名称随机哈希固定序号 (0, 1, 2…)随机哈希 + 节点名约束
存储 (PVC)共享存储 (通常)独立专属存储 (每个 Pod 一块盘)通常为 HostPath (读取宿主机信息)
网络标识随机 IP,Service 负载均衡固定 DNS 主机名,Headless Service每个 Node 一个 IP
启动顺序并行启动严格有序 (必须等前一个 Ready)并行启动
副本数控制手动/Auto Scaling手动 Scaling自动等于 Node 数量
典型代表Nginx, TomcatMySQL, Redis, KafkaFluentd, CNI 插件

记忆口诀

  • Deployment:大家一起上,谁上都行。
  • StatefulSet:排好队,一人一个号,谁也别抢谁的盘。
  • DaemonSet:每个房间放一个保安,寸步不离。

网络与存储

Kubernetes 的服务发现方式有哪些?(ClusterIP、NodePort、LoadBalancer、Ingress)

一句话原理 Kubernetes 服务发现通过 ServiceIngress 两种资源,将抽象的 Pod 网络端点暴露给内部或外部调用者,实现了从“内部互通”到“外网访问”的四级流量暴露体系。

一句话源码 Service 通过 spec.type 字段定义暴露方式(ClusterIP/NodePort/LoadBalancer),Ingress 通过定义路由规则将外部 HTTP/HTTPS 流量分发到 Service。

一句话项目/场景

  • ClusterIP:前端服务调用后端 API(内部通信)。
  • NodePort:开发测试环境快速通过节点端口访问服务。
  • LoadBalancer:公网暴露 API 网关(对接云厂商负载均衡)。
  • Ingress:生产环境基于域名(如 api.example.com)将流量路由到不同微服务。

详细解析:四种服务发现方式

ClusterIP(默认方式)

  • 定义:分配一个集群内部的虚拟 IP (VIP),仅能在集群内部访问
  • 原理:kube-proxy 在节点上配置 iptables/IPVS 规则,将访问 VIP 的流量转发到后端 Pod。
  • 应用场景
    • 微服务之间的内部调用(如 Order Service 调用 User Service)。
    • 不需要对外暴露的数据库、缓存服务。
  • 缺点:外部用户无法直接访问。

NodePort(节点端口)

  • 定义:在 ClusterIP 基础上,在每个 Node 节点上开放一个静态端口(默认范围 30000-32767)。
  • 原理:外部用户通过 NodeIP:NodePort 访问,流量被转发到 Service,再分发到 Pod。
  • 应用场景
    • 测试环境、演示环境。
    • 需要快速暴露非 HTTP 服务(如游戏服务器、自定义 TCP 服务)。
  • 缺点
    • 端口号难记且不美观。
    • 每个节点都会占用端口,存在安全风险。
    • 无法基于域名路由。

LoadBalancer(负载均衡器)

  • 定义:在 NodePort 基础上,自动调用云厂商(AWS/阿里云/腾讯云)的 API,创建一个外部的负载均衡器(如 ELB/SLB)。
  • 原理:云 LB -> NodePort -> ClusterIP -> Pod。
  • 应用场景
    • 生产环境中需要对外暴露 TCP/UDP 服务。
    • 通常用于暴露 API 网关或需要公网 IP 的服务。
  • 缺点
    • 成本高:每创建一个 LoadBalancer Service,云厂商都会分配一个独立的公网 IP 和 LB 实例,费用昂贵。
    • 资源浪费。

Ingress(智能路由)

  • 定义:这不是 Service 的一种类型,而是一个独立的资源对象。它作为一个智能网关,统一管理集群的入口流量。
  • 原理
    1. 部署一个 Ingress Controller(如 Nginx Ingress),它本身通常是一个 LoadBalancer 或 HostNetwork 类型的 Pod。
    2. Ingress 规则定义:host: a.com -> Service A; host: b.com -> Service B
    3. 外部流量 -> Ingress Controller -> 根据 HTTP Header/路径 -> 对应 Service。
  • 应用场景
    • 生产环境 HTTP/HTTPS 服务暴露
    • 一个 IP 地址服务多个域名(虚拟主机)。
    • 需要统一处理 SSL 证书、限流、鉴权的场景。
  • 优点:成本最低(只需一个公网 IP),配置灵活。

总结对比表

方式访问范围访问地址示例成本适用场景
ClusterIP集群内部10.96.0.1:80内部微服务通信
NodePort集群外部NodeIP:30080测试、非 HTTP 服务
LoadBalancer集群外部42.10.1.1:80 (公网IP)高 (每服务一个IP)生产环境 TCP/UDP 暴露
Ingress集群外部api.example.com低 (多服务共享IP)生产环境 Web 服务暴露

最佳实践架构

在真实的生产环境中,通常遵循以下链路: 外部用户 -> 云厂商 LB (LoadBalancer) -> Ingress Controller -> Service (ClusterIP) -> Pod

这样既利用了云厂商 LB 的稳定性,又利用了 Ingress 的灵活性,还能节省公网 IP 资源。

什么是 ConfigMap 和 Secret?如何使用?

一句话原理 ConfigMapSecret 是 Kubernetes 解耦配置数据容器镜像的两种资源对象。ConfigMap 用于存储非敏感的配置信息,Secret 用于存储敏感信息( Base64 编码)。

一句话源码 ConfigMap 存储明文键值对(key: value),Secret 存储编码后的键值对。Pod 通过环境变量挂载卷的方式引用它们。

一句话项目/场景 在“多环境部署”中,镜像代码不变,通过挂载不同的 ConfigMap 切换开发/生产环境的数据库地址;通过 Secret 安全注入数据库密码,避免密码明文写在代码或 YAML 中。


详细解析与对比

维度ConfigMapSecret
存储内容明文配置(配置文件、命令行参数、环境变量)。敏感数据(密码、证书、Token、SSH Key)。
编码方式直接存储(明文)。Base64 编码(注意:仅编码非加密,ETCD 需加密配置才安全)。
大小限制最大 1MB。最大 1MB。
安全性权限控制较宽松。权限控制更严格,且节点上通常存储在内存文件系统。

如何使用?(三种方式)

创建资源(准备数据)

ConfigMap 创建示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 命令行直接创建
kubectl create configmap app-config --from-literal=db_host=192.168.1.100

# 或者 YAML 文件
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  db_host: "192.168.1.100"
  db_port: "3306"

Secret 创建示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 命令行创建(注意:会自动进行 base64 编码)
kubectl create secret generic db-secret --from-literal=password=123456

# 或者 YAML 文件(需手动 base64 编码)
# echo -n "123456" | base64 -> MTIzNDU2
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  password: MTIzNDU2 

在 Pod 中使用(注入方式)

方式一:环境变量 适合传递少量参数或启动参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
  - name: app
    image: busybox
    command: ["sh", "-c", "echo $DB_HOST"]
    env:
      # 1. 注入 ConfigMap 的单个值
      - name: DB_HOST
        valueFrom:
          configMapKeyRef:
            name: app-config
            key: db_host
      
      # 2. 注入 Secret 的单个值
      - name: DB_PASSWORD
        valueFrom:
          secretKeyRef:
            name: db-secret
            key: password

方式二:挂载为文件 适合配置文件场景,如 Nginx.conf 或 application.properties。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
  - name: app
    image: busybox
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config  # 容器内路径
    - name: secret-volume
      mountPath: /etc/secret
      readOnly: true          # 安全起见,Secret 建议只读
  volumes:
  # 1. 挂载 ConfigMap
  - name: config-volume
    configMap:
      name: app-config
  # 2. 挂载 Secret
  - name: secret-volume
    secret:
      secretName: db-secret

注:挂载后,ConfigMap 的 Key 会变成文件名,Value 会变成文件内容。Secret 的内容会被自动解码还原为明文存入文件。

最佳实践

  1. 不可变配置:对于不需要频繁修改的配置,建议设置 immutable: true,可以防止意外更新,也能降低 kube-apiserver 的负载。
  2. 不要滥用 Secret 存大文件:Secret 存储在 etcd 中,大小限制 1MB,不适合存放大文件(如视频、大型图片)。
  3. 安全加密:默认 Secret 只是 Base64 编码,建议在 etcd 层面开启静态数据加密,或使用第三方保险库(如 HashiCorp Vault)。
  4. 配置更新
    • 环境变量方式:Pod 启动后注入,ConfigMap 更新不会自动更新环境变量,必须重启 Pod。
    • 挂载卷方式:ConfigMap 更新后,Kubelet 会自动更新节点上的文件,应用需支持热加载配置。

如何在 K8s 中实现持久化存储?(PV、PVC、StorageClass)

一句话原理 Kubernetes 通过 PV(存储资源)PVC(存储需求)StorageClass(存储模板) 三层抽象,将存储基础设施与业务应用解耦,实现数据的持久化保存。

一句话源码 用户在 YAML 中声明 PVC(我要多大的盘),StorageClass 自动调用云厂商 API 创建 PV(实际的存储卷),Pod 挂载 PVC 使用存储。

一句话项目/场景 在“MySQL 数据库迁移”场景中,即使 MySQL Pod 被删除重建,只要挂载了同一个 PVC,数据依然存在,因为数据实际保存在云厂商的硬盘(PV)中,而非容器内部。


核心概念解析

PV (Persistent Volume) - 存储资源

  • 定义持久卷。它是集群中的一块存储资源(如 NFS、Ceph、AWS EBS 硬盘)。
  • 角色:类似于 Node(节点资源),属于集群级资源,由运维人员管理。
  • 生命周期:独立于 Pod,Pod 删除后 PV 依然存在。

PVC (Persistent Volume Claim) - 存储需求

  • 定义持久卷声明。它是用户对存储资源的“申请书”(例如:“我要 10G 的读写盘”)。
  • 角色:类似于 Pod(消耗资源),属于命名空间级资源,由开发人员管理。
  • 绑定机制:K8s 会根据 PVC 的需求(大小、访问模式)自动匹配并绑定最合适的 PV。

StorageClass (SC) - 存储模板

  • 定义存储类。它是一个动态创建 PV 的模板。
  • 作用:解决了运维手动创建 PV 的痛苦。当用户申请 PVC 时,SC 自动调用 Provisioner(存储供应商)创建对应的底层存储并生成 PV。
  • 参数:定义了存储类型(如 SSD、HDD)、回收策略等。

存储使用流程

阶段一:静态供给- 传统模式

运维手动创建 PV,用户手动申请 PVC。

  1. 运维:手动在云厂商买一块盘,创建 PV 对象。
  2. 用户:创建 PVC,请求 10G 空间。
  3. K8s:寻找匹配的 PV 并绑定。

阶段二:动态供给- 推荐模式

用户申请 PVC,系统自动创建 PV。

  1. 运维:创建 StorageClass(定义了存储供应商和参数)。
  2. 用户:创建 PVC,并在 YAML 中指定 storageClassName
  3. K8s:PVC 触发 SC,SC 调用底层 API 自动创建硬盘并生成 PV,最后绑定给 PVC。

实战 YAML 示例

StorageClass (定义模板)

1
2
3
4
5
6
7
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-storage
provisioner: kubernetes.io/aws-ebs # 假设使用 AWS EBS
parameters:
  type: gp2 # SSD 类型

PVC (用户申请)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce # 单节点读写
  resources:
    requests:
      storage: 10Gi
  storageClassName: fast-storage # 引用上面的 SC,触发自动创建

Pod (挂载使用)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
  name: mysql
spec:
  containers:
  - name: mysql
    image: mysql
    volumeMounts:
    - mountPath: /var/lib/mysql
      name: data
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: mysql-pvc # 引用 PVC

关键概念补充

  • 访问模式

    • ReadWriteOnce (RWO):读写,只能被单个节点挂载(常用块存储)。
    • ReadOnlyMany (ROX):只读,可被多个节点挂载。
    • ReadWriteMany (RWX):读写,可被多个节点挂载(常用 NFS/CephFS)。
  • 回收策略

    • Retain:Pod 删了,数据保留(需手动清理)。
    • Delete:PVC 删除,PV 和底层存储自动删除(动态供给默认行为)。

总结记忆口诀

  • PV:仓库里的货(硬盘)。
  • PVC:提货单(申请书)。
  • StorageClass:自动售货机(自动造货)。

调度与扩缩容

什么是水平自动伸缩(HPA)?基于哪些指标?

一句话原理 HPA (Horizontal Pod Autoscaler) 是 Kubernetes 的一种控制器,它根据 Pod 的实时负载情况,自动增加或减少 Pod 的副本数量,实现业务的“弹性伸缩”。

一句话源码 HPA 控制器通过轮询 Metrics Server 获取指标数据,计算当前指标与目标值的比率,动态调整 Deployment 的 replicas 字段。

一句话项目/场景 在“双11大促”期间,随着请求量激增,CPU 使用率飙升至 80%,HPA 自动将 Pod 数量从 3 个扩展到 50 个以分担流量;流量低谷期自动缩容回 3 个以节省成本。


详细解析

核心工作流程

HPA 的工作是一个闭环控制过程:

  1. 获取指标:HPA 控制器定期向 Kubernetes API Server 发起请求,API Server 从 Metrics Server(必须安装的插件)获取资源使用数据。
  2. 计算期望副本数
    • 公式:期望副本数 = ceil(当前副本数 * (当前指标值 / 目标指标值))
    • 例如:目标 CPU 是 50%,当前 CPU 是 100%,则 当前副本数 * (100/50) = 2倍,需要扩容一倍。
  3. 执行伸缩:更新 Deployment 的 replicas 数量,触发新的 Pod 创建或删除。

基于哪些指标?

HPA 支持三类指标来源:

A. 资源指标 —— 最常用

由 Metrics Server 提供,K8s 原生支持。

  • CPU 利用率:最常用的指标。例如设置目标为 60%。
  • 内存利用率:适用于内存密集型应用(如 Redis、Java 应用)。
  • 注意:CPU 支持弹性伸缩较好,内存通常不支持频繁释放,扩容效果不如 CPU 直观。
B. Pods 指标

与 Pod 直接相关的自定义指标。

  • QPS (每秒查询数):例如 Nginx Ingress 的每秒请求数。
  • 并发连接数:Web 服务器的当前活跃连接数。
C. Object / 外部指标

由第三方监控系统(如 Prometheus + Prometheus Adapter)提供。

  • 消息队列深度:例如 RabbitMQ 或 Kafka 队列中堆积的消息数量。如果堆积超过 1000 条,就扩容消费者 Pod。
  • HTTP 请求延迟:基于响应时间进行扩容。

实战 YAML 示例

以下示例定义了一个 HPA:目标 CPU 使用率 50%,最少 2 个 Pod,最多 10 个 Pod。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 2               # 最小副本数
  maxReplicas: 10              # 最大副本数
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50 # 目标 CPU 使用率 50%

关键机制与避坑

冷却时间

为了防止指标抖动导致 Pod 频繁创建删除(“颤抖”),K8s 默认设置:

  • 扩容:即时进行(无需等待)。
  • 缩容:默认需要等待 5 分钟 稳定期,确认负载持续降低才会缩容。

就绪延迟

对于刚启动的 Pod,HPA 默认会给予一段缓冲期,等待 Pod 准备就绪并收集到足够的数据后,再将其纳入指标计算范围,避免启动瞬间的 CPU 峰值误触发扩容。

依赖条件

使用 HPA 必须确保集群中安装了 Metrics Server

1
kubectl top pods # 如果能查到数据,说明 Metrics Server 正常运行

如何实现滚动更新?如何控制更新策略?

一句话原理 Kubernetes 的滚动更新通过逐个替换旧 Pod 的方式升级服务,通过 maxSurge(最大激增)和 maxUnavailable(最大不可用)两个参数精确控制更新速度与可用性之间的平衡。

一句话源码 在 Deployment 的 YAML 文件中设置 spec.strategy.type: RollingUpdate 并配置相关参数,执行 kubectl set image 命令触发更新。

一句话项目/场景 在“电商平台发版”时,确保始终有足够的 Pod 在线处理订单,用户无感知地从 v1 版本平滑过渡到 v2 版本;一旦发现 v2 报错,立即回滚到 v1。


详细解析

滚动更新的工作机制

假设当前有 3 个 v1 Pod,我们要更新到 v2。

更新流程:

  1. 创建:创建 1 个 v2 Pod。
  2. 就绪:等待 v2 Pod 通过 ReadinessProbe(就绪探针)检查,变为 Ready 状态。
  3. 销毁:删除 1 个 v1 Pod。
  4. 循环:重复上述步骤,直到所有 v1 Pod 被替换为 v2 Pod。

关键点:在整个过程中,服务是不中断的,Service 始终有后端 Pod 可用。

如何控制更新策略?

通过 Deployment 的 spec.strategy.rollingUpdate 字段控制。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 10
  strategy:
    type: RollingUpdate  # 默认策略
    rollingUpdate:
      maxSurge: 25%      # 最多可以比期望副本数多出的 Pod 数量
      maxUnavailable: 25% # 最多可以比期望副本数少出的 Pod 数量
A. maxSurge(最大激增)
  • 定义:更新过程中,允许同时存在的总 Pod 数量上限(旧+新)。
  • 作用:控制更新速度
  • 示例(Replicas=10, maxSurge=2):
    • 更新时,集群最多会有 10+2=12 个 Pod。
    • 如果设为 0,则必须先删掉一个旧 Pod,才能创建一个新 Pod(速度慢)。
B. maxUnavailable(最大不可用)
  • 定义:更新过程中,允许不可用 Pod 数量上限
  • 作用:控制服务稳定性
  • 示例(Replicas=10, maxUnavailable=0):
    • 更新时,必须保证 10 个 Pod 全部在线。
    • 这意味着必须先启动新 Pod 并就绪,才能删除旧 Pod(最平滑,但资源消耗大)。
典型组合场景:
场景配置策略特点
平稳过渡(推荐)maxSurge: 25%, maxUnavailable: 0先增后减,保证总容量不下降,资源占用短暂增加。适合生产环境。
快速更新maxSurge: 50%, maxUnavailable: 25%大批量并行更新,速度快,但更新期间服务容量可能下降,风险高。
资源紧张maxSurge: 0, maxUnavailable: 1先减后增,不需要额外资源,但更新期间服务能力会短暂下降(少一个Pod)。

触发更新与回滚命令

触发更新:

1
2
# 修改镜像版本,触发滚动更新
kubectl set image deployment/web-app nginx=nginx:1.18 --record

注意:--record 参数(旧版 K8s)会将命令记录到 revision 中,方便回滚。新版建议使用注解管理。

查看更新状态:

1
kubectl rollout status deployment/web-app

暂停与恢复(金丝雀发布利器):

1
2
3
4
5
6
7
# 1. 触发更新后立即暂停,此时只更新了一部分
kubectl set image deployment/web-app nginx=nginx:1.19
kubectl rollout pause deployment/web-app

# 2. 观察线上流量,验证新版本无误
# 3. 验证通过,恢复更新,完成剩余部分
kubectl rollout resume deployment/web-app

回滚操作: 如果新版本有问题,一键回到上一个版本。

1
2
3
4
5
6
7
8
# 查看历史版本
kubectl rollout history deployment/web-app

# 回滚到上一版本
kubectl rollout undo deployment/web-app

# 回滚到指定版本
kubectl rollout undo deployment/web-app --to-revision=2

什么是亲和性(Affinity)和反亲和性(Anti-Affinity)?

一句话原理 亲和性与反亲和性是 Kubernetes 中更高级的调度规则,用于精确控制 Pod 更愿意调度到哪些节点,或者坚决不调度到哪些节点,解决了简单的 NodeSelector 无法表达的复杂逻辑。

一句话源码 通过在 Pod Spec 中定义 affinity 字段,配置 requiredDuringScheduling(硬性要求)或 preferredDuringScheduling(软性偏好)。

一句话项目/场景

  • 亲和性:将“前端 Web 服务”调度到部署了“高性能 SSD 磁盘”的节点上,或者让同一业务的 Pod 尽量聚拢。
  • 反亲和性:将“数据库主节点”和“从节点”坚决隔离在不同的物理机上,防止单机故障导致双挂;或者将“高 CPU 消耗”的服务分散开。

详细解析

为什么需要它?(对比 NodeSelector)

  • NodeSelector:只能根据节点的标签进行简单的“相等”匹配,功能太弱。
  • Affinity
    • 支持软策略(Preferred):优先满足,实在不行也能跑。
    • 支持硬策略(Required):必须满足,否则 Pod 一直 Pending。
    • 支持运算符:In, NotIn, Exists, DoesNotExist, Gt, Lt。
    • 支持拓扑域:可以基于节点、机架、可用区等维度调度。

两种类型的亲和性

A. 节点亲和性

关注 Pod 与 Node 的关系。

  • 硬亲和性:Pod 必须运行在满足条件的节点上。如果不存在,Pod 处于 Pending。
    • 场景:数据库 Pod 必须运行在标签为 disk=ssd 的节点上。
  • 软亲和性:Pod 倾向运行在满足条件的节点上,如果没有,也能调度到其他节点。
    • 场景:Web 服务优先调度到 zone=east,但如果该区域满了,也可以去 zone=west
B. Pod 亲和性/反亲和性

关注 Pod 与 Pod 之间的关系(通过 Node 标签作为中介)。

  • Pod 亲和性:让某些 Pod 靠近运行。
    • 场景:前端服务应该和缓存服务在同一个节点/机架上,降低网络延迟。
  • Pod 反亲和性:让某些 Pod 分散运行。
    • 场景:为了保证高可用,同一个服务的 3 个副本必须分散在不同的节点或可用区。

实战 YAML 示例

场景一:节点亲和性(必须用 SSD 盘)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
  name: db-pod
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution: # 硬性要求
        nodeSelectorTerms:
        - matchExpressions:
          - key: disk-type
            operator: In
            values:
            - ssd
  containers:
  - name: db
    image: mysql

场景二:Pod 反亲和性(多副本必须分散)

这是生产环境保证高可用的经典配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  template:
    spec:
      affinity:
        podAntiAffinity: # Pod 反亲和性
          requiredDuringSchedulingIgnoredDuringExecution: # 硬性要求
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-app
            topologyKey: "kubernetes.io/hostname" # 拓扑域:以节点主机名为界
      containers:
      - name: web
        image: nginx
  • 解读:必须调度到不包含 app=web-app 标签 Pod 的节点上(topologyKey: kubernetes.io/hostname)。这意味着每个节点最多只能跑一个该服务的 Pod。

核心概念补充

硬策略 vs 软策略

策略类型字段名行为适用场景
硬策略requiredDuringScheduling...必须满足。找不到节点则 Pending。强制性约束(如 GPU、SSD、高可用打散)。
软策略preferredDuringScheduling...尽力满足。找不到则选次优节点。性能优化(如同机架低延迟、偏好某可用区)。

拓扑域

这是 Pod 亲和性/反亲和性的核心。它定义了“靠近”或“分散”的范围。

  • kubernetes.io/hostname:范围是单个节点(分散在不同机器)。
  • topology.kubernetes.io/zone:范围是可用区(分散在不同机房/可用区)。
  • topology.kubernetes.io/region:范围是区域

总结记忆口诀

  • 节点亲和:找好房子(Node),比如带泳池的(SSD)。
  • Pod 亲和:找好邻居,比如跟缓存住一块(低延迟)。
  • Pod 反亲和:躲着邻居,比如分居两地(高可用)。
  • 硬策略:非你不可,宁死不屈。
  • 软策略:有则最好,无也凑合。