K8S作业实战:从零搭建Kubernetes集群到部署应用全记录
发布时间:2026/9/16 4:28:44 作者:尧图编辑部 阅读量:1,286

我最近刚完成一份K8S作业不是那种随便跑个nginx就交差的应付活儿而是从零开始搭了一套完整的Kubernetes集群再往集群里部署业务应用最后把服务暴露出去供外部访问。整套流程走下来对k8s的理解比只看文档强太多了。这篇就把我做这份k8s作业的全过程整理出来从环境准备、集群搭建、应用部署到问题排查每一步都写清楚为什么这么做、怎么做、踩过什么坑给正在学k8s或者准备动手搭集群的同学一份可以直接照着抄的参考。这份作业覆盖的内容比较全先搞清楚k8s和docker的区别然后从一台裸机开始初始化集群加入工作节点部署一个有状态应用配置Service和Ingress再调通配置管理最后还做了简单的滚动更新验证。适合刚学完k8s基础概念、想动手实践的人也适合准备k8s面试的人——很多面试题其实就是作业里要踩的坑你亲手处理过一轮印象会深得多。1. K8S作业的目标拆解与关键概念速览1.1 作业到底是什么从“跑起一个容器”到“编排一组服务”很多人学容器的时候觉得docker run一下就能起个服务挺简单。但K8S作业不是让你再跑一次docker run而是要求你理解当你有几十上百个容器需要同时管理时靠手工一个个起肯定不行得有一个东西帮你做自动化部署、自动扩缩容、故障自愈这就是Kubernetes存在的意义。这份作业本质上是在逼你完成一个思维转换——从单机视角切到集群视角。我把这次K8S作业拆成了几个递进的目标这样做的好处是每个阶段都有明确的验收标准不会学着学着就迷茫目标一搭起一个高可用的Kubernetes集群至少一主一从条件允许就三主多从目标二掌握核心工作负载的部署方式从手动Pod到Deployment管理副本目标三让集群内部的服务能被外部访问理解Service和Ingress的分工目标四把配置管理、存储挂载、滚动更新这些生产必备能力全部用一遍这四个目标做完k8s的常用操作你基本都过了一遍日常工作里绝大部分场景都不会发怵了。1.2 先搞懂k8s和docker的区别别被概念绕晕我刚开始学的时候k8s和docker的关系一直没搞明白总觉得这俩是不是竞争对手。实际上docker主要负责单个容器的生命周期管理而k8s是负责整个集群层面的编排调度。docker好比是你自己开一辆车k8s则是一个智能车队调度中心它管理的是车辆节点和车辆上的货物容器而不是亲自去造每一辆车。在这个作业里我选择用containerd而不是docker作为容器的运行引擎这里要特别说明一下。k8s从1.24版本开始已经移除对docker shim的原生支持虽然docker生成的镜像依然可以正常使用但kubelet不再直接通过docker来调用容器而是通过CRIContainer Runtime Interface接口对接。如果你还在用老教程里的docker作为runtime集群初始化的时候会多出不少麻烦。所以我的建议是直接用containerd架构更干净也符合当前的主流部署方式。1.3 学习路径规划把作业拆成三个里程碑k8s的学习曲线确实陡但拆开之后就能找到节奏。我把这份K8S作业规划成三个里程碑每个里程碑花一到两天时间整体一周左右做完。第一个里程碑是环境准备和集群初始化目标是让kubectl get nodes能看到节点全部Ready。第二个里程碑是核心概念的落地包括Pod、Deployment、Service、Ingress、ConfigMap、PV/PVC这些对象的实际使用。第三个里程碑是运维能力的初体验比如滚动更新、故障恢复、日志查看和资源监控。这种拆法很实用。里程碑之间是层层递进的关系前面搭好了后面才能继续而且每个节点都有“能看到的东西”作为反馈不会觉得枯燥。做作业的时候我遇到很多同学卡在某一步就放弃了大多数是因为一口气想吞完整个知识体系反而被复杂的概念劝退。按里程碑走至少你能不断获得“跑通了”的正反馈这是坚持学下去的重要动力。2. 环境准备与集群搭建实操2.1 硬件选型与系统配置按自己的资源条件来搭建k8s集群的第一步不是安装软件而是先把机器准备好。如果条件允许用云服务器实例最方便我这里用的是三台4C8G的云主机操作系统是Rocky Linux 10.2这是当前比较新的系统版本网上关于rocky10.2搭k8s的资料虽然不多但基本上按RHEL系的操作方法走没问题。如果你的机器配置没这么高2C4G也能玩只是别同时跑太多组件。这里有个选型建议学习阶段不要一上来就上生产级配置而是尽量接近真实环境。比如我就特别不建议把etcd、控制平面和业务应用全挤在单台机器上那样看起来省事但你会错过很多集群环境才会出现的问题。三台机器的分工是一台控制平面节点跑kube-apiserver、kube-controller-manager、kube-scheduler、etcd两台工作节点跑真正的业务容器。如果以后需要高可用再往控制平面节点后面加几台机器组成HA这是后话。系统层面的配置有几个关键点做作业的时候最好一开始就弄好关闭swap分区这一步很关键。kubelet默认要求swap必须关闭不然节点会报错或者启动失败加载br_netfilter模块并开启iptables对桥接流量的转发让集群内的网络策略正常工作同步系统时间集群节点之间时间漂移会导致证书校验失败修改主机名和hosts文件用主机名而不是IP来标识节点这些配置看似零碎但任何一个都会在后续步骤变成莫名其妙的问题。宁可前面花20分钟统一处理好也别等都搭完了再返工。2.2 安装containerd、kubeadm、kubelet与kubectl这一步是整个集群最核心的准备工作我把具体的操作步骤列出来你可以根据自己的系统版本微调版本号。先装容器运行时containerd。要注意的是如果你直接用系统的默认源拿到的containerd版本可能比较老建议配置好官方源或国内可用的镜像源再安装。安装完成后需要生成默认配置文件并修改sandbox_image参数把pause镜像地址改成国内能拉到的地址否则后面初始化集群时会卡在拉取pause镜像这一步。装好containerd之后继续安装kubeadm、kubelet和kubectl。这三个工具的分工是kubeadm负责把集群拉起来kubelet是运行在每个节点上的核心代理kubectl是跟集群交互的命令行工具。三个组件的版本必须保持一致否则会出现兼容性问题。我的建议是不要用默认最新版而是选一个稳定的版本线比如1.28或1.29这样后续查资料、找教程时对应内容更丰富也不会踩到太新的坑。2.3 初始化控制平面与加入工作节点逐步操作控制平面初始化是整个k8s作业里最让人紧张的一步其实只要前面环境配置好命令本身不复杂。核心命令是kubeadm init需要指定apiserver的地址控制平面节点的IP、Pod网段和Service网段。Pod网段我这里用了10.244.0.0/16Service网段用了10.96.0.0/12这两个网段要跟你选择的网络插件匹配比如Calico的话推荐用192.168.0.0/16Flannel的话用10.244.0.0/16比较常见。初始化成功之后kubeadm会输出一段token和哈希值这是工作节点加入集群时用的凭证一定记得保存。接着要按照输出提示配置kubectl的配置文件把admin.conf拷贝到当前用户的.kube目录下。配置完成后用kubectl get nodes看一下这时控制平面节点的状态应该是NotReady因为还没装网络插件这是正常的。工作节点加入集群倒是很省事在每台工作节点上执行kubeadm join命令把刚才保存的token和哈希值带上就行。加入成功的标志是看到“This node has joined the cluster”的提示。回到控制平面节点再执行kubectl get nodes应该能看到两台工作节点已经出现状态也还是NotReady。此时不要慌安装网络插件之后才会全部Ready。2.4 安装网络插件让Pod之间能互相通信k8s集群要正常工作Pod一定是需要跨节点通信的而Pod网络的实现靠网络插件CNI。我选择的是Calico因为它的功能全面、性能也稳定生产环境用得特别多。安装Calico的方式很简单执行kubectl apply -f calico.yaml就行但这个yaml文件里包含了CustomResourceDefinition和DaemonSet等一堆资源如果要调整网段得先把里面的IP池改成跟kubeadm init时指定的Pod网段一致否则数据包就会发错地方。网络插件装好后等待一两分钟让Pod慢慢拉起来再用kubectl get pods -n kube-system查看所有系统组件的运行状态。当看到coredns、calico-node这类Pod都是Running状态时再执行kubectl get nodes这时所有节点都变成Ready了。看到全绿色的那一刻整个集群就算真正跑起来了k8s作业里最辛苦的硬骨头也啃完了。3. 部署第一个业务应用理解核心工作负载3.1 从Pod开始手动创建你的第一个Pod集群就绪后先别急着上复杂的Deployment从最基础的Pod开始感受一下k8s是怎么调度容器的。我用kubectl run的方式快速起一个nginx Pod这个命令看起来跟docker run很像但背后的逻辑完全不同。kube-apiserver收到创建Pod的请求后会把这个期望状态写入etcdkube-scheduler发现有待调度的Pod就根据资源需求和节点状态选一个最合适的工作节点然后通知该节点上的kubelet去拉镜像、启动容器。等Pod跑起来之后kubectl get pods -o wide可以查看它被调度到了哪台节点并看到Pod的IP地址。这个IP是集群内部的虚拟IP你在宿主机上直接ping不通因为Pod网络跟宿主机网络不在一个网段这是很多初次接触k8s的人容易困惑的地方。要进入Pod排查问题用kubectl exec -it pod名 -- /bin/bash这就相当于进入了容器内部。还要说一下Pod回收的问题。我们用kubectl run直接创建的Pod如果手动删掉它真的就消失了不会自动重建。这暴露了手动管理Pod的局限性也是引入Deployment的契机——Deployment能够描述“这个Pod应该有几个副本”如果Pod挂了它会自动创建新的替代。3.2 用Deployment管理副本见证自愈能力Deployment是k8s最常用的工作负载它定义了期望的副本数、镜像版本、升级策略等信息。我写了一个最简单的Deployment yamlreplicas设为3镜像用nginx:1.26-alpine。apply上去之后kubectl get deployment会显示3/3的可用副本同时会看到ReplicaSet被自动创建出来——其实Deployment并不直接管理Pod而是通过ReplicaSet来管理Pod的副本数量。这里我想让你亲手做一个破坏性的实验随机删除一个Pod然后快速执行kubectl get pods观察变化。你会发现集群立刻新建了一个Pod来补齐副本数而且新Pod往往在几秒内就完成调度和启动。这就是k8s的自愈能力也是Deployment存在的意义。做完这个实验你就不会再把Pod和普通容器画等号了。Deployment还会自动记录发布历史你可以随时回滚到之前的版本。这个在作业里我会建议做一次演示把镜像版本从nginx:1.26改成nginx:1.25执行kubectl rollout status deployment/名称观察滚动更新过程再执行kubectl rollout undo把它回滚回去。看到旧版本Pod一个接一个地被新版本替换又不影响服务可用性你会对“滚动更新”这个词有非常直观的理解。3.3 Service与Ingress让外部流量进得来Pod默认只提供一个集群内部的IP且这个IP会随Pod重建而变化。要让外部访问到业务必须引入Service对象。Service相当于一组Pod的稳定访问入口它通过标签选择器找到背后的Pod并把请求负载均衡到这些Pod上。最常用的是ClusterIP类型的Service它提供一个集群内部可访问的虚拟IP如果想从集群外部访问可以改成NodePort类型在每个节点上开一个端口。我在作业里创建了一个Service类型先用ClusterIP。kubectl get service看到分配的ClusterIP之后在集群内部任意节点上用curl访问它都能拿到nginx的默认页面。这说明Service已经正确把请求转发到了后端多个Pod上面。NodePort类型的效果更直观你用宿主机IP加端口号就能从浏览器访问服务但NodePort有个明显的缺陷生产环境不太可能让用户通过IP加端口来访问服务这就要把Ingress请出来了。Ingress是七层负载均衡它工作在网络栈的应用层可以根据域名和路径把请求路由到不同Service。我部署了一个nginx-ingress-controller然后创建Ingress规则把myapp.example.com这个域名指向刚才创建的Service。这样我在本地电脑上把域名解析到集群节点的IP再用curl带上Host头访问就能通过Ingress访问到后端的nginx服务了。到这一步整个“从外部用户到Pod容器”的完整链路就跑通了Ingress - Service - Pod。3.4 配置管理与存储挂载让应用有配置也有数据业务应用没有一个不需要配置的数据库密码、API地址、日志级别这些不能写死在镜像里。k8s提供了ConfigMap和Secret两种配置资源。我在作业里创建了一个ConfigMap里面写了一个自定义的nginx配置文件然后通过volume的方式把它挂载到nginx Pod的 /etc/nginx/conf.d 目录下。改配置的时候不用重新构建镜像修改ConfigMap后Pod里的文件会同步更新虽然要等几秒这在管理多环境配置时非常高效。Secret的用法类似但它存储的是敏感信息比如密码、密钥。Secret在etcd里虽然也做了base64编码但严格来说生产环境还需要配合加密等额外手段学习阶段用默认的base64方式了解原理就可以了。写yaml文件的时候如果你的Label和容器的volume挂载路径不正确应用往往不会有明显报错而是运行后读取不到配置文件。这种软性问题排查起来比较花时间建议每加一个配置挂载就用kubectl exec进容器里确认一下文件是否真的出现在目标位置。存储这块也有很关键的概念要考虑。Pod是“用后即弃”的容器一删容器内部的文件就全没了。如果应用是数据库这类需要持久化数据的必须使用持久化存储。k8s里由PVPersistentVolume提供存储资源PVCPersistentVolumeClaim是应用对存储资源的使用请求。我在作业里用hostPath类型创建了一个PV把宿主机的一个目录作为存储然后创建一个PVC去绑定它最后在Deployment的yaml里声明挂载这个PVC。这样Pod在哪台节点上数据就会写到那台节点的对应目录里。不过hostPath只是单节点存储生产环境还是要用NFS、Ceph或者云厂商的块存储但原理是相通的。4. 作业中的避坑手册与排查技巧4.1 镜像拉取失败的几个常见原因做k8s作业大家几乎都会遇到ImagePullBackOff。这个报错看着吓人其实原因就那么几个逐个排查就好。最常见的是镜像地址拉不到特别是gcr.io、quay.io这些老外镜像源在国内直接访问不通尤其像Pause镜像这类基础设施容器kubelet会自己拉起你要是没改过配置就直接用默认地址初始化那一步就会卡住。这个坑可以直接避掉安装containerd后一定修改config.toml里的sandbox_image为国内可用的pause镜像地址否则后面会走弯路。另一种常见原因是镜像标签写错了。比如你输入nginx:latest可能由于语法或者对应平台架构不对节点拉不下来。查询的方法很简单kubectl describe pod pod名看Event里的具体报错信息如果显示manifest unknown或者not found多半是标签有问题如果是timeout那就是网络不通需要检查镜像源或者代理配置。记住看到报错先describe再查日志不要只盯着Pending发呆。4.2 Pod一直Pending怎么排查Pod如果一直处于Pending状态说明还没有被调度到任何节点上这时候kubectl get events能帮上大忙。最常见的是资源不足比如你设置了requests.cpu为2核但节点上没有那么多可用资源调度器只能干等着。解决方法是降低资源请求值或者增加节点也可以用kubectl describe node 节点名查看每个节点的资源分配情况。另外一种Pending原因是节点上的污点和Pod的容忍度不匹配。默认情况下控制平面节点是带有NoSchedule污点的普通Pod不会被调度上去。所以如果你想让某个Pod跑到控制平面节点上需要给Pod加对应的tolerations或者去掉节点的污点。实际操作中大部分人只在工作节点上跑业务这个了解即可。如果做了上述检查还是Pending别忘了看下有没有节点处于NotReady状态节点本身有问题的话Pod自然无处可去。4.3 CrashLoopBackOff的完整排查思路CrashLoopBackOff意思是Pod启动后就崩溃然后被kubelet反复重启而且每次重启的间隔会逐步拉长。排查这个问题的第一步是看Pod的日志kubectl logs pod名如果容器启动时执行过多个容器进程还需要指定容器名。如果日志是空的可以加上--previous参数查看上一次崩溃容器的日志很多应用在正常退出时会把错误信息写到标准错误流里不指定这个参数往往看不到。日志没问题的话继续检查配置和挂载。很多时候应用崩溃是因为启动时读取不到配置文件的某项参数而ConfigMap挂载路径不对或Secret没有正确注入导致应用初始化失败。还有一种冷门的可能性应用启动后会自动检测自身是否以root用户运行在k8s里默认的securityContext如果对用户权限做了限制也可能导致应用启动即退出。解决方法是调整securityContext或者使用镜像默认用户。CrashLoopBackOff的排查本质上就是一个层层逼近的过程日志优先配置其次最后看安全策略和资源限制。4.4 kubeadm init失败的正确处理方式kubeadm init失败的时候很多人第一反应是重装系统其实完全不用。kubeadm help里提供了reset命令执行kubeadm reset -f然后手动清理网络配置和iptables规则就能让环境恢复到可以重新初始化的状态。不过reset之后要留意之前生成的配置文件如果污染了当前环境比如/etc/kubernetes残留的证书文件可能会影响第二次初始化建议手动清理掉。init时常见的错误是端口被占用、swap未关闭、或者kubelet服务没有启动成功。端口被占用的话用ss或netstat排查是谁占用了443、6443等端口把无关进程停掉再试。swap未关闭时kubelet会直接启动失败用free -h检查一下然后swapoff -a并注释掉/etc/fstab里的swap行。还有一个老是踩坑的地方初始化时指定的apiserver-advertise-address必须是你当前机器上真实存在的IP否则apiserver监听失败初始化也会卡住。处理init失败不要怕心态放稳严格按照报错提示来八成都能自己解决。5. 面试与深化学习从做完作业到真正的理解5.1 高频面试题如何用作业成果去答k8s面试题网上有很多但如果你只是背题面试官几个追问就露馅了。这次作业做完之后我建议你把这几个高频问题用自己的语言重新组织一遍我会把怎么结合实操来说这个问题讲透。比如“k8s和docker的关系与区别是什么”千万别只回答“docker是容器引擎k8s是编排系统”。你可以说我用containerd作为运行时kubelet通过CRI接口跟containerd交互而不是直接通过docker。然后把docker类比成单机工具k8s类比成分布式操作系统这样面试官会认为你真的动手搭过集群。再比如“Pod和Deployment的关系”你可以直接说Pod是k8s的最小调度单位Deployment通过ReplicaSet管理Pod副本Deployment负责声明期望状态ReplicaSet负责控制实际状态向期望状态靠拢。然后把你做过删除Pod自动重建的实验讲出来用实际经历让答案更有画面感和说服力。还有“如何实现滚动更新和回滚”这个就更简单了直接把作业里那两条rollout命令说出来然后解释maxSurge和maxUnavailable两个参数是如何控制更新节奏的。这里的经验是如果这两个参数没设置好更新过程中会出现服务中断。当你能够回答“为什么要设置maxSurge为25%”而不是单纯背诵参数时面试官会很认可的。5.2 作业完成后还能扩展哪些方向集群跑通、应用部署成功这只是k8s学习的起点。做完作业之后你可以在这个基础上继续扩展让技能水平再上一个台阶。第一个扩展方向是HPAHorizontal Pod Autoscaler让Pod副本数根据CPU或内存使用率自动扩缩容。我自己做了个实验用压测工具给nginx施压然后观察HPA自动扩容Pod压力下降后再缩容。这个动态过程会让你对“弹性伸缩”有非常直观的掌握也是面试中竞争力很强的话题。第二个扩展方向是监控告警体系。k8s本身不提供完善的监控能力需要引入Prometheus和Grafana。把节点和Pod的指标采集进来做几个可视化面板再配一条告警规则这样你的运维闭环就成立了。尤其是“定位问题”的能力借助监控能看到CPU突刺、内存泄漏、网络延迟的走势排查故障会快很多。第三个扩展方向是高可用集群。现在这个集群只有一个控制平面节点如果这个节点挂了整个集群的控制面就不可用了。生产环境至少需要三个控制平面节点配合负载均衡器把流量分发到多个apiserver实现控制面的高可用。这个方向能加深你对etcd、kube-apiserver这些核心组件工作机制的理解是k8s进阶的重要一环。5.3 学习资源与实际建议关于k8s修炼市面上的资料鱼龙混杂我的建议是从官方文档和权威书籍入手不要东一榔头西一棒子地收集碎片知识。官方文档kubernetes.io的“概念”和“任务”两个栏目写得非常清楚适合当字典查想要系统梳理可以找一本口碑好、成体系的k8s图书比如《Kubernetes权威指南》从头到尾系统过一遍知识框架会更牢固。做作业的过程里我还有个体会不要贪多求快但也不要只停留在“能跑就行”的程度。每次操作完都问问自己“刚才发生了什么”“为什么这个报错会出现”然后用kubectl describe、kubectl logs、kubectl get events这些命令把过程还原出来。这种“知其所以然”的思维方式比敲再多命令都有价值。最后再分享一个小技巧建议把常用命令整理成自己的速查表比如查看节点状态、查看Pod分布、查看滚动更新进度、查看事件异常。这听起来很简单但真正用起来尤其是在排查故障的时候能节省大量时间。等你把这份K8S作业从头到尾做透你不仅仅是在完成一个任务你已经积累了一套可迁移到生产环境的运维方法论——这可能是这次作业最值得珍惜的收获。