
1. 从一台服务器到一群服务器为什么我们需要虚拟化编排工具先聊点实际的。如果你接手过稍微像样点的业务一定遇到过这种场景上线一个应用要准备机器、装环境、改配置、起进程再一遍遍跟运维确认端口通没通。第一台机器还好第二台也勉强到第五十台的时候你就开始怀疑人生了。更别提流量一涨你得手动把应用复制到新机器上注册进负载均衡然后祈祷不要再出幺蛾子。我早期做部署就是这么干的后来接触了 Docker再后来被同事按着头学了 Kubernetes才彻底明白什么叫“大解放”。Kubernetes简称 K8s是目前全球最主流的容器编排平台。它本质上是一个虚拟化编排工具用来管理大量容器的创建、调度、伸缩、故障恢复和网络连通。不懂概念的话你可以把它想象成一个高度智能的快递分拣中心你只负责把包裹容器放到传送带上剩下的自动称重、分拣、装车、运输都由调度系统完成。你的应用是容器K8s 就是那个能把成千上万个容器安排得明明白白的调度中枢。这篇文章的核心价值就是帮你把 Kubernetes 从“听过名字”变成“能上手用”。我准备了适合零基础入门的核心概念拆解也准备了基于 Nginx 部署的完整实操记录最后还有一堆我踩过的坑和排查套路。学完你至少能看懂 K8s 的大部分资源清单能独立把一个应用跑起来并对集群运作机制有基本的判断力。2. 整体架构与部署方案的选型逻辑K8s 的学习曲线确实陡峭但陡不在概念多而在概念之间的关系太抽象。所以第一步我的建议不是直接敲命令而是先把它的整体架构印在脑子里。你不需要背住每个组件但要知道谁在干什么、东西往哪里走。2.1 控制面与工作节点集群的“大脑”和“四肢”Kubernetes 基本可以拆成两部分控制面Control Plane和工作节点Worker Node。控制面是集群的“大脑”它由四个关键组件构成kube-apiserver所有请求的唯一入口是集群的“前台接待”。你敲的每条 kubectl 命令最终都会打到它这里。etcd集群所有数据的“保险柜”负责保存状态比如有哪些节点、哪些 Pod、期望什么状态。它是 K8s 的最终事实来源。kube-scheduler负责决定一个新 Pod 应该放在哪台节点上。它会根据内存、CPU、亲和性等条件做调度。kube-controller-manager负责“盯状态”的纠察队。它通过 apiserver 持续观察集群现状一旦现状跟目标状态不一致就启动修复流程。比如 Deployment 期望 5 个副本实际只有 3 个它就会补拉起 2 个。工作节点则是真正跑应用的地方每台节点上必须装有三个组件kubelet节点上的“监工”负责与 apiserver 通信并执行下发的任务管理本节点的 Pod 生命周期。kube-proxy负责节点上的网络规则为 Service 提供负载均衡能力流量从 Service 过来后分发到后端 Pod。容器运行时Container Runtime真正跑容器的引擎比如 containerd或者旧版本的 Docker。一个常见的问题如果被管理的节点挂了怎么办答案是控制面会等待一段时间默认大约 5 分钟后认为节点失联然后在其他健康节点上重建 Pod。这种机制保障了业务的自愈能力也是 K8s 能做大规模服务的一个重要原因。2.2 选型思考为什么不是 Docker Compose 或裸跑容器我知道很多人脑海里有一个疑问我用 Docker Compose 不是也能跑多个容器吗为什么非要整 K8s关键区别在于覆盖的故障域不同。Docker Compose 是“单机多容器编排”它解决的是“一台机器上容器怎么配合”的问题。K8s 解决的是“几十甚至几百台机器上容器怎么调度、怎么自愈、怎么平滑升级”的问题。你拿 Docker Compose 做生产多节点高可用会发现节点宕机之后一切都凉了而 K8s 会在设计层面就把“节点宕机”当作常态事件来处理。另外一个容易被低估的差异是“声明式配置”。K8s 里你写的是“我要什么状态”而不是“帮我执行什么命令”。比如声明式我要 3 个 Nginx 副本随时可用。命令式帮我启动 nginx 容器。这两种表达方式的背后是完全不同的运维哲学。声明式能让机器自动对系统状态进行调谐命令式则依赖人去执行每一步。生产环境里人总会犯错所以把“期望状态”交给系统去维护是更可靠的方案。这也是 K8s 最值得学的设计思想之一不光是学命令更是学这一套状态驱动、自治修复的思维方式。提示如果你是刚开始学建议直接安装 minikube 或 k3s 这类轻量级集群。别一开始就纠结建生产集群先把单节点玩明白再逐步了解高可用架构。3. 核心概念拆解与新手必知的资源清单学习 K8s 最容易懵的一点就是迎面砸来一堆英文名词Pod、Deployment、Service、Ingress、Namespace……这些概念如果不建立联系看 yaml 就像看天书。我建议你按“从小到大”的顺序来搞懂它们。3.1 Pod 与 Deployment最小调度单位和期望状态Pod是 K8s 中最小的调度单元一个 Pod 里可以装一个或多个容器。可为什么 K8s 不直接调度容器非要包一层 Pod因为有些业务场景里两个进程必须“同生共死”、共享网络、共享存储。比如一个日志采集容器需要跟业务容器并肩运行把业务日志捞走。如果只调度容器就很难把“一组容器的整体状态”管理起来有了 Pod 这层抽象“同生共死”就变成天然的约束。但生产里你不会直接建 Pod因为裸 Pod 不具备自愈能力机器一挂它就没了。你更常用的资源是Deployment。Deployment 的工作方式是你声明“我要 3 个副本”它创建一个 ReplicaSet让 ReplicaSet 始终去维持 3 个 Pod。这中间多出来的管理者层次让你能非常方便地做滚动更新、回滚、横向伸缩。用一个例子来说明整体关系apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80这份清单里最关键的一行是selector.matchLabels它决定了哪些 Pod 归这个 Deployment 管。如果 Pod 上打有app: nginx这个标签那就归它管。这是 K8s 的关联方式不是靠“名字”硬绑而是靠“标签”做松耦合关联。理解了这一点你就能明白为什么很多 yaml 里反复出现app: xxx这种标签了。3.2 Service 与 Ingress服务发现与统一入口Pod 是有生命周期的说没就没。如果让外部直接访问 PodPod 一重建IP 就变了这显然没法用。于是 K8s 引入了Service提供稳定的虚拟 IP 和 DNS 名字。Service 背后自动关联一组符合条件的 Pod并把流量均衡分发到每个 Pod 上。Service 有几种类型新手阶段最常见的是ClusterIP集群内部访问给 Service 一个集群内部 IP适合微服务之间的互相调用。NodePort在集群每台节点上开一个固定端口外部通过“节点IP:端口”访问服务。LoadBalancer对接云厂商的负载均衡器是云环境里的常用方案。为了让 Service 能找到 Pod核心还是靠标签选择器。比如下面的配置会把标签为app: nginx的所有 Pod 整合成一个稳定的访问入口apiVersion: v1 kind: Service metadata: name: nginx-service spec: type: NodePort selector: app: nginx ports: - port: 80 targetPort: 80 nodePort: 30080而Ingress则是更上层的七层入口负责把 HTTP/HTTPS 流量按域名或路径转发到不同 Service。它就像大楼门口的物业前台访客说“我要去 301 室”前台根据门牌号指引到对应房间。Ingress 适合做域名路由、HTTPS 证书管理、以及多服务共用同一个入口的场景。相比每个 Service 都暴露一个 NodePortIngress 的方式要干净太多。3.3 Namespace 与其他常用资源Namespace命名空间是 K8s 里做隔离的一种逻辑划分机制。它不隔离网络主要用来隔离对象名称和管理边界。比如你可以建一个dev命名空间放开发环境的应用建一个prod命名空间放生产应用。两个命名空间里可以有相同名字的 Deployment互相之间不冲突。其他常见的资源还有ConfigMap存放非敏感配置比如环境变量、配置文件内容。Secret存放敏感信息比如数据库密码、API Key存储时用 Base64 编码。PersistentVolumeClaimPVC声明应用需要多少存储空间。DaemonSet确保每个节点上都运行一个 Pod典型的用途是日志采集组件。StatefulSet管理有状态应用跟 Deployment 类似但会为每个 Pod 分配稳定的网络标识和存储。对于新手来说最先要掌握的四个资源是Deployment、Service、Pod、Namespace。先把这四个玩熟大部分入门操作都能应付再往后碰到 ConfigMap、Secret、Ingress 时你会发现因为基础概念的通了理解起来非常快。4. 完整实操在 Kubernetes 上部署 Nginx概念说再多都不如亲手把一个 Nginx 跑起来。这一节我带你完整体验一遍从零部署 Nginx 的流程包括编写资源配置、应用配置、验证服务、水平扩容和滚动更新。哪怕你本地还没有集群也可以先准备一个 minikube按照下面的步骤走一遍。4.1 环境准备与 kubectl 连接检查在开始之前确保你的环境里有 kubectl 命令行工具并且能连上集群。我本地用的是 minikube安装完直接跑minikube start --driverdocker --cpus2 --memory4096这个命令会启动一个单节点集群Docker 作为驱动。如果你的机器内存不够也可以把内存降到 2048但最小不建议低于 2048否则压测或者拉镜像时容易卡顿。启动后检查 kubectl 能否正常通信执行下面的命令看输出是否为节点信息kubectl cluster-info如果提示“Unable to connect”优先检查环境变量KUBECONFIG是否指向正确的配置文件以及当前 context 是否选对kubectl config get-contexts kubectl config use-context minikube实际经验kubectl 大部分连接问题都出在 context 指错了集群上。你用多个集群时一定要养成每次操作前先看kubectl config current-context的习惯否则搞错集群易发生误操作。4.2 编写 Deployment 配置并创建应用先建一个工作目录k8s-nginx-lab然后在里面写deployment.yamlapiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80这里有几个细节需要解释清楚避免你以后栽跟头apiVersion 不能乱写Deployment 稳定版的 apiVersion 是apps/v1。早期版本里的extensions/v1beta1早就不推荐使用了。image 要写 tagnginx:1.25表示明确版本。如果只写nginxK8s 会默认用 latest 标签而生产环境用 latest 是很大的隐患因为后续更新无法控制、回滚也难以做。replicas 是期望值不是一次性拉起的硬性指令而是系统要维持的目标值。Node 宕机后控制器会在其他节点补副本就是这个字段在起作用。在编写完配置后执行创建命令并查看状态kubectl apply -f deployment.yaml kubectl get deployment kubectl get pods -o wide正常情况下你会看到 3 个 Pod 短暂处于ContainerCreating然后转成Running。如果一直卡在ImagePullBackOff一般是镜像拉取失败要么是版本号不存在要么是网络无法访问镜像仓库。4.3 创建 Service 并暴露服务Deployment 建好了但此时外部访问不到 Nginx。因为 Pod 的 IP 是集群内部的只能在集群内部访问。要对外暴露需要创建 Service。这一步我会用 NodePort 类型因为它在任何本地集群里都能直接验证。编写service.yamlapiVersion: v1 kind: Service metadata: name: nginx-service spec: type: NodePort selector: app: nginx ports: - port: 80 targetPort: 80 nodePort: 30080参数说明port是 Service 自己对外暴露的端口其他集群内服务访问时使用。targetPort是容器内业务进程实际监听的端口Nginx 监听 80。nodePort是节点上对外开的端口范围必须是 30000-32767默认配置。如果你省略 nodePortK8s 会自动分配一个范围内的随机端口。执行kubectl apply -f service.yaml kubectl get svc这时你会看到类似下面的输出nginx-service的 TYPE 为 NodePort对外端口是80:30080/TCP。接下来在浏览器访问http://节点IP:30080Node IP 可以通过minikube ip获取。如果想用 curl 直接验证则执行curl http://$(minikube ip):30080如果输出一堆 Nginx 默认欢迎页的 HTML说明部署链路已经通了。4.4 验证负载均衡与水平伸缩Service 建好后我们验证一下它有没有把流量均衡转发到 3 个 Pod。换个思路不访问 Nginx 页面而是登录到某个 Pod 里看自己的主机名kubectl exec -it nginx-deployment-xxxxx -- hostname多执行几次会发现返回的主机名可能不同说明 Service 确实把请求分散到了不同后端 Pod。不过要注意这种在 Pod 里执行命令的方式适合调试不适合生产环境排查因为生产环境通常没有多余 exec 权限给你。接着演示水平伸缩。这是 K8s 的核心价值之一业务流量涨了一条命令加副本没流量了一条命令缩回去kubectl scale deployment nginx-deployment --replicas5 kubectl get pods -o wide如果想验证滚动更新可以把镜像版本从nginx:1.25改成nginx:1.26然后重新 applykubectl apply -f deployment.yaml kubectl rollout status deployment/nginx-deployment kubectl rollout history deployment/nginx-deployment这套机制让你做升级时无需停服。K8s 会逐个替换旧 Pod旧 Pod 全部退掉前新 Pod 已经就绪并承接流量整个过程中 Service 的访问不会中断。注意如果 yaml 里没有把image变化体现出来kubectl apply会告诉你结果没变化。想让 deployment 重新拉一个新镜像要么改镜像 tag要么用kubectl set image deployment/nginx-deployment nginxnginx:1.26命令来触发。5. 常见问题与排查技巧实录K8s 的应用逻辑一旦跑通剩下的日子大概率会花在排查问题上。我把自己和身边同事踩过的高频问题整理成了一张速查表放在这里希望你能少走些弯路。5.1 高频问题速查表现象可能原因排查手段解决参考Pod 一直Pending节点资源不足或调度约束不满足kubectl describe pod pod看 Events扩容节点或移除nodeSelector等约束Pod 一直ImagePullBackOff镜像名/标签拼写错误或镜像仓库不可达kubectl describe podkubectl logs查看拉取错误修正镜像地址配置 imagePullSecretPod 一直CrashLoopBackOff容器启动后立即崩溃循环重启kubectl logs --previous看崩溃前日志排查应用启动参数、依赖服务、挂载配置Service 不通Pod 正常标签选择器不匹配或 targetPort 对不上检查 Service 的 Endpoints 是否有地址kubectl get endpoints修正 selectorNodePort 无法访问防火墙未放行或 nodePort 超出范围ss -lntp看端口监听检查集群安全组放行端口改用合法范围内的 nodePort删除后 Pod 又自动起来Deployment/ReplicaSet 在维持期望状态kubectl get replicasets查看要删除资源得删 Deployment而不是删 Pod5.2 不同场景的排查思路分析场景一Pod 停在 Pending这是新手最容易遇到的情况。Pending表示 Pod 还没有被调度到任何节点上。最直接的办法是看详细事件kubectl describe pod nginx-deployment-xxxxxEvents 区域会给出明确线索比如0/1 nodes are available: 1 Insufficient cpu说明节点 CPU 不足。有时候也会提示node(s) had untolerated taint说明节点有污点Pod 没有对应容忍度。场景二Service 访问 502 或者超时Service 不通时第一步不是看 Pod 日志而是先看 Endpointskubectl get endpoints nginx-service如果 Endpoints 列表为空说明 Service 的 selector 没匹配到任何 Pod。检查 Deployment 里 Pod 的 labels 和 Service selector 是否一致这个是最常见的坑。如果 Endpoints 有 IP那就从 Service 的 ClusterIP 直接 curl 测试能通说明链路正常不能通再进一步查 kube-proxy 规则。场景三镜像更新后 rollout 卡住执行kubectl rollout status后一直等待大概率是新 Pod 启动失败或者 readness 探针没过。这时先看新 Pod 状态kubectl get pods如果新版 Pod 处于CrashLoopBackOff可以用kubectl logs deployment/nginx-deployment直接看日志很多明显的启动错误一眼就能看出来。5.3 我踩过的一些经验教训第一yaml 里的缩进是硬性规定。K8s yaml 对缩进极其敏感一个空格错位apply 时不会报语法错误而是直接error: error validating。我建议你坚持使用空格缩进统一用 2 个空格不要用 Tab因为 K8s 对 Tab 非常不友好。第二删不掉的资源大多数是因为有“爸爸”在管着它。很多新手直接删 Pod发现删掉后又自动弹出来以为见鬼了。实际上是因为 Deployments 或 StatefulSet 还在运行它们会保证“期望副本数”不变。不是要删除 Pod而是要删除上层控制器。第三namespaces 会把你的命令“禁锢”在一个视图里。如果你不确定某个资源在哪个命名空间先用kubectl get all -A查看全部空间的资源。-A这个参数能帮你省下大量的“为什么查不到”的疑惑。第四镜像版本要显式声明。我踩过一次很尴尬的坑写image: nginx:latest第二天环境里 Nginx 自动升级出兼容性问题排查了整整一上午。生产环境请一律使用明确的 tag不要让部署依赖“latest”。6. 从“能跑”到“会用”后续扩展与学习建议前面这一套 Nginx 部署实验做完你已经能上手“把一个静态服务跑起来”了。但真实的业务环境远不止这么简单。这里简单讲讲接下来可以往哪些方向继续深入以及我总结的一些思路。6.1 进阶方向一配置管理实际项目里几乎没有只跑 Nginx 就够了的情况你大概率需要把环境配置传给容器。比如前端项目需要后端 API 地址或者应用需要特定的日志级别。此时就应该把配置从镜像里抽出来放进 ConfigMap 或 Secret。apiVersion: v1 kind: ConfigMap metadata: name: nginx-config data: nginx.conf: | server { listen 80; server_name example.com; location / { proxy_pass http://backend-service; } }然后在 Deployment 的 volumes 里挂载这个 ConfigMap通过文件的形式覆盖容器里默认的 Nginx 配置。好处是改配置不用重新打镜像改完 ConfigMap 再加一次kubectl rollout restart即可生效。6.2 进阶方向二存储容器是无状态的进程重启后数据就没了。但很多业务比如数据库、文件存储明明需要持久化。此时你需要 PVC 和 PV 这套机制。举个例子部署 MySQL 时你需要给 MySQL Pod 申请一块持久化存储并确保 Pod 重建后数据不丢。StatefulSet 在这里比 Deployment 更合适因为数据库对标识、顺序、稳定性有要求StatefulSet 能保证每个 Pod 有固定的身份和存储绑定。这块知识偏运维但如果你想在生产环境落地绕不开它。6.3 进阶方向三网络策略与 Ingress 治理NodePort 适合实验和演示生产环境大概率不会用它做对外入口因为端口很多、管理混乱、安全上也难以控制。推荐的做法是部署一个 Ingress Controller比如 Nginx Ingress Controller 或 Traefik统一承接 HTTP/HTTPS 流量然后通过 Ingress 资源把同一入口转发到集群内部的多个 Service。Ingress 还能很方便地配置 HTTPS 证书。你把证书放到 Secret 里然后在 Ingress 上声明 TLS就不需要自己维护反向代理服务器K8s 会在入口层帮你在整条链路上终止 TLS。6.4 建议的学习路径我不太推荐上来就啃官方文档。我的建议是自己搭一条循序渐进的路线先把 Docker 容器基础吃透理解镜像、容器、卷的基本概念。用 minikube 或 k3s 本地跑起来至少把 Deployment、Service、配置、存储都手动部署一遍。再学 Helm。Helm 是 K8s 的包管理工具能把一堆 yaml 打包成可复用的 Chart。实际工作里你直接手写若干 yaml 的场景很少大多是基于 Helm 模板做修改早学会能大幅缩短适应期。最后是集群层面的运维比如多节点部署、备份恢复、监控告警。如果你不是专职运维可以用托管集群比如各大云厂商的托管 K8s 服务来省掉一部分底层维护负担。实操心得和我踩过坑后的建议一样不管你在什么阶段都要养成一个习惯每次改动配置前把核心资源先导出来做备份比如kubectl get deployment nginx-deployment -o yaml backup.yaml。这个习惯会帮你省掉无数还原现场的时间。7. 最后分享两个实用小技巧我在实操里发现K8s 有很多看起来不起眼、但用起来极其顺手的命令这里挑两个最值得记住的分享出来。第一个是kubectl describe。它和kubectl get是不同的信息维度。get告诉你“资源现在是什么状态”describe会告诉你“它为什么会变成这个状态”。除了第一部分基本信息最关键的要看 Events 段那里记录了调度、拉取镜像、启动容器等关键事件。遇到 Pod 起不来优先执行kubectl describe pod name而不是先去翻应用日志因为很多问题发生在容器真正启动之前。第二个是kubectl get xxx -w。-w表示持续监视变化类似 Linux 命令watch的效果。例如执行kubectl get pods -w你能实时看到 Pod 从Pending、ContainerCreating到Running的整个过程。排查滚动更新和伸缩问题时这个命令能提供非常好的时间线视角。另外如果你在多个集群之间切换比较多可以给 context 起容易被记住的名字在操作前先用kubectl config current-context明确当前所在集群。这个习惯我在生产环境吃过亏后才彻底养成的分享给你希望能帮你省去不必要的惊吓。Kubernetes 值得投入时间一旦跨过最初的陡峭学习曲线它能给你带来的自动化能力回馈会非常可观。