1. 项目背景与学习动机1.1 为什么要折腾Kubernetes先说说我自己的情况。工作里接触微服务架构快三年了一直用的是传统虚拟机加部署脚本的方式一台机器一个服务Nginx做路由Supervisor管进程出了问题SSH上去看日志。这套方案在小规模时其实够用但服务一多就开始抓狂——十几个服务要逐个发版、回滚、扩缩容每次发版都像拆炸弹。后来接触到容器化Docker Compose把多服务编排变成了YAML文件确实省了不少事。可Compose有个致命短板它只适用于单机。一旦流量上来需要横向扩展或者机器挂了需要自动迁移服务Compose就无能为力了。Kubernetes简称K8s这套容器编排系统本质上就是解决很多台机器上的很多个容器如何统一调度、管理、故障恢复的问题。它把数据中心变成了一台逻辑上的巨型计算机你只需要声明想要的最终状态剩下的事它自己搞定——扩容、自愈、滚动更新、服务发现全部自动完成。这一点在做业务支撑平台时价值极大。1.2 这次学习记录适合谁参考这篇学习记录是我自己从零开始、边学边踩坑的过程整理主要面向以下读者有一定Docker基础知道docker run和docker-compose怎么用但没系统学过K8s的后端开发或运维人员已经看过官方文档但觉得抽象、不知道从哪下手的初学者正在做技术选型想评估Kubernetes能解决什么问题的架构师或技术负责人我会把核心概念、实操步骤、排错经验放在一起讲尽量用大白话解释原理。如果你对Pod、Deployment这些名词还比较陌生这篇文章可以帮你把碎片知识串成体系。2. 核心架构与概念拆解2.1 先从控制平面和工作节点说起Kubernetes集群从逻辑上分为两大部分控制平面Control Plane和工作节点Node。控制平面是集群的大脑负责做出全局决策工作节点是手脚真正跑容器的地方。控制平面里最重要的组件是kube-apiserver所有操作请求都经过它它是集群的总入口etcd负责存储集群所有状态数据相当于记忆中枢kube-scheduler负责决定新Pod调度到哪个节点kube-controller-manager负责运行各种控制器确保集群状态与用户声明一致。工作节点上则运行着kubelet接收并执行控制平面的指令、kube-proxy处理网络规则、负载均衡和容器运行时如containerd、Docker。把抽象概念做个类比控制平面就像公司总部制定战略、发布任务工作节点像一线员工执行具体工作并反馈状态。总部挂了公司瘫痪员工挂了总部会调度新人顶上——这就是Kubernetes自愈能力的来源。2.2 核心API对象Pod、Deployment、Service学习K8s绕不开这些核心API对象我把它们分为三组理解PodKubernetes最小的调度单位。一个Pod可以包含一个或多个容器这些容器共享网络命名空间和存储卷。比如业务容器加Sidecar日志采集容器就可以放在同一个Pod里它们天然共享localhost通信。Deployment无状态应用的控制器。定义了你想要多少个副本replicas、用什么镜像、如何升级。Deployment会创建ReplicaSetReplicaSet负责维持指定数量的Pod副本。升级时它按策略滚动替换不会让服务中断。ServicePod的访问入口。Pod的IP是临时的重建就会变化Service提供了一组稳定的VIP虚拟IP通过标签选择器筛选后端Pod对外暴露服务。Service类型有ClusterIP集群内部访问、NodePort节点端口访问、LoadBalancer云厂商负载均衡。这三者的协作关系可以这样理解Deployment负责养PodPod负责跑业务Service负责让别人能找到Pod。缺了任何一个集群都不完整。2.3 网络的三个层次Kubernetes网络是一个让新手最容易晕的部分因为它涉及三个层次的通信Pod与Pod之间集群内所有Pod可以互相直接通过IP访问不管它们在哪个节点上。这是通过CNI插件如Calico、Flannel、Cilium实现的覆盖网络。Service与Pod之间Service通过iptables或IPVS规则把流量转发到后端Pod。外部访问集群通过NodePort或Ingress暴露服务。我建议初学阶段不要深挖底层实现先用任何Pod之间都能互通、Service做了一层稳定VIP转发这个认知打底等实操熟练后再去看CNI源码或抓包验证。2.4 配置与密文的两种姿势有状态信息不能写死在镜像里Kubernetes提供了ConfigMap和Secret。ConfigMap用来保存普通配置比如Nginx的nginx.conf、Java应用的application.ymlSecret用来保存敏感信息比如数据库密码、API密钥它会以Base64编码存储注意Base64不是加密只是编码真正的安全要靠RBAC和加密存储。使用时有两种挂载方式环境变量注入和卷挂载。环境变量适合少量配置卷挂载适合配置文件整个替换。我的经验是尽量使用卷挂载方式因为配置更新后只需要让Pod滚动重启即可生效而环境变量方式一旦注入就无法热更新。3. 环境搭建与实操过程3.1 本地环境选择Minikube还是Kubeadm初学阶段不建议直接在云上搞生产集群本地环境用Minikube最省心。Minikube会帮你把控制平面和节点都跑在一个虚拟机或容器里一条命令启动适合学习和开发调试。我用的是Docker驱动方式# 安装minikube后启动 minikube start --driverdocker --cpus4 --memory8g如果电脑配置不够可以减少CPU和内存参数。minikube status可以查看状态。生产环境或想要更真实的集群体验选择kubeadm。它可以把多个机器初始化为集群过程更加原汁原味。初始化命令# 在Master节点执行 kubeadm init --pod-network-cidr10.244.0.0/16这里指定了Pod网段后面安装Flannel或Calico网络插件时要保持一致。初始化成功后kubeadm会输出一段kubeadm join命令工作节点执行它就能加入集群。3.2 第一个应用是怎么跑起来的用Minikube启动集群后我部署了第一个Nginx应用来验证整体流程。先创建Deployment YAML文件apiVersion: apps/v1 kind: Deployment metadata: name: nginx-demo spec: replicas: 2 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-deployment.yaml后Kubernetes会做几件事API Server校验配置Scheduler挑选合适节点kubelet在节点上拉镜像并启动PodReplicaSet持续监控Pod数量。查看状态kubectl get pods -o wide kubectl describe pod nginx-demo-xxxxxdescribe是排错最常用的命令它会显示Pod生命周期事件比如镜像拉取失败、健康检查不通过等。3.3 暴露服务的两种方式Deployment部署完成后Pod有自己的IP但外部访问不了。我创建了一个ServiceapiVersion: v1 kind: Service metadata: name: nginx-service spec: type: NodePort selector: app: nginx-demo ports: - port: 80 targetPort: 80 nodePort: 30080port是Service暴露的端口targetPort是后端Pod的容器端口nodePort是每个节点上对外提供访问的端口。执行kubectl apply -f后通过minikube service nginx-service就能自动打开浏览器访问。如果想要统一的入口管理多个服务还需要Ingress。Ingress不是Service的一种类型而是一个独立的API对象它通过Ingress Controller比如Nginx Ingress实现七层路由。请求先到Ingress Controller再按域名或路径转发到不同Service。3.4 存储有状态数据从emptyDir到PVC无状态应用好部署但碰到数据库、消息队列这类有状态应用就必须考虑持久化存储。Kubernetes提供了Volume和PersistentVolumeClaimPVC两件套。Volume描述存储的具体实现方式比如emptyDir临时目录Pod删除则数据丢失、hostPath节点本地路径、云厂商磁盘等。PVC是存储的声明它像申请资源一样申请存储而不关心底层具体是什么。我实际测试了一个MySQL的持久化存储配置apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi创建PVC后如果集群里有可用的PersistentVolumePV它们会自动绑定。在Deployment的Pod模板中引用这个PVCspec: containers: - name: mysql image: mysql:8.0 volumeMounts: - name: mysql-data mountPath: /var/lib/mysql volumes: - name: mysql-data persistentVolumeClaim: claimName: mysql-pvc这样重启Pod后数据也不会丢。注意accessModes要仔细选择ReadWriteOnce表示单节点读写ReadWriteMany表示多节点共享读写用错了挂载会失败。3.5 使用Helm简化重复部署手动写YAML文件在简单场景下没问题但服务一多就出现大量重复。Helm是Kubernetes的包管理工具类似于Linux的apt或yum。Helm的基本概念有Chart打包好的应用模板、ReleaseChart的部署实例。一个Chart的目录结构大致是myapp/ ├── Chart.yaml ├── values.yaml ├── templates/ │ ├── deployment.yaml │ ├── service.yaml │ └── _helpers.tplvalues.yaml里定义可配置的参数模板文件里用{{ .Values.replicaCount }}这样的占位符引用。helm install myapp ./myapp就能把整套资源部署到集群helm upgrade myapp ./myapp可以更新配置。Helm的大厂发布价值在于版本回滚。helm history myapp可以查看版本记录helm rollback myapp 1一键回到旧版本这比手动逐个删除Deployment再重建高效得多。4. 实战排错我踩过的那些坑4.1 Pod一直Pending刚启动集群后部署应用发现Pod卡在Pending状态。kubectl describe pod看到的事件是0/1 nodes are available: 1 Insufficient cpu或FailedScheduling。排查思路检查节点资源kubectl top nodes看CPU和内存是否够用检查是否有资源请求配置过大Deployment里resources.requests如果写得比节点实际可用还大就永远调度不上去检查节点是否有污点Taintkubectl describe node看Taints如果节点有Master角色默认污点普通Pod就不会调度上去我那次是给节点加了污点之后忘记在Pod里加容忍Toleration去掉污点或者加上容忍就好了。4.2 CrashLoopBackOff容器反复崩溃排在第一高频的报错就是CrashLoopBackOff。这个状态表示容器启动后崩溃然后kubelet不断重启它但每次都失败。常见原因镜像CMD或ENTRYPOINT写错容器根本起不来启动命令依赖外部服务而那个服务还没就绪探针配置太严格健康检查超时被误杀配置文件挂载路径错误容器内找不到配置排查方法按照这个顺序来kubectl logs pod-name看容器日志这是最直接的线索kubectl describe pod pod-name看Last State的Exit Code退出码137是被杀1是程序错误如果日志为空可能是探针问题查看Liveness和Readiness策略我遇到过一次很隐蔽的情况容器内服务监听在8080端口但探针检查的是9090端口导致Readiness探针一直失败Pod进入NotReady状态。这提醒我配置探针前一定要确认应用实际监听端口。4.3 镜像拉取超时或失败ImagePullBackOff是另一个高频问题。国内网络环境拉取国外镜像源经常超时这是最让人头疼的。实际解决办法检查image字段是否写错最常见的是版本tag不存在确认私有仓库认证是否存在kubectl create secret docker-registry创建拉取凭证并在Deployment的imagePullSecrets中引用换镜像源比如把k8s.gcr.io替换为registry.cn-hangzhou.aliyuncs.com等国内可用源或者配置镜像加速器这里强调一点不要在生产环境用latest标签。latest会在每次拉取时重新解析万一镜像仓库被意外覆盖你再部署就可能拿到不确定的版本导致环境无法复现。4.4 Service连不上但Pod是Running的Pod明明Running日志也没有异常但通过Service访问就是不通。逐步排查工具列表场景命令说明查看Endpointskubectl get endpoints如果Endpoints为空说明标签选择器没匹配到Pod查看Service详情kubectl describe svc确认端口映射和目标端口是否正确进入Pod测试kubectl exec -it pod -- curl svc-name验证服务名解析和网络连通性查看CoreDNS状态kubectl get pods -n kube-system -l k8s-appkube-dnsDNS故障会导致Service域名解析失败有一次我改了Pod标签但忘了改Service的Selector导致Endpoints列表为空。这个坑非常常见改标签时一定要全局搜索关联的Service。4.5 证书过期与节点失联如果集群搭建时间较久可能出现Unable to connect to the server: x509: certificate has expired的错误。K8s各组件的客户端证书有效期默认是一年到期不续就断连。处理方案# 备份并更新证书 kubeadm certs renew all # 重启相关服务 systemctl restart kubelet如果证书已经彻底过期可以查看当前证书过期时间kubeadm certs check-expiration。这提醒我们集群搭建后最好配置好证书续期机制或者提前写入监控告警。4.6 Namespace资源被占满当kubectl get pods报错Failed to create pod sandbox或Namespace处于Terminating状态却一直删不掉通常与命名空间泄漏有关。删除卡住的Namespace可以强制清理kubectl delete namespace my-namespace --force --grace-period0但更关键的是要找到为什么卡住。多数情况下是因为Namespace里有资源没被彻底删除或者API Server的finalizers无法完成。查看详细信息kubectl get namespace my-namespace -o yaml观察status.conditions。5. 学习路线规划与心得建议5.1 我的学习资料搭配方案学Kubernetes最忌讳抱着官方文档从第一章读到最后一章资料太多太杂读到后面前面就忘了。我的搭配方案是入门书Kubernetes权威指南人民邮电用来查概念不要通读按需翻阅免费视频B站或YouTube上的Kubernetes基础实战课程看一遍视频胜过阅读两章文档官方文档主要用来查API参数和常用命令用法实操复盘把每次部署、每次排错都记录成笔记积累一段时间就是独家财富最关键的一点是必须在本地亲手敲一遍部署流程把Pod、Deployment、Service、Ingress、ConfigMap、PVC都完整跑一遍这样对概念的理解才会从听过变成会用。5.2 学完基础后往哪个方向深入基础可用之后再往几个方向进阶服务网格Istio、Linkerd解决服务之间的流量治理、可观测性、安全通信问题多集群管理Federation或KubeEdge管理跨区域的多套集群Operator模式把运维知识编码到Kubernetes中比如数据库Operator、消息队列Operator云原生应用开发结合Service Mesh、CI/CD、GitOpsArgoCD、Flux让应用和发布流程完全自动化Kubernetes运行时底层研究容器运行时、CRI-O、containerd深入理解容器隔离原理我的建议是按自己的业务场景选方向。做平台类工作优先学Operator和Helm封装做业务架构优先学网关与流量治理做一身运维多关注安全策略和集群高可用。5.3 这份学习记录后续怎么扩展学Kubernetes不能停在会部署一个Nginx的程度。整个技术栈其实像一张网把知识点连起来才能看到全貌。我给自己规划的后续路线是先用Kubeadm搭一个三节点集群把网络插件、Dashboard、监控Prometheus Grafana都装上至少保证集群运行一个月不出问题。然后开始在集群里部署一套完整的业务系统包括前端、后端、数据库、缓存、消息队列配合GitLab CI打通从代码提交到自动发布的全流程。在这个过程里持续记录配置文件和排错案例最终沉淀成一份属于自己的云原生运维手册。5.4 一件值得坚持的小事最后分享一个我坚持下来的习惯每次踩坑后不管多小的问题都把它记录成一条经验卡片。卡片写三个部分问题的表象、排查思路、最终的解决办法。不需要长篇大论能让自己一个月后看懂就行。这个习惯最大的好处是三个月后你再遇到同样的问题花在回忆上的时间几乎是零。技术能力不是学会多少命令而是遇到未知问题时能否快速定位和解决。这些经验卡片就是解决问题最快的索引。