K8s Lab 把当前仓库文档整理成一个可阅读的网页站点

Repository Reading Site

K8s 架构全景 — 先看地图再上路

假设你有一个 Web 应用,跑在一台服务器上。当用户量增长,你会: 1. **加机器** — 但怎么决定哪个请求去哪台机器?(负载均衡) 2. **进程挂了怎么办** — 手动 SSH 上去重启?凌晨三点也要?(自动恢复) 3. **发版怎么不停机** — 先停旧的再起新的?用户会断线(滚动更新) 4. **配置怎么管** — 每台机器手动改配置文件?(配置

Markdownphase-0/01-k8s-architecture.md2026年4月9日 09:49

K8s 架构全景 — 先看地图再上路

为什么需要 Kubernetes?

假设你有一个 Web 应用,跑在一台服务器上。当用户量增长,你会:

  1. 加机器 — 但怎么决定哪个请求去哪台机器?(负载均衡)
  2. 进程挂了怎么办 — 手动 SSH 上去重启?凌晨三点也要?(自动恢复)
  3. 发版怎么不停机 — 先停旧的再起新的?用户会断线(滚动更新)
  4. 配置怎么管 — 每台机器手动改配置文件?(配置管理)
  5. 不同服务怎么互相找到对方 — 硬编码 IP?机器换了就挂(服务发现)

Kubernetes 就是解决这些问题的容器编排平台。它的核心价值是:

你告诉 K8s "我想要什么状态"(声明式),K8s 负责"怎么达到并维持这个状态"。

比如你说"我要 3 个 nginx 副本",K8s 会确保始终有 3 个在运行。挂了一个?自动补一个。


K8s 集群的组成

一个 K8s 集群由两类节点组成:

┌─────────────────────────────────────────────────────────────┐
│                    Control Plane (Master)                     │
│                                                               │
│  ┌──────────┐  ┌───────────┐  ┌──────────────┐  ┌────────┐ │
│  │ API Server│  │ Scheduler │  │ Controller   │  │  etcd  │ │
│  │          │  │           │  │ Manager      │  │        │ │
│  └────┬─────┘  └─────┬─────┘  └──────┬───────┘  └───┬────┘ │
│       │              │               │               │       │
└───────┼──────────────┼───────────────┼───────────────┼───────┘
        │              │               │               │
        ▼              ▼               ▼               ▼
   所有通信的       决定Pod放        确保实际状态      存储所有
   唯一入口        在哪个节点       匹配期望状态      集群数据
        │
        │ kubelet 定期汇报 + 接收指令
        │
┌───────┼─────────────────────────────────────────────────────┐
│       ▼            Worker Node                               │
│  ┌──────────┐  ┌────────────┐  ┌────────────────┐          │
│  │ kubelet  │  │ kube-proxy │  │ Container      │          │
│  │          │  │            │  │ Runtime        │          │
│  │ 节点代理  │  │ 网络代理    │  │ (containerd)   │          │
│  └──────────┘  └────────────┘  └────────────────┘          │
└─────────────────────────────────────────────────────────────┘

Control Plane 组件(跑在 Master 节点上)

1. API Server(kube-apiserver)— 集群的"前台"

是什么: 所有操作的唯一入口。你敲的每一条 kubectl 命令,都是发 HTTP 请求给 API Server。

为什么这样设计:

  • 统一入口意味着统一鉴权:谁能做什么,在一个地方控制
  • 所有组件都通过 API Server 通信,而不是互相直连,降低了耦合
  • API Server 是无状态的,可以水平扩展(多个 API Server 实例 + 负载均衡)

底层细节:

  • 监听端口:默认 6443(HTTPS)
  • 每个请求经过:认证(Authentication)→ 授权(Authorization/RBAC)→ 准入控制(Admission Control)→ 持久化到 etcd
  • 支持 Watch 机制:其他组件(如 Scheduler)通过 Watch 实时感知变化,而不是轮询

2. etcd — 集群的"数据库"

是什么: 分布式键值存储,保存集群的所有状态:节点信息、Pod 定义、ConfigMap、Secret、RBAC 规则……

为什么用 etcd 而不是 MySQL/Redis:

  • 分布式一致性:基于 Raft 协议,保证多副本数据一致(即使少数节点挂掉)
  • Watch 机制:API Server 可以"订阅"数据变化,实时收到通知
  • 简单:K8s 只需要键值存储,不需要关系型查询

底层细节:

  • 默认监听端口:2379(客户端通信)、2380(节点间通信)
  • 数据存储路径:/var/lib/etcd
  • 所有数据以 /registry/ 为前缀。例如 default 命名空间的 Pod 存在 /registry/pods/default/
  • 生产环境必须定期备份 etcd,因为丢了 etcd = 丢了整个集群状态

面试重点: etcd 的 Raft 协议需要超过半数节点存活才能写入。所以生产环境通常部署 3 或 5 个 etcd 节点(2/3 或 3/5 存活即可工作)。我们的实验环境只有 1 个 Master,所以 etcd 是单点——这在生产中是不可接受的。

3. Scheduler(kube-scheduler)— "调度员"

是什么: 负责决定新创建的 Pod 应该放在哪个 Node 上运行。

调度流程(两步):

  1. 过滤(Filtering): 排除不满足条件的节点。比如:
    • 节点资源不够(CPU/内存不足)
    • 节点有 Taint 而 Pod 没有对应 Toleration
    • Pod 指定了 nodeSelector 但节点没有对应标签
  2. 打分(Scoring): 对剩余节点打分,选最高分的。打分考虑:
    • 资源均衡(避免把所有 Pod 塞到一个节点)
    • 亲和性偏好(preferredDuringScheduling)
    • 拓扑分散(TopologySpreadConstraints)

为什么不是随机分配? 因为生产环境中要考虑:资源利用率、故障域隔离、数据本地性、合规要求等。调度器是 K8s 最复杂的组件之一。

4. Controller Manager(kube-controller-manager)— "自动驾驶系统"

是什么: 一组控制循环(Control Loop),不断检查"当前状态"是否匹配"期望状态",不匹配就采取行动。

核心控制器举例:

控制器 职责
ReplicaSet Controller "期望 3 个副本,目前只有 2 个" → 创建 1 个新 Pod
Node Controller "节点 40 秒没心跳了" → 标记 NotReady → 5 分钟后驱逐 Pod
Endpoint Controller "Service 的 selector 匹配到新 Pod" → 更新 Endpoints 列表
Job Controller "Job 完成了 3/5 个任务" → 再创建 2 个 Pod

面试重点(声明式 vs 命令式):

命令式:kubectl scale deployment nginx --replicas=5
         "帮我把副本改成5个"(一次性动作)

声明式:deployment.yaml 里写 replicas: 5,然后 kubectl apply
         "我要求始终保持5个副本"(持续维持)

K8s 的哲学是声明式。你不需要说"创建第3个Pod",你只说"我要3个",Controller 会持续确保这个状态。


Worker Node 组件(跑在每个工作节点上)

1. kubelet — "节点管家"

是什么: 每个节点上都运行的代理进程,负责:

  • 向 API Server 注册本节点
  • 接收 API Server 下发的 Pod 定义
  • 调用容器运行时(containerd)来启动/停止容器
  • 定期向 API Server 汇报节点状态(心跳)和 Pod 状态

底层细节:

  • 通过 CRI(Container Runtime Interface)与容器运行时通信
  • 心跳间隔:默认 10 秒
  • 如果 kubelet 挂了,Master 不会立刻感知,而是等心跳超时(默认 40 秒)后标记节点 NotReady

2. kube-proxy — "网络规则管理员"

是什么: 负责实现 K8s Service 的网络转发。当你创建一个 Service,kube-proxy 会在每个节点上配置网络规则,让发往 Service ClusterIP 的流量转发到后端 Pod。

实现方式(三种模式):

  • iptables 模式(默认):用 Linux iptables 规则做 DNAT(目标地址转换)
  • IPVS 模式:用 Linux IPVS 做负载均衡,性能更好,支持更多算法
  • userspace 模式:已弃用,性能差

面试常问: "Service 的 ClusterIP 是虚拟的,没有任何网卡绑定这个 IP。流量是靠 iptables/IPVS 规则在内核层面做转发。"

3. Container Runtime — "容器引擎"

是什么: 真正负责拉取镜像、创建容器、管理容器生命周期的组件。

为什么是 containerd 而不是 Docker?

  • K8s 1.24 之后移除了对 Docker 的直接支持(dockershim)
  • Docker 其实是一个"全家桶":Docker CLI + Docker Daemon + containerd + runc
  • K8s 只需要 containerd 这一层,Docker 的上层是多余的开销
  • 你之前用 docker run 的镜像,在 containerd 下照样能跑(OCI 镜像标准兼容)
之前:kubelet → dockershim → Docker Daemon → containerd → runc → 容器
现在:kubelet → CRI → containerd → runc → 容器(少了两层中间人)

我们的实验集群架构

                   LA Region (<2ms 内网延迟)
┌────────────────────────────────────────────────────┐
│                                                    │
│  ┌─────────────────────┐                           │
│  │ Master              │  运行:                    │
│  │ *.*.176.193     │  API Server, Scheduler,   │
│  │ 16C/16G             │  Controller Manager, etcd │
│  │ ⚠ VPN:443/40000    │  + kubelet, kube-proxy    │
│  └──────────┬──────────┘                           │
│             │                                      │
│  ┌──────────┼──────────┐  ┌──────────────────────┐ │
│  │ Worker-1 │          │  │ Worker-2             │ │
│  │ .164.1180.6ms    │  │ *.*.27.60          │ │
│  │ ⚠ VPN   │          │  │ 无冲突               │ │
│  └──────────┘          │  └──────────────────────┘ │
└─────────────┬──────────────────────────────────────┘
              │
              │ ~146ms 跨太平洋延迟
              │
┌─────────────┼──────────────────────────────────────┐
│             │        HK Region (~20ms)             │
│  ┌──────────┴──────────┐  ┌──────────────────────┐ │
│  │ Worker-3            │  │ Worker-4             │ │
│  │ *.*.221.17        │  │ *.*.104.66       │ │
│  │ 4C/8G (异构)        │  │ 150G (存储节点)       │ │
│  └─────────────────────┘  └──────────────────────┘ │
└────────────────────────────────────────────────────┘

为什么 Master 放在 LA?

etcd 使用 Raft 共识协议,对延迟敏感。API Server 和 etcd 跑在同一台机器上(kubeadm 默认部署方式),如果 Master 在 HK,LA 的 3 台 Worker 每次与 API Server 通信都要跨洋 146ms。反过来,Master 在 LA,只有 HK 的 2 台 Worker 承受延迟。

Worker 节点的 kubelet 通过公网连接 API Server。 这不是生产最佳实践(生产环境通常在内网),但我们的机器分布在不同机房,公网是唯一选择。kubeadm init 时会为 API Server 生成 TLS 证书,所有通信都是加密的。


K8s 中的核心概念预览

在开始操作之前,先建立这些概念的直觉:

Pod — 最小调度单元

Pod ≠ 容器。一个 Pod 可以包含多个容器,它们:

  • 共享同一个网络命名空间(同一个 IP,互相用 localhost 访问)
  • 可以共享存储卷
  • 被当作一个整体调度到同一个节点

为什么不直接调度容器? 因为有些应用天然需要"伴生"容器。比如一个 Web 应用 + 一个日志收集 sidecar,它们必须在同一台机器上、共享日志目录。Pod 就是这个"共生组"的抽象。

Namespace — 虚拟集群

一个 K8s 集群可以被划分成多个 Namespace,实现:

  • 资源隔离(dev 和 prod 的 Pod 不会互相干扰)
  • 权限隔离(开发人员只能操作 dev namespace)
  • 资源配额(dev 最多用 4 核 CPU)

Label & Selector — 万物皆可贴标签

K8s 中最强大的组织机制。给任何资源贴标签(key=value),然后用 Selector 筛选。 比如 Service 通过 selector: app=nginx 找到所有标签为 app=nginx 的 Pod 来转发流量。

Controller — 确保状态的"永动机"

Deployment、StatefulSet、DaemonSet、Job 都是 Controller。它们的共同模式是:

while true:
    当前状态 = 观察集群
    期望状态 = 读取用户定义
    if 当前状态 ≠ 期望状态:
        采取行动让当前状态趋近期望状态
    sleep(一小段时间)

这就是 K8s 的灵魂 — Reconciliation Loop(调和循环)


接下来

理解了架构后,下一步是准备操作系统环境。每个前置条件(关闭 swap、加载内核模块等)都不是"照做就行",而是有明确的技术原因。

02-os-preparation.md