很多朋友问我,Kubernetes(后面简称 K8s)入门到底该怎么走,网上教程一大堆,看的时候觉得都懂了,关上浏览器还是一头雾水。我之前带过几个刚转云原生的同事,发现他们卡住的地方其实非常集中:不是看不懂概念,而是不知道这些概念对应到实际操作里到底长什么样;不是不想动手,而是被“装环境”这件事先劝退了一半。这篇博客我换个写法,不按官方文档的目录从头念经,而是从“我手里有一堆容器,怎么把它们管起来”这个真实需求出发,把 K8s 入门实践中一定会碰到的核心概念、环境搭建、第一个应用部署、常见故障排查全部串一遍。适合刚接触容器编排的开发者,或者正在公司内部做技术分享、想给团队做一次实操演示的同学。1. 内容整体设计与思路拆解1.1 入门 K8s 前,先搞清楚它到底解决什么问题我先讲个故事。某个团队早期上线一个 Web 服务,部署方式很原始:把代码打成 jar 包,放到一台服务器上,用 nohup 启动就算完事。后来流量涨了,一台机器扛不住,就把同一个服务部署到三台机器上,前面加了一层负载均衡。再后来,每次发版都要在三台机器上重复操作,漏了一台,那台机器的旧进程还在跑,用户请求随机打到上面,线上 bug 就出现了。这其实就是 K8s 出现的核心背景:当容器数量变多、部署环境变复杂之后,人工管理容器已经不可行了。Docker 解决了“怎么把应用和依赖一起打包”的问题,但一堆容器分散在不同机器上,谁负责调度、谁负责重启挂掉的容器、谁负责服务发现、谁负责流量分配,Docker 本身不回答。K8s 回答的,就是这一整套“容器编排”的问题。但这里要泼一盆冷水:K8s 不是用来“让单个应用跑起来”的,而是用来“让大量应用稳定地跑着”的。如果你的项目规模就是一台虚拟机跑两三个容器,那引入 K8s 反而是负担,光运维控制面和网络插件可能就比你的业务还复杂。所以开始之前先想清楚,你的场景是不是真的需要它。1.2 学习路径设计:先建认知,再碰工具,最后踩坑我见过两种极端的学习方式。一种是上来就啃官方文档,从 API 对象逐字逐句读到控制器原理,结果读了两个星期还没部署过第一个 Pod,热情全消耗完了。另一种是赶紧找个教程复制粘贴,kubectl apply 一把梭,跑通了很开心,但集群一崩就完全不知道从哪里排查,因为他根本不理解自己敲的每条命令背后发生了什么。比较合适的方式,我自己的经验是三步走:第一步,建立认知地图。不需要深入每个细节,但是要清楚 K8s 集群里有哪些核心组件,比如 API Server、etcd、Scheduler、Kubelet,他们各自干什么,一条请求从 kubectl 到 Pod 大概经过了哪些环节。这个阶段可能只需要半天。第二步,动手部署一个真实应用。在本地环境里把 Nginx 或一个简单的 Web 服务跑起来,然后试着扩容、更新、回滚、暴露服务,感受 K8s 的基本操作流。第三步,带着问题深入底层。比如某个 Pod 一直 CrashLoopBackOff,你就去查日志、查事件、查资源限制,在这个过程中逐步理解容器运行时的细节。这篇文章的主体部分,就是沿着这三步展开的。1.3 环境选型:为什么我建议先从本地单机集群开始很多初学者纠结要不要先买几台云服务器搭一个多节点集群,理由是想体验“真实的生产环境”。但我的建议很明确:入门阶段一定先在本地搭一个单机集群,把核心概念和操作流程跑熟了,再考虑多节点。原因是,多节点集群的复杂度不在功能,而在网络。Pod 调度到不同节点上之后,跨节点通信、服务发现、负载均衡、存储挂载,每一项都会引入额外的网络问题。如果你连单机环境下“Deployment 创建的 Pod 为什么重启了”都还没查明白,直接跳到多节点环境,你会同时面对“K8s 本身的坑”和“网络插件的坑”,排查起来非常痛苦。本地环境的具体搭建方案,我放在后面第 3 章详细讲。这里先给一个结论:最快的方式是用 Docker Desktop 内置的 Kubernetes,或者用 minikube,两者我后面会对比。2. 核心概念一次讲透:Pod、Deployment、Service2.1 Pod:K8s 里最小的调度单元,但不是“单容器”的代名词很多人以为 Pod 就是一个容器,其实不准确。Pod 是 K8s 中最小、也是最基本的调度单元,它可以包含一个或多个容器。这些容器共享同一个网络命名空间、共享存储卷,并且总是被调度到同一个节点上。为什么需要这么一个“中间层”?一个常见场景是:有个应用由两个容器组成,一个容器跑主业务逻辑,另一个容器做日志收集,它俩需要互相通信、共享日志目录。如果 K8s 直接把每个容器当作独立的调度单元,那么这两个容器可能被调度到不同节点上,它们之间的本地 Socket 通信和共享文件就没办法实现了。所以 K8s 引入了 Pod 这个概念,把一组“必须同行同止”的容器打包成一个整体。打个比方,Pod 就像一房一厅的合租房,里面的多个容器是合租室友。他们共享这个房间的网络出口和起居空间,彼此可以直接交流,但对外来说,这个房间是一个整体。我实操中经常提醒新同事:查看 Pod 状态的时候,注意看READY列。比如1/1表示 Pod 里有一个容器且正常运行,如果是2/3,说明这个 Pod 里有三个容器,但只有两个正常。很多人排查看半天找不到问题,就是没注意某个多容器 Pod 里有一个副容器挂了。2.2 Deployment:你只管描述“想要的状态”,K8s 负责“变成这个状态”第一次部署应用的时候,可以直接创建一个 Pod,但这在实践里基本不会这么做,因为裸 Pod 没有“自愈”能力,它挂了就挂了,没有人会帮你拉起一个新的。生产实践中,你管理的是 Deployment,它是一个定义“期望状态”的控制器。Deployment 的描述里一般包含三件事:你想要的副本数量、每个副本对应的 Pod 模板、以及更新策略。当你在 YAML 里写好replicas: 3并提交给集群之后,K8s 会持续保证集群里随时有 3 个符合条件的 Pod 在运行。某个 Pod 所在的节点宕机了,控制器会自动在其他可用节点上重建一个新的 Pod。这里就体现了我前面说的“K8s 是声明式管理”的思想:你不需要告诉系统“请把 Pod A 移到节点 B 上去”,你只需要说“我永远要有 3 个副本”,剩下的事情由控制器自行完成。在实际操作中,我也建议团队尽量用 Deployment 而不是手动管理 Pod,甚至如果你只是临时想跑个测试任务,也可以用kubectl run一键生成一个 Deployment。2.3 Service:Pod 是会“漂移”的,服务访问必须另想办法Pod 有一个特点:它是有“临时性”的。因为 Deployment 滚动更新、故障重建、手动扩缩容,都会导致旧的 Pod 被销毁、新的 Pod 被创建。每次 Pod 被创建,都会被分配一个新的 IP 地址。如果你在代码里写死了某个 Pod 的 IP,一旦它被重建,你的服务就不可用了。Service 就是用来解决这个问题的。它为一组 Pod 提供一个稳定的虚拟 IP(ClusterIP)和固定的 DNS 名称。所有访问都打到 Service 上,由 Service 根据标签选择器把流量转发给后面实际的 Pod。你可以把 Service 理解为公司前台的总机号码。无论某个员工换了多少次办公室座位,你只需要拨打总机号,前台就会帮你转接到那个员工的当前座位。这里我分享一个自己踩过的坑:Service 是通过标签选择器(selector)来匹配后端 Pod 的。我有一段时间调试一个服务,发现有一个 Pod 一直处于NotReady状态,但 Service 的后端端点里却一直带着这个 Pod 的 IP。后来检查发现,这个 Service 的 YAML 里selector配得太宽了,把很多不属于它的 Pod 也选了进来。Service 转发流量给你,是不关心这个 Pod 是否 Ready 的,只要标签匹配它就会被选入后端列表。所以标签这个东西,一定要遵循团队的统一规范。2.4 Namespace:不要把所有东西都扔在默认空间最后补充一个基础概念:Namespace。它不是多节点集群那样的“物理隔离”,而是 K8s 里一种逻辑隔离机制,用于把不同团队、不同环境(比如 dev、staging、prod)的资源区分开。初次实践的时候,很多人图省事,所有资源都直接丢在default命名空间里。短时间看没什么问题,但随着资源变多,你会发现在kubectl get pods里看到一堆别人的或别的环境的 Pod,很容易搞混。我在团队里推行了一条比较简单的工作规范:每个项目、每个环境都建立独立的 Namespace,命名格式统一为项目名-环境名,例如order-svc-dev。这样看日志、查资源、配权限都清爽很多。3. 实操准备:在本地把 K8s 环境跑起来3.1 方案对比:Docker Desktop 内置 K8s 和 minikube 怎么选我在第 1 章提到本地环境建议,这里给出具体的对比,方便你根据自己的系统情况做选择。对比项Docker Desktop 内置 K8sminikube适用系统macOS、Windows(需要开启 WSL2)macOS、Windows、Linux 都支持安装复杂度简单,勾选一个选项即可需要一个安装器,驱动配置略复杂对系统资源占用取决于 Docker Desktop 本身,通常较高配置文件可调,相对可控是否贴近生产一般,适合日常开发调试支持指定 Kubernetes 版本,适合学习踩坑概率低中等,网络驱动容易出错如果你用的 macOS 或 Windows,而且电脑上本来就装了 Docker Desktop,我建议先用它,因为它不用额外安装,在设置里勾选启用 Kubernetes 就能用。如果你在用 Linux,或者想体验一个更“独立”的环境,那 minikube 会更合适。这里有个很重要的经验:入门阶段不管用哪种方案,优先保证“能快速用起来”,不要在这个环节过度纠结。我见过有人在 minikube 的驱动选择上折腾了两天,还没开始学 Kubernetes 就把热情耗光了。先能跑起来,再说好不好用。3.2 通过 Docker Desktop 启用 K8s 的步骤以 macOS 版本为例,操作路径是这样的:打开 Docker Desktop,进入Settings。在左侧选择Kubernetes,勾选Enable Kubernetes。点击Apply & Restart,等待 Docker Desktop 自动下载依赖并启动集群。启动完成之后,在终端执行kubectl version,如果能看到Client Version和Server Version两行输出,说明你的本地集群已经可用了。注意:第一步装完后,Docker Desktop 自带的 kubectl 可能是老版本,建议执行kubectl version确认一下。如果客户端和集群版本差距太大,后面执行kubectl apply时可能会出现奇怪的报错。对于大量 macOS 用户来说,最常见的问题是启用 Kubernetes 后 Docker Desktop 一直停留在Starting...状态。这时候不要反复点击开关,先重启 Docker Desktop,还不行就把~/.kube/config和 Docker 的数据目录做一次备份后重置,大多数情况都能恢复。3.3 minikube 方式:面向 Linux 用户的推荐路径Linux 上的安装过程也很快。通常只需要两段命令:curl -LO https://github.com/kubernetes/minikube/releases/latest/download/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube minikube start --driver=docker这里有个比较常见的坑:minikube start的时候它会自动探测驱动,如果探测不到就会尝试使用none驱动,也就是直接在当前机器的容器运行时上以 Root 启动集群,这往往会让后续的网络配置变得很复杂。所以更稳妥的方式是主动指定--driver=docker,让 minikube 通过 Docker 驱动创建一个管控节点。启动完成后,记得执行:kubectl get nodes确认节点的状态是Ready。这里想提醒一下,minikube 默认给集群分配的资源不算大,如果你电脑本身内存不足,建议在启动时加上--memory=4096 --cpus=2,避免后续部署时资源不足导致 Pod 被驱逐。3.4 安装 kubectl 和网络插件注意事项不管用哪种方案,最终都需要 kubectl 这个命令行工具。macOS 可以用brew install kubectl,Windows 可以用choco install kubernetes-cli,Linux 可以用包管理器或者手动下载二进制。有些教程会说,本地单机集群不需要装网络插件,这个说法不完全对。Docker Desktop 内置的 K8s 和 minikube 都会默认预装自己的网络方案(比如 minikube 用的是基于 Docker 网络的一种原生产物),所以一般不需要额外安装 Calico 或 Flannel。但如果你是用 kubeadm 自己搭集群,那网络插件就是必须的了,否则 Pod 之间无法通信。这里我要特别强调一个新手容易忽略的点:装完环境之后,第一件事不是急着跑应用,而是先验证集群基本功能。我的习惯是部署一个极简的 Nginx 测试一下:kubectl create deployment nginx-test --image=nginx kubectl get pods如果能看到nginx-test-xxx这个 Pod 进入Running状态,说明你的集群调度、容器运行时、网络、镜像拉取几个核心链路都是通的。我自己见过一个同事,集群搭建好之后一直部署不了自己的应用,排查来排查去,最后发现是镜像仓库地址连不通,如果先跑一个 nginx 验证,这个问题几分钟就能定位。4. 第一个应用的完整部署流程4.1 从一行命令到一份 YAML:读懂声明式配置刚入门的时候,很多人喜欢用kubectl create deployment nginx --image=nginx这种命令直接创建资源,因为快。但我想说的是:命令能用,但别只依赖命令。K8s 的核心理念是声明式管理,生产实践里,所有资源配置都会以 YAML 文件的形式保存在代码仓库中,然后通过kubectl apply来生效。这样做的价值在于:所有人的操作都是可配置、可审计、可回滚的。下面这份 YAML 是一个最小可用的 Deployment 示例:apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80初次看到这个文件,你不需要马上理解每一行的含义,但有几个关键点要留意:apiVersion决定了 K8s 使用哪个 API 版本,不同的资源类型可能对应不同的版本。spec.selector.matchLabels定义了这个 Deployment 管理哪些 Pod。它是和下面template.metadata.labels对应的,两者必须匹配,否则控制器找不到自己要管理的 Pod。spec.template是 Pod 模板,所有通过这个 Deployment 创建的 Pod 都由这个模板生成。提交这份 YAML 的命令很简单:kubectl apply -f nginx-deploy.yaml这里顺便说一句,apply 和 create 是有区别的。create 是“创建”一个不存在的资源,如果资源已经存在会报错;apply 是“声明并同步”资源最终状态,即使资源已存在,也能把 YAML 里的变更合并到现有资源上。所以团队里一般统一用 apply。4.2 让服务可以被访问:编写 ClusterIP、NodePort 和 LoadBalancerDeployment 创建好之后,Pod 已经跑起来了,但此时它们只存在于集群内部网络中,从集群外部是访问不到的,你需要创建一个 Service。Service 有几种不同类型的实现,入门阶段至少要分清三种最常见的:第一种是 ClusterIP,它是默认类型。创建一个 Service 后,K8s 会分配一个集群内部虚拟 IP,集群内的其他资源可以通过这个 IP 访问,但集群外部访问不了。第二种是 NodePort。它在 ClusterIP 的基础上,会在每个工作节点上开放一个指定端口(范围是 30000-32767),外部流量访问任意节点的这个端口就能进入集群。第三种是 LoadBalancer。这主要是云厂商环境下使用的类型,它会在云平台上创建一个负载均衡器,把公网流量转发到集群内的 Service 上。在生产环境用得最多,但在本地环境用不上。以 NodePort 为例,一个最简 Service 配置如下:apiVersion: v1 kind: Service metadata: name: nginx-svc namespace: default spec: type: NodePort selector: app: nginx ports: - port: 80 targetPort: 80 nodePort: 30080这里port是 Service 对外暴露的端口,targetPort是 Pod 里容器实际监听的端口。很多新手容易把这两个搞混,导致端口明明是通的,但访问始终 403。我自己排查过一例:Service 的 port 写成了 8080,Pod 里的业务监听的也是 8080,但 NodePort 暴露的 30080 转发过去却不通,最后发现 targetPort 写成了容器内的 80,容器里根本没有进程监听 80,所有数据包都被丢了。创建 Service 的命令:kubectl apply -f nginx-svc.yaml创建后,可以通过下面的命令查看 Service 的详细信息:kubectl get svc如果状态正常,你会看到nginx-svc的PORT(S)列显示80:30080/TCP,表示集群内通过 80 端口访问 Service,外部通过NodeIP:30080访问。4.3 Pod 调度、滚动更新与回滚的现场观察应用部署起来之后,你可能会想体验一下 K8s 最吸引人的能力:滚动更新和快速回滚。滚动更新的场景很常见:你需要把 Nginx 镜像版本从1.25升到1.26。推荐的做法是修改 YAML 文件,把image字段改了,然后重新 apply:kubectl apply -f nginx-deploy.yaml此时执行:kubectl rollout status deployment/nginx-deploy你会看到类似下面的输出:Waiting for deployment "nginx-deploy" rollout to finish: 1 out of 3 new replicas have been updated...这就是滚动更新正在进行。K8s 会按照spec.strategy里定义的规则,先启动一个新副本,等它 Ready 后再杀掉一个旧副本,以此类推。默认的RollingUpdate策略会保证新老 Pod 交替替换,整个过程中服务不会中断。如果更新之后发现问题,回滚命令非常简单:kubectl rollout undo deployment/nginx-deploy在实战中,我强烈建议团队在每次正式更新前,记录好当前使用的镜像版本与对应配置,因为如果更新伴随 YAML 文件的多次修改,单纯依赖 undo 可能回滚到你不需要的历史版本。还有一个好习惯是开发成员每次发布前先跑一次金丝雀发布,而不是直接全量升级,尤其是业务流量还比较重要的时候。4.4 扩缩容实战:手动调整和快速压测验证扩缩容在 K8s 里是成本最低的操作。手动扩容只需要一条命令:kubectl scale deployment nginx-deploy --replicas=5执行完再看kubectl get pods,你会发现新的 Pod 正在被创建。在本地单节点环境下,你甚至可以在几秒钟内观察到它变成 Running 状态。不过我想多说一句:手动 scale 适合日常调试,但真正生产环境的容量弹性,一般依赖 HorizontalPodAutoscaler(HPA)来做自动伸缩。HPA 会根据 CPU 使用率或自定义指标动态调整副本数,这是后面要进阶的主题。入门阶段先掌握手动 scale,再理解 HPA 是如何选择指标的,会顺很多。我还发现一个提升感知很有效的小实验:在本地集群上部署一个压测工具(比如用 hey 或者 ab 简单打一下 Nginx 服务),观察 CPU 指标,然后用 HPA 把指标连起来,等你把请求打上去的时候,就能看到 Pod 自动扩容。这个实验对理解“声明式状态”非常有帮助。5. 常用运维操作与故障排查实战5.1 一套高频命令体系:describe、logs、exec 是排查三板斧K8s 入门后,用得最多的命令其实就三五个。我这里列一份“必会清单”:kubectl get ...查看资源的当前状态,比如kubectl get pods、kubectl get svc等。kubectl describe ...查看资源的详细信息,包括事件(Events),排查定位基本靠它。kubectl logs ...查看容器日志,排除业务本身的问题。kubectl exec -it ... -- /bin/sh进入容器内部调试,适合检查容器内的进程、网络、文件。kubectl port-forward把本地端口转发到集群内的 Service 或 Pod,临时调试很好用。我不止一次跟团队里新同学说,排查问题不要凭感觉瞎猜,一定要先kubectl describe。举个例子:Pod 一直没办法被调度,显示Pending状态,如果不 describe,你只能猜是不是镜像拉不到,但 describe 的输出里会直接告诉你Events里写的是FailedScheduling,原因是节点内存不足。事件里已经把结论了写得很清楚,直接看就行。5.2 Pod 状态机:从 Pending 到 Running,每一步都可能出问题Pod 在生命周期里有几个常见状态,我把它们串起来讲:Pending:Pod 已被集群接受,但还没运行。原因可能是调度器找不到合适的节点,或者镜像正在拉取。ContainerCreating:容器正在被创建,一般很快会变成 Running。如果卡在这里太久,大概率是镜像拉取太慢或存储卷挂载问题。Running:容器已经启动并正常运行。CrashLoopBackOff:容器启动后马上崩溃,然后被 K8s 反复重启,重试间隔会指数增加。Error或Failed:容器以非零状态退出。Terminating:正在被销毁。在入门实践中,CrashLoopBackOff应该是曝光率最高的一个状态。遇到它不要慌,按这个顺序排查:先用kubectl logs看应用日志,确认是不是业务启动失败。然后用kubectl describe看事件,确认容器退出原因。检查 YAML 里的command、args、env、volumeMounts是否配置正确。这里有一个很经典的例子:某个服务在本地用 Docker 跑得好好的,丢到 K8s 里就CrashLoopBackOff。排查半天发现,这个服务启动时会从一个文件里读配置,而那个文件在 K8s 环境里没有挂载进去。容器里缺文件,启动就报错。所以遇到崩溃重启,先检查“代码运行依赖的本地资源是否都通过卷挂载进去了”。5.3 镜像拉取失败的几种原因与解决路径新手本地实验时,最常见的另一种问题是ImagePullBackOff,也就是镜像拉不下来。我总结过大概三种原因:第一种是镜像地址写错。比如打错了一个字符,K8s 去仓库里找不到这个镜像。这时只需要核对image字段,并注意 tag。第二种是仓库认证问题。如果你用的是私有仓库,需要在 K8s 里创建一个imagePullSecret,并在 Deployment 的spec.template.spec.imagePullSecrets里引用它。第三种是网络问题,本地环境拉不到远程仓库的镜像。这种最让人头疼,因为 K8s 集群本身没有好的“重试”日志,只会在事件里反复显示Failed to pull image。对本地学习来说,解决网络问题最简单的方式是提前用 Docker 在本地docker pull好镜像,并给镜像打好本地 tag。如果是用 minikube,可以执行minikube image load nginx:1.25把镜像提前导入集群节点。这个操作建议收藏,非常实用。5.4 常见问题速查表为了节省你在入门阶段查来查去的时间,我把最容易踩的几个坑整理成一张速查表:症状常见原因排查与解决Pod 一直Pending节点资源不足kubectl describe node查看资源分配,扩容节点或清理占用Pod 反复CrashLoopBackOff应用启动失败或缺配置先查日志kubectl logs,再检查环境变量和卷挂载镜像拉不下来ImagePullBackOff镜像地址错误、认证失败、网络不通核对地址、创建imagePullSecret、预先导入本地镜像Service 访问不通selector 匹配错误或端口配置不对检查 YAML 里 selector、port、targetPort 三者对应关系更新后服务中断滚动更新策略过于激进增加maxSurge和maxUnavailable的合理配置执行 kubectl 报连接不上本地 kubeconfig 没生效检查~/.kube/config是否存在,集群是否在运行Pod 启动成功但日志里连接数据库失败网络策略或服务发现配置问题检查 Service 名称与端口是否匹配,必要时进入 Pod 用 nslookup 验证这张表挺值得贴在工位上。不是说遇到问题照着查就一定能解决,但它能帮你在面对一堆报错时快速锁定检查方向,不会乱成一团。5.5 给新手的几条避坑心得最后这部分,说几条我在实操中反复验证过的经验,文字不多,但每条都是从真心教训里提炼的:第一,不要在一知半解的时候直接改生产集群。本地集群怎么折腾都行,线上集群每一条命令都要带着敬畏执行,尤其是kubectl delete。我见过有人把整个 Deployment 误删了,然后发现没有备份 YAML,只能凭记忆重建,直接把服务恢复时间拉长了三倍。第二,尽量少用kubectl create,多用kubectl apply。前者是命令式操作,资源一旦创建,后续想全局改配置就比较费劲;后者能保持配置与 YAML 的一致性,改动内容和过程都容易追溯。第三,一定要培养“先 describe,后 logs”的习惯。很多新手遇到 Pod 起不来,第一反应就是看日志,但有些底层的错误(比如调度失败、镜像拉取失败、挂载失败)在容器日志里根本不会出现,必须通过describe看 Events 才能定位。可以说,describe是 K8s 运维的“第一现场”。第四,本地环境建议把资源配额调大一点,至少保证--memory=4096级别。我的一个同学在本地部署一个带数据库的应用时,因为默认资源限制太小,Pod 一直在被 OOM Kill,他一度以为是自己代码写得有问题,最后发现只是资源不够。按照我自己带人的经验,如果能把上面这些命令和排查逻辑完整操作一遍,从零到能独立部署一个小型服务、并且能自己定位常见的 Pod 启动问题,大概需要一到两天。跑通了后面的路就会顺很多。这个过程中最大的收获,其实不是记住了哪个命令,而是建立了对“声明式状态”的感觉:你不再像以前一样手把手地指挥每一台服务器,而是告诉集群“最终长什么样”,剩下的交给它自己完成。这种感觉刚接触时会有点不习惯,但用顺手之后,你会觉得运维这件事从来没这么省心过。
很多朋友问我,Kubernetes(后面简称 K8s)入门到底该怎么走,网上教程一大堆,看的时候觉得都懂了,关上浏览器还是一头雾水。我之前带过几个刚转云原生的同事,发现他们卡住的地方其实非常集中:不是看不懂概念,而是不知道这些概念对应到实际操作里到底长什么样;不是不想动手,而是被“装环境”这件事先劝退了一半。这篇博客我换个写法,不按官方文档的目录从头念经,而是从“我手里有一堆容器,怎么把它们管起来”这个真实需求出发,把 K8s 入门实践中一定会碰到的核心概念、环境搭建、第一个应用部署、常见故障排查全部串一遍。适合刚接触容器编排的开发者,或者正在公司内部做技术分享、想给团队做一次实操演示的同学。1. 内容整体设计与思路拆解1.1 入门 K8s 前,先搞清楚它到底解决什么问题我先讲个故事。某个团队早期上线一个 Web 服务,部署方式很原始:把代码打成 jar 包,放到一台服务器上,用 nohup 启动就算完事。后来流量涨了,一台机器扛不住,就把同一个服务部署到三台机器上,前面加了一层负载均衡。再后来,每次发版都要在三台机器上重复操作,漏了一台,那台机器的旧进程还在跑,用户请求随机打到上面,线上 bug 就出现了。这其实就是 K8s 出现的核心背景:当容器数量变多、部署环境变复杂之后,人工管理容器已经不可行了。Docker 解决了“怎么把应用和依赖一起打包”的问题,但一堆容器分散在不同机器上,谁负责调度、谁负责重启挂掉的容器、谁负责服务发现、谁负责流量分配,Docker 本身不回答。K8s 回答的,就是这一整套“容器编排”的问题。但这里要泼一盆冷水:K8s 不是用来“让单个应用跑起来”的,而是用来“让大量应用稳定地跑着”的。如果你的项目规模就是一台虚拟机跑两三个容器,那引入 K8s 反而是负担,光运维控制面和网络插件可能就比你的业务还复杂。所以开始之前先想清楚,你的场景是不是真的需要它。1.2 学习路径设计:先建认知,再碰工具,最后踩坑我见过两种极端的学习方式。一种是上来就啃官方文档,从 API 对象逐字逐句读到控制器原理,结果读了两个星期还没部署过第一个 Pod,热情全消耗完了。另一种是赶紧找个教程复制粘贴,kubectl apply 一把梭,跑通了很开心,但集群一崩就完全不知道从哪里排查,因为他根本不理解自己敲的每条命令背后发生了什么。比较合适的方式,我自己的经验是三步走:第一步,建立认知地图。不需要深入每个细节,但是要清楚 K8s 集群里有哪些核心组件,比如 API Server、etcd、Scheduler、Kubelet,他们各自干什么,一条请求从 kubectl 到 Pod 大概经过了哪些环节。这个阶段可能只需要半天。第二步,动手部署一个真实应用。在本地环境里把 Nginx 或一个简单的 Web 服务跑起来,然后试着扩容、更新、回滚、暴露服务,感受 K8s 的基本操作流。第三步,带着问题深入底层。比如某个 Pod 一直 CrashLoopBackOff,你就去查日志、查事件、查资源限制,在这个过程中逐步理解容器运行时的细节。这篇文章的主体部分,就是沿着这三步展开的。1.3 环境选型:为什么我建议先从本地单机集群开始很多初学者纠结要不要先买几台云服务器搭一个多节点集群,理由是想体验“真实的生产环境”。但我的建议很明确:入门阶段一定先在本地搭一个单机集群,把核心概念和操作流程跑熟了,再考虑多节点。原因是,多节点集群的复杂度不在功能,而在网络。Pod 调度到不同节点上之后,跨节点通信、服务发现、负载均衡、存储挂载,每一项都会引入额外的网络问题。如果你连单机环境下“Deployment 创建的 Pod 为什么重启了”都还没查明白,直接跳到多节点环境,你会同时面对“K8s 本身的坑”和“网络插件的坑”,排查起来非常痛苦。本地环境的具体搭建方案,我放在后面第 3 章详细讲。这里先给一个结论:最快的方式是用 Docker Desktop 内置的 Kubernetes,或者用 minikube,两者我后面会对比。2. 核心概念一次讲透:Pod、Deployment、Service2.1 Pod:K8s 里最小的调度单元,但不是“单容器”的代名词很多人以为 Pod 就是一个容器,其实不准确。Pod 是 K8s 中最小、也是最基本的调度单元,它可以包含一个或多个容器。这些容器共享同一个网络命名空间、共享存储卷,并且总是被调度到同一个节点上。为什么需要这么一个“中间层”?一个常见场景是:有个应用由两个容器组成,一个容器跑主业务逻辑,另一个容器做日志收集,它俩需要互相通信、共享日志目录。如果 K8s 直接把每个容器当作独立的调度单元,那么这两个容器可能被调度到不同节点上,它们之间的本地 Socket 通信和共享文件就没办法实现了。所以 K8s 引入了 Pod 这个概念,把一组“必须同行同止”的容器打包成一个整体。打个比方,Pod 就像一房一厅的合租房,里面的多个容器是合租室友。他们共享这个房间的网络出口和起居空间,彼此可以直接交流,但对外来说,这个房间是一个整体。我实操中经常提醒新同事:查看 Pod 状态的时候,注意看READY列。比如1/1表示 Pod 里有一个容器且正常运行,如果是2/3,说明这个 Pod 里有三个容器,但只有两个正常。很多人排查看半天找不到问题,就是没注意某个多容器 Pod 里有一个副容器挂了。2.2 Deployment:你只管描述“想要的状态”,K8s 负责“变成这个状态”第一次部署应用的时候,可以直接创建一个 Pod,但这在实践里基本不会这么做,因为裸 Pod 没有“自愈”能力,它挂了就挂了,没有人会帮你拉起一个新的。生产实践中,你管理的是 Deployment,它是一个定义“期望状态”的控制器。Deployment 的描述里一般包含三件事:你想要的副本数量、每个副本对应的 Pod 模板、以及更新策略。当你在 YAML 里写好replicas: 3并提交给集群之后,K8s 会持续保证集群里随时有 3 个符合条件的 Pod 在运行。某个 Pod 所在的节点宕机了,控制器会自动在其他可用节点上重建一个新的 Pod。这里就体现了我前面说的“K8s 是声明式管理”的思想:你不需要告诉系统“请把 Pod A 移到节点 B 上去”,你只需要说“我永远要有 3 个副本”,剩下的事情由控制器自行完成。在实际操作中,我也建议团队尽量用 Deployment 而不是手动管理 Pod,甚至如果你只是临时想跑个测试任务,也可以用kubectl run一键生成一个 Deployment。2.3 Service:Pod 是会“漂移”的,服务访问必须另想办法Pod 有一个特点:它是有“临时性”的。因为 Deployment 滚动更新、故障重建、手动扩缩容,都会导致旧的 Pod 被销毁、新的 Pod 被创建。每次 Pod 被创建,都会被分配一个新的 IP 地址。如果你在代码里写死了某个 Pod 的 IP,一旦它被重建,你的服务就不可用了。Service 就是用来解决这个问题的。它为一组 Pod 提供一个稳定的虚拟 IP(ClusterIP)和固定的 DNS 名称。所有访问都打到 Service 上,由 Service 根据标签选择器把流量转发给后面实际的 Pod。你可以把 Service 理解为公司前台的总机号码。无论某个员工换了多少次办公室座位,你只需要拨打总机号,前台就会帮你转接到那个员工的当前座位。这里我分享一个自己踩过的坑:Service 是通过标签选择器(selector)来匹配后端 Pod 的。我有一段时间调试一个服务,发现有一个 Pod 一直处于NotReady状态,但 Service 的后端端点里却一直带着这个 Pod 的 IP。后来检查发现,这个 Service 的 YAML 里selector配得太宽了,把很多不属于它的 Pod 也选了进来。Service 转发流量给你,是不关心这个 Pod 是否 Ready 的,只要标签匹配它就会被选入后端列表。所以标签这个东西,一定要遵循团队的统一规范。2.4 Namespace:不要把所有东西都扔在默认空间最后补充一个基础概念:Namespace。它不是多节点集群那样的“物理隔离”,而是 K8s 里一种逻辑隔离机制,用于把不同团队、不同环境(比如 dev、staging、prod)的资源区分开。初次实践的时候,很多人图省事,所有资源都直接丢在default命名空间里。短时间看没什么问题,但随着资源变多,你会发现在kubectl get pods里看到一堆别人的或别的环境的 Pod,很容易搞混。我在团队里推行了一条比较简单的工作规范:每个项目、每个环境都建立独立的 Namespace,命名格式统一为项目名-环境名,例如order-svc-dev。这样看日志、查资源、配权限都清爽很多。3. 实操准备:在本地把 K8s 环境跑起来3.1 方案对比:Docker Desktop 内置 K8s 和 minikube 怎么选我在第 1 章提到本地环境建议,这里给出具体的对比,方便你根据自己的系统情况做选择。对比项Docker Desktop 内置 K8sminikube适用系统macOS、Windows(需要开启 WSL2)macOS、Windows、Linux 都支持安装复杂度简单,勾选一个选项即可需要一个安装器,驱动配置略复杂对系统资源占用取决于 Docker Desktop 本身,通常较高配置文件可调,相对可控是否贴近生产一般,适合日常开发调试支持指定 Kubernetes 版本,适合学习踩坑概率低中等,网络驱动容易出错如果你用的 macOS 或 Windows,而且电脑上本来就装了 Docker Desktop,我建议先用它,因为它不用额外安装,在设置里勾选启用 Kubernetes 就能用。如果你在用 Linux,或者想体验一个更“独立”的环境,那 minikube 会更合适。这里有个很重要的经验:入门阶段不管用哪种方案,优先保证“能快速用起来”,不要在这个环节过度纠结。我见过有人在 minikube 的驱动选择上折腾了两天,还没开始学 Kubernetes 就把热情耗光了。先能跑起来,再说好不好用。3.2 通过 Docker Desktop 启用 K8s 的步骤以 macOS 版本为例,操作路径是这样的:打开 Docker Desktop,进入Settings。在左侧选择Kubernetes,勾选Enable Kubernetes。点击Apply & Restart,等待 Docker Desktop 自动下载依赖并启动集群。启动完成之后,在终端执行kubectl version,如果能看到Client Version和Server Version两行输出,说明你的本地集群已经可用了。注意:第一步装完后,Docker Desktop 自带的 kubectl 可能是老版本,建议执行kubectl version确认一下。如果客户端和集群版本差距太大,后面执行kubectl apply时可能会出现奇怪的报错。对于大量 macOS 用户来说,最常见的问题是启用 Kubernetes 后 Docker Desktop 一直停留在Starting...状态。这时候不要反复点击开关,先重启 Docker Desktop,还不行就把~/.kube/config和 Docker 的数据目录做一次备份后重置,大多数情况都能恢复。3.3 minikube 方式:面向 Linux 用户的推荐路径Linux 上的安装过程也很快。通常只需要两段命令:curl -LO https://github.com/kubernetes/minikube/releases/latest/download/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube minikube start --driver=docker这里有个比较常见的坑:minikube start的时候它会自动探测驱动,如果探测不到就会尝试使用none驱动,也就是直接在当前机器的容器运行时上以 Root 启动集群,这往往会让后续的网络配置变得很复杂。所以更稳妥的方式是主动指定--driver=docker,让 minikube 通过 Docker 驱动创建一个管控节点。启动完成后,记得执行:kubectl get nodes确认节点的状态是Ready。这里想提醒一下,minikube 默认给集群分配的资源不算大,如果你电脑本身内存不足,建议在启动时加上--memory=4096 --cpus=2,避免后续部署时资源不足导致 Pod 被驱逐。3.4 安装 kubectl 和网络插件注意事项不管用哪种方案,最终都需要 kubectl 这个命令行工具。macOS 可以用brew install kubectl,Windows 可以用choco install kubernetes-cli,Linux 可以用包管理器或者手动下载二进制。有些教程会说,本地单机集群不需要装网络插件,这个说法不完全对。Docker Desktop 内置的 K8s 和 minikube 都会默认预装自己的网络方案(比如 minikube 用的是基于 Docker 网络的一种原生产物),所以一般不需要额外安装 Calico 或 Flannel。但如果你是用 kubeadm 自己搭集群,那网络插件就是必须的了,否则 Pod 之间无法通信。这里我要特别强调一个新手容易忽略的点:装完环境之后,第一件事不是急着跑应用,而是先验证集群基本功能。我的习惯是部署一个极简的 Nginx 测试一下:kubectl create deployment nginx-test --image=nginx kubectl get pods如果能看到nginx-test-xxx这个 Pod 进入Running状态,说明你的集群调度、容器运行时、网络、镜像拉取几个核心链路都是通的。我自己见过一个同事,集群搭建好之后一直部署不了自己的应用,排查来排查去,最后发现是镜像仓库地址连不通,如果先跑一个 nginx 验证,这个问题几分钟就能定位。4. 第一个应用的完整部署流程4.1 从一行命令到一份 YAML:读懂声明式配置刚入门的时候,很多人喜欢用kubectl create deployment nginx --image=nginx这种命令直接创建资源,因为快。但我想说的是:命令能用,但别只依赖命令。K8s 的核心理念是声明式管理,生产实践里,所有资源配置都会以 YAML 文件的形式保存在代码仓库中,然后通过kubectl apply来生效。这样做的价值在于:所有人的操作都是可配置、可审计、可回滚的。下面这份 YAML 是一个最小可用的 Deployment 示例:apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80初次看到这个文件,你不需要马上理解每一行的含义,但有几个关键点要留意:apiVersion决定了 K8s 使用哪个 API 版本,不同的资源类型可能对应不同的版本。spec.selector.matchLabels定义了这个 Deployment 管理哪些 Pod。它是和下面template.metadata.labels对应的,两者必须匹配,否则控制器找不到自己要管理的 Pod。spec.template是 Pod 模板,所有通过这个 Deployment 创建的 Pod 都由这个模板生成。提交这份 YAML 的命令很简单:kubectl apply -f nginx-deploy.yaml这里顺便说一句,apply 和 create 是有区别的。create 是“创建”一个不存在的资源,如果资源已经存在会报错;apply 是“声明并同步”资源最终状态,即使资源已存在,也能把 YAML 里的变更合并到现有资源上。所以团队里一般统一用 apply。4.2 让服务可以被访问:编写 ClusterIP、NodePort 和 LoadBalancerDeployment 创建好之后,Pod 已经跑起来了,但此时它们只存在于集群内部网络中,从集群外部是访问不到的,你需要创建一个 Service。Service 有几种不同类型的实现,入门阶段至少要分清三种最常见的:第一种是 ClusterIP,它是默认类型。创建一个 Service 后,K8s 会分配一个集群内部虚拟 IP,集群内的其他资源可以通过这个 IP 访问,但集群外部访问不了。第二种是 NodePort。它在 ClusterIP 的基础上,会在每个工作节点上开放一个指定端口(范围是 30000-32767),外部流量访问任意节点的这个端口就能进入集群。第三种是 LoadBalancer。这主要是云厂商环境下使用的类型,它会在云平台上创建一个负载均衡器,把公网流量转发到集群内的 Service 上。在生产环境用得最多,但在本地环境用不上。以 NodePort 为例,一个最简 Service 配置如下:apiVersion: v1 kind: Service metadata: name: nginx-svc namespace: default spec: type: NodePort selector: app: nginx ports: - port: 80 targetPort: 80 nodePort: 30080这里port是 Service 对外暴露的端口,targetPort是 Pod 里容器实际监听的端口。很多新手容易把这两个搞混,导致端口明明是通的,但访问始终 403。我自己排查过一例:Service 的 port 写成了 8080,Pod 里的业务监听的也是 8080,但 NodePort 暴露的 30080 转发过去却不通,最后发现 targetPort 写成了容器内的 80,容器里根本没有进程监听 80,所有数据包都被丢了。创建 Service 的命令:kubectl apply -f nginx-svc.yaml创建后,可以通过下面的命令查看 Service 的详细信息:kubectl get svc如果状态正常,你会看到nginx-svc的PORT(S)列显示80:30080/TCP,表示集群内通过 80 端口访问 Service,外部通过NodeIP:30080访问。4.3 Pod 调度、滚动更新与回滚的现场观察应用部署起来之后,你可能会想体验一下 K8s 最吸引人的能力:滚动更新和快速回滚。滚动更新的场景很常见:你需要把 Nginx 镜像版本从1.25升到1.26。推荐的做法是修改 YAML 文件,把image字段改了,然后重新 apply:kubectl apply -f nginx-deploy.yaml此时执行:kubectl rollout status deployment/nginx-deploy你会看到类似下面的输出:Waiting for deployment "nginx-deploy" rollout to finish: 1 out of 3 new replicas have been updated...这就是滚动更新正在进行。K8s 会按照spec.strategy里定义的规则,先启动一个新副本,等它 Ready 后再杀掉一个旧副本,以此类推。默认的RollingUpdate策略会保证新老 Pod 交替替换,整个过程中服务不会中断。如果更新之后发现问题,回滚命令非常简单:kubectl rollout undo deployment/nginx-deploy在实战中,我强烈建议团队在每次正式更新前,记录好当前使用的镜像版本与对应配置,因为如果更新伴随 YAML 文件的多次修改,单纯依赖 undo 可能回滚到你不需要的历史版本。还有一个好习惯是开发成员每次发布前先跑一次金丝雀发布,而不是直接全量升级,尤其是业务流量还比较重要的时候。4.4 扩缩容实战:手动调整和快速压测验证扩缩容在 K8s 里是成本最低的操作。手动扩容只需要一条命令:kubectl scale deployment nginx-deploy --replicas=5执行完再看kubectl get pods,你会发现新的 Pod 正在被创建。在本地单节点环境下,你甚至可以在几秒钟内观察到它变成 Running 状态。不过我想多说一句:手动 scale 适合日常调试,但真正生产环境的容量弹性,一般依赖 HorizontalPodAutoscaler(HPA)来做自动伸缩。HPA 会根据 CPU 使用率或自定义指标动态调整副本数,这是后面要进阶的主题。入门阶段先掌握手动 scale,再理解 HPA 是如何选择指标的,会顺很多。我还发现一个提升感知很有效的小实验:在本地集群上部署一个压测工具(比如用 hey 或者 ab 简单打一下 Nginx 服务),观察 CPU 指标,然后用 HPA 把指标连起来,等你把请求打上去的时候,就能看到 Pod 自动扩容。这个实验对理解“声明式状态”非常有帮助。5. 常用运维操作与故障排查实战5.1 一套高频命令体系:describe、logs、exec 是排查三板斧K8s 入门后,用得最多的命令其实就三五个。我这里列一份“必会清单”:kubectl get ...查看资源的当前状态,比如kubectl get pods、kubectl get svc等。kubectl describe ...查看资源的详细信息,包括事件(Events),排查定位基本靠它。kubectl logs ...查看容器日志,排除业务本身的问题。kubectl exec -it ... -- /bin/sh进入容器内部调试,适合检查容器内的进程、网络、文件。kubectl port-forward把本地端口转发到集群内的 Service 或 Pod,临时调试很好用。我不止一次跟团队里新同学说,排查问题不要凭感觉瞎猜,一定要先kubectl describe。举个例子:Pod 一直没办法被调度,显示Pending状态,如果不 describe,你只能猜是不是镜像拉不到,但 describe 的输出里会直接告诉你Events里写的是FailedScheduling,原因是节点内存不足。事件里已经把结论了写得很清楚,直接看就行。5.2 Pod 状态机:从 Pending 到 Running,每一步都可能出问题Pod 在生命周期里有几个常见状态,我把它们串起来讲:Pending:Pod 已被集群接受,但还没运行。原因可能是调度器找不到合适的节点,或者镜像正在拉取。ContainerCreating:容器正在被创建,一般很快会变成 Running。如果卡在这里太久,大概率是镜像拉取太慢或存储卷挂载问题。Running:容器已经启动并正常运行。CrashLoopBackOff:容器启动后马上崩溃,然后被 K8s 反复重启,重试间隔会指数增加。Error或Failed:容器以非零状态退出。Terminating:正在被销毁。在入门实践中,CrashLoopBackOff应该是曝光率最高的一个状态。遇到它不要慌,按这个顺序排查:先用kubectl logs看应用日志,确认是不是业务启动失败。然后用kubectl describe看事件,确认容器退出原因。检查 YAML 里的command、args、env、volumeMounts是否配置正确。这里有一个很经典的例子:某个服务在本地用 Docker 跑得好好的,丢到 K8s 里就CrashLoopBackOff。排查半天发现,这个服务启动时会从一个文件里读配置,而那个文件在 K8s 环境里没有挂载进去。容器里缺文件,启动就报错。所以遇到崩溃重启,先检查“代码运行依赖的本地资源是否都通过卷挂载进去了”。5.3 镜像拉取失败的几种原因与解决路径新手本地实验时,最常见的另一种问题是ImagePullBackOff,也就是镜像拉不下来。我总结过大概三种原因:第一种是镜像地址写错。比如打错了一个字符,K8s 去仓库里找不到这个镜像。这时只需要核对image字段,并注意 tag。第二种是仓库认证问题。如果你用的是私有仓库,需要在 K8s 里创建一个imagePullSecret,并在 Deployment 的spec.template.spec.imagePullSecrets里引用它。第三种是网络问题,本地环境拉不到远程仓库的镜像。这种最让人头疼,因为 K8s 集群本身没有好的“重试”日志,只会在事件里反复显示Failed to pull image。对本地学习来说,解决网络问题最简单的方式是提前用 Docker 在本地docker pull好镜像,并给镜像打好本地 tag。如果是用 minikube,可以执行minikube image load nginx:1.25把镜像提前导入集群节点。这个操作建议收藏,非常实用。5.4 常见问题速查表为了节省你在入门阶段查来查去的时间,我把最容易踩的几个坑整理成一张速查表:症状常见原因排查与解决Pod 一直Pending节点资源不足kubectl describe node查看资源分配,扩容节点或清理占用Pod 反复CrashLoopBackOff应用启动失败或缺配置先查日志kubectl logs,再检查环境变量和卷挂载镜像拉不下来ImagePullBackOff镜像地址错误、认证失败、网络不通核对地址、创建imagePullSecret、预先导入本地镜像Service 访问不通selector 匹配错误或端口配置不对检查 YAML 里 selector、port、targetPort 三者对应关系更新后服务中断滚动更新策略过于激进增加maxSurge和maxUnavailable的合理配置执行 kubectl 报连接不上本地 kubeconfig 没生效检查~/.kube/config是否存在,集群是否在运行Pod 启动成功但日志里连接数据库失败网络策略或服务发现配置问题检查 Service 名称与端口是否匹配,必要时进入 Pod 用 nslookup 验证这张表挺值得贴在工位上。不是说遇到问题照着查就一定能解决,但它能帮你在面对一堆报错时快速锁定检查方向,不会乱成一团。5.5 给新手的几条避坑心得最后这部分,说几条我在实操中反复验证过的经验,文字不多,但每条都是从真心教训里提炼的:第一,不要在一知半解的时候直接改生产集群。本地集群怎么折腾都行,线上集群每一条命令都要带着敬畏执行,尤其是kubectl delete。我见过有人把整个 Deployment 误删了,然后发现没有备份 YAML,只能凭记忆重建,直接把服务恢复时间拉长了三倍。第二,尽量少用kubectl create,多用kubectl apply。前者是命令式操作,资源一旦创建,后续想全局改配置就比较费劲;后者能保持配置与 YAML 的一致性,改动内容和过程都容易追溯。第三,一定要培养“先 describe,后 logs”的习惯。很多新手遇到 Pod 起不来,第一反应就是看日志,但有些底层的错误(比如调度失败、镜像拉取失败、挂载失败)在容器日志里根本不会出现,必须通过describe看 Events 才能定位。可以说,describe是 K8s 运维的“第一现场”。第四,本地环境建议把资源配额调大一点,至少保证--memory=4096级别。我的一个同学在本地部署一个带数据库的应用时,因为默认资源限制太小,Pod 一直在被 OOM Kill,他一度以为是自己代码写得有问题,最后发现只是资源不够。按照我自己带人的经验,如果能把上面这些命令和排查逻辑完整操作一遍,从零到能独立部署一个小型服务、并且能自己定位常见的 Pod 启动问题,大概需要一到两天。跑通了后面的路就会顺很多。这个过程中最大的收获,其实不是记住了哪个命令,而是建立了对“声明式状态”的感觉:你不再像以前一样手把手地指挥每一台服务器,而是告诉集群“最终长什么样”,剩下的交给它自己完成。这种感觉刚接触时会有点不习惯,但用顺手之后,你会觉得运维这件事从来没这么省心过。