☰
Kubernetes入门实践:从零搭建集群到部署应用全流程指南
2026/10/21 4:30:49 网站建设 项目流程

直接说结论:如果你打算花一个月时间从零开始接触 Kubernetes,这一篇“kubernetes 入门实践”就是你抄作业用的。我不会跟你扯复杂的架构图,也不会上来就让你背一大堆组件名称,我会用一台普通笔记本、几个命令行操作,把 Kubernetes 的核心玩法串一遍:集群怎么搭、应用怎么部署、服务怎么暴露、配置和存储怎么挂、出现问题怎么查。适合刚学完 Docker、知道容器是什么、但打开 Kubernetes 官方文档就头晕的人,也适合那些已经被各种“云原生”概念轰炸、想亲手跑一遍看个究竟的人。

我标注一下这篇实践的性质:它不是面试八股,也不是生产环境运维手册。它解决的是“从零到一”的体验问题。你跟着做完,会在脑子里形成一张清晰的地图:API 对象负责描述状态,控制循环负责纠正状态,网络和存储负责提供支撑。等这张地图建立起来,再去看生产方案就顺很多。

1. 入门 Kubernetes 前,先把要解决的问题想清楚

1.1 容器编排到底在编排什么

很多人一上来就背概念:Kubernetes 是容器编排平台。但“编排”这个词太空了,我换个说法——它帮你回答三个问题:

  • 容器跑在哪台机器上?
  • 如果容器挂了,谁来重新拉起它?
  • 别人怎么访问这一堆容器?

这三个问题,用 Docker 单机其实也能部分解决,比如docker run --restart=always能自动重启,docker-compose能编排多容器,端口映射能暴露服务。但一旦机器数量变多,容器数量变成几十上百个,单机的方案就不成立了。你需要一个东西能横跨多台机器,统一调度、统一维护状态,Kubernetes 就是干这个的。

Kubernetes 解决问题的核心思想是“声明式”的。什么意思?你不是告诉它“帮我启动 3 个 nginx 容器,然后运行更新命令”,而是告诉它“我要 3 个副本的 nginx”,剩下的事情它自己想办法:不够就补,多了就删,节点挂了就在别的节点重建。你描述“想要什么”,而不是“怎么做”,这就是声明式。

我第一次接触这个思想时有点不适应,总觉得事情失控了。后来想通了:Kubernetes 就像一个非常靠谱的物业经理。你对他说“这个小区要有 10 户人家住着”,他不会跟你讨论每家水电怎么走、门锁怎么装,他只保证最终有 10 户人住着,哪家搬走了,他会自动安排新住户进来。你要做的是把“需求”写清楚,而不是亲自上楼挨家敲门。

1.2 本地环境选型:从最小的闭环开始

入门实践最大的障碍是环境。网上教程动辄要求你准备三台虚拟机、四核八G、配置一堆网络插件,还没开始跑就已经劝退了。我的建议是:本地学习阶段,不要追求“生产级”,要追求“最小闭环”。

我推荐用 minikube,它是官方提供的单机 Kubernetes 集群工具。在一台笔记本上就能把完整的控制平面和一个工作节点跑起来,镜像拉取、Pod 调度、Service 暴露这些核心流程全都有。相比另外两个常见选项:

  • kind:全称是 Kubernetes in Docker,它的节点本身就是容器,启动很快,适合做 CI 场景的测试集群。
  • k3s:面向边缘和资源受限环境,安装很轻,但是一个单二进制,和标准发行版的差异会让新手产生困惑。

所以我的结论很直白:本地入门,优先 minikube。等你想反复演练集群升级、节点故障这类场景时,再考虑 kind,不迟。

选择 minikube 不是因为它功能最强,而是因为它最接近“真实集群”的体验:它有一个真正的kubectl上下文,有minikube start这样明确的操作边界,网络模式也是走标准 Service 抽象,不是另外一套教学语法。起点贴近实战,后面迁移到多节点集群时,你学到的东西几乎不用推翻重来。

2. 用一台机器搭出一个可用的 Kubernetes 集群

2.1 环境准备与集群启动

动手之前,确保你的机器满足三个条件:

  • Docker 已经安装并能正常使用,docker info不报错。
  • kubectl 已安装,版本最好不要和集群差太大,用kubectl version --client查看。
  • 机器内存至少 4G,推荐 8G。minikube 默认分配给集群的资源和 CPU 可以调整。

