1.1. Kubernetes 系统基础
容器解决的是「如何打包并隔离一个进程」,编排解决的是「成百上千个这样的进程如何被调度、连接、升级和自愈」。这一章不背概念,而是沿着一条请求的路径,把容器运行时、CRI、CNI、CSI 与控制平面串成一张可以上手验证的图。
- 层级:
L1 · 入门
- 难度:
入门
- 预计:
50 分钟
- 前置:
Linux 基础、Docker 或 Podman 使用经验
1.1.1. 你将学到
容器隔离依赖的内核机制,以及单机 Docker 解决不了什么问题
OCI、CRI、CNI、CSI 各自约束哪一层,为什么它们能解耦
控制平面与工作节点的组件边界,以及「一切经过 API Server」的含义
Pod 网络模型的四条通信路径
声明式 API 与控制器协调循环的工作方式
Helm、Operator、Service Mesh、可观测性在生态中的位置
用
crictl与kubectl观察运行时与组件健康
1.1.2. 从容器到编排
容器本质上是「被内核限制起来的进程」:namespaces 负责让进程看不见别的进程、网络和挂载点,cgroups 负责限制它能用多少 CPU 和内存,联合文件系统负责把镜像分层叠加。它不是一个轻量虚拟机,也没有自己的内核。
于是单机 Docker 很快会撞到墙:
问题 |
单机 Docker 的做法 |
编排系统要回答的 |
|---|---|---|
跑在哪台机器 |
人工 |
调度器按资源、亲和、污点决策 |
进程挂了怎么办 |
|
控制器在别的节点重建副本 |
多副本如何被发现 |
手工维护 IP 列表 |
Service + DNS 提供稳定入口 |
滚动升级与回滚 |
脚本编排容器 |
ReplicaSet 历史 + 发布策略 |
存储跟着谁走 |
|
PV/PVC 与 CSI 动态供给 |
Kubernetes 的答案不是「更强的 Docker」,而是把「期望状态」写成对象存进 etcd,再让一组控制器不断把现实拉向期望。
备注
Docker 仍然有用
Docker 依然是本地构建镜像的好工具,Kubernetes 也照样运行 Docker 构建的镜像。变化的是运行时接口:自 1.24 起 dockershim 被移除,kubelet 通过 CRI 直接对接 containerd 或 CRI-O,Docker Engine 不再作为集群运行时被支持。
1.1.3. 标准、接口与实现
理解 Kubernetes 的关键,是分清「标准」和「实现」:
标准 |
约束什么 |
典型实现 |
|---|---|---|
OCI Image Spec |
镜像的 layer、manifest、config 格式 |
buildkit、Harbor、Docker Registry |
OCI Runtime Spec |
如何把镜像变成运行中的进程 |
runc、crun、gVisor、Kata |
CRI |
kubelet 与容器运行时之间的 gRPC 接口 |
containerd、CRI-O |
CNI |
创建/删除 Pod 网络与 IP 的调用约定 |
Calico、Cilium、Flannel |
CSI |
卷的创建、挂载、快照的调用约定 |
csi-driver-nfs、Ceph CSI、EBS CSI |
图:组件只与 API Server 对话,kubelet 只认 CRI,运行时不认识 Kubernetes API。
这种分层带来一个很实际的排错结论:Pod 起不来时,先判断问题在 API 层、调度层还是运行时层。kubectl describe 能看到的错误,通常已经不在应用代码里了。
1.1.4. 集群架构
组件 |
位置 |
一句话职责 |
|---|---|---|
kube-apiserver |
控制平面 |
唯一读写 etcd 的入口,负责认证、授权、准入 |
etcd |
控制平面 |
保存全部集群状态,强一致 KV |
kube-scheduler |
控制平面 |
为未绑定的 Pod 选择节点,只写绑定结果 |
kube-controller-manager |
控制平面 |
运行 Deployment、Node、Job 等协调循环 |
kubelet |
每个节点 |
让本节点的 Pod 实际状态靠近 spec |
容器运行时 |
每个节点 |
通过 CRI 拉镜像、建沙箱、跑容器 |
kube-proxy |
每个节点 |
用 iptables/IPVS 实现 Service 虚拟 IP |
CNI 插件 |
每个节点 |
分配 Pod IP、配置路由、执行 NetworkPolicy |
控制平面通常为奇数个节点,因为 etcd 需要多数派;工作节点可以随时增减。小集群里控制平面也会承载业务 Pod,但生产环境建议用污点把它们隔开。
1.1.5. Pod 网络模型
Kubernetes 对网络只提出四条要求:
每个 Pod 拥有集群内唯一的 IP;
同一 Pod 内的容器共享网络命名空间,用
localhost互通;任意两个 Pod 之间可以直接通信,不需要 NAT;
节点上的进程(kubelet、监控 agent)能直接访问 Pod IP。
由此可以推出四条需要分别排查的路径:同 Pod 内容器之间、Pod 之间、Pod 到 Service、集群外到 Service。很多「网络不通」的问题,其实卡在第二条之外的 DNS 或 Service 选后端阶段。
1.1.6. 声明式 API 与控制器
你提交的 YAML 是期望状态,不是命令。API Server 把它存进 etcd 后,控制器会进入一个永不停止的循环:
读取期望状态(
spec);读取实际状态(
status与集群现实);若有差异,采取行动;若无差异,什么都不做。
这叫水平触发(level-triggered):控制器不关心「发生了什么事件」,只关心「现在差多少」。因此它天然能处理重启、漏事件和重复执行。三个直接推论:
删除一个由 Deployment 管理的 Pod,它会被重建,因为期望副本数没变;
手工
kubectl edit改掉的字段,可能被控制器改回去;自定义资源(CRD)+ 自定义控制器 = Operator,把「人类运维知识」编码进协调循环。
1.1.7. 生态地图
方向 |
代表项目 |
解决的问题 |
|---|---|---|
包管理 |
Helm、Kustomize |
模板化、分环境渲染、版本化发布 |
扩展 API |
CRD + Operator |
把数据库、中间件的运维流程自动化 |
服务网格 |
Istio、Linkerd、Cilium Mesh |
mTLS、流量治理、灰度、可观测性下沉 |
可观测性 |
Prometheus、Loki、Tempo、OpenTelemetry |
指标、日志、链路三件套 |
GitOps / 策略 |
Argo CD、Flux、Kyverno |
Git 为事实源持续同步,准入阶段强制规范 |
初学阶段不必全学,但要记住一条主线:Kubernetes 只提供机制,不提供策略。副本数、探针、配额、网络策略这些「策略」需要你自己写进对象里。
1.1.8. 实验:用 crictl 与 kubectl 观察运行时
1.1.8.1. 1. 找到 CRI 端点
sudo crictl info | head -20
cat /etc/crictl.yaml 2>/dev/null
sudo cat /var/lib/kubelet/kubeadm-flags.env | tr ' ' '\n' | grep runtime
# 没有配置文件时显式指定端点
sudo crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps
1.1.8.2. 2. 从 Pod 反查到容器
kubectl run web --image=nginx:1.27 --port=80
kubectl get pod web -o wide
POD_ID=$(sudo crictl pods -q --name web)
sudo crictl ps --pod "$POD_ID"
sudo crictl inspect "$(sudo crictl ps -q --pod "$POD_ID")" \
| jq -r '.status.labels["io.kubernetes.pod.name"]'
最后一条命令会输出 web。这说明 kubectl 与 crictl 看的是同一批对象,只是视角不同:
视角 |
看到的东西 |
适合回答 |
|---|---|---|
|
API 对象与 status |
调度、准入、控制器、Service |
|
沙箱、容器、镜像、日志文件 |
拉镜像、CRI 报错、容器是否真的在跑 |
|
kubelet 与 CRI 的交互日志 |
挂载失败、探针执行、CNI 调用 |
1.1.8.3. 3. 检查控制平面健康
kubectl get --raw '/readyz?verbose'
kubectl get --raw '/livez?verbose'
kubectl -n kube-system get pods -o wide
kubectl -n kube-system logs kube-apiserver-<控制平面节点名> --tail=20
readyz?verbose 会逐项列出 etcd、informers、poststarthooks 的状态,是判断「API 慢/不可用」的第一步。
1.1.8.4. 4. 观察一次 Pod 启动
kubectl describe pod web | sed -n '/Events/,$p'
kubectl get events -A --sort-by=.lastTimestamp | tail -10
sudo crictl images | grep -E 'pause|nginx'
kubectl delete pod web
pause 镜像就是 Pod 沙箱:它持有网络命名空间,业务容器加入其中,因此同 Pod 内容器共享 IP。
1.1.9. 验证清单
kubectl get nodes全部为Readykubectl get --raw '/readyz?verbose'所有检查项为okkubectl -n kube-system get pods中 CNI、CoreDNS、kube-proxy 均Runningcrictl info能连上运行时,crictl ps能看到业务容器能通过 CRI label
io.kubernetes.pod.name把容器映射回 Pod跨节点 Pod 互 ping 通,且
kubectl exec能解析 Service 名
1.1.10. 排错表
现象 |
可能原因 |
排查入口 |
|---|---|---|
|
端点错误或运行时未启动 |
|
|
连到了另一个运行时 socket |
对比 kubelet 的 |
Node |
kubelet 异常或 CNI 未就绪 |
|
Pod |
CNI 未安装、路由或防火墙阻断 |
|
|
API Server 连不上 etcd |
|
镜像拉取失败 |
仓库不可达、凭据缺失、沙箱镜像缺失 |
|
1.1.11. 进阶挑战
用
crictl inspectp找到 Pod 沙箱的网络命名空间路径,nsenter进去执行ip addr,验证 Pod IP 就在这个命名空间里。在某节点停掉 kubelet,观察 Node 变为
NotReady的时间,以及 Pod 多久后被驱逐重建(提示:node-monitor-grace-period与污点驱逐)。用
kubectl get --raw /metrics找出 apiserver 的请求延迟指标,解释apiserver_request_duration_seconds的分位数含义。画一张从
kubectl apply到容器启动的时序图,标注 CRI、CNI、CSI 分别在哪个时刻介入。
1.1.12. 小结
容器是内核能力的组合,编排解决的是调度、自愈、发现、存储与发布。
OCI 定义镜像与运行时,CRI/CNI/CSI 定义 kubelet 与基础设施插件的契约。
所有组件通过 API Server 协作,etcd 是唯一状态源。
Pod 网络有四条路径,排错要逐条验证,不要一上来就怀疑 CNI。
声明式 API + 控制器 = 水平触发的自愈系统;Operator 是把运维知识写成控制器。