K8S作业实战:从零搭建Kubernetes集群到部署应用全记录
2026/9/16 4:28:40 网站建设 项目流程

我最近刚完成一份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来调用容器,而是通过CRI(Container 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/16,Service网段用了10.96.0.0/12,这两个网段要跟你选择的网络插件匹配,比如Calico的话推荐用192.168.0.0/16,Flannel的话用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的请求后,会把这个期望状态写入etcd,kube-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 yaml,replicas设为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里由PV(PersistentVolume)提供存储资源,PVC(PersistentVolumeClaim)是应用对存储资源的使用请求。我在作业里用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学习的起点。做完作业之后,你可以在这个基础上继续扩展,让技能水平再上一个台阶。

第一个扩展方向是HPA(Horizontal 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作业从头到尾做透,你不仅仅是在完成一个任务,你已经积累了一套可迁移到生产环境的运维方法论——这可能是这次作业最值得珍惜的收获。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询