路线图

K8s 是什么

星辉 2026-07-02 阅读 4 min 709 字 路线图
K8s 是什么 封面

当你掌握了 Docker 容器的基本用法后,自然会遇到下一个问题:我有 20 台服务器,几百个容器,怎么高效管理?本章讲解 Kubernetes(简称 K8s)解决的核心问题、整体架构,以及它最核心的设计理念——声明式 API。


K8s 解决什么问题#

先想象一个没有 K8s 的场景,这也是大多数团队刚开始用 Docker 时的真实状态:

场景1:版本不一致 你本地用 python:3.11 跑得好好的,同事用 python:3.11.2,生产环境装的是 python:3.9。三个人三个版本,bug 只在生产出现,排查半天发现是 Python 版本差异。Dockerfile 解决了"在我机器上能跑"的问题,但谁来保证所有环境用的是同一个镜像?

场景2:扩缩靠人 促销活动开始前,你手动在多台机器上 docker run 启动容器,活动结束后再手动 docker stop。流量突增时,你正在吃饭,没人扩容,服务被打挂。流量下去后,容器还占着资源,没人缩容,钱白白浪费。

场景3:故障靠运气 某个容器悄悄挂了,没人知道,用户投诉了才发现。你设置了监控脚本,但脚本本身也会挂。节点宕机了,上面的 20 个容器全挂,你要在新节点上手动把容器一个个拉起来。

场景4:服务发现靠猜 容器 IP 每次重启都会变,A 服务要调用 B 服务,IP 写哪里?写配置文件里,B 重启了 IP 变了,A 就连不上了。有人用配置文件硬写,有人用脚本定时更新,有人干脆用 host 网络——每种方案都有坑。

K8s 的答案:你告诉它"我要什么状态",它负责把现实变成你想要的那个状态,并且在现实变化时自动维持这个状态。

痛点K8s 的解决方案
版本不一致镜像 tag 固定 + 声明式配置,所有环境用同一份 YAML
扩缩靠人HPA(自动扩缩容),也可以 kubectl scale 手动调整副本数
故障靠运气健康检查探针 + 自动重启 + 节点故障时自动调度到其他节点
服务发现靠猜Service 提供稳定虚拟 IP 和 DNS 名,自动更新后端 Pod 列表

集群架构#

K8s 集群由两种角色组成:Control Plane(控制面)Worker Node(工作节点)。用公司架构来类比:

整体架构层次#

text
┌─────────────────────────────────────────────────────────────┐
│                      Control Plane                           │
│  (集群大脑:做决策、存状态、接收指令)                        │
│                                                             │
│  ┌──────────┐  ┌──────────┐  ┌──────────────┐             │
│  │ API      │  │ Scheduler│  │ Controller    │             │
│  │ Server   │  │          │  │ Manager       │             │
│  │ (前台)   │  │ (调度员) │  │ (监控员×N)   │             │
│  └────┬─────┘  └────┬─────┘  └──────┬───────┘             │
│       └──────────────┼───────────────┘                     │
│                      ▼                                      │
│               ┌──────────────┐                             │
│               │    etcd      │                             │
│               │  (状态数据库) │                             │
│               └──────────────┘                             │
└─────────────────────────────────────────────────────────────┘
                        ▼ 指令下发
┌─────────────────────────────────────────────────────────────┐
│                      Worker Node 1                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  kubelet (节点代理:执行指令、汇报状态)               │   │
│  │  kube-proxy (网络代理:维护 Service 转发规则)         │   │
│  │  Container Runtime (容器运行时:containerd/CRI-O)    │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  Pod 1 (容器A)    Pod 2 (容器B + sidecar)           │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│                      Worker Node 2  (同理)                   │
└─────────────────────────────────────────────────────────────┘

各层职责#

Control Plane(控制面):集群的决策中心,通常部署在独立的机器上。生产环境一般部署 3 个 Control Plane 节点做高可用。

Worker Node(工作节点):真正干活的地方,运行容器。可以按需增加节点,K8s 会自动把 Pod 调度上去。

etcd:K8s 的唯一状态存储,所有集群数据(Pod 状态、节点信息、配置)都存在这里。etcd 是 K8s 的"真理来源"。

注意事项:初学者可以用 minikube 或 kind 在本地跑一个"单节点集群"(Control Plane 和 Worker 在同一台机器),功能和生产集群一样,只是没有高可用。


核心组件职责#

每个组件只做一件事,通过 API Server 通信。这是 K8s 架构清晰的地方。

API Server —— 集群前台#

所有操作(不管是 kubectl 命令、Dashboard 点击,还是 Controller 内部调用)都要经过 API Server。它是集群的唯一入口。

类比:公司前台——所有外部请求先到前台,前台验证身份、检查格式,然后记录到系统里,再通知对应部门处理。

bash
# 每次 kubectl 命令,实际上都是给 API Server 发 HTTP 请求
kubectl get pods
# → GET /api/v1/pods?namespace=default

kubectl apply -f deployment.yaml
# → POST /apis/apps/v1/namespaces/default/deployments

Scheduler —— 调度员#

Scheduler watch(监听)API Server,发现有新 Pod 创建但还没分配到节点时,就根据资源需求、节点亲和性、污点等规则,决定把 Pod 放到哪台机器上。

类比:HR 招聘时分配工位——要看候选人需要什么配置(requests)、哪些工位有空、候选人有没有特殊要求(nodeSelector/affinity),然后决定安排到哪张桌子。

Scheduler 只负责"决定放哪里",不负责"启动容器"——那是 kubelet 的活。

Controller Manager —— 监控员#

Controller Manager 里运行着多个 Controller,每个 Controller 负责一种资源。它的工作模式是控制循环(Control Loop):不断比较"期望状态"和"实际状态",如果不一样就执行操作让实际状态靠拢期望状态。

类比:公司的 QA 巡检——期望状态是"3 个服务实例在线",实际巡检发现只有 2 个,就立刻启动第 3 个。

常见的 Controller:

  • Deployment Controller:保证指定数量的 Pod 副本在运行
  • Node Controller:监控节点健康状态
  • Job Controller:保证批处理任务完成

kubelet —— 节点代理#

每个 Worker Node 上跑一个 kubelet,它是 Control Plane 在节点上的"眼睛和手":

  • 从 API Server 接收指令(比如"在这个节点上启动 Pod xyz")
  • 通过 CRI 调用容器运行时(containerd/CRI-O)拉镜像、启动容器(自 1.24 起 dockershim 已移除,Docker 不再是 K8s 直接支持的运行时)
  • 定期汇报节点和 Pod 的状态给 API Server
  • 执行存活探针(livenessProbe),必要时重启容器

类比:部门助理——接收总部指令,落实到具体人员(容器),并定期向总部汇报部门状态。

kube-proxy —— 网络代理#

每个节点上跑一个 kube-proxy,负责维护网络规则,让 Service 的流量能正确转发到后端 Pod。

实现方式有几种:iptables(当前默认)、IPVS(大规模集群性能更好)、nftables(1.33 GA,新一代替代 iptables,需内核 5.13+,但尚未成为默认)。早期的 userspace 模式已在 1.26 中彻底移除。

etcd —— 状态数据库#

etcd 是一个分布式键值存储,K8s 的所有数据都存在这里:Pod 列表、节点状态、ConfigMap 内容、RBAC 规则……

重要:etcd 的数据是 K8s 集群的"命根子",生产环境 etcd 必须做备份,并且部署 3 或 5 个副本做高可用。

bash
# 查看 etcd 里存了什么(需要 etcdctl 客户端)
etcdctl get /registry/pods/default/my-pod --prefix

声明式 vs 命令式#

这是 K8s 最核心的设计理念,理解了它,后面所有操作都顺了。

命令式:告诉 K8s"怎么做"#

bash
# 命令式:直接下指令
kubectl run nginx --image=nginx:alpine --port=80
kubectl scale deployment nginx --replicas=3
kubectl set image deployment nginx nginx=nginx:1.25

这种方式直观,但有问题:你执行完命令,K8s 不会记住你是怎么把系统变成现在这个状态的。下次你想重建环境,得把之前所有命令翻出来按顺序执行。更麻烦的是,别人不知道你做过什么操作。

声明式:告诉 K8s"要什么状态"#

yaml
# deployment.yaml —— 声明期望状态
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3          # 期望:始终有 3 个副本
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.25-alpine   # 期望:用这个镜像
        ports:
        - containerPort: 80
bash
# 声明式:让 K8s 照着这个文件调整现实
kubectl apply -f deployment.yaml

执行 kubectl apply 后,K8s 会:

  1. 读取文件里的期望状态
  2. 查询 API Server 获取当前实际状态
  3. 计算差异
  4. 执行必要的操作让实际状态 = 期望状态

关键区别

维度命令式 kubectl run声明式 kubectl apply
思路一步一步下指令描述最终状态,让系统自己想办法
可重复性差,需要记录所有执行过的命令好, YAML 文件就是完整声明
适合场景临时调试、快速测试生产部署、版本控制、团队协作
状态追踪K8s 不记录你的操作历史K8s 把期望状态存在 etcd 里

核心思想:K8s 的所有资源(Deployment、Service、Pod……)都遵循这个模式。你描述你想要什么,K8s 负责怎么达到。这也是为什么 K8s 配置文件用 YAML 而不是 Shell 脚本——YAML 描述状态,脚本描述过程。

实战对比#

假设你要部署一个 3 副本的 nginx 服务:

命令式做法(5 个命令,顺序不能错):

bash
kubectl run nginx --image=nginx:alpine --port=80
kubectl expose pod nginx --port=80 --target-port=80 --type=ClusterIP
kubectl scale deployment nginx --replicas=3   # 哦不对,run 创建的是 Pod 不是 Deployment...
# 重来,这次用 create deployment
kubectl create deployment nginx --image=nginx:alpine --replicas=3
kubectl expose deployment nginx --port=80 --target-port=80

声明式做法(1 个文件,apply 一次):

yaml
# nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
bash
kubectl apply -f nginx.yaml
# 下次要改副本数,编辑 yaml 把 replicas 改成 5,再 apply 一次

注意事项:生产环境始终坚持用声明式。把 YAML 文件存在 Git 里,这就是 GitOps 的基础——集群的状态和 Git 里的配置文件保持一致。


小结#

本章核心要点:

  • K8s 解决什么问题:手动管理容器的四大痛点——版本不一致、扩缩靠人、故障靠运气、服务发现靠猜
  • 集群架构:Control Plane(决策)+ Worker Node(执行)+ etcd(状态存储)三层结构
  • 核心组件:API Server(集群唯一入口)、Scheduler(决定 Pod 放哪)、Controller Manager(监控并修正状态)、kubelet(节点上的执行者)
  • 声明式 vs 命令式:声明式(kubectl apply -f xxx.yaml)描述期望状态,让 K8s 自己收敛;命令式(kubectl run/scale/set)适合临时调试,不适合生产

理解了这些概念,下一章的 kubectl 命令学起来会非常快——因为你知道每个命令背后是在跟哪个组件打交道。