条件准备好了,直接执行:

minikube start --driver=docker --cpus=2 --memory=4096

这里我指定了 docker 驱动,意思是让 minikube 通过 Docker 创建节点容器。参数--cpus=2 --memory=4096是给集群分配的资源配额。如果你机器配置紧张,改成--cpus=1 --memory=2048也能跑,但后面编排两个应用时可能会卡。

启动过程中,minikube 会下载 K8s 组件镜像,耗时取决于网络状况。第一次启动多花点时间很正常,别急着 Ctrl+C。等出现kubectl is now configured to use "minikube",说明成功了。

验证一下:

kubectl get nodes

正常情况下,你会看到名称为minikube的节点,状态为Ready。看到Ready这个单词,意味着控制平面和节点间的通信是通的。这一点非常重要,后面所有问题排查的第一步基本都是先看节点状态。

2.2 把第一个 Pod 跑起来

集群起来了,但它是空的。我建议第一次部署不要用 YAML,先用命令式操作感受一下,等理解了对象之间的关系再看 YAML。

执行:

kubectl create deployment my-nginx --image=nginx:1.25

这条命令的含义是:创建一个 Deployment 对象,它管理的容器镜像为nginx:1.25。执行完后,Kubernetes 后台会自动创建 ReplicaSet,再由 ReplicaSet 创建出 Pod。

此时你可能会问:我明明只输入了一条指令,怎么冒出来三个对象?这里就要建立一个基本认知——Deployment 是一个“多级管家”,它不直接管容器,而是管一组 Pod 的“设计图纸”。Kubernetes 的原生工作方式就是一个对象管另一个对象:

  • Deployment 管理 ReplicaSet
  • ReplicaSet 管理 Pod
  • Pod 才是真正运行容器的载体

看下状态:

kubectl get deployment my-nginx kubectl get pods

当你看到 Pod 的状态从ContainerCreating变成Running,第一个容器就跑起来了。这个过程其实包含了镜像拉取、容器创建、健康检查等多个环节,但这里你只需要记住一句话:Pod 的Running不代表应用已经服务正常,只代表容器进程活着。

2.3 Deployment、Pod、Service 三者的关系

现在有一个容器在跑,但我打赌你还访问不到它。因为 Pod 的 IP 属于集群内部网络,宿主机上访问不了。为了让它对外提供服务,还需要引入 Service。

执行:

kubectl expose deployment my-nginx --port=80 --type=NodePort

然后:

kubectl get svc my-nginx

你会看到 Service 分配了一个 ClusterIP,还有一个端口范围在 30000-32767 之间的 NodePort。访问方式:

minikube service my-nginx --url

它会返回一个类似http://192.168.49.2:31876的地址,浏览器打开这个地址,看到 nginx 欢迎页,说明一条完整的访问链路已经打通。

你看整个链路是这四步:

  1. Pod 在集群里运行,内部 IP 是 10.x.x.x。
  2. Deployment 保证 Pod 数量始终等于期望值。
  3. Service 选择一组 Pod,提供一个稳定的访问入口。
  4. NodePort 把 Service 的端口映射到宿主机上,让你能从外部访问。

这个过程中,Service 的选择机制是靠 Label 实现的。Deployment 创建 Pod 时会给它打标签,Service 通过selector匹配标签,从而把流量转发到对应的 Pod 上。这是 Kubernetes 里一个非常基础但非常高级的设计思想:对象之间不直接引用对方的名字,而是通过标签这种轻量级的标识来建立关系。好处是解耦,坏处是新手容易踩坑——标签写错了,Service 就选不中 Pod,流量自然不通。

3. 用 YAML 写好你的第一个应用部署

3.1 Deployment 清单的核心字段

命令式操作适合体验,但真正要进入实践,必须学会写 YAML。原因很简单:命令操作是不可复现的,它记录在你的 shell 历史里,却不在项目里;而 YAML 文件可以提交到代码仓库,可以评审,可以重复执行。

一个最精简的 Deployment YAML 长这样:

apiVersion: apps/v1 kind: Deployment metadata: name: web-demo spec: replicas: 3 selector: matchLabels: app: web-demo template: metadata: labels: app: web-demo spec: containers: - name: web-demo image: nginx:1.25 ports: - containerPort: 80

执行起来:

kubectl apply -f deployment.yaml

这个文件里有几个字段,新手经常犯迷糊,我逐个说一下我的理解:

  • apiVersion:对象的 API 版本。不同对象版本不同,apps/v1是工作负载对象的稳定版本。
  • metadata.name:Deployment 的名称,集群内唯一。
  • spec.replicas:期望副本数,这里写 3。
  • spec.selector:Deployment 如何找到自己要管理的 Pod。这里的matchLabels必须和 template 中的 labels 匹配。
  • spec.template:Pod 模板,描述 Pod 长什么样,里面包含 containers、镜像、端口、环境变量等。

selector和template之间的关系是最容易出错的地方。如果 template 里的 labels 写成了app: web-demo-v2,而 selector 还匹配app: web-demo,Deployment 就盯不住自己创建的 Pod,结果就是 ReplicaSet 会不断创建新 Pod,集群里出现一堆不属于任何人的孤儿 Pod。

部署完了看效果:

kubectl get pods -l app=web-demo -o wide

你会看到 3 个 Pod,分布在不同的工作节点上。虽然现在是单节点,但它们仍然会模拟出多副本的效果:删除任意一个 Pod,Deployment 会立即补一个新的。

3.2 Service 清单与访问链路

Service 的 YAML 比 Deployment 短得多:

apiVersion: v1 kind: Service metadata: name: web-demo-svc spec: type: NodePort selector: app: web-demo ports: - port: 80 targetPort: 80 nodePort: 30080

port是 Service 对外暴露的端口,targetPort是容器内部监听的端口,nodePort是宿主机上暴露的端口。注意:nodePort范围是 30000-32767,你写 30080 是可以的。

访问链路现在变成了这样:宿主机 30080 端口 → Service 的 ClusterIP:80 → Pod 的 IP:80。这个链路中,Service 本身是不处理请求的,它只是一个负载均衡入口,真正处理请求的是后端 Pod。

你可以在宿主机上直接验证:

curl http://localhost:30080

看到 nginx 欢迎页的 HTML,说明链路通了。多副本情况下,你多 curl 几次,如果后端日志有记录,可以发现请求被分发到了不同 Pod,这个是 kube-proxy 在每台机器上做的转发工作。你不需要深入理解 iptables 和 IPVS 的区别,但你需要知道:Service 不是代理进程,它是一组规则,流量分发发生在内核态。

3.3 滚动更新与回滚

应用上线后,总要更新镜像版本。如果你用的是命令式操作,很多人会直接删除老 Deployment 然后重新创建,这会导致服务中断。正确的做法是用滚动更新,Kubernetes 会一个一个替换 Pod,保证服务不中断。

把镜像版本改一下,重新 apply:

kubectl set image deployment/web-demo web-demo=nginx:1.26

或者改 YAML 里的 image 字段后kubectl apply -f deployment.yaml。

观察更新过程:

kubectl rollout status deployment/web-demo

你会看到类似 “Waiting for deployment spec update to be observed...”、“Rolling update complete” 的输出。真正更新时,新旧 Pod 会同时存在一小段时间。Kubernetes 的默认策略是RollingUpdate,它有一个关键参数maxUnavailable(最多允许几个副本不可用)和maxSurge(最多允许超出期望副本几个)。默认情况下,这两个参数都很保守,更新过程中始终有可用副本。

如果新镜像有问题怎么办?回滚:

kubectl rollout undo deployment/web-demo

这条命令会把 Deployment 恢复成上一次的版本。回滚在 Kubernetes 里不是概念的魔法,而是 Deployment 本身会保留历史 ReplicaSet 的“快照”,undo 的本质就是把 Pod 模板恢复到上一个 ReplicaSet 的状态。

刚开始学习时,我强烈建议你手动做一次这样的实验:把镜像改成一个不存在的版本,比如nginx:no-such-tag,然后 apply。你会看到 rollout 卡住,新 Pod 一直拉镜像失败,但旧 Pod 还在对外服务。这个过程非常直观地告诉你:滚动更新不是为了追求“新”,而是为了保证“稳”。

4. 配置、存储与有状态应用的起步

4.1 ConfigMap 与 Secret

跑起来一个 nginx 还不能算真正的实践,因为你还没有处理配置。实际应用几乎都要配置:环境变量、配置文件、密钥。把这些东西硬编码进镜像里是反模式,Kubernetes 给出的方案是 ConfigMap 和 Secret。

