资讯详情

资讯详情

Kubernetes入门:从核心概念到集群实战的完整学习路径

聊到 Kubernetes以下简称 k8s很多人第一反应是“复杂”。Pod、Deployment、Service、Ingress、ConfigMap、RBAC……一堆名词扑面而来还没开始用就已经劝退了。我当年也是这样看了几篇概念文章脑子里只剩一个印象这玩意儿是给大厂准备的小项目根本用不上。但真正上手之后回头看Kubernetes 解决的是一个特别朴素的问题当你的应用多了、机器多了靠人肉去部署、维护、扩容已经完全不现实你需要一套系统来自动干这些事。它不是某个具体功能的替代品而是整个应用交付和运维方式的底座。不管你是后端开发、运维、测试还是做边缘计算、物联网、机器学习平台只要涉及容器、涉及多机部署、涉及服务高可用k8s 的基本功早晚要补上。这篇内容不是一个面面俱到的官方文档而是一条从零开始的学习路径。我会把 k8s 的核心概念、架构组成、常用对象、真实操作命令、常见问题排查一股脑串起来尽量用大白话讲清楚原理同时附上我实际踩坑后的经验。适合刚准备学 k8s 的开发者也适合已经部署过几个 Pod 但总觉得概念模糊、想系统梳理一遍的人。1. 为什么要学 Kubernetes它解决的是“规模”带来的问题1.1 从脚本部署到容器编排中间发生了什么先想一个场景你有三台服务器要部署一个 Web 服务、一个定时任务、一个消息消费者。一开始用脚本部署一个deploy.sh搞定服务器上配好环境进程用 systemd 管起来挂了自动拉起。这套方案在一台机器上完全够用而且稳定。但请求量大了你得扩容。加一台服务器手动装环境、拷贝代码、改负载均衡配置、重启服务半小时过去了。如果每周都要发版两次每次都要重复这套流程人就会开始想办法。容器在这里解决了一个核心问题把应用和它的运行环境打包在一起让“在哪里运行”变得不再重要。你本地能跑的容器镜像推到服务器上一样能跑环境差异被镜像层抹平了。可容器只是第一步。你有了十个容器分别跑在不同机器上谁负责把容器调度到空闲的机器某个容器挂了谁负责重新拉起访问量上来谁负责自动扩展多个服务之间怎么互相发现、怎么负载均衡这些“容器多起来了以后才出现的问题”就是 Kubernetes 的战场。1.2 Kubernetes 到底是什么控制平面加工作节点官方定义说它是“用于自动部署、扩展和管理容器化应用程序的开源系统”。这个定义很准确但不好理解。我更愿意把它比作一个“数据中心操作系统”。想象你的机房或者云上一堆服务器每台服务器就像一台“裸机电脑”而 k8s 把这些电脑的逻辑资源CPU、内存、磁盘汇聚在一起对外提供统一的抽象。你告诉系统“我要跑 3 个 Nginx 实例”它自动选择 3 台合适的机器把容器跑起来你说“这 3 个实例要给它们配一个统一的访问入口”它就创建一个 Service自动帮你做负载均衡。整个过程你不用关心具体是哪台机器就像你写代码时不用关心进程具体跑在哪个 CPU 核心上。这套系统的动作分解下来就两类控制平面Control Plane负责做决策工作节点Worker Node负责执行。控制平面上的组件包括 API Server所有操作的人口、Scheduler决定 Pod 放哪台机器、Controller Manager确保当前状态往期望状态靠近、etcd保存所有配置和状态数据的数据库。工作节点上则有 kubelet与 API Server 通信、管理本机容器的代理、容器运行时真正跑容器、kube-proxy实现 Service 的网络转发和负载均衡。理解了这两层后面所有概念都会变得顺理成章你在kubectl里敲的每条命令本质上都是在往 API Server 发请求API Server 把请求写入 etcdController Manager 和 Scheduler 监听到状态变化后开始工作最终把指令下发到具体节点上的 kubeletkubelet 再让容器运行时把容器拉起来。2. 核心对象逐个拆解Pod、Deployment、Service、Ingress2.1 Podk8s 里最小的调度单位但不是“一个容器”很多初学者第一个卡住的点就是 Pod。直觉上会觉得最小单位应该是容器为什么要多包一层因为有些应用天生“必须待在一起”。比如一个日志收集 sidecar 进程它需要和主应用共享网络空间、共享存储目录再比如一个代理进程它需要拦截主进程的流量。如果两个容器分别被调度到不同机器它们之间的通信就只能走网络延迟、安全、配置复杂度都上来了。Pod 把这组容器捆绑在同一个“运行环境”里同一个网络命名空间共享 IP 和端口、可以共享存储卷、生命周期完全一致。所以记住一个判断标准如果两个进程强耦合、必须同生共死、共享资源就放进同一个 Pod否则就不要往一个 Pod 里塞。实际工作中一个 Pod 里通常只有一个主容器这是标准姿势。多容器 Pod 属于高级用法不要一开始就强行设计。Pod 还有一个特点它是“有生之年非常短暂”的实体。节点挂了、资源不够、镜像拉取失败、被重新调度Pod 随时可能被删除重建。它的 IP 是动态的今天是这个地址明天可能就换了。所以生产中你很少直接操作单个 Pod而是通过上层控制器来管理它。2.2 Deployment声明期望状态剩下交给控制器Deployment 是你平时打交道最多的对象。它描述的是“我期望有多少个副本、用什么镜像、怎么更新”这样一种目标状态具体怎么实现由控制器负责。举个例子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: nginx image: nginx:1.25 ports: - containerPort: 80这段 YAML 声明得很清楚跑 3 个副本镜像用nginx:1.25标签是appweb-demo。控制器看到之后会持续检查当前存活的 Pod 数量发现只有 2 个就自动创建第 3 个发现有 4 个就删掉多余的。Deployment 还自带滚动更新能力。你修改镜像版本后执行kubectl apply它会分批创建新版本 Pod等新 Pod 健康了再销毁旧 Pod整个过程服务不中断。如果新版本启动失败滚动更新会暂停旧版本继续运行你有很多时间回滚。这套机制在生产环境的发布流程里非常重要。2.3 Service 与 Ingress从“Pod 会死”到“服务要稳”Pod 的 IP 会变、Pod 会被删除重建那谁能给客户端提供一个固定的访问地址Service 就是干这个的。Service 是一组 Pod 的访问抽象。它通过标签选择器Label Selector筛选出符合条件的 Pod然后给这组 Pod 分配一个稳定的虚拟 IPClusterIP再用 kube-proxy 把转发规则下发到每台机器上。客户端访问 ClusterIP 时kube-proxy 负责把请求负载均衡到背后的真实 Pod 上。Service 有几种类型ClusterIP只能在集群内部访问常用于服务间调用。NodePort在每个节点上开一个高危端口外部通过节点IP:端口访问。LoadBalancer云厂商提供负载均衡器把流量转发到 NodePort 或直接转发到 Pod。外部真正面向用户的 HTTP/HTTPS 流量一般用 Ingress 管理。Ingress 就是集群里的“七层网关”根据域名、路径把请求路由到不同 Service。你在 Ingress 里配置api.example.com路由到 A 服务、admin.example.com路由到 B 服务和用 Nginx 做反向代理的思路一模一样只是这个 Nginx 变成了声明式配置还能自动加载证书。2.4 其他高频对象ConfigMap、Secret、Namespace、StatefulSetConfigMap把配置从镜像里抽出来。环境变量、配置文件内容都可以放进去Pod 挂载后使用。这样可以做到同一套镜像在不同环境开发、测试、生产用不同配置。Secret存敏感信息比如数据库密码、API Key。和 ConfigMap 类似只是数据会做 base64 编码存储使用时挂载为文件或环境变量。Namespace集群内部的逻辑隔离单位。可以建dev、test、prod三个命名空间每个空间的资源、权限可以独立管理互不干扰。StatefulSet给有状态应用用的控制器。数据库、消息队列这类需要稳定网络标识、稳定存储、按顺序启动的应用用 StatefulSet 管理。它和 Deployment 最大的区别是每个副本有唯一且稳定的身份。3. 实操从零搭建一个单机集群并部署应用3.1 环境准备与工具选型学习阶段别一上来就搭三节点高可用集群那是运维干的活。先用工具快速起一个单机集群把概念跑通成本最低。我的建议是先用 kindKubernetes in Docker或者 k3s。kind 把整个集群跑在 Docker 容器里创建、销毁都很快特别适合本地学习和 CI 测试。k3s 是轻量级发行版单机安装一条命令搞定资源占用小还内置了 Traefik 作为 Ingress Controller对新手特别友好。如果只是学语法、调 YAML甚至可以用云平台上的托管集群很多云厂提供免费额度省去维护控制平面的成本。但我个人更推荐本地 kind因为你可以随便折腾把集群搞坏了直接删掉重建没有任何成本。需要提前装好的工具Dockerkind 依赖它kubectl和集群交互的命令行工具kind 或者 k3s按自己喜好选一个一个趁手的 YAML 编辑器VS Code 加 YAML 插件就够3.2 用 kind 创建集群并验证状态创建集群的命令非常简单kind create cluster --name learn等一两分钟一个单节点集群就建好了。然后验证一下kubectl cluster-info kubectl get nodeskubectl get nodes会看到一个节点状态应该是Ready。这就说明控制平面和 kubelet 已经正常工作了。这里解释一下发生了什么kind 在你本机的 Docker 里启动了一个容器这个容器内部运行着完整的 k8s 组件API Server、etcd、kubelet、容器运行时等。你的kubectl通过~/.kube/config里的配置连接到容器内的 API Server。所以你不用装任何虚拟机也不用买云服务器。之后你会频繁用到几个命令先记住它们kubectl get nodes # 查看节点 kubectl get pods -A # 查看所有命名空间的 Pod kubectl get deploy # 查看 Deployment kubectl get svc # 查看 Service kubectl logs pod-name # 查看 Pod 日志3.3 部署第一个应用并暴露服务集群建好了现在部署一个 Nginx 并让它能被本地访问到。第一步创建 Deploymentkubectl create deployment nginx-demo --imagenginx:1.25这个命令相当于帮你生成了一份 Deployment YAML 并提交到集群。执行完可以看一下状态kubectl get pods你会看到一个nginx-demo-xxxxxxxxxx-xxxxx这样的 Pod状态先是ContainerCreating过一会儿变成Running。这个过程中控制平面在协调一切Scheduler 把 Pod 调度到你的节点上kubelet 通过容器运行时拉取镜像然后启动容器。现在这个 Nginx 只能在集群内部访问外部还访问不到。我们需要一个 Service。把端口暴露出来kubectl expose deployment nginx-demo --typeNodePort --port80然后查看 Servicekubectl get svc输出里会有一个端口映射类似80:31456/TCP。这表示外部可以通过localhost:31456访问到这个 Nginx 服务。在浏览器里打开http://localhost:31456能看到 Nginx 欢迎页恭喜你第一个应用已经跑在 k8s 上了。试完可以直接删掉避免占资源kubectl delete deployment nginx-demo kubectl delete svc nginx-demo3.4 用 YAML 文件管理资源而不是命令kubectl create和kubectl expose适合快速验证真实项目里应该把所有资源定义写入 YAML 文件用kubectl apply统一管理。这样配置可以版本化、可以评审、可以回滚。下面是一份完整的 YAML包含 Deployment 加 ServiceapiVersion: apps/v1 kind: Deployment metadata: name: web-demo spec: replicas: 2 selector: matchLabels: app: web-demo template: metadata: labels: app: web-demo spec: containers: - name: web image: nginx:1.25 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: web-demo spec: type: NodePort selector: app: web-demo ports: - port: 80 targetPort: 80 nodePort: 30080保存为web-demo.yaml后执行kubectl apply -f web-demo.yamlapply命令的特点是幂等的第一次是创建后续再执行就是更新描述的是期望状态而不是“执行这个操作”这一下动作。这套声明式 API 是整个 k8s 最核心的设计思想花点时间理解它比背命令有价值得多。4. 深入原理控制器、探针、资源限制与调度4.1 控制器模式Kubernetes 的“自动驾驶”逻辑如果你理解了 Deployment 控制器那么 k8s 里几乎所有对象都遵循同一个模式你声明期望状态控制器负责把现实改造成期望状态。这个模式叫“控制循环”Control Loop。它由三部分组成观察当前状态、对比期望状态、执行纠正动作。控制器每分钟都在做这件事一旦发现偏差就采取措施。比如你的 Deployment 声明replicas: 3现在集群里只有 2 个 Pod控制器就去创建 1 个新的当前有 4 个控制器就删掉多余的有 3 个但其中一个不健康控制器先删掉异常的再创建一个补上。这种设计的好处是系统具备自愈能力。节点宕机了控制器检测到 Pod 失联后会在其他节点重新创建。不需要人来写“如果节点挂了就执行某某脚本”的逻辑这些已经在控制器内部实现好了。学习 k8s 的时候遇到一个新的控制器先问三个问题它管理什么对象它维持什么期望状态它怎么处理偏差这三个问题想明白了对象的行为模式就掌握了。4.2 探针让系统能判断应用是真的“活”还是“可用”k8s 能自动替换故障 Pod靠的是探针Probe。探针有三种存活探针livenessProbe判断容器是否还活着如果失败kubelet 会杀掉容器并重启。就绪探针readinessProbe判断容器是否已经准备好接收流量如果失败Pod 会被从 Service 的端点列表里摘除不接收新流量。启动探针startupProbe判断容器是否完成启动主要给启动时间很长的应用使用。启动探针成功之前其他探针不会启动。探针的检测方式有几种HTTP 请求指定路径、TCP 连接指定端口、执行容器内的命令判断退出码。常见配置如下livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 10这段配置的意思容器启动后等 5 秒开始探测每 10 秒请求一次/healthz连续失败阈值达到failureThreshold默认 3 次就重启容器。这里有个极易踩的坑把就绪探针的periodSeconds设得太短。我见过一个团队给 Java 应用设置每 1 秒探测一次结果应用启动时频繁触发探针导致 Pod 一直处于未就绪状态流量全部被摘除用户体验极差。建议按应用的启动速度来配启动探针的initialDelaySeconds大于实际启动时间的 1/3就绪探针periodSeconds设置 5 秒以上比较稳妥。4.3 资源请求与限制集群调度的核心依据每个 Pod 里的容器都可以声明resources包含requests和limits两部分resources: requests: cpu: 100m memory: 256Mi limits: cpu: 500m memory: 512Mirequests是调度时用的依据告诉调度器“这个容器至少需要这么多资源”limits是运行时用的上限容器不能超过这个配额。100m表示 0.1 个 CPU 核心256Mi表示 256 兆内存。很多新手只写limits不写requests这是个大坑。因为调度只看requests你只设limits的话调度器会认为这个容器不需要资源可能把所有 Pod 都塞到一台机器上运行后再被limits限制性能反而更差。合理做法是先不设limits只设requests观察一段时间内的真实峰值再按峰值加 20% 缓冲设置limits。内存的limits尤其要小心超过之后容器会发生 OOMKilled直接被杀掉。CPU 超过limits只是被限流内存超过则是被杀这点务必记牢。4.4 调度器怎么决定 Pod 去哪台机器Scheduler 的决策分三步过滤Filtering、打分Scoring、绑定Binding。过滤阶段先把不满足条件的节点排除掉资源不够、有污点不允许调度、节点处于 NotReady 状态都会在这一步被踢出局。打分阶段根据各种策略给剩余节点打分比如资源越空闲分越高、已分配的 Pod 越少分越高。最后把 Pod 绑定到得分最高的节点上后续 kubelet 负责实际启动容器。这个流程不难理解但有几个高级概念新手容易忽略nodeSelector可以指定 Pod 只跑在带某个标签的节点上亲和性Affinity描述的是“优先靠近某些 Pod 或节点”污点和容忍Taints and Tolerations则是控制“哪些节点不允许普通 Pod 调度上来”。生产环境做异构集群比如 GPU 机器单独标注时这些概念才会真正派上用场。5. 常见问题与排查技巧实录5.1 镜像拉取失败ImagePullBackOff症状Pod 状态一直是ImagePullBackOff或者ErrImagePull。排查步骤kubectl describe pod pod-name重点看 Events 字段最下面几条。常见原因有三个镜像名写错、标签版本不存在、私有仓库没有配置认证。第三个问题最常见需要在 Pod 里配置imagePullSecretsspec: imagePullSecrets: - name: registry-secret如果用的是 Docker Hub还要注意限流问题匿名拉取有每小时次数限制频繁重建 Pod 容易触发。解决方法是登录 Docker Hub用个人访问令牌生成拉取密钥并配置到 ServiceAccount 上。5.2 Pod 一直 Pending 不 Running症状Pod 卡在Pending几分钟动不了。Pending意味着调度还没完成先执行kubectl describe pod pod-name看到0/3 nodes are available这类提示后面会跟具体原因。常见几种资源不足Insufficient cpu、Insufficient memory说明节点空闲资源不够。要么扩容集群要么调低requests。端口冲突port already used把hostPort去掉或者换端口。节点有污点taint相关提示要么删掉污点要么在 Pod 上加对应的容忍。还有一种情况不是调度问题而是拉取镜像失败后重试时间很长状态也会显示Pending用kubectl describe仔细看 Events。5.3 Pod 反复重启CrashLoopBackOff症状Pod 状态CrashLoopBackOff说明容器启动后立刻崩溃被 kubelet 反复重启。先看日志kubectl logs pod-name --previous--previous参数很关键它显示上一次崩溃的容器日志因为当前容器可能根本没来得及写日志就退了。常见原因有启动命令出错args或command写错进程直接退出。环境变量缺失应用启动时读不到某个配置直接 panic。配置不合法挂载的 ConfigMap 内容不对应用启动校验失败。livenessProbe配置太激进进程其实活着但探针没通过被杀又重启。处理 CrashLoopBackOff 时切忌只看当前日志一定配合--previous和describe一起看否则很容易被“假日志”带偏。5.4 Service 不通外部访问不到 Pod症状Service 创建了但通过 ClusterIP 或 NodePort 访问超时。排查思路从外往里走kubectl get endpoints service-name如果 Endpoints 列表为空说明 Service 的selector没有匹配到任何 Pod。先检查 Pod 的标签kubectl get pods --show-labels再检查 Service YAML 里的selector必须完全匹配k8s 的标签匹配是精确相等的。如果 Endpoints 有值但 NodePort 访问不通检查 kube-proxy 是不是正常运行了kubectl get pods -n kube-system | grep kube-proxy还有一种很容易被忽略的情况集群网络插件CNI没装好Pod 之间网络不通。本地 kind 一般自带网络方案但如果是二进制方式部署的集群忘了装网络插件Pod 之间永远不互通。装网络插件时注意版本兼容不同插件对内核版本和 iptables/nftables 的依赖不一样。5.5 问题排查速查表现象第一步操作高频原因Pod Pendingkubectl describe pod资源不足、端口冲突、污点ImagePullBackOffkubectl describe pod镜像名/版本错误、私仓没认证CrashLoopBackOffkubectl logs --previous启动命令错、配置缺、探针过激删除不了的 Podkubectl get ns检查 terminating命名空间里还挂着资源Evictedkubectl describe pod看 FreeMemory 等指标节点内存/磁盘压力太大触发驱逐Node NotReadykubectl describe nodekubelet 失联、CNI 异常、磁盘满排查这类问题时我最常使用的套路是先describe看事件再logs看应用日志最后才去翻配置。事件视图往往几句话就指向根因比自己瞎猜高效得多。6. 从入门到能干活学习路径与避坑建议6.1 分阶段学习路线如果完全零基础建议按顺序走完下面这几个阶段每个阶段都动手验证后再往下走。第一阶段容器基础。花几天把 Docker 镜像构建、容器运行、数据卷、网络模式搞熟。k8s 的一切都建立在镜像之上镜像推不上仓库后面全是白搭。第二阶段单机 k8s。用 kind 或 k3s 把集群跑起来把本文涉及的 Pod、Deployment、Service、ConfigMap 全部手动创建一遍改动配置观察变化。这一阶段目标不是背命令而是建立“声明式操作”的心智模型。第三阶段真实项目改造。找一个自己写的简单服务打成镜像加上 Deployment、Service、Ingress 部署到集群里。这个练习非常关键因为你会在打包镜像、写 YAML、改配置的过程中遇到所有文档里没写的问题。我当年花了一周把一个 Flask 应用迁移上集群踩的坑比看两个月文档都多。第四阶段进阶能力。开始学命名空间隔离、RBAC 权限控制、持久化存储PV/PVC、Helm 打包部署、Operator 和自定义资源。这些内容可以等你工作中真的需要再深入前期不必贪多。6.2 我是怎么避坑的几条实战心得镜像要写精确版本不要用latest。latest标签的镜像是会变的你在某天拉到的版本和别人拉到的可能不一样复现问题变得极其困难。生产环境里镜像的 tag 应该是版本号加 Git commit 的组合比如web-app-v1.2.0-a3f9c1这样能准确定位代码状态。YAML 缩进错误是新手提报率最高的问题。selector、template、metadata这几类字段的层级关系很容易搞错。建议所有 YAML 都先用kubectl apply --dry-runclient校验一遍kubectl apply -f web-demo.yaml --dry-runclient --validatefalse -o yaml或者直接kubectl explain查字段层级这是被很多人忽略的好命令kubectl explain deployment.spec.template.spec.containers.resources一个命名空间里不要塞太多东西。刚开始学的时候图省事把所有测试资源都放在default命名空间结果后面垃圾资源越积越多查询起来极其费劲。从第一天就建立“一个项目一个命名空间”的习惯后面管理成本低很多。6.3 kubectl 高频命令速查随手整理一份我每天都用的命令清单把这些用熟日常操作基本覆盖了kubectl get all -n namespace # 快速看命名空间里所有资源 kubectl describe resource name -n namespace # 看资源详情和事件 kubectl logs -f pod-name -n namespace # 跟踪日志 kubectl exec -it pod-name -- /bin/sh # 进入容器调试 kubectl get events -n namespace --sort-by.lastTimestamp # 按时间排序看事件 kubectl rollout status deployment/name # 查看滚动更新状态 kubectl rollout undo deployment/name # 回滚到上一版本 kubectl top nodes / pods # 看资源实际占用kubectl top是我最后才学会用但后悔没早用的命令。它直接显示节点和 Pod 的实时 CPU、内存占用排查资源瓶颈和配置requests时特别有用。要注意的是它依赖 metrics-server本地 kind 默认不一定装需要手动部署。7. 写在最后学 k8s 最忌讳的是“只看不做”。概念看十遍不如亲手kubectl apply一遍报错报得多了理解自然就深了。我在实际使用中最大的感受是k8s 本身不难难的是概念太多、抽象层次太厚一开始很容易迷失在细节里。你只需要记住最根本的一条主线——声明期望状态控制器自动维持——然后围绕这条主线去理解其他功能思路就会清晰很多。最后再分享一个小技巧当你对某个对象的行为不确定时直接在集群里创建它再用kubectl describe看它的最终状态和事件列表。k8s 的事件机制会把整个决策链路的关键节点记录下来这比看任何文档都直观。保持好奇心多折腾你的 k8s 基本功会比你想象的更扎实。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →