21 KiB
tags, create time
| tags | create time | |||
|---|---|---|---|---|
|
2026-07-07 15:48 |
Kubernetes 概述
概述
Kubernetes(简称 K8s)是一个开源的容器编排平台,由 Google 内部的 Borg 系统演化而来,于 2014 年开源,目前由 CNCF(Cloud Native Computing Foundation)托管。它的核心使命是:让部署、扩展和管理容器化应用变得自动化且可预测。简单来说,Docker 解决了「如何打包一个应用」的问题,而 K8s 解决的是「如何在成百上千台机器上高效地运行和管理这些应用」的问题。
为什么需要 K8s
从 Docker 单机到集群编排
让我们先回顾一下容器技术的演进路径:
- 裸机/虚拟机时代:应用直接部署在物理机或 VM 上,手动管理依赖、端口和资源分配。
- Docker 单机时代:容器解决了环境一致性和依赖打包的问题,但单机资源有限,且面临应用故障恢复、滚动更新等挑战。
- 集群编排时代:当你的服务需要跨多台机器运行时,你需要一个「大脑」来决定:把容器调度到哪台机器?挂了怎么重启?流量怎么分配?——这就是 K8s 存在的意义。
[!tip] 一个类比 如果 Docker 集装箱标准化了「货物打包」,那 K8s 就是自动化码头——它决定集装箱该放哪艘船、航线怎么规划、遇到风暴如何重新调度。
Docker Compose vs Docker Swarm vs Kubernetes
| 特性 | Docker Compose | Docker Swarm | Kubernetes |
|---|---|---|---|
| 定位 | 单机多容器编排 | 轻量级集群编排 | 企业级容器编排平台 |
| 规模 | 单主机 | 中小规模集群 | 大规模生产环境 |
| 学习曲线 | 低 | 中 | 高 |
| 自动扩缩容 | 不支持 | 基础支持 | HPA / VPA / Cluster Autoscaler |
| 自愈能力 | 无 | 基础(重启策略) | 强(Liveness/Readiness Probe + 控制器) |
| 滚动更新 | 不支持 | 支持 | 细粒度控制(maxUnavailable / maxSurge) |
| 生态 | 仅 Docker 生态 | 仅 Docker 生态 | CNCF 生态,插件丰富 |
| 服务发现 | 容器名 DNS | 内置 DNS | CoreDNS + Service 资源 |
| 适用场景 | 本地开发、测试 | 快速上手小集群 | 生产环境首选 |
[!info] 为什么 Swarm 没能打败 K8s? Docker Swarm 的设计哲学是「简单优先」,但现实世界的生产需求远比想象中复杂。K8s 的声明式 API、丰富的控制器模型、以及 CNCF 生态的飞轮效应,最终让它成为事实标准。
核心架构
K8s 采用经典的 Master-Worker 架构(官方文档中 Master 节点现在更常称为 Control Plane)。
架构总览
graph TB
subgraph ControlPlane["Control Plane(控制平面)"]
API["API Server<br/>集群的唯一入口"]
etcd["etcd<br/>分布式 KV 存储"]
sched["Scheduler<br/>Pod 调度决策"]
cm["Controller Manager<br/>维持期望状态"]
end
subgraph Worker1["Worker Node 1"]
kubelet1["kubelet<br/>管理 Pod 生命周期"]
proxy1["kube-proxy<br/>网络规则管理"]
cr1["Container Runtime<br/>containerd / CRI-O"]
P1a["Pod A"]
P1b["Pod B"]
end
subgraph Worker2["Worker Node 2"]
kubelet2["kubelet"]
proxy2["kube-proxy"]
cr2["Container Runtime"]
P2a["Pod C"]
P2b["Pod D"]
end
subgraph Client["用户 / CI/CD"]
kubectl["kubectl / SDK"]
end
kubectl -->|"REST API"| API
API <-->|"读写"| etcd
API -->|"监听事件"| sched
API -->|"监听事件"| cm
kubelet1 -->|"汇报状态"| API
kubelet2 -->|"汇报状态"| API
kubelet1 --> cr1
kubelet2 --> cr2
cr1 --> P1a & P1b
cr2 --> P2a & P2b
proxy1 -.->|"Service 流量"| P1a & P1b
proxy2 -.->|"Service 流量"| P2a & P2b
Control Plane 组件详解
API Server(kube-apiserver)
API Server 是整个集群的唯一入口,所有组件之间的通信都通过它进行。它承担的核心职责:
- RESTful API 网关:暴露 K8s API,所有
kubectl操作最终都到达这里 - 认证与授权:RBAC、ServiceAccount、Webhook Token 等
- 准入控制(Admission Control):在资源持久化之前进行校验和修改(如 MutatingWebhook、ValidatingWebhook)
- Watch 机制:其他组件通过 Watch API Server 获取资源变更事件
[!tip] 为什么所有组件都要通过 API Server 通信? 这是 K8s 的一个重要设计决策——中心化 API 网关保证了统一的认证、授权、审计和数据校验逻辑。如果各组件直接读写 etcd,安全策略将难以实施。
etcd
etcd 是一个高可用的分布式 KV 存储,是 K8s 的「唯一数据源」。所有集群状态(Pod、Service、ConfigMap 等资源对象)都持久化在 etcd 中。
- 基于 Raft 共识算法保证数据一致性
- 生产环境通常部署 3 或 5 个节点(奇数个,便于选举)
- Watch 机制:支持高效地监听 key 的变更
[!warning] etcd 是整个集群的 Single Point of Truth 如果 etcd 数据丢失且没有备份,整个集群状态将不可恢复。生产环境务必做好 etcd 的定期备份。
Scheduler(kube-scheduler)
Scheduler 负责将新创建的 Pod 绑定到合适的 Node 上。调度过程分为两个阶段:
- 过滤(Filtering):排除不满足条件的 Node(如资源不足、Taint 不匹配、亲和性规则冲突)
- 打分(Scoring):对剩余 Node 进行评分,选择得分最高的
常见调度策略:
- 资源请求与限制:基于 Pod 的
requests和limits - 亲和性与反亲和性:
nodeAffinity、podAffinity、podAntiAffinity - Taint 和 Toleration:驱逐不兼容的 Pod
- 拓扑分布约束:
topologySpreadConstraints跨可用区均匀分布
Controller Manager(kube-controller-manager)
Controller Manager 是一组控制器的集合,每个控制器负责一种资源类型的「调谐循环(Reconciliation Loop)」。核心思想:持续比较「期望状态」和「实际状态」,并采取行动使实际状态趋近期望状态。
常见控制器:
| 控制器 | 职责 |
|---|---|
| Deployment Controller | 管理 ReplicaSet 的创建/更新 |
| ReplicaSet Controller | 确保指定数量的 Pod 副本运行 |
| Node Controller | 监控 Node 心跳,处理节点故障 |
| Job Controller | 管理一次性任务的 Pod |
| ServiceAccount Controller | 为新 Namespace 创建默认 ServiceAccount |
Worker Node 组件详解
kubelet
kubelet 是运行在每个 Worker Node 上的代理进程,负责:
- 从 API Server 获取分配给本节点的 Pod Spec
- 调用 Container Runtime 拉取镜像、启动容器
- 执行健康检查(Liveness Probe、Readiness Probe、Startup Probe)
- 向 API Server 汇报节点和 Pod 状态
- 管理 Volume 的挂载和卸载
[!info] kubelet 不是通过 API Server 启动容器的 kubelet 通过 CRI(Container Runtime Interface) 与容器运行时交互,而非直接调用 Docker CLI。
kube-proxy
kube-proxy 负责在每个节点上维护网络规则,实现 Service 的负载均衡。它有三种工作模式:
| 模式 | 实现方式 | 性能 | 推荐度 |
|---|---|---|---|
| iptables | Linux iptables 规则 | 中等(规则数线性增长) | 默认模式 |
| IPVS | Linux IPVS(内核级 LB) | 高(哈希表查找) | 大规模集群推荐 |
| nftables | nftables 规则 | 高 | K8s 1.29+ 实验性 |
Container Runtime
Container Runtime 是真正执行容器生命周期操作的组件。K8s 通过 CRI(Container Runtime Interface) 标准化了与运行时的交互方式:
- containerd:目前最主流的选择,Docker 的核心运行时组件被独立出来
- CRI-O:Red Hat 主导,专为 K8s 设计的轻量级运行时
- Docker:自 K8s 1.24 起不再直接支持(dockershim 已移除),但 containerd 仍可使用
关键概念
Pod
Pod 是 K8s 中最小的可部署单元,而不是容器。一个 Pod 可以包含一个或多个容器,它们:
- 共享同一个 Network Namespace(即共享 IP 和端口空间)
- 可以通过 Volume 共享存储
- 总是被调度到同一个 Node 上
[!tip] 为什么需要 Pod 而不是直接管理容器? 现实中有些应用需要「伴生容器(Sidecar)」——比如日志收集器、Service Mesh 代理。Pod 将这些紧密耦合的容器打包在一起,共享网络和存储,比独立容器更优雅。
# 一个包含 Sidecar 的 Pod 示例
apiVersion: v1
kind: Pod
metadata:
name: app-with-logging
labels:
app: my-app # 用于 Service 选择和筛选
env: production
spec:
containers:
- name: app
image: my-app:v1.2.3
ports:
- containerPort: 8080
resources:
requests:
cpu: "100m" # 0.1 核
memory: "128Mi"
limits:
cpu: "500m" # 最多 0.5 核
memory: "512Mi"
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 3
- name: log-collector
image: fluentd:v1.16
volumeMounts:
- name: log-volume
mountPath: /var/log/app
volumes:
- name: log-volume
emptyDir: {} # Pod 内共享的临时目录
Pod 生命周期中的几个关键阶段:
| Phase | 含义 |
|---|---|
| Pending | 已被 API Server 接受,但尚未调度或镜像未拉取 |
| Running | 至少一个容器正在运行 |
| Succeeded | 所有容器正常退出(退出码 0) |
| Failed | 至少一个容器异常退出 |
| Unknown | 无法获取 Pod 状态(通常是 Node 通信故障) |
Node
Node 是 K8s 集群中的工作机器(物理机或虚拟机)。每个 Node 包含运行 Pod 所需的服务(kubelet、kube-proxy、Container Runtime)。
查看 Node 信息:
# 查看集群所有节点
kubectl get nodes -o wide
# 查看某个节点的详细信息(资源、条件、运行的 Pod)
kubectl describe node <node-name>
Node 有几个重要的条件(Conditions):
| Condition | 说明 |
|---|---|
| Ready | 节点健康且准备好接受 Pod |
| MemoryPressure | 节点内存不足 |
| DiskPressure | 节点磁盘空间不足 |
| PIDPressure | 节点进程数过多 |
| NetworkUnavailable | 节点网络未正确配置 |
Namespace
Namespace 是 K8s 中的逻辑隔离机制,用于在同一物理集群中划分多个虚拟集群。适用于:
- 多团队共享集群(
team-a、team-b) - 环境隔离(
dev、staging、production) - 资源配额管理(每个 Namespace 设置独立的 ResourceQuota)
# 创建一个 Namespace
apiVersion: v1
kind: Namespace
metadata:
name: team-backend
labels:
team: backend
env: production
---
# 在该 Namespace 下设置资源配额
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-backend-quota
namespace: team-backend
spec:
hard:
requests.cpu: "10" # 总 CPU 请求上限 10 核
requests.memory: "20Gi" # 总内存请求上限 20Gi
limits.cpu: "20"
limits.memory: "40Gi"
pods: "50" # 最多 50 个 Pod
K8s 默认创建的 Namespace:
| Namespace | 用途 |
|---|---|
default |
未指定 Namespace 时的默认值 |
kube-system |
K8s 系统组件(CoreDNS、kube-proxy 等) |
kube-public |
公共资源(集群信息等) |
kube-node-lease |
节点心跳 Lease 对象 |
# 在指定 Namespace 下操作
kubectl get pods -n team-backend
# 设置默认 Namespace(避免每次都输入 -n)
kubectl config set-context --current --namespace=team-backend
[!warning] Namespace 不提供网络隔离 默认情况下,不同 Namespace 的 Pod 可以互相通信。如需网络隔离,需要配合 NetworkPolicy 实现。
Label & Selector
Label 是附加在 K8s 对象上的键值对,是 K8s 组织和筛选资源的核心机制。你可能会问:「为什么 K8s 不用目录/文件夹的方式来组织资源?」——因为资源之间的关系是多对多的,一个 Pod 可能同时属于某个应用、某个环境、某个团队,Label 的灵活性远超树形结构。
# Label 的常见命名约定
metadata:
labels:
app.kubernetes.io/name: my-app # 应用名称
app.kubernetes.io/version: "1.2.3" # 版本
app.kubernetes.io/component: frontend # 组件
app.kubernetes.io/part-of: platform # 所属系统
app.kubernetes.io/managed-by: helm # 管理工具
env: production # 环境
team: backend # 团队
Selector 的三种用法:
# 等值选择器
kubectl get pods -l app=my-app,env=production
# 集合选择器
kubectl get pods -l 'env in (production, staging)'
kubectl get pods -l 'env notin (dev)'
# 否定选择器
kubectl get pods -l 'app!=my-app'
在资源定义中使用 Selector(以 Service 为例):
apiVersion: v1
kind: Service
metadata:
name: my-app-svc
spec:
selector:
app: my-app # 匹配所有带 app=my-app 标签的 Pod
env: production
ports:
- port: 80
targetPort: 8080
Annotation
Annotation 用于存储非标识性的附加信息,这些信息不会被 K8s API 用于筛选,但对工具、库和运维人员很有用。典型的 Annotation 内容:
metadata:
annotations:
# 记录维护者信息
owner: "backend-team@company.com"
# Git 提交信息(CI/CD 注入)
git-commit: "abc123f"
git-branch: "main"
# 构建时间
build-time: "2026-07-07T15:00:00Z"
# Ingress 配置(Nginx Ingress Controller 使用)
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
# Prometheus 自动发现
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
[!tip] Label vs Annotation 如何选择? 问自己一个问题:「我需要根据这个信息来筛选或分组资源吗?」—— 如果是,用 Label;如果只是附加的描述性信息,用 Annotation。Label 有长度和字符限制,Annotation 则更宽松。
K8s 与 Docker 的关系
常见误解
很多初学者会问:「K8s 是不是要替代 Docker?」答案是:不完全是。准确地说:
- K8s 替代的是 Docker Swarm(编排层面的竞争)
- K8s 不再直接使用 Docker Engine 作为容器运行时(自 1.24 起)
- 但你仍然可以用 Docker 构建镜像,K8s 运行的是 OCI 标准镜像,与构建工具无关
容器运行时的演进
timeline
title K8s 容器运行时演进
2014-2016 : Docker 是唯一选择
: kubelet 直接调用 Docker API
2017 : CRI 标准引入
: dockershim 作为适配层
2018 : containerd 从 Docker 拆分
: CRI-O 项目成熟
2020 : dockershim 弃用公告
2022-04 : K8s 1.24 移除 dockershim
: containerd 成为主流
2024+ : CRI-O 广泛使用
: Kata / gVisor 沙箱运行时兴起
CRI(Container Runtime Interface) 是 K8s 定义的一套 gRPC 接口规范,任何实现了 CRI 的运行时都可以被 kubelet 使用:
| 运行时 | 特点 | 适用场景 |
|---|---|---|
| containerd | Docker 核心组件独立版,功能全面 | 通用场景,最广泛使用 |
| CRI-O | 专为 K8s 设计,轻量级 | Red Hat / OpenShift 生态 |
| Kata Containers | 轻量级 VM,安全隔离 | 多租户、不可信工作负载 |
| gVisor (runsc) | 用户态内核沙箱 | 安全敏感场景 |
[!info] 理解 CRI 的分层 kubelet → CRI (gRPC) → containerd → runc → Linux Kernel(namespaces, cgroups)
每一层都有明确的职责边界。containerd 负责镜像管理和容器生命周期,runc 负责实际创建 Linux 容器。
为什么移除 dockershim?
移除 dockershim 的主要原因:
- 维护成本:dockershim 是 kubelet 内部的一层额外抽象,增加代码复杂度
- 性能开销:kubelet → dockershim → Docker daemon → containerd,调用链过长
- Docker 的「额外功能」:Docker Engine 包含很多 K8s 不需要的组件(如 Docker Swarm、docker build 等),但作为运行时又必须加载整个 daemon
- CRI 标准成熟:containerd 和 CRI-O 直接实现 CRI,不再需要中间层
部署方式对比
| 方案 | 类型 | 节点数 | 生产就绪 | 学习成本 | 适用场景 |
|---|---|---|---|---|---|
| minikube | 单节点本地集群 | 1 | 否 | 低 | 本地开发和学习,支持 Addons |
| kind | Docker 容器模拟 Node | 1-N | 否 | 低 | CI/CD 测试、多节点本地模拟 |
| kubeadm | 生产级部署工具 | N | 是 | 中高 | 自建集群、对基础设施有完全控制 |
| EKS | AWS 托管 | N | 是 | 中 | AWS 生态用户 |
| AKS | Azure 托管 | N | 是 | 中 | Azure 生态用户 |
| GKE | Google Cloud 托管 | N | 是 | 中 | GCP 生态用户,Autopilot 模式 |
| ACK | 阿里云托管 | N | 是 | 中 | 国内云生态用户 |
[!tip] 如何选择?
- 学习阶段:minikube 或 kind(零成本,快速启动)
- CI/CD:kind(轻量,测试完即销毁)
- 小团队生产:托管服务(减少运维负担)
- 大规模/定制需求:kubeadm 或 Cluster API
常见陷阱与最佳实践
陷阱
-
不设置 Resource Requests/Limits
# 反面教材:Pod 没有资源声明 containers: - name: app image: my-app:v1后果:调度器无法做出合理决策,某个 Pod 可能吃掉整个节点的资源,导致其他 Pod 被 OOM Kill。
-
把所有东西塞进一个 Pod Pod 中的容器应该有紧密的生命周期耦合。如果两个应用可以独立部署、独立扩缩容,它们应该在不同的 Pod 中。
-
忽略 Liveness/Readiness Probe 没有 Probe 的 Pod,K8s 无法知道它是否真的健康。一个「Running」状态的 Pod,内部应用可能已经死锁。
-
在生产环境使用
latest标签# 反面教材 image: my-app:latest你无法确定每次部署用的是哪个版本,回滚更是噩梦。始终使用明确的版本标签(如
v1.2.3或 Git SHA)。 -
直接操作集群资源而不使用 GitOps 手动
kubectl apply的变更不可追溯、不可审计。使用 Helm / Kustomize + ArgoCD / Flux 进行声明式管理。
最佳实践
-
声明式 > 命令式:始终用 YAML 定义资源,而不是
kubectl run/kubectl create的命令式操作。声明式配置可版本控制、可审计、可复现。 -
合理使用 Namespace:按团队或环境划分 Namespace,配合 ResourceQuota 和 LimitRange 进行资源管控。
-
Pod 反亲和性确保高可用:
spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: ["my-app"] topologyKey: kubernetes.io/hostname这确保同一应用的多个副本不会被调度到同一个 Node 上。
-
使用
imagePullPolicy控制镜像拉取策略:Always:总是拉取(适用于latest标签,但不推荐用 latest)IfNotPresent:本地没有才拉取(推荐用于明确版本标签)Never:从不拉取(仅用于本地开发)
-
优雅终止(Graceful Shutdown):
spec: terminationGracePeriodSeconds: 30 containers: - name: app lifecycle: preStop: exec: command: ["/bin/sh", "-c", "sleep 5"]preStop中的sleep 5给 kube-proxy 更新规则留出时间,避免请求被发送到正在终止的 Pod。 -
LimitRange 设置默认值:
apiVersion: v1 kind: LimitRange metadata: name: default-limits namespace: team-backend spec: limits: - default: cpu: "500m" memory: "256Mi" defaultRequest: cpu: "100m" memory: "128Mi" type: Container防止单个未配置资源限制的容器拖垮整个节点。
延伸阅读
- Kubernetes 官方文档 — 最权威的参考
- Kubernetes 官方教程 — 动手入门
- CNCF 技术雷达 — 云原生技术选型参考
- Kelsey Hightower - Kubernetes The Hard Way — 手动从零搭建 K8s 集群,深入理解每个组件
- 《Kubernetes in Action (第二版)》 — Marko Lukša 著,深入原理的经典教材
关联笔记
- k8s/k8s-workload-resources — Deployment、StatefulSet、DaemonSet 等工作负载详解
- k8s/k8s-networking — Service、Ingress、NetworkPolicy 网络模型
- k8s/k8s-storage — PV、PVC、StorageClass 存储体系