搞K8s这几年,踩过的坑比写过的yaml都多。之前帮一个朋友排查节点初始化问题,日志停在[init] using kubernetes version: v1.26.0和[preflight] running pre-flight checks半天不动,最后发现是cgroup驱动和容器运行时没对齐,这问题在很多人第一次搭建集群时候都会撞上。这篇文章不绕弯子,直接把我从部署、排障到生产实践里总结的Kubernetes知识点串起来,从核心设计讲清楚为什么这么设计,再到实操中怎么配置、怎么选型、怎么定位问题,帮你把K8s从概念到落地这条路走通。
1. K8s的核心设计,先搞懂它到底在解决什么问题
1.1 从"应用运行"到"声明式编排"的思路转变
很多人学K8s一上来就背概念,Pod、Service、Deployment背了一堆,但遇到实际问题还是无从下手。我建议换个角度切入:先想清楚,你的应用跑在几十台机器上,要升级、要扩容、要保证不宕机,手动操作根本忙不过来,你要的是什么?你要的其实是一个"声明"能力——你告诉系统"我要10个副本、每次滚动升级最多不可用1个",剩下的事情它自己搞定。
这就是K8s和传统运维工具最本质的区别。传统方式写脚本、写执行步骤,是"命令式",你得事无巨细地告诉系统每一步怎么做;K8s是"声明式",你描述期望状态,控制循环不断对比当前状态和期望状态,有偏差就想办法纠正。
用一个不太严谨但很好懂的类比:传统运维像你雇了个做饭的阿姨,你得一步步告诉她"先洗菜、再切菜、然后放油";K8s像是你告诉厨房"中午12点我要一桌8个人的川菜",至于怎么买菜、怎么分配灶台、哪个厨师炒哪道菜,厨房自己调度。K8s就是那个"中央厨房调度系统",而Pod就是装菜的餐盒,Deployment就是后厨的管理员。
1.2 核心组件各司其职,谁负责调度、谁负责存储、谁负责网络
理解了方向,再记组件就不容易乱了。K8s控制面主要组件就四个大块:
- kube-apiserver:所有操作的入口,也是唯一和etcd通信的组件。你执行的每一个
kubectl命令,最终都会变成对apiserver的API请求。它管着认证、鉴权、准入控制,相当于公司的前台加行政。 - etcd:集群状态的唯一存储,所有配置、期望状态、实际状态全存在这里。重要性怎么强调都不过分,etcd挂了整个集群就变成只读甚至不可用。
- kube-scheduler:负责决定新Pod放在哪个节点。它的决策依据是资源请求、亲和性、污点容忍这些条件,相当于排课表的教务处。
- kube-controller-manager:运行着一堆控制器,Node控制器、Deployment控制器、Endpoint控制器等等。它们负责维护各种资源的期望状态,相当于各业务线的负责人,发现实际状态和预期不符就想办法纠正。
工作节点上则是三个关键角色:kubelet负责管理本节点的Pod生命周期,向apiserver汇报节点和Pod状态,相当于驻场管家;kube-proxy负责实现Service的负载均衡和访问规则,通常基于iptables或IPVS实现,相当于门卫,负责把外面的请求分发到正确的Pod;容器运行时则真正负责跑容器,比如containerd。
很多初次接触K8s的人会问:apiserver和控制器之间通信是怎么实现的?答案是:所有组件都只和apiserver打交道,不做点对点通信。这种"一切经由API"的设计,保证了集群状态的一致性和可审计性——你随时可以查看某个对象的yaml,看看它当前的spec、status、events到底发生了什么。
1.3 Namespace和Label,多维度的资源管理视角
集群里资源多了以后,怎么组织才是关键问题。Namespace负责"隔离":把资源在逻辑上分成不同的空间,一个团队一个Namespace,或者一个环境一个Namespace,配合RBAC做权限隔离。Label负责"选择":给对象打标签,然后通过标签选择器把相关对象关联起来。
这是两个容易混淆的概念,我理清楚一下:Namespace做的是"分门别类放东西",Label做的是"给每样东西贴属性标签"。举个例子:同样是nginx的Pod,你可以打上app=nginx、env=prod、tier=web三个标签,然后Deployment通过selector选出app=nginx的Pod来管理,Service通过selector选出tier=web的Pod来接入流量,各选各的,互不干扰。
理解这个机制非常重要。因为K8s里大量资源间的关联关系,都是靠Label Selector这种松耦合方式建立的,而不是在yaml里写死引用关系。这也是K8s扩展性好的原因之一——你随时可以叠加上新的标签维度,而不需要修改原有配置。
2. 集群搭建实操,从裸机到可用的生产集群
2.1 kubeadm初始化全流程,卡在preflight多半是环境问题
现在搭建K8s集群,主流方案还是kubeadm,它把证书生成、控制面组件容器化、引导token这些复杂步骤都封装好了。但网上千篇一律的教程往往会漏掉最容易踩坑的前置环节,导致最后卡在这两行日志上:
[init] using kubernetes version: v1.26.0 [preflight] running pre-flight checks这两行之间如果长时间不动,或者直接报错,不用怀疑,八成是环境问题。preflight阶段会检查一大堆内容:内核模块是否加载、swap是否关闭、端口是否被占用、容器运行时是否正常、cgroup驱动是否一致,每一项失败都会给出明确提示,比如"Port 6443 is in use"这类。
我整理了一个核对清单,第一次装集群前逐项过一遍:
| 检查项 | 要求 | 说明 |
|---|---|---|
| 操作系统 | Ubuntu 20.04+/CentOS 7.9+/Rocky 8+ | 内核建议4.18以上,太老的内核跑K8s会有各种诡异问题 |
| swap | 必须关闭 | kubelet默认要求swap关闭,不关会直接报错 |
| 内核模块 | br_netfilter、overlay必须加载 | 不加载会导致iptables转发和容器存储出问题 |
| sysctl参数 | net.bridge.bridge-nf-call-iptables=1 | 这个参数不设,集群内DNS解析可能会间歇性失败 |
| 容器运行时 | containerd 1.6+/cri-o | 注意和K8s版本兼容性 |
| cgroup驱动 | systemd(推荐) | 和容器运行时保持一致,不一致会导致kubelet无法启动 |
| 网络插件 | flannel/calico/cilium三选一 | 必须装,不装集群网络是broken的,Pod之间无法通信 |
| 端口 | 6443、2379、2380等 | 控制面节点必须放通这些端口,用firewalld/ufw的注意检查 |
上面每一项都展开说说。内核模块这块,很多人直接在云主机上装,发现iptables规则不生效,Pod之间网络不通,就是因为br_netfilter没加载。sysctl参数也一样,net.bridge.bridge-nf-call-iptables=1决定了经过网桥的IPv4流量是否会被iptables规则处理,K8s的Service转发依赖这个,不设置的话Service偶尔通偶尔不通,非常难排查。
2.2 cgroup驱动不一致,kubelet反复崩溃的元凶
很多人在kubeadm init的时候都遇到了cgroup driver报错。K8s从1.20版本开始,容器运行时的cgroup驱动推荐使用systemd,因为cgroupfs在systemd作为init的系统上会和systemd自己的cgroup管理冲突,导致资源统计错误,极端情况下会让kubelet直接崩溃。如果你的containerd配置里用的是cgroupfs,而kubelet用的是systemd,初始化会直接失败,日志明确提示:
failed to run Kubelet: failed to create kubelet: misconfiguration: kubelet cgroup driver: "systemd" is different from docker cgroup driver: "cgroupfs"解决方法是修改containerd的配置文件。执行containerd config default生成默认配置,然后找到SystemdCgroup这个字段,把它设为true。实际上新版本的containerd默认配置里,SystemdCgroup通常已经是true了,这个问题的根源往往在于你用了旧版本的containerd,或者手改配置的时候改错了位置。
有个小经验:先systemctl status containerd确认容器运行时是活的,再用crictl info查看当前的cgroup驱动到底是什么。crictl是调试容器运行时非常好用的工具,很多网上教程都忽略了它。
2.3 初始化之后的三个必做步骤:kubectl配置、CNI插件、控制面节点Ready
kubeadm init成功之后,控制面节点默认是不Ready的,因为有node-role.kubernetes.io/control-plane这个污点在,普通Pod不会调度上去,但这不是节点不Ready的原因。节点不Ready通常是因为CNI网络插件没装,kubelet会持续等待网络就绪。很多人以为init成功就万事大吉,一看kubectl get nodes发现节点是NotReady,就开始慌了,其实只要装上CNI插件,等一两分钟节点就会变为Ready。
具体步骤是三步:
- 配置kubectl访问凭证:
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config- 安装CNI插件,我近些年比较推荐Calico:
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.26/manifests/calico.yaml- 验证节点状态:
kubectl get nodes kubectl get pods -n kube-system这条命令的输出里,可以看到coredns、etcd、kube-apiserver这些Pod都在Running状态,说明集群正常了。
CNI插件选择这里多说一句:Flannel部署简单,适合测试环境,但性能一般,不支持NetworkPolicy;Calico功能全,支持NetworkPolicy,性能也不错,是生产环境的常见选择;Cilium基于eBPF,性能最好,功能最强,但对内核版本要求较高,5.8以上内核体验才好。如果不想折腾,就选Calico,覆盖的场景最广。
2.4 工作节点加入集群,token过期怎么处理
控制面就绪后,工作节点通过kubeadm join加入集群。但有个经典坑:init生成的token有效期只有24小时,如果你隔了两天再去加节点,token已经过期了,join直接失败。
解决办法是重新生成token:
kubeadm token create --print-join-command这条命令会输出带新token的join命令,直接复制到工作节点执行就行。join过程同样有preflight检查,如果工作节点没装容器运行时,或者swap没关,同样会卡住。另外别忘了在工作节点上装kubeadm、kubelet、kubectl,版本尽量和控制面一致,跨多个小版本的兼容性虽然官方声称支持,但没太大必要给自己找麻烦。
3. Service的流量入口,externalIPs和访问方式的取舍
3.1 Service类型怎么选,NodePort、LoadBalancer、externalIPs到底有什么区别
集群搭好之后,第一步往往是部署一个测试应用,然后想办法访问它。这就涉及Service对外暴露的方式。Service类型一共有四种:ClusterIP、NodePort、LoadBalancer、ExternalName,日常用得最多的是前三种。
ClusterIP只为集群内部访问提供虚IP,外部无法直接访问;NodePort在每个节点上开一个端口,把流量转发到后端Pod;LoadBalancer依赖云厂商的负载均衡器,把流量打到节点上再转发到Pod,是云上最常用的方式;ExternalName则是把Service映射到一个外部DNS名称,不走Pod转发。
这里重点说externalIPs——热词里有k8s externalips,说明很多人没搞懂这个字段的语义。externalIPs并不是Service的一种类型,而是Service资源上的一个字段,你可以给Service指定一个或多个externalIP,K8s会在所有节点上把这个IP的流量转发到Service的后端Pod。它相当于一个"手动版"的LoadBalancer:你有一个闲置的公网IP或内部IP,不想买云负载均衡器,就把这个IP配置到Service上,让K8s帮你把流量导进来。
这个玩法在裸金属环境很实用。比如你有一台物理机,公网IP是203.0.113.5,你可以在Service配置里写:
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: my-app ports: - port: 80 targetPort: 8080 externalIPs: - 203.0.113.5然后确保这个IP绑定到了某个节点上(或者通过路由/ARP指向节点),来自该IP的80端口流量就会被转发到my-app的8080端口。
但要注意,你还需要把外部请求路由到这个IP上,而且externalIPs不会自动给你做高可用——IP绑定的节点挂了,流量就断了。所以生产环境还是优先考虑LoadBalancer或Ingress方案比较稳妥。
3.2 kube-proxy的两种转发模式,iptables和IPVS怎么选
理解了Service类型,有时候还是会遇到"Service存在但流量不通"的情况,这时候就要关注kube-proxy的转发模式了。kube-proxy支持userspace、iptables、IPVS三种模式,现在userspace基本废弃了,主要区分后两种。
iptables模式的核心逻辑是:kube-proxy watch到Service和Endpoint变化,动态生成iptables规则,把发往Service VIP的流量DNAT到后端Pod。优点是简单可靠、兼容性极好;缺点是Service数量一旦超过几千条,iptables规则会变得非常庞大,更新规则时CPU占用飙升,而且规则是链式的,匹配效率低。
IPVS模式则是把转发规则放到内核的IPVS表中,使用hash表查找,性能和规模承载能力明显更强,还支持更丰富的负载均衡算法(如rr、wrr、lc等)。如果你的集群规模预期会超过1000个Service,建议直接开启IPVS模式:
kubeadm init --pod-network-cidr=10.244.0.0/16 # 或者在kube-proxy的ConfigMap里修改mode字段为ipvs修改方法是用kubectl edit configmap kube-proxy -n kube-system,把mode: ""改成mode: "ipvs",然后滚动重启kube-proxy的Pod。IPVS模式有个小注意事项:节点上要确保ipvsadm和ipset这两个工具装了,否则模式起不来。
3.3 Ingress承担流量入口,对外网关怎么配置
生产环境真正承担对外流量的,通常不是直接暴露Service,而是Ingress。Ingress本质上是七层负载均衡的反向代理,将HTTP/HTTPS请求按域名或路径规则路由到不同的Service。比较流行的Ingress Controller有nginx-ingress(基于Nginx)、traefik、ingress-nginx(K8s官方维护的Nginx Ingress)等。
用Ingress的好处是:只需一个入口点,比如一个NodePort类型的ingress-nginx Service,然后把所有域名的流量都指向这个Service,由Ingress规则决定最终转发到哪个后端Service。你不需要为每个应用都创建一个LoadBalancer,成本低,也方便统一做TLS证书管理和访问控制。
一个典型的Ingress配置:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress spec: ingressClassName: nginx rules: - host: app1.example.com http: paths: - path: / pathType: Prefix backend: service: name: app1-service port: number: 80 - host: app2.example.com http: paths: - path: / pathType: Prefix backend: service: name: app2-service port: number: 80这里稍微提醒一句:很多人觉得Ingress配好就能访问了,但如果你的Ingress Controller前面没有负载均衡器,或者DNS没有指向正确的节点IP,流量一样进不来。实际排查时,先确认Ingress Controller的Service能通,再检查Ingress规则是否生效,这样定位问题比较快。
4. 生产环境的高可用与故障排查,替用户挡住可见故障
4.1 etcd备份与恢复,没有备份的集群是定时炸弹
在K8s里,etcd就是整个集群的"黑匣子",所有状态都存在这里。一旦etcd的数据损坏或者节点丢失,控制面组件无法正常工作,节点上的Pod即使还在跑,你也没办法再管理它们。
生产环境必须做好etcd备份。最简单的做法是用etcdctl做快照备份:
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ snapshot save /backup/etcd-snapshot-$(date +%Y%m%d).db恢复则是:
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-snapshot-xxx.db \ --data-dir=/var/lib/etcd-restore恢复完成后把数据目录替换回etcd的数据目录,重启etcd。
我的经验是:每天至少做一次快照,保留最近7天的备份文件,同时把备份文件复制到集群外部存储。etcd本身就是分布式存储,但这不代表不需要备份——集群里节点的磁盘一样会坏,误删Namespace这种操作也一样会发生。个人印象最深的一次事故,就是有人在生产环境误删了一个Namespace,里面所有Deployment、Service、ConfigMap全没了,如果没有快照,只能按业务代码重新部署一遍,恢复时间就要以小时甚至天来计。
4.2 资源请求和限制不配置,节点被Pod打爆的连锁反应
生产环境中一个非常常见但又容易被新手忽略的问题是:Deployment不配置resources字段。不配置的话,K8s认为这个Pod可以无限使用CPU和内存,调度器无法评估节点容量,运行时也会出现一个Pod吃掉整个节点内存的情况,触发OOM Killer,把其他无辜的Pod杀掉,甚至把kubelet自己搞崩溃。
正确的做法是给每个容器配置requests和limits。requests是调度依据,limits是运行时上限:
resources: requests: cpu: 500m memory: 512Mi limits: cpu: "1" memory: 1GiCPU的500m表示0.5个核,memory的512Mi是500兆,这些单位一定要搞清楚,不然配置会偏差很大。
提起这个我多讲一个坑:只看limits不看requests。有些团队设置了limits但不设置requests,调度器按requests来分配Pod,集群实际可能超卖非常厉害,节点上Pod的limits总和远远超过节点容量,关键时候就会触发节点压力驱逐,Pod不断被驱逐和重建,应用表现为间歇性不可用。
检查你的deployment资源配置,推荐一个工具:kubectl describe node,查看Allocated resources一栏,你的requests是否合理,一目了然。
4.3 镜像拉取失败和CrashLoopBackOff,这两类故障怎么优雅处理
在集群中遇到的最高频故障是ImagePullBackOff和CrashLoopBackOff。前者字面意思很直接:拉取镜像失败。常见原因包括:镜像地址写错、私有仓库需要认证、镜像Tag不存在、节点无法访问镜像仓库(网络/DNS问题)、镜像仓库限流。
最值得留意的是"镜像Tag不存在"。很多镜像仓库的latest标签是动的,但你的Deployment如果引用了某个不存在的tag(比如v1.0.0打成了v1.0),Pod就会一直ImagePullBackOff。排查方式很简单:先kubectl describe pod xxx看事件,再去节点上手动crictl pull试试,基本能确认问题。
CrashLoopBackOff则说明镜像能拉下来,但容器启动后马上退出,一直循环重启。排查思路:看一眼日志kubectl logs pod --previous,把上一次退出的日志拉出来看。常见原因包括:环境变量缺失导致启动失败、健康检查配置太严格导致探针未通过被kill、数据卷权限不足无法写文件、配置文件中某个必填项为空。
有个小技巧:如果CrashLoopBackOff是因为探针失败,你会看到事件里有Unhealthy记录,如果你的应用启动比较慢,initialDelaySeconds设太低就会误杀容器。我给建议是:对于大型Java应用,initialDelaySeconds至少给30秒以上,periodSeconds用10-15秒,给足启动时间。
4.4 节点NotReady和高可用设计,从节点角度看集群稳定性
节点NotReady是集群运维中最让人紧张的告警之一。kubectl get nodes看到NotReady状态时,常见的排查路径是:
- 先看kubelet状态:
systemctl status kubelet,确认它有没有在运行、有没有反复重启。 - 看kubelet日志:
journalctl -u kubelet -f,找error信息。 - 检查节点资源:
df -h看磁盘,内存不足或磁盘满会导致kubelet无法正常上报状态。 - 检查容器运行时:
systemctl status containerd确认容器运行时正常。 - 最后用
kubectl describe node NODE_NAME看Conditions字段,里面有明确的reason,比如KubeletNotReady、MemoryPressure或DiskPressure。
高可用层面,生产集群至少要做三件事:
- 控制面多副本:至少3个控制面节点,apiserver前面加负载均衡,etcd是集群模式,保证控制面不因单点故障而整体不可用。
- 工作节点预留系统资源:节点上除了跑业务Pod,还要跑kubelet、容器运行时、监控Agent,别把节点资源用满,留出系统余量。
- 合理配置Pod的topologySpreadConstraints和反亲和性,让同一应用的副本尽量分散在多个节点,避免一个节点宕机带走所有副本。
这里特别强调的是:"高可用"不等于"高配置",而是架构层面的冗余设计。你买一台128G内存的超级大机器,跑10个应用副本,远不如4台32G机器各跑2-3个副本更可靠。
5. 监控体系、GPU调度和Operator机制,让K8s为大模型应用赋能
5.1 Prometheus监控K8s,核心指标怎么看
集群已经稳定运行之后,接下来要做的就是监控。热词里提到"部署prometheus监控k8s",这是每个K8s集群的标配动作。
K8s官方推荐用Prometheus + Grafana组合来监控集群自身。需要采集的核心指标包括几类:
- 控制面指标:apiserver请求延迟、etcd的fsync耗时、scheduler调度失败次数。
- 节点指标:CPU使用率、内存使用率、磁盘IO和磁盘空间、网络流量。
- Pod指标:CPU/内存的实际使用量(对比requests和limits)、重启次数、就绪状态。
- 自定义业务指标:通过Prometheus client暴露自定义指标,比如HTTP请求数、队列深度、错误率。
部署Prometheus生态,很多人用Helm安装Prometheus Stack(以前叫kube-prometheus-stack),它会自动带上kube-state-metrics、node-exporter、alertmanager和Grafana。我建议保留几个核心的默认告警规则即可:比如KubePodCrashLooping、KubeDeploymentReplicasMismatch、KubeNodeNotReady、KubeCPUOvercommit等,这些告警基本覆盖了日常80%的问题。
一个常见的坑是:node-exporter的Pod被调度到了所有节点,但有些节点因为污点拉不起来,导致监控出现盲区。解决办法是把node-exporter的DaemonSet配置适当的tolerations,确保每个节点都能跑起来。
5.2 GPU调度入门,让K8s管理你的GPU资源
大模型火了之后,"k8s调用gpu"成了高热度搜索词。K8s通过device plugin机制来感知并调度GPU资源。NVIDIA官方提供了nvidia-device-plugin这个DaemonSet,运行在每个有GPU的节点上,把GPU资源以nvidia.com/gpu的形式上报给Kubelet。
部署方法很简单,创建DaemonSet,然后验证节点状态,如果GPU已经上报,节点上应该能看到类似nvidia.com/gpu: 8的可用资源。调度方面只需要在Pod的resources里请求:
resources: limits: nvidia.com/gpu: 1K8s就会自动把Pod调度到一个有GPU的节点上。注意requests和limits都要写,否则调度器可能无法正确评估。
有几个生产环境的实际经验值得分享:
- GPU节点建议打上专门的taint,避免无关的普通Pod调度上去。毕竟GPU资源贵,跑一些轻量业务是一种浪费。
- GPU节点的内存和CPU配置要高,大模型load进显存需要消耗不少CPU内存用于数据传输和框架运行。
- 如果GPU型号不同(比如A100和4090混用),建议通过label区分节点,配合nodeSelector做精细化调度,避免深度学习框架因为GPU能力不一致出现性能下降。
- 显存不够导致OOM时,容器会直接被杀,这种问题比较难排查,建议在Pod里加合适的健康探针,同时关注GPU相关监控。
5.3 Operator机制,用K8s的方式管理复杂应用生命周期
热词里有一条"k8s中operator案例",Operator是K8s很精华的扩展机制。简单理解,K8s的controller-manager已经帮你实现了Deployment、Service这些内置资源的管理逻辑,而Operator就是让你可以"自定义资源类型"并编写自己期望的管理逻辑。
典型案例:etcd-operator通过CRD定义EtcdCluster资源,用户在yaml里声明一个3副本的etcd集群,Operator收到这个资源事件后,自动去创建Pod、Service、维护成员关系,之后你改了副本数它也会自动执行扩缩容和故障恢复。
Operator的价值在于把"运维知识编码"——原本需要人工处理的备份、升级、扩容操作,写进程序里给机器执行,好处是减少人为失误,也让非资深工程师能维护复杂中间件。很多数据库、消息队列、日志系统都提供了Operator,比如Elasticsearch的ECK、Redis的redis-operator、RabbitMQ的Cluster Operator。
写一个完整Operator是很大的工程,需要Operator SDK(如kubebuilder)辅助。不过只要理解它的核心逻辑——Controller不断watch CRD对象,对比实际状态并执行"调和"操作,就能理解这套机制不难:本质上和Deployment管理Pod的逻辑是一样的,只不过管理目标从Pod变成了自定义资源。
6. 实用工具和工作流,日常操作和面试准备经验谈
6.1 日常高频命令,运维手边不能少的技能
K8s的使用核心是kubectl,但很多人的掌握程度还停留在get pods和describe。工作中实际高频用到的命令,我整理成了一张备忘清单:
# 查看Pod和节点 kubectl get pods -A -o wide kubectl get nodes -o wide # 进入Pod查看内部状态 kubectl exec -it pod-name -- bash # 查看应用日志,previous表示上一个已经退出的容器 kubectl logs pod-name --previous kubectl logs -f deployment/xxx # 端口转发到本地,临时调试很有用 kubectl port-forward pod-name 8080:80 # 查看资源编辑和导出 kubectl edit deployment/xxx kubectl get deployment xxx -o yaml > back.yaml # 异常事件排查 kubectl get events --sort-by=.lastTimestamp | tail -20另外一个很多人没充分利用的是kubectl get all -A,一条命令能列出所有Namespace下的Workload相关资源,排查问题的时候省很多事。配合-o wide可以看到Pod所在的节点和IP,合理使用这些参数能事半功倍。
6.2 学习路径和面试考察点
学习K8s,我建议的顺序是:先搭一个单节点或三节点集群(kubeadm装),把基本概念过一遍,然后部署几个常见应用(nginx、MySQL、Redis),用Service和Ingress把它们暴露出来,再做一些故障演练(杀掉Pod、清空节点数据),最后去了解控制器和调度的内部原理。光看不练不行,K8s的知识点只有亲手操作过才会有立体感。
面试题这块,K8s常考的点其实很集中:etcd的作用和数据一致性、Pod调度流程、StatefulSet和Deployment的区别、Service的负载均衡原理、Pod的生命周期、探针的类型和区别、PV/PVC的绑定机制、Namespace隔离和资源Quota。如果能结合项目谈谈实际遇到过的灾难和排查思路,面试官通常会更认可,因为这说明你是真正在生产环境踩过坑的。
版本选择上,新部署的集群建议用官方当前支持的稳定版本(比如1.27+),不要用太老的版本,也不要追最新的。升级要谨慎,通常先在测试集群验证,再逐批升级生产节点。K8s的兼容性窗口是三个小版本,你拖到第四个版本的时候,很多API已经废弃了,升级路径就会变得很痛苦。
6.3 关于自主可控和生态选择的一些思考
热词里出现"k8s是不是自主可控",这确实是很多团队选型时会被问到的问题。从技术本质看,K8s是一个Apache 2.0协议开源的云原生容器编排平台,源代码完全开放,任何人都可以审查、修改和分发。K8s的治理由CNCF(云原生计算基金会)主导,这个基金会本身是开放中立的组织,主导了Kubernetes的版本发布和生态发展。
自主可控的关键不在于"是不是某个国家开发",而在于你是否有源代码的完整掌握能力、是否有二次开发和持续运维的技术储备。K8s的核心代码和生态组件都是开源的,国内很多云厂商也深度参与并推动K8s的发展和迭代,大量国内企业基于K8s构建了自己的云原生平台。
实际使用中,完全可以在国内的基础设施环境里合规地部署和运营K8s集群,使用国内镜像源加速镜像拉取,搭配自研中间件和监控体系,形成一套自主可控、闭环管理的云原生技术栈。对于企业来说,相比争论"是否自主可控",更务实的做法是:评估自己团队对K8s源码和底层原理的掌握程度,建立与K8s版本匹配的内部知识库和运维体系,确保异构环境下的稳定运行。
7. 写在最后的一些实战体会
文章最后,聊几句掏心窝的话。
K8s真正难的地方,不是学概念,而是遇到问题时的第一反应。我第一次搭集群时,看到preflight失败,第一反应是去搜报错信息,然后逐个试网上给的命令,折腾了一晚上没搞定。后来才明白,与其头疼医头脚疼医脚,不如先把集群的组件逻辑摸透,理解每个报错背后的原因链,排查效率会提升一个量级。比如,preflight检查失败,先检查的不是某个具体配置项,而是先确认容器运行时版本、内核参数、cgroup驱动这些基础环境,排查路径就清晰很多。
再就是"最小化变更原则"。生产环境的集群,升级组件版本前一定要先在测试环境完整验证,不要图方便跳版本升级。K8s的组件之间版本耦合很紧,apiserver、kubelet、kubectl之间都有版本兼容窗口,强行升级某一个组件,很可能引发集群内部通信失败。
最后一个小技巧分享给你:多利用kubectl explain这个内置功能。很多人写yaml时记不住字段,全靠网上copy,但copy来的东西往往有版本差异或缩进问题。kubectl explain deployment.spec.template.spec.containers.resources会告诉你每个字段的语义和示例,在写配置的时候边查边写,比自己硬背效率高太多了。
K8s的学习没有捷径,但有一条更高效路径——先把一套集群真实跑起来,然后让问题引导你深入,一层层把网络、存储、调度、扩展都摸透。这套技术栈虽然复杂,但它解决的是分布式时代最核心的问题,值得投入时间。