搞容器化半年之后,我发现自己天天敲docker run,但一说到"上生产",马上就被问倒了:容器挂了怎么自动恢复?流量大了怎么扩容?多台机器上容器怎么调度?新版本怎么无损发布?这些问题单靠 Docker 根本答不上来。这时候我才老老实实去学 Kubernetes,也就是大家口中的 K8s(读作"k8s"或"kube"),学完之后回头看,之前那些零散的容器操作只是地基,K8s 才是真正把容器变成"服务"的那一层。
这篇内容就是给正在学或者准备学 K8s 的朋友准备的。我不会先从理论名词轰炸你,而是按照我自己从"会跑容器"到"敢上 K8s"的真实路径来写:先讲清楚它到底解决了什么问题,再拆架构、讲核心对象,最后给一套可以直接照着搭的环境步骤和排错思路。如果你是刚接触 K8s 的开发者、运维,或者正在准备容器化改造的技术负责人,这篇应该能帮你少走我踩过的那些弯路。
1. 先搞明白:K8s 到底解决了什么问题
1.1 容器时代的问题:跑起来容易,管起来难
很多人第一次接触容器时都觉得很爽:docker build打个包,docker run一条命令起来,端口映射一下就能访问了,比传统部署方式省心太多。
但当你手上的容器数量从 3 个变成 30 个,机器从 1 台变成 3 台时,问题就来了:
- 某个容器挂了,谁来把它重新拉起来?
- 流量高峰期来了,想从 5 个实例扩到 20 个,难道要一个个手动
docker run吗? - 一台宿主机宕机了,上面跑的十几个容器怎么办?
- 发布新版本时,怎么做到不停机更新、有问题秒级回滚?
- 多个容器之间互相通信,IP 地址一变就得改配置,这谁受得了?
这些问题往本质里说就一句话:容器本身解决的是"打包和隔离",但大规模运行时的"编排"问题,需要另外一套系统来解决。K8s 就是这套系统的行业事实标准。
1.2"编排"到底编排的是什么
网上讲 K8s 时常说"容器编排平台",这个词听起来有点抽象。我用大白话翻译一下:K8s 就是一个"容器管家",你告诉它"我要跑什么、要几个副本、需要什么资源",它负责找机器去跑、盯着它们的健康状况、挂了就重启、流量大了就扩容、流量小了就缩容、新版本发布时按节奏滚动替换、老版本随时可以回退。
它做的事情本质上和"调度"两个字高度相关。你可以类比一支球队:K8s 是主教练,每个容器是球员,球员状态不好(容器崩溃)就换人(重建),比赛强度加大(高并发)就多上几个人(扩容),受伤了就去替补席(重新调度到健康节点)。
这里有个关键认知要纠正:K8s 不会让你写的应用变强,也不会自己提升性能,它做的是让"部署、恢复、扩展、升级"这些运维动作变得可靠和自动化。很多人以为上了 K8s 系统就稳了,结果应用代码自己写得稀烂,照样崩,只是崩了以后恢复得比以前快而已。
提示:如果你的应用是无状态、可水平扩展的,上了 K8s 会非常舒服。如果是有状态应用(数据库、消息队列),K8s 也能管,但复杂度会高一个量级,这部分后面讲存储时会单独说。
2. 整体架构:控制平面和工作节点
2.1 控制平面:集群的大脑
K8s 集群从逻辑上分成两部分:控制平面和工作节点。控制平面负责做出所有决策,工作节点负责实际跑容器。
控制平面上一共跑着这几个核心组件:
API Server(也叫 kube-apiserver):整个集群的唯一入口。你用
kubectl敲的每条命令,本质都是发给它的一串请求。它做了三件事:认证请求、校验请求、把状态写入存储。你可以把它理解成"前台接待员",所有人都得找它办事。etcd:K8s 用来保存所有状态的高速键值数据库。集群里每个资源的状态、期望值、实际值全部存在 etcd 里。etcd 挂了,整个集群就瞎了,因为 API Server 不知道该相信谁。
Scheduler(调度器):它的任务只有一个——决定"一个新创建的 Pod 应该放在哪台节点上"。决策依据包括资源剩余量、节点标签、亲和性规则、污点容忍等。
Controller Manager(控制器管理器):它是一堆控制器的集合,负责让集群的"实际状态"不断趋近于"期望状态"。比如 Deployment 控制器发现副本数从 3 变成了 1,它就会马上创建新的 Pod 补回去。这个"不断对比期望和实际、然后纠偏"的循环,就是 K8s 最核心的设计思路。
这里有个特别容易让新手蒙圈的点:控制平面的这些组件,自己也是容器,也跑在机器上。甚至在单机学习环境里,控制平面和你的应用可以跑在同一台机器上。只是生产环境里为了可靠性,控制平面通常是至少 3 台机器(奇数,配合 etcd 做选主)。
2.2 工作节点:真正干活的地方
工作节点上的组件就简单多了,主要有三个:
Kubelet:每个节点上的"监工",负责跟控制平面通信,接收 Pod 定义,然后调用容器运行时把容器真实跑起来。它还会定期向控制平面上报本节点的状态和资源使用量。
Container Runtime(容器运行时):真正拉起容器进程的底层组件,早期是 Docker,现在主流是 containerd。Kubelet 通过容器运行时的接口操作容器,自己不直接碰容器细节。
Kube-proxy:负责实现 Service 的负载均衡。它维护节点上的网络规则(通过 iptables 或 ipvs),让请求按照规则转发到后端 Pod 上。
我还想强调一个容易被忽略的组件:CoreDNS。它提供集群内部的域名解析服务,比如你在 Pod 里访问svc-name.namespace.svc.cluster.local,就是 CoreDNS 解析的。没有它,集群内的服务发现基本没法用。
2.3 请求的完整流转
理解了组件,再看一条请求从进入到返回的全过程,印象会深刻很多。
假设你先写好了一个 Deployment YAML,然后执行kubectl apply:
- kubectl 把你的 YAML 解析成 API 对象,发给 API Server。
- API Server 校验并写入 etcd,然后将事件通知给 Controller Manager。
- Deployment Controller 发现有新的期望状态,创建 ReplicaSet,ReplicaSet 控制器计算出需要创建 3 个 Pod,于是创建 Pod 对象。
- API Server 继续把 Pod 写入 etcd。Scheduler 监听到有未分配的 Pod,综合资源、约束条件,决定分配到 node-01 上。
- API Server 将调度结果写入 etcd,并通知 node-01 上的 Kubelet。
- Kubelet 调用 containerd 拉取镜像、创建容器、启动进程,最后回传状态。
- 你创建 Service 时,Kube-proxy 在各节点上配置转发规则,把 Service 虚拟 IP 的流量导到这批 Pod 上。
整个流程快的话几秒钟就完成。我当年梳理完这条链路之后,突然觉得kubectl敲下去之后的世界不再是黑盒,排查问题也有方向感了。
3. 每天打交道最多的核心对象
3.1 Pod:最小调度单元
Pod 是 K8s 里最小的部署和调度单元,它裁一个或多个容器,共享网络命名空间和存储卷。注意:K8s 不直接调度容器,而是调度 Pod。
这句话怎么理解?你可以把 Pod 想象成"一个炕上睡的几个兄弟"——它们共享一个 IP、共享端口空间,可以通过 localhost 互相访问,共享挂载的磁盘目录。这种设计的价值在于:有些业务场景里,两个进程必须同生共死、共用在同一个环境里(比如一个进程写日志、一个进程收集日志,或者一个反代、一个业务),把它们塞进同一个 Pod 能做到"要么一起起来,要么一个都别想跑"。
不过实际工作中,绝大多数 Pod 里只有一个容器。多容器 Pod 属于进阶用法,我建议新手先记住"Pod 是原子单位",别一上来就把多个容器塞进去。
3.2 工作负载:Deployment、StatefulSet、DaemonSet、Job
光有 Pod 还不够。Pod 挂了谁来重建?升级了怎么回滚?有状态的数据怎么保序?K8s 用一堆"工作负载资源"来回答这些问题,最常用的四个:
| 资源类型 | 用途 | 适合场景 |
|---|---|---|
| Deployment | 管理无状态应用的副本、滚动更新、回滚 | Web 服务、API、微服务、任何能随时重建的应用 |
| StatefulSet | 管理有状态应用,保证稳定网络标识、存储绑定 | 数据库、消息队列、ZooKeeper 类应用 |
| DaemonSet | 保证每个节点上跑一个副本 | 日志采集、监控 Agent、网络插件 |
| Job / CronJob | 跑一次性任务或定时任务 | 数据批处理、定时备份、清理任务 |
再说仔细一点。ReplicaSet是 Deployment 底下的控制器,管副本数;你平时很少直接写 ReplicaSet,通常声明 Deployment,Deployment 自动创建和管理 ReplicaSet。每次你更新 Deployment 的镜像版本,它都会创建一个新的 ReplicaSet,滚动升级的同时保留旧版本记录,方便回滚——这是我们能做到"发布五分钟、回滚十秒钟"的根本原因。
StatefulSet和 Deployment 的根本区别在于"身份"。StatefulSet 里的每个 Pod 有固定名称和稳定的网络标识(比如db-0、db-1),启动顺序是严格从 0 到 N,每个 Pod 挂载的存储卷是独立绑定的。这让数据库这类应用在 K8s 里勉强能活,但代价是运维复杂度暴涨。
新手建议:K8s 成熟之前,数据库、缓存这类有状态服务尽量不迁移上 K8s。用云厂商的托管数据库或自建独立集群,比硬放 K8s 里省心得多。先把无状态应用跑顺,有状态等团队沉淀出完整的 operator 方案再说。
3.3 Service 和 Ingress:让流量找得到门
Pod 是朝生暮死的:IP 地址每次重建都可能变。如果让应用之间直接记 Pod IP,那集群一乱就全乱了。K8s 给每个服务抽象了一个稳定的入口,叫Service。
Service 有三种最常用的类型:
- ClusterIP:集群内部访问你,分配一个稳定的虚拟 IP。比如 A 服务要调 B 服务,就是通过 B 的 ClusterIP 或域名。
- NodePort:在每个节点上开一个端口(30000-32767),外部可以通过任意节点 IP + 这个端口访问。常用于测试环境,生产不太用。
- LoadBalancer:和云厂商的负载均衡器对接,外部流量直接打到负载均衡 VIP 上,再分发到后端的 Pod。这是生产环境最常见的暴露方式。
Service 怎么找到后端 Pod?靠 Selector。你给 Pod 打上标签(比如app: nginx),Service 通过标签选择器把符合条件的 Pod 全部纳入后端列表。Kube-proxy 再更新转发规则,请求就会被分发到这些 Pod 上。
Ingress是更上层的"入口网关",它管的是 HTTP/HTTPS 层的路由:根据域名、路径把请求转给不同的 Service。比如api.example.com转给 A 服务,web.example.com转给 B 服务。Ingress 本身是个对象,真正干活的是一个叫 Ingress Controller 的组件(常见的有 Nginx Ingress、Traefik、云厂商的 Ingress 产品)。
3.4 Namespace 和配置对象
有了 Namespace 之后,一个集群可以逻辑上划分为多个虚拟空间。很多团队用它做环境隔离:dev、test、prod各占一个 Namespace,互不干扰。但是注意,Namespace 不是强隔离,它管不了网络策略,跨 Namespace 的访问默认是通的。
ConfigMap和Secret是用来做配置注入的:
- ConfigMap 存非敏感的配置,比如服务端口、日志级别、开关项。
- Secret 存敏感信息,比如数据库密码、Token、私钥。它只做了 Base64 编码,不是加密,存到 etcd 时建议开启加密功能。
把配置从镜像里拆出来的核心好处是:同一个镜像,通过挂载不同的配置,就能适配不同环境。这样做到"一次构建,到处运行",CI/CD 里就不用为每个环境重新构建镜像了。
4. 动手实操:从零搭一套 K8s 集群
概念看十遍不如动手搭一遍。K8s 的安装方式非常多,我强烈建议新手不要一开始就用托管服务(比如云厂商卖的一键集群),那样你会错过理解组件关系的最好机会。先自己在虚拟机或云主机上通过 kubeadm 搭一套两节点集群,跑通以后再上托管服务,看任何东西都通透很多。
4.1 准备环境
需要两台干净的 Linux 虚拟机或云主机,配置至少 2 核 CPU、2GB 内存(最好 4GB),装 Ubuntu 22.04 或 CentOS 7.9。一台做控制平面节点,一台做工作节点。我用的实验环境就按这个标准搭的双机。
先做基础的系统初始化,每台机器都要执行:
# 关闭 swap,kubelet 默认要求关闭 swap sudo swapoff -a sudo sed -i '/ swap / s/^/#/' /etc/fstab # 加载内核模块 cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf br_netfilter EOF sudo modprobe br_netfilter # 设置内核参数 cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 vm.swappiness = 0 EOF sudo sysctl --system这个步骤很多人会略过,但漏掉ip_forward=1会导致 Pod 访问外部网络失败,漏掉swapoff会导致 kubelet 直接拒绝启动。
4.2 安装 containerd 和 kubeadm
早期教程都让装 Docker,现在 K8s 官方早就把 containerd 作为默认运行时了。配置好 containerd 之后,重点是给它的配置文件里设置SystemdCgroup = true,否则节点上的容器无法正常统计 CPU 和内存:
# 安装 containerd sudo apt update sudo apt install -y containerd # 生成默认配置 sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml # 修改配置:SystemdCgroup 改为 true sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml # 重启 sudo systemctl restart containerd sudo systemctl enable containerd接着安装 kubeadm、kubelet 和 kubectl。版本要选一致,这里用当前主流的 1.28 系列:
sudo apt update sudo apt install -y apt-transport-https ca-certificates curl gpg curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt update sudo apt install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectlapt-mark hold这步很多人不知道,它会把这三个包的版本锁住,防止apt upgrade把它们升到不兼容的版本。我在这上面吃过亏:版本一升,整个集群就没法用了。
4.3 初始化控制平面
在控制平面节点上执行kubeadm init:
# 单网卡环境一般不用指定,多网卡时要指定 API Server 地址 sudo kubeadm init \ --apiserver-advertise-address=<控制平面节点IP> \ --pod-network-cidr=10.244.0.0/16 \ --kubernetes-version=1.28.2--pod-network-cidr指定的是 Pod 网段,这个值必须和后面的网络插件(比如 Flannel 用 10.244.0.0/16)保持一致,否则 Pod 之间无法正常通信。
初始化成功后,按提示去配置 kubectl:
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config kubectl get nodes这时候如果你执行kubectl get nodes,控制平面节点会显示NotReady,这是正常的,因为还缺 CNI 网络插件。
4.4 安装 Flannel 网络插件
CNI(容器网络接口)插件负责给每个 Pod 分配 IP、打通跨节点通信。我第一套环境用的 Flannel,因为它最省心:
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml等一分钟再看节点状态,变成Ready就说明网络通了。这一步如果卡住,最常见的原因是控制平面节点的 6443 端口没放通,或者--pod-network-cidr和 Flannel 配置不一致。
4.5 加入工作节点
控制平面初始化完成后会输出一条kubeadm join命令,里面带 Token。Token 有效期默认 24 小时,如果过期了,在控制平面节点重新生成:
kubeadm token create --print-join-command然后到工作节点上执行输出的命令,比如:
kubeadm join <控制平面节点IP>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>回到控制节点验证:
kubectl get nodes看到两个节点都是Ready,集群就搭完了。第一次搭成功的那一刻,你会对整个架构图里那些组件有完全不同的感觉——原来每个组件都不是虚的。
4.6 部署你的第一个应用
集群空转没意义,跑一个应用感受一下完整工作流。创建nginx-deploy.yaml:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-demo labels: app: nginx-demo spec: replicas: 3 selector: matchLabels: app: nginx-demo template: metadata: labels: app: nginx-demo spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80应用并观察滚动:
kubectl apply -f nginx-deploy.yaml kubectl get pods -o wide再创建一个 Service 把它暴露出来:
kubectl expose deployment nginx-demo --type=NodePort --port=80 --target-port=80 kubectl get svc用任意节点IP:NodePort访问一下,页面能出来,你的第一套 K8s 环境就算是真正跑通了。
实操心得:这一步做完别急着关机器,留着它继续实验。后面学习 Service、存储、滚动更新、故障恢复,全都需要一套真实环境。我见过太多人看完教程没实操,过两周再来问"为什么会 CrashLoopBackOff",原因就是 Pod 失败了没看过真实输出。
5. 调度、网络与存储:绕不过去的三座山
集群搭起来以后,应用也跑起来了,但有几个机制如果你不理解,遇到问题会很抓狂:Pod 为什么跑在这台不跑在那台?Service 负载均衡是谁在干活?数据库这种有状态应用在 K8s 里怎么存数据?
5.1 调度器怎么决定 Pod 跑在哪个节点
Scheduler 的核心工作流程可以理解成一个"筛选+打分"的过程。
筛选阶段(Filter):先把完全不符合条件的节点踢掉。比如 Pod 声明需要 2 核 CPU、4G 内存,那可用资源少于这个值的节点直接出局;Pod 限制了只在带disk=ssd标签的节点上跑,其他节点也出局。
打分阶段(Score):对剩下的节点按各种策略打分,比如资源最充足的节点分数高、已经跑了同类 Pod 的节点分数低(尽量分散)、节点上待调度的 Pod 越少分越高等。最后得分最高的节点获胜。
新手经常会遇到的坑是"Pod 一直 Pending"。排查第一步永远是用kubectl describe pod看调度器给出的提示,常见原因包括:
- 节点资源不足,所有节点都过了资源水位。
- Pod 有
nodeSelector,但没有节点匹配。 - 节点有污点(Taint),Pod 没有对应的容忍(Toleration)。
污点和容忍这一对概念,理解起来很快:节点上打了污点,就像贴了"生人勿近"的牌子;Pod 加了容忍,相当于"我是熟人可以靠近"。控制平面节点默认有污点,所以业务 Pod 不会调度到它上面。
5.2 Service 负载均衡是怎么生效的
Service 创建以后,kube-proxy 会做一件关键的事:在集群每台节点的 iptables 里写入规则。当一个请求访问 Service 的虚拟 IP 时,iptables 规则会把流量随机转发到某个后端 Pod 的真实 IP 上。
底层的转发链路大致是这样:客户端 → ClusterIP:Port → iptables DNAT → Pod IP:Port。这里有一个值得注意的点:转发行为发生在发出请求的那台节点上,如果请求是集群外部发来的,会先进入某台节点(NodePort/LoadBalancer 场景),然后再做 DNAT。所以 Kube-proxy 必须在每台节点上保持一致的规则,这也是为什么 Service 是集群级别的对象。
如果你在高并发场景下做性能调优,会发现 iptables 模式在大规模 Service(几千条规则)下性能衰减明显。这时可以改成 IPVS 模式,它用内核态哈希表做转发,性能好很多。在 kube-proxy 的启动参数里加--proxy-mode=ipvs即可。
5.3 存储卷:有状态应用怎么保存数据
Pod 里的容器一重建,写入容器本地文件系统的数据就灰飞烟灭了。如果数据要持久化,必须挂存储卷。
K8s 的存储体系分三层:
- Volume:Pod 级别声明的存储,生命周期和 Pod 一致(所以只能解决容器重启数据丢失,解决不了 Pod 被删)。
- PV(PersistentVolume)和 PVC(PersistentVolumeClaim):PV 是集群里的一块真实存储(可以是云硬盘、NFS、本地盘),PVC 是使用方提出的"我要一块 20G 的读写盘"的申请,系统自动把合适的 PV 和 PVC 绑定。
- StorageClass:负责"按需动态创建 PV"的模板。PVC 里指定 StorageClass,系统就自动创建对应类型的 PV,省去了人工预分配盘的工作。
看到这里你应该明白了:StatefulSet 里每个副本之所以能保持自己独立的数据,是因为每个 Pod 绑定了一个独立的 PVC,Pod 重建后重新挂载同一个 PVC,数据就还在。这套机制叫做"稳定存储身份"。
但我要给你一个忠告:在团队能力没成熟之前,不要在 K8s 上跑核心数据库。K8s 的存储编排、故障转移、备份恢复,每一样都有一套复杂的细节,生产环境出事时的心理压力远高于收益。等团队把这套机制玩明白了,再考虑把无状态应用之外的东西迁移过去。
6. 常见问题与排查实录
这一部分我想把学习过程中反复出现的几类问题集中列出。记不清的时候直接翻这个速查表,能省下不少时间。
| 现象 | 最常见原因 | 优先排查命令 |
|---|---|---|
| Pod 一直 Pending | 节点资源不足、污点不匹配、缺 PVC | kubectl describe pod <pod-name> |
| Pod 一直 ImagePullBackOff | 镜像名写错、镜像不存在、仓库认证失败 | kubectl logs <pod-name>、登录仓库测试 |
| 容器反复重启(CrashLoopBackOff) | 启动命令报错、配置缺失、依赖没就绪 | kubectl logs --previous <pod-name> |
| 节点状态 NotReady | 网络插件没装好、kubelet 挂掉、资源耗尽 | systemctl status kubelet、看 node 事件 |
| Service 访问不通 | 标签选择器写错、后端 Pod 没就绪 | kubectl get endpoints <svc-name> |
| DNS 解析失败 | CoreDNS 没起来或资源受限 | kubectl logs -n kube-system -l k8s-app=kube-dns |
| 集群版本升级后 hang 住 | kubelet 和 API Server 版本不一致 | kubectl version,对比版本 |
6.1 Pod 一直 Pending,怎么办
Pending 意味着调度器还没把它放到任何节点上。别猜,直接看事件:
kubectl describe pod <pod-name>在 Events 末尾会给你明确原因,比如 "0/2 nodes are available: insufficient cpu" 或者 "node(s) had untolerated taint"。前者就是节点资源不足,后者要看那个污点是不是节点故意打的。
6.2 镜像拉取失败,怎么判断
先分清楚是"拉不到"还是"认证失败":
# 拉不到一般是网络问题或镜像名不对 kubectl logs <pod-name> # 手动在节点上测试一下 sudo crictl pull nginx:1.25如果私仓镜像需要认证,就得在 Pod 里配置imagePullSecrets,把认证信息提前存储到 Secret 里。拉取失败时千万别只盯着 Pod 日志,crictl pull能直接告诉你 root cause 是不是仓库连通问题。
6.3 CrashLoopBackOff 是哪些坑
CrashLoopBackOff 的意思是:容器起来了,但马上挂了,然后不断重试,重试间隔越来越长。
排查第一步永远是看日志:
# 看当前容器日志 kubectl logs <pod-name> # 如果容器一直在重启,看上一次的日志 kubectl logs <pod-name> --previous这里的--previous参数我每次都要强调:它能看到上一个崩溃实例留下的输出。很多新手只调kubectl logs,结果看到的是空输出,以为 Pod 没问题,实际上上一个实例已经把报错写进日志了。
在我的经验里,CrashLoopBackOff 最常见的原因是"应用启动时依赖了不存在的环境变量或配置",其次是"启动命令路径写错"和"健康检查配置太激进(应用还没起来就认为不健康,然后被干掉)"。
6.4 节点 NotReady,先看 kubelet
节点报 NotReady 时,第一时间登录到问题节点上看服务状态:
systemctl status kubelet journalctl -u kubelet --no-pager -n 100常见的几条原因:
- kubelet 没启动或启动失败(证书过期、配置错)。
- 机器资源耗尽(磁盘满、内存耗尽),Kubelet 被 OOM 干掉。
- CNI 插件配置丢失,导致 Pod 网络整理失败。
- 节点和 API Server 之间网络不通(防火墙、安全组拦了 10250 端口)。
修复节点网络挂掉的问题时,我会先 ping 一下 API Server 的 IP,确定基础网络通不通,再逐层看 kubelet 日志,通常都能找到定位点。
6.5 一些避坑心得
最后写几条心得体会,都是我实际踩过的:
- 每台机器的时钟要同步。集群里节点间时间偏差过大,会导致证书校验失败、日志排查混乱。部署集群前先配好 NTP 或 chrony。
- 不要动
/etc/kubernetes/manifests下的静态 Pod 文件除非你清楚后果。API Server、etcd 这些组件本身就是静态 Pod,改错一步集群直接瘫痪。 - 资源 requests 和 limits 一定要写。不写 requests,调度器不知道节点真正的容量;不写 limits,某个应用可能把节点内存吃满导致整机 OOM。这是新手最容易忽略、也最容易被线上事故教育出来的点。
- etcd 一定要备份。集群配置、状态全在 etcd 里,简称"一丢全丢"。我现在每周定时做一次快照备份。
收尾:我的个人上手建议
如果你正准备开始啃 K8s,我建议你按这个顺序来:先搭一套 kubeadm 集群(别用托管集群,这步省了后面会补课),然后用 Deployment + Service 部署一个无状态应用,接着尝试修改副本数、触发滚动更新和回滚,再故意手动删一个 Pod 看看它怎么被重建——这些动作做一遍之后,你对"控制平面在管什么、节点在干什么"就有了体感。
学习方法上有个反直觉的经验:不要一开始就背所有 YAML 字段、看官方文档的每个概念页。抓主线,主线就是那三件事——期望状态、控制器纠偏、API 驱动。剩下的 Pod 生命周期、探针、HPA、NetworkPolicy 这些都是在主线上生长的枝叶。
我在这个项目上最大的体会是:K8s 学的不是"某个工具怎么用",而是一整套"大规模分布式系统的运维思维"。容器只是砖头,K8s 是施工规范,真正决定房子质量的,是你对这套规范的理解深度。把这些基础知识吃透之后,再去看云原生的其他东西——服务网格、可观测性、GitOps——你就会有同一个根系往上长的感觉,越学越顺。