ConfigMap 存非敏感配置,Secret 存敏感信息。两者的使用方法有重叠,最大的区别是 Secret 的 value 存储时做了一次 base64 编码,另外在内存中的使用方式也更谨慎。别以为 base64 是加密,它只是编码,没有任何保护能力。Secret 真正的安全边界在于:谁有权限访问 API Server,谁就能看到 Secret。所以生产环境里还要配合加密存储和 RBAC 权限,本地实践阶段知道这一点就够了。

创建一个 ConfigMap:

kubectl create configmap nginx-config --from-literal=server_name=my-app.local

查看:

kubectl get configmap nginx-config -o yaml

挂载到 Pod 里的方式有两种:环境变量和文件挂载。环境变量适合简单 key-value,文件挂载适合配置文件。举一个最常见的文件挂载场景——用 ConfigMap 替换 nginx 的默认配置:

spec: containers: - name: web-demo image: nginx:1.25 volumeMounts: - name: config-volume mountPath: /etc/nginx/conf.d volumes: - name: config-volume configMap: name: nginx-config

这里把名为nginx-config的 ConfigMap 挂载到容器的/etc/nginx/conf.d目录。nginx 启动时会读取这个目录下的所有.conf文件,所以你的 ConfigMap 里的 key 如果叫default.conf,value 是 Nginx 配置内容,就能生效。

Secret 的用法和 ConfigMap 几乎一样,只是创建命令不同:

kubectl create secret generic db-secret --from-literal=password=my-password

如果你不想让密码出现在 shell 历史里,可以写成文件再导入,或者用标准输入的方式,但这个就属于安全习惯的进阶话题了。

4.2 持久化存储的入门姿势

无状态应用不需要关心数据保存,但数据库、文件服务这类场景必须有持久化。Kubernetes 的存储体系有三层概念:

  • Volume:Pod 级别的存储卷,生命周期跟随 Pod。
  • PersistentVolumeClaim(PVC):用户对存储的“申请单”。
  • PersistentVolume(PV):实际存在的存储后端。

我帮你理一下关系:PV 是资源,PVC 是申请。就像一个仓库里有若干货物(PV),你提一个申请单(PVC)说明自己要多大、什么类型,系统把匹配的货物分配给你。Pod 挂载的是 PVC,不是直接挂 PV,这样应用和底层存储解耦。

minikube 自带一个名为standard的 StorageClass,可以用动态供给的方式帮我们自动创建 PV。所以入门时你只需要写 PVC:

apiVersion: v1 kind: PersistentVolumeClaim metadata: name:>spec: containers: - name: app image: nginx:1.25 volumeMounts: - name: data mountPath: /data volumes: - name: data persistentVolumeClaim: claimName:>kubectl exec deploy/web-demo -- sh -c "echo 'hello persist' > /data/test.txt" kubectl delete pod web-demo-xxxxx kubectl exec deploy/web-demo -- cat /data/test.txt

Pod 删掉重建后,/data/test.txt应该还在。这就是持久化存储的意义:Pod 可以“消失”,数据不会跟着消失。

5. 入门阶段一定会踩的坑与排查思路

5.1 镜像拉取失败的几个真实原因

入门阶段最常见的报错就是ImagePullBackOff。很多人一看到这个错误就开始上网搜,其实它背后的原因就那么几类。

第一类是镜像地址写错。nginx:no-such-tag这种不存在的 tag,拉取必然失败。解决方法:先手动docker pull验证一下镜像是否存在。

第二类是镜像需要登录才能拉取。如果你引用了私有仓库镜像,在 Pod 里默认是没有凭据的。需要先创建docker-registry类型的 Secret,再用imagePullSecrets字段挂到 Deployment 上。

第三类是网络问题。有些环境访问 Docker Hub 不稳定,Pod 拉镜像拉了半天然后超时。本地实践时我建议先用一个已经拉取过的镜像,或者配置好镜像加速器,别让网络问题干扰你的学习节奏。

排查镜像问题有一个固定命令:

kubectl describe pod <pod-name> | tail -30

重点是 Events 部分的输出,它会告诉你当前卡在哪一步,是拉取失败还是调度失败。describe是入门阶段使用频率最高的命令,没有之一。

5.2 Pod 卡在 Pending:资源与调度

Pod 状态是Pending,说明还没被调度到任何节点上。新手最容易忽略的原因就是资源不足。minikube 默认只分配了 4G 内存,如果你一次性部署了 5 个镜像比较大的应用,某些 Pod 就会因为节点资源不够而一直 Pending。

怎么查?

kubectl describe pod <pod-name>

Events 里会有一行0/1 nodes are available,后面跟着每个节点的资源不足原因,比如Insufficient memory。看到这种信息,你有两个选择:给 minikube 加资源,或者减少副本数。

另一个常见原因是镜像太大,拉取时间很长,Pod 长时间处于ContainerCreating,进而被某些人误判为 Pending。这种其实不是调度问题,是拉取问题,等一等就好,或者describe看一下 Events 结束。

5.3 CrashLoopBackOff 的排查路径

CrashLoopBackOff 是入门时第二个高频状态,意思是 Pod 启动后很快就崩溃了,Kubernetes 在不断重启它,但重启也没用,就进入了指数退避的状态。

遇到这个状态,我最常用的排查顺序是:

kubectl logs <pod-name>

看看应用启动时输出什么。如果是应用启动脚本有问题,日志里会直接报错。如果日志没有内容,说明应用可能在写日志之前就崩了,这时候可以用更早的容器实例日志:

kubectl logs <pod-name> --previous

--previous参数能看到上一次崩溃的容器日志。这个参数是排查 CrashLoopBackOff 的神器,很多新手不知道它,明明上一次容器输出过红色报错,它看的是一个空日志,然后就无从下手了。

如果日志显示启动正常还在崩溃,那就要查探针问题了。当你给容器配置了livenessProbe,探针失败超过一定次数,K8s 就会认为容器不健康,主动杀掉并重启。这个机制本身是保护应用的手段,但探针配置过于严苛也会造成无限重启。比如初始化时间不够、探针路径写错、返回码不符合预期,都会让健康检查一直失败。

5.4 NodePort 访问不通

本地集群起来后,Service 也有 NodePort,但就是访问不通。我见过太多人卡在这一点。

第一部分,先确认 Service 本身有没有选到后端 Pod:

kubectl get endpoints <service-name>

Endpoints 列表里应该有 Pod 的 IP。如果列表是空的,说明 Service 的 selector 匹配不到任何 Pod,去检查 Deployment 的 label 和 Service 的 selector 是否一致。

第二部分,检查 NodePort 到底监听在哪。minikube service <service-name>会帮你自动做端口转发,但如果不用它,直接通过curl localhost:30080访问,在某些驱动下是不通的,因为 localhost 是当宿主机访问,而 NodePort 是绑定在 minikube 节点 IP 上的。正确做法是先minikube ip拿到集群节点 IP,再访问http://<节点IP>:30080。

第三部分,防火墙。这一步在本地实验里不太会遇到,但如果你用的是云服务器,记得在安全组放行对应的 NodePort 端口。这不算 K8s 的问题,而是环境问题,但初学者经常把这两者搞混,排查半天发现在自己机器上一切正常,一放到云环境就傻了。

最后一个建议:破坏之后再重建

我的入门经历告诉我,看 10 篇教程不如亲手破坏一次集群。所以你完成上面所有步骤之后,我强烈建议你做一次这样的实验。

先写一个正常的 Deployment,然后故意改几个地方:

  • 把 selector 写成不匹配的标签。
  • 把探针的路径改成错误地址。
  • 把副本数从 3 改成 10,看看资源不够时会出现什么状态。
  • 把 Service 的 selector 删掉,然后查看 endpoints 是不是空的。

每做一次错误修改,记录现象,再describe出 Events,把关键信息抄在文档里。这个过程比任何模拟考试都有用。等你做完这些,其实你已经不是纯新手了——你会开始理解 Kubernetes 不强依赖一条命令、一个动作,而是一套状态驱动的系统。所有对象都在努力让“当前状态”逼近“期望状态”,而我们能做的最好的事,就是清晰地把期望状态描述出来。

我最后再分享一个小技巧:任何时候想不起来某个字段该怎么写,不要硬背 YAML,直接kubectl explain deployment.spec.template.spec.containers。这个命令会返回字段的解释、类型、默认值,所有官方 API 对象的说明都能在命令行里查到。它比浏览网页文档快得多,而且永远和你当前集群版本一致。把kubectl explain当成你的字典,你会少走很多弯路。

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

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

立即咨询