这个系列写到第5篇后台留言明显分成了两拨一拨是刚上手k8s的新手还在纠结Pod和容器到底啥关系YAML文件里一堆字段看着就头疼另一拨是已经搭好集群、开始往生产环境推的老手天天在监控告警和故障排查里扑腾磁盘一告警就抓瞎。索性这一篇就把两条线都收进来从核心概念讲到部署落地再到日常运维和监控告警体系搭建一篇能当五篇用。不管你是准备面试、正在搭测试环境还是已经在维护线上集群这篇都值得存一下。我先把最容易被问懵的几个基础概念拆开讲清楚再带着你把单节点、集群、权限、监控挨个走一遍。全文涉及的所有命令和配置我都在实际环境里跑过不是那种抄官方文档的复读机内容放心往下看。1. 先把K8s的另一半讲明白Pod、Entrypoint、Cmd 到底怎么回事1.1 为什么非要搞个Pod出来直接跑容器不行吗很多新手对Pod的第一反应是Docker里我直接docker run nginx就完事了到了K8s里非要包一层Pod不是多此一举吗。我第一次也有这个疑问但真去用了才发现这一层抽象是K8s整个调度模型的地基。Pod是K8s里最小的调度和部署单元一个Pod里可以有一个或多个容器这些容器共享同一个网络命名空间、IPC命名空间还能通过共享卷交换数据。最典型的例子就是日志边车容器主容器写日志到共享卷sidecar容器去读取并转发两个容器通过localhost就能互相访问。这在纯Docker环境里要搞一堆网络配置才能实现在Pod里是天然支持的。还有个容易被忽略的点Pod是“瞬时的”。K8s里Pod是有生命周期的对象它的IP每次重建都可能变所以上层有Deployment、StatefulSet这些控制器来管理Pod的期望状态。你把Pod理解成“一组容器的调度单位”把Deployment理解成“管理Pod死活的管理员”这个认知框架就立住了。1.2 Entrypoint和Cmd这两个字段很多人用错坑了半夜说实话K8s里command和args这两个字段我见过太多人搞混了而且一错就是“容器启动即退出”这种经典故障。先记一个对应关系K8s的command对应Docker的ENTRYPOINTK8s的args对应Docker的CMD。在Dockerfile里正常会写两种风格一种是用ENTRYPOINT指定主程序、CMD传默认参数另一种是在CMD里写完整命令。到了K8s的YAML里如果你只写了command那Dockerfile里的ENTRYPOINT会被覆盖如果你只写args那只会覆盖Dockerfile里的CMD。关键区别在于ENTRYPOINT是容器启动后执行的程序本身CMD是传给这个程序的参数。所以很多人在K8s里写containers: - name: myapp image: myapp:latest command: [-c, echo hello]然后容器直接报错退出因为这里的command会被当成可执行文件去查找系统里根本没有名叫-c的程序。正确的做法是containers: - name: myapp image: myapp:latest command: [/bin/sh] args: [-c, echo hello]或者更符合直觉的写法如果镜像本身ENTRYPOINT已经写好了你只需要传参那就只用args不要去碰command。还有一个隐蔽细节如果镜像里有ENTRYPOINT你在K8s里只写了command那会连ENTRYPOINT一起覆盖如果希望追加参数而不是覆盖程序就必须只写args。我在排查类似故障时第一件事永远是kubectl get pod xxx -o yaml看最终的command字段到底变成了什么而不是猜。1.3 探针和资源限制新手最容易忽略的Pod配置再说两个和Pod紧密相关的配置探针和资源限制。探针分为存活探针livenessProbe、就绪探针readinessProbe和启动探针startupProbe。就绪探针决定流量是否打到这个Pod上存活探针决定Pod死了要不要重启。很多人图省事不写探针结果就是明明服务已经挂了Service还在往这个Pod转发流量用户一脸懵。资源限制就是requests和limits。requests是调度器为Pod预留的资源limits是Pod能用到的上限。如果只写limits不写requestsK8s会默认把requests设成和limits一样导致调度器认为每个Pod都会占用那么大资源集群里能塞的Pod数量大幅缩水。实践上我建议requests按真实使用量填limits留出一定余量两个都别空着。2. K8s和Docker的关系不是替代是各管一段2.1 从运行时讲清楚K8s并不直接管理Docker容器很多人都没意识到K8s在很早之前就支持多种容器运行时了通过CNI、CRI这些标准接口对接。Docker只是其中一种而且现在很多新集群默认用的是containerd连Docker都不装了。所以K8s和Docker的关系准确来说是“调度平台”和“容器运行时”的关系。K8s负责的是你的应用该在哪个节点上跑、跑几个副本、挂了怎么恢复、怎么滚动更新、怎么把流量均衡到各个Pod上。Docker负责的是镜像怎么构建、容器怎么启动、文件系统怎么隔离。理解了这个再看到kubectl get nodes里显示containerd://而不是docker://就完全不慌了。至于很多教程里用docker ps查看集群内容器这在新版集群里基本是看不到的因为Pod里的容器是由containerd起的管理你该用的是crictl ps或者kubectl系列命令。我见过不少老哥上生产环境后第一件事就是找Docker socket结果发现压根没装Docker。2.2 共享内核容器隔离的边界在哪还有一个必须说清的点Docker容器和虚拟机不一样它不是完整的操作系统而是共享宿主机内核通过名称空间和控制组做隔离。所以你在容器里跑uname -r看到的是宿主机的内核版本不是CentOS或Ubuntu的内核。这带来了两个实际影响。第一个是兼容性容器镜像里的二进制必须能在宿主机内核上运行所以你在镜像里用的glibc版本、依赖的内核模块都要心里有数。第二个是安全性既然共享内核内核漏洞就可能影响所有容器这也是K8s里要有PodSecurityPolicy现在叫Pod Security Admission这类安全机制的原因。我之前在项目里遇到一个服务在测试环境跑得好好的上生产就段错误排查了半天发现是生产内核版本太老镜像里用了比较新的系统调用。这种问题在虚拟机时代很少见但在容器环境下会频繁出现这也是“容器不等同于虚拟机”的活教材。2.3 生产环境两者是怎么配合的现在很多公司的实际架构是开发环境用Docker Compose快速起服务生产环境用K8s来编排。Docker负责开发时的一键启动和镜像构建K8s负责生产环境的多副本调度、自动伸缩、滚动更新。在CI/CD流水线里Docker构建出镜像推到私有仓库然后K8s的Deployment通过更新镜像版本来完成发布。这个链路里每个环节都有明确分工。我给团队的建议是不要想着用K8s完全替代Docker也不要以为有了Docker就不需要K8s它们本来就是不同层级的工具。3. 实操第一步单节点部署K8s新手最稳妥的上手路径3.1 环境准备先别急着敲命令把前置条件理顺新建一台干净的服务器或者虚拟机推荐Ubuntu 22.04 LTS2核4G起步磁盘至少20G。系统装好后先把三件事做了关闭swap、加载内核模块、配置系统参数。关闭swap的原因是Kubelet在默认配置下要求必须关闭swap才能正常工作否则节点会一直处于NotReady状态。执行swapoff -a只是临时关闭要持久化还需要注释掉/etc/fstab里的swap行。我见过太多人只执行了swapoff -a重启后又开始swap然后一脸迷茫地来问为什么K8s又挂了。内核模块方面需要加载overlay和br_netfiltermodprobe overlay modprobe br_netfilter然后创建/etc/sysctl.d/k8s.conf写入net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1执行sysctl --system让配置生效。这一步是为了让iptables能正确处理K8s网络转发流量不然后面部署网络插件会出一堆玄学问题。3.2 用kubeadm部署单节点集群的具体步骤环境准备好后开始装三个核心组件kubelet、kubeadm、kubectl。这里推荐直接配置阿里云镜像源或中科大镜像源在国内环境下速度快很多没必要在默认源上干等。apt-get update apt-get install -y apt-transport-https curl curl -fsSL https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - cat EOF /etc/apt/sources.list.d/kubernetes.list deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main EOF apt-get update apt-get install -y kubelet kubeadm kubectl注意装完后要把三个包的版本锁住防止升级导致集群不一致apt-mark hold kubelet kubeadm kubectl然后初始化控制面kubeadm init \ --apiserver-advertise-address192.168.1.10 \ --image-repository registry.aliyuncs.com/google_containers \ --pod-network-cidr10.244.0.0/16--pod-network-cidr的值要和后面用的网络插件匹配。我用的是Flannel它的默认网段是10.244.0.0/16保持一致能少踩很多坑。初始化完成后按提示执行mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config这里有个很多人忽略的点这个admin.conf就是你的集群管理员凭证放到家目录的.kube/config是为了让kubectl默认就能找到它。如果你换了台机器想管理这个集群把这个文件复制过去设置KUBECONFIG环境变量指向它就行。3.3 安装CNI网络插件并验证单节点集群控制面的污点还在得先安装网络插件再考虑调度Pod。装Flannelkubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml然后查看Pod状态kubectl get pods -n kube-flannel等所有Pod变成Running之后如果想让这个单节点也能跑应用要解除控制面的污点kubectl taint nodes --all node-role.kubernetes.io/control-plane-这样你的单节点集群就能正常调度业务Pod了。验证一下kubectl run nginx-test --imagenginx kubectl get pod如果状态是Running恭喜你的第一个K8s集群已经能干活了。4. 从单节点到多节点集群搭建和网络方案选型4.1 多节点集群里控制面和工作节点各干什么生产环境不可能只有一台机器肯定要拆成控制面节点和工作节点。控制面跑的是apiserver、scheduler、controller-manager、etcd这些组件工作节点只跑kubelet和kube-proxy外加一堆业务Pod。在物理拓扑上etcd最好单独部署或者至少放在独立节点上因为etcd是整个集群的“大脑”存储了所有集群状态。etcd一挂整个集群就只读了啥都改不了。我在一个同事那边见过etcd和业务Pod混布的结果业务Pod一通流量高峰直接把etcd的磁盘IO打满整个集群开始抖动恢复起来极其痛苦。4.2 多节点部署时的具体操作步骤控制面节点初始化完成后工作节点加入集群只需要两步。第一步拿到控制面的加入命令。在控制面节点上执行kubeadm token create --print-join-command这个命令会输出类似kubeadm join 192.168.1.10:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx第二步在工作节点上先把环境准备里的swap、内核模块、系统参数全部做一遍然后安装kubelet、kubeadm、kubectl最后执行刚才拿到的join命令。等节点加入后在控制面节点查看kubectl get nodes如果新节点状态是NotReady九成是网络插件问题或kubelet没起来。看kubelet日志的方式是journalctl -u kubelet -f这里再次强调工作节点不需要装kubectl也不需要有kubeconfig文件它只需要kubelet和kubeadm就够了kubelet会向apiserver注册自己并接收指令。4.3 网络插件的区别别随便选一个就上K8s本身不实现Pod间的网络互通需要第三方CNI插件来做。最主流的三选一插件优势劣势适合场景Flannel简单易用VXLAN模式稳定性能一般功能少学习环境、小规模集群Calico支持网络策略BGP模式性能好配置复杂对内核有要求生产环境、需要对Pod做网络隔离Cilium基于eBPF性能极强功能全学习曲线陡依赖较新内核大规模集群、对性能敏感我自己的经验是测试环境用Flannel图省心生产环境用Calico或Cilium尤其是对安全隔离有要求的场景Flannel默认是允许所有Pod互通的等于没有网络安全组。如果要用Calico初始化时--pod-network-cidr要改成192.168.0.0/16因为Calico默认用这个网段。4.4 搭建完集群后必做的检查清单很多人集群起来了就觉得完事了实际上隐藏问题一大堆。我每次搭完都要跑一遍这些检查kubectl get nodes所有节点Ready版本一致。kubectl get pods -A核心组件和网络插件都Running。kubectl describe node看节点上的资源压力和污点。部署一个测试Deployment然后手动删掉一个Pod看它会不会自动重建。滚动更新一个镜像版本看是否平滑过渡。这套检查跑下来集群才算真正可用。特别是最后两步能帮你提前暴露调度和控制器的问题而不是等业务上线了才发现。5. 日常运维必备常用命令全集和只读权限配置5.1 高频命令手册这些不用想顺手就敲出来很多人学K8s卡在记不住命令上其实核心就几十条。我按用途分类平时最常用的是这些查看类kubectl get nodes # 查看节点 kubectl get pods -A # 查看所有命名空间的Pod kubectl get deploy,svc,cm,secret -n xxx # 查看工作负载和配置 kubectl describe pod pod-name # 看Pod详细状态和事件 kubectl logs -f pod-name --tail100 # 看日志操作类kubectl apply -f xxx.yaml # 创建或更新资源 kubectl delete -f xxx.yaml # 删除资源 kubectl scale deploy xxx --replicas5 # 扩缩容 kubectl rollout restart deploy xxx # 重启Deployment kubectl exec -it pod-name -- /bin/sh # 进入容器 kubectl edit deploy xxx # 在线编辑配置排查类kubectl top node # 查看节点资源使用 kubectl top pod -A # 查看Pod资源使用 kubectl get events -n xxx --sort-by.lastTimestamp # 查看事件 kubectl api-resources # 查看所有资源类型个人习惯是把常用命令写进一个shell别名文件比如kgp代表kubectl get podskd代表kubectl describe用久了速度能快不少。5.2 只读权限用户配置别再给所有人都发管理员证书搜索热词里那句“k8s只开只读权限的用户”我猜肯定是有人拿着管理员权限被坑过了。K8s的权限控制走RBAC核心是Role、ClusterRole和RoleBinding、ClusterRoleBinding这四类对象。给一个用户配只读权限有三个步骤。第一步创建RBAC配置apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: readonly-role rules: - apiGroups: [] resources: [pods, pods/log, services, endpoints, configmaps, secrets, nodes, namespaces, events] verbs: [get, list, watch] - apiGroups: [apps] resources: [deployments, statefulsets, daemonsets, replicasets] verbs: [get, list, watch] - apiGroups: [metrics.k8s.io] resources: [pods] verbs: [get, list, watch]保存为readonly-clusterrole.yaml执行kubectl apply -f。第二步给这个用户或服务账户绑定apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: readonly-binding subjects: - kind: User name: zhangsan apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: readonly-role apiGroup: rbac.authorization.k8s.io第三步为用户生成凭证。最常见的做法是生成一个签名的证书或者直接用kubeconfig文件的token方式。简单起见可以先把管理员kubeconfig复制一份然后把user字段换成新用户并移除client-certificate-data和client-key-data中的管理员信息替换成新证书。但这一步操作稍繁琐更推荐的做法是用服务账户ServiceAccount加tokenkubectl create serviceaccount readonly-sa kubectl create token readonly-sa拿到token后放到kubeconfig里注意默认情况下ServiceAccount是无权限的所以要给它绑定刚才的只读ClusterRolekubectl apply -f readonly-binding-sa.yaml这个思路要记牢先建角色和权限规则再建绑定把角色和用户/服务账户关联起来最后分发凭证。顺序反了或者漏了绑定就会遇到“明明建了用户却什么都看不了”的问题。5.3 RBAC配置的几个坑第一个坑是apiGroups写错。比如deployments属于apps这个apiGroup如果你写成apiGroups: []那permissions会失效。我排查过一个同事的授权问题最后发现就是apiGroup空字符串和apps写混了。第二个坑是secrets资源的可见性。很多只读角色忘了加secrets结果用户连查看Secret里的配置都做不到。这在排查问题时会非常折腾因为某些控制器会把Secret内容编进Pod的环境变量里。第三个坑是RBAC的变更不是实时对所有会话生效的有时需要重新加载kubeconfig或新开一个会话才生效遇到权限“没变化”可以先考虑这个因素。6. 监控告警体系搭建node-exporter Prometheus Grafana 磁盘告警实战6.1 监控体系的分工采集、存储、展示、告警各司其职Google到“k8s监控告警体系中node-exporter,prometheus,grafana,磁盘告警规则是如何配置”的朋友大概率是想搞懂一条完整的监控链路。我直接把这条链路拆开讲。Prometheus负责抓取指标并存储它通过HTTP周期性访问各种exporter暴露的/metrics接口把数据存进自己的时序数据库。node-exporter是跑在每个节点上的DaemonSet专门负责采集节点的CPU、内存、磁盘、网络指标。Grafana负责从Prometheus读取数据把数据变成可视化面板同时也能配置告警规则。这四者配合起来才是一个完整的监控体系。6.2 用Helm一条命令部署监控全家桶手动一个个写YAML部署Prometheus全家桶太痛苦了我建议直接用Helm。先确保Helm已经装好helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update然后创建命名空间并一键部署kubectl create namespace monitoring helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring这个chart会把Prometheus、Grafana、node-exporter、kube-state-metrics、alertmanager全装进去版本之间都是匹配好的比自己手动拼省心太多。等Pod全部Running后看服务对应的端口kubectl get svc -n monitoring默认情况下是ClusterIP外部访问不到。测试环境可以直接把Grafana的Service改成NodePortkubectl edit svc prometheus-grafana -n monitoring把type: ClusterIP改成type: NodePort然后访问http://节点IP:NodePort默认账号密码是admin/prom-operator。生产环境建议用Ingress暴露并开启认证别裸奔。6.3 磁盘告警规则到底怎么写磁盘告警是运维里最刚需的告警之一磁盘一满各种服务就接二连三地挂。Prometheus里配置告警规则的思路是定义一条PromQL表达式当表达式查出来的值满足条件时就触发告警。kube-prometheus-stack自带的磁盘告警规则已经比较完整但如果要自定义在values里覆盖alertmanager和prometheus的rules即可。我先讲一下核心的PromQLgroups: - name: disk-alerts rules: - alert: NodeDiskUsageHigh expr: (1 - (node_filesystem_avail_bytes{fstype!~tmpfs|overlay|squashfs, mountpoint!~/var/lib/docker/containers} / node_filesystem_size_bytes{fstype!~tmpfs|overlay|squashfs, mountpoint!~/var/lib/docker/containers})) * 100 80 for: 5m labels: severity: warning annotations: summary: 节点 {{ $labels.instance }} 磁盘使用率超过 80% description: 当前使用率: {{ $value | humanizePercentage }}, 挂载点: {{ $labels.mountpoint }}这条规则的逻辑是先算出磁盘使用率(总容量 - 可用容量) / 总容量再乘以100得到百分数当超过80时触发告警。fstype的过滤是为了排除tmpfs和overlay这类虚拟文件系统for: 5m表示使用率持续超过80%达到5分钟才告警避免瞬时抖动导致误报。在实际配置时你可以把PromQL里的80改成自己业务能接受的阈值。我一般会把告警分两级使用率超过80%发Warning超过90%发Critical这样团队能有缓冲时间处理。配置方法如果你用Helm需要在values.yaml里通过additionalPrometheusRulesMap添加自定义规则。示例prometheus: prometheusSpec: additionalPrometheusRulesMap: custom-disk-alerts: groups: - name: disk-alerts rules: - alert: NodeDiskUsageHigh expr: ...然后升级releasehelm upgrade prometheus prometheus-community/kube-prometheus-stack -n monitoring -f values.yaml6.4 Grafana面板配置重点看这几个PanelGrafana装好后默认会自带一堆面板。我日常最常用的是“Kubernetes / Views / Nodes”这个面板可以看到集群整体资源使用情况。如果要自己做一个磁盘使用率面板只需要加一个Panel数据源选PrometheusQuery填1 - (node_filesystem_avail_bytes{mountpoint/} / node_filesystem_size_bytes{mountpoint/})Legends和Unit都配置成百分比。然后配置Alert条件当该值大于0.8时触发告警。这样不写PromQL规则也能在Grafana里直接收到告警通知。顺带说一句Grafana上面的Alert功能已经比较完善但生产环境我还是建议把告警规则放在Prometheus里统一管理Grafana只做展示因为Prometheus的Alertmanager能对接钉钉、飞书、邮件、Slack等一大堆通知渠道Grafana的通知渠道相对有限。6.5 磁盘告警踩过的几个坑第一个坑node-exporter采集的磁盘指标里有很多虚拟文件系统的挂载点比如/etc/hosts、/var/lib/kubelet这种如果不加mountpoint过滤会出现磁盘使用率虚高导致误报。我见过有人没过滤挂载点磁盘用了30%就开始疯狂告警。第二个坑默认的磁盘告警按“整个节点”维度告警但很多时候一个节点上有多个数据盘某个盘满了其他盘还很空。建议把告警规则改成按挂载点分开统计比如用mountpoint/data单独对数据盘设阈值。第三个坑告警通知渠道没配置。规则写了、表达式也正确但Alertmanager没配通知一切等于白干。我建议在部署的时候就在values里把alertmanager.config里加好webhook或邮箱配置别等告警来了再补。7. 故障排查思路从Pod到节点的常见问题实录7.1 Pod一直Pending到底卡在哪Pod状态是Pending说明调度器还没把它放到某个节点上。最常见的三个原因节点资源不足、节点有污点、有resourceQuota限制或亲和规则。排查思路固定两步看事件和看资源。kubectl describe pod pod-name kubectl get nodes --show-labels kubectl describe node node-namedescribe里会直接写“0/3 nodes are available”并给出原因比如“Insufficient memory”或“node(s) had taint”。如果是资源不足就扩容或者删掉些无用Pod如果是污点问题要么解除污点要么给Pod加对应的容忍。还有一个容易被忽略的点是PVC挂载问题。如果Pod依赖的PVC绑定不上Pod也会一直Pending。这种错误通过事件和kubectl get pvc -A就能看到。7.2 CrashLoopBackOff容器反复重启这个报错应该是最常见的了。核心原因是容器启动后立刻退出K8s不断重启它每次重启间隔越来越长指数退避所以叫BackOff。排查步骤是看日志kubectl logs pod-name --previous。注意加--previous看上一次退出的日志因为当前容器可能已经重启没了。看事件kubectl describe pod pod-name里面会有 “Back-off restarting failed container” 的提示。判断退出码Exit Code 1通常是业务代码错误Exit Code 137是内存杀死OOMExit Code 126/127是命令不存在或无法执行。我把遇到过的情况列成速查表方便对照退出码大概率原因解决方向1业务代码异常、StartupProbe失败去日志看异常栈137OOM或被杀死调大limits看代码内存使用126权限不够或文件格式不对检查二进制是否有执行权限、入口是否正确127命令不存在检查command和镜像里的路径130被信号终止排查是否被手动kill或oom143SIGTERM可能是优雅终止流程卡住7.3 节点状态NotReady从kubelet开始排查节点NotReady问题大概率出在节点上的kubelet或运行时。第一步永远是去看kubelet日志journalctl -u kubelet -n 100 --no-pager常见的NotReady原因包括swap没关、网络插件没生效、kubelet证书过期、容器运行时异常。另外还有一个高频原因节点资源耗尽比如磁盘满了导致kubelet无法上报心跳。如果kubelet日志看不清可以查看状态systemctl status kubelet如果是证书过期日志里会出现类似“certificate has expired”的字眼解决方式是执行kubeadm certs renew all或者直接重新加入集群。7.4 服务间访问超时或连接失败Pod起来了节点也Ready了但服务访问就是不通。先判断是集群内部访问还是外部访问问题。如果是集群内部访问先看Service的Endpointskubectl get endpoints service-name如果Endpoints为空说明Service的selector和Pod标签对不上或者Pod不是Running状态。如果Endpoints正常再看kube-proxy的规则或者直接进入一个Pod ping一下ServiceIP和PodIP。如果是外部访问问题大概率是Service类型或Ingress配置错了。NodePort模式下要确认节点的安全组放行了对应端口Ingress模式下要确认Ingress Controller的Service已经暴露出来且DNS解析指向了正确的入口地址。这里有个很实用的经验排查网络问题时先在同一个命名空间里起一个临时测试Pod用kubectl run -it test --imagebusybox --rm进去测试连通性速度快而且不影响业务。8. 总结建议这套体系要掌握到什么程度这个系列如果是面试导向的我建议你重点梳理Pod的生命周期、Deployment的滚动更新策略、Service的ClusterIP转发原理、RBAC的模型、以及监控告警里PromQL的常见写法。这些都是面试官必问的高频点。如果是为了实际生产环境我的建议是先别追求大而全把基础链路跑通单节点搭建、多节点扩展、命名空间隔离、资源配额、监控告警闭环。每一块都真正亲手配过出了问题能独立排查再去看服务网格、多集群管理这些进阶方向。踩过几次坑之后我个人的体会是K8s上手最大的门槛不是命令记不住而是脑子里没有一个完整的心智模型。你不理解控制面和工作节点的关系不理解控制器如何通过etcd实现期望状态一遇到故障就会卡在“看日志→还是看不懂”的循环里。先把Pod、控制器、Service、RBAC、监控这几条主线打通后面的路就顺了。最后再分享一个小技巧每次部署完一个新功能都养成kubectl get events和kubectl describe的习惯很多问题会在事件里提前暴露而不是等你用户来报了才发现。K8s这套系统本身就是一个巨大的状态机学会读懂它的状态变化比背任何命令都值钱。