资讯详情

K8s从零到一:容器编排原理、集群搭建与生产实践指南

📅 2026/10/20 22:15:15 | 华诺云谱 👁 阅读
K8s从零到一:容器编排原理、集群搭建与生产实践指南
做了这么多年容器化改造几乎每个团队最后都要走到 Kubernetes 这一步也就是大家常说的 K8s。原因不是因为它名字念着顺口而是当你的容器数量突破几十个、上百个之后靠脚本和手工去管理根本撑不住。K8s 解决的核心问题就是容器太多怎么自动调度、自动恢复、自动扩展。这篇文章我想用自己的实操经验完整讲一遍从零到一认识 K8s、再到把应用稳稳跑在上面的全过程。不管你是刚接触容器的开发还是正打算把微服务迁上 K8s 的运维都可以照着这条路走一遍。需要提前说明的是K8s 的学习曲线确实有点陡。但它真正的难度并不在概念多而在声明式这套思维方式——你告诉集群我想要什么状态剩下的它自己想办法达成。刚上手的人最容易卡在这里所以我先把这条主线讲透再一步步带你把集群搭起来、把应用跑起来、把问题排掉。1. 先搞清楚 K8s 到底是什么为什么非学不可1.1 容器编排要解决的三个实际问题先说结论K8s 是一个容器编排平台核心职责可以压缩成一句话——在成千上万台机器上自动把容器放到合适的位置运行并保证它一直处于你期望的状态。拆开来讲K8s 解决的是三个再实际不过的问题调度集群里有好几台机器新容器到底放哪台是挑 CPU 空闲的还是挑和调用方网络最近的人工指定一两台没问题规模一上去就只能交给调度器做决策。自愈某个容器崩了谁把它拉起来某台机器宕机了上面跑的容器怎么迁移到别的机器K8s 的控制器会持续对比当前状态和期望状态发现问题就自动纠正。伸缩流量涨了应用实例不够用怎么办流量降了资源白白占着怎么办K8s 可以根据监控指标或者你的配置自动增减容器副本数。这三个问题本质上就是分布式系统在运行阶段必须面对的日常操作。Docker 教你怎么把应用装进集装箱K8s 则告诉你这些集装箱怎么在港口里自动装卸、堆叠、转运。没有编排层容器化应用跑在两三台机器上还行再往上走运维成本会像滚雪球一样膨胀。1.2 从 Docker 到 K8s中间隔着一座调度大山很多初学者最初会用 Docker Compose 管理容器就是把多个容器写在一个 YAML 文件里在单台机器上按依赖关系启动。这套方案对本地开发和单机测试来说非常方便但一旦到了生产环境问题就出现了Compose 没有跨节点调度能力一台机器挂了就是单点故障它也不管负载均衡和服务发现多个副本之间的流量分配全靠外部介入。K8s 和 Docker 最本质的区别在于它是一个完整的集群操作系统。你可以把它理解成一个机器的操作系统Docker 是跑在单机上的进程管理器K8s 则是跑在整个集群上的资源管理器。它会持续监听集群状态做决策再通过各个节点上的代理去执行决策。这个架构决定了它能承载的规模和服务等级完全不一样。还有一个经常被忽略的点K8s 并不依赖 Docker 作为唯一的容器运行时。早期确实主要用 Docker后来业界逐步迁移到 containerd、CRI-O 这些更轻量的运行时。学习阶段你只要掌握一个通用概念就行——K8s 通过容器运行时接口CRI和底层运行时打交道你写的 YAML 不直接关心运行时是谁它们之间的解耦让整个体系更灵活。2. 入门阶段最该吃透的核心概念2.1 一切皆 Pod最小编排单位的底层逻辑K8s 里最小的调度单元不是容器而是 Pod。一个 Pod 里可以有一个或多个容器这些容器共享同一个网络命名空间和存储卷。很多人一开始不理解既然容器已经够小了为什么还要在外面包一层原因其实很实在有些应用之间有极强的同生共死关系比如一个主进程配一个日志采集进程、一个代理进程它们必须运行在同一台机器上而且要共享网络栈否则要么连不上要么多几次没必要的网络转发。把这对进程打包进同一个 PodK8s 就能保证它们永远一起调度、一起运行、一起消亡。提示我见过不少初学者习惯一个容器一个 Pod如果你的应用之间没有什么强耦合关系这完全没问题。判断标准很简单——这两个进程能不能分开部署、分开伸缩如果能就别硬塞进一个 Pod。Pod 是临时工不是铁饭碗。它被创建出来后可能因为节点故障、资源不足、被人工删除等各种原因消失。所以你基本不会直接创建单个 Pod 用于生产而是通过下面要讲的控制器来管理它。2.2 Controller 与声明式 API把想要的状态交给集群K8s 的 API 是声明式的。你提交的不是帮我启动 3 个容器然后做这些操作而是一份描述我期望的最终状态的 YAML。控制器会一直盯着系统实际状态发现偏离了就执行操作把它拉回来。最常见的几个控制器Deployment管理无状态应用的副本支持滚动更新和回滚。这是你日常打交道最多的对象。StatefulSet用于有状态应用比如数据库。它会给每个副本一个稳定的网络标识和独立的存储卷重启之后身份不变。DaemonSet保证每个节点上恰好运行一个副本比如日志采集、监控 agent。Job / CronJob跑一次性任务或者定时任务执行完就退出。举个例子你用 Deployment 声明 nginx 副本数3如果其中一个 Pod 挂了控制器会立刻重新创建一个新的 Pod 把总数补齐。你不需要关心它是在哪个节点上拉起来的也不需要关心它叫什么名字每次生成的 Pod 名都是随机后缀。注意声明式配置有一个前提——你的 YAML 必须描述的是期望状态而不是过程步骤。很多人刚开始写配置时总忍不住把运维习惯带进来比如在 YAML 里写先做什么再做什么。K8s 不接受这种过程式逻辑它只认结果。2.3 Service 与 Ingress流量接入的两层路径Pod 的 IP 是不稳定的每次重建都会变。所以 K8s 引入了 Service 这个概念——它是一组 Pod 的固定入口提供稳定的虚拟 IP 和 DNS 名字。你可以给 Service 指定一个 selector它会自动把匹配的 Pod 作为后端并做简单的负载均衡。Service 有几种类型按场景选择ClusterIP默认类型只在集群内部可达适合服务间调用。NodePort在每个节点的固定端口上暴露出 Service外部可以通过任意节点 IP 端口访问。LoadBalancer由云平台提供负载均衡器流量打到负载均衡器后再分发到各节点。如果你想把 HTTP/HTTPS 流量暴露到集群外部更规范的做法是用 Ingress。你可以把 Ingress 理解成集群内部的七层网关它根据域名和路径把请求路由到不同的 Service。Ingress 本身只是一个 API 对象真正干活的是 Ingress Controller比如实现为 Nginx 或 Traefik 的控制器所以在装集群时别只创建 Ingress 资源还得部署对应的 Controller这是个特别容易漏掉的细节。2.4 配置管理ConfigMap 与 Secret容器里不要写死配置。K8s 给配置管理准备了两类对象ConfigMap 存普通配置Secret 存敏感信息。它们既可以作为环境变量注入 Pod也可以挂载成文件。实际使用中我强烈建议配置尽量用文件挂载的方式。原因很简单环境变量在配置更新后不会自动生效而文件挂载做了更新或者通过相关同步机制处理后进程就能动态感知变化不用重新创建 Pod。这一点在调配置排障时能少很多折腾。3. 动手搭建一套可用的 K8s 集群3.1 搭建方案的选型与取舍搭建 K8s 集群的路径有很多条我按场景给你梳理一下免得一上来就被各种方案绕晕场景推荐方案理由快速本地体验minikube / kind单机就能起一个完整集群几分钟搞定适合先跑通概念多节点测试环境kubeadm官方支持手动控制每个环节能学到集群组件的运作方式生产环境自建kubeadm 高可用配置可控性最强不依赖任何云厂商云上托管托管服务控制面不用自己管按量付费省心但灵活性稍弱我建议你学的时候选 kubeadm因为它既能暴露集群的真实组成又不会像完全裸装那样繁琐。kubeadm 做的事情是帮你把控制面组件和节点组件都配好、拉起静态 Pod但它不会帮你解决网络插件和负载均衡这些正好是你理解集群的最佳教材。3.2 十个步骤拉起集群下面这套流程我在多台干净的系统上跑过很多遍照着来基本不会出问题。环境是 3 台机器一个作为控制面节点另外两个作为工作节点。先说明一句所有节点都要装好容器运行时并在节点之间配置好网络连通性策略上默认不允许关闭 swap这一点和早期版本不一样留意一下。步骤一设置主机名和 hosts 给每台机器设置不重复的主机名并在/etc/hosts里写清楚每个节点的 IP 和主机名对应关系。这样节点间通信能直接用主机名避免在证书和访问地址上踩坑。步骤二安装容器运行时 K8s 从特定版本起对 Docker 的依赖已经大幅减弱默认推荐 containerd。安装 containerd 后要确认 CRI 插件已启用默认的 sandbox 镜像地址可以保持官方默认不建议在生产环境随意修改。提示很多新手在这一步卡住是因为 containerd 的配置文件没有生成完整。安装后你通常需要先执行命令生成默认配置再手动修改disabled_plugins和sandbox_image相关设置最后重启 containerd。这也是所有节点必须保持一致的地方。步骤三安装 kubeadm、kubelet 和 kubectl 在每台节点上都装上这三个工具。安装时注意锁定版本号保证三个组件版本一致避免控制面和工作节点版本错位之后出现版本 skew 过大的报错。步骤四控制面节点初始化 在控制面节点上执行 kubeadm init指定 API Server 地址和 Pod 网段。Pod 网段不能和你的机器网段冲突这里我习惯用--pod-network-cidr10.244.0.0/16。初始化结束后终端会输出一段配置 kubectl 的命令按提示复制执行让当前用户能访问集群。步骤五安装网络插件 这一步不做节点会一直处于 NotReady 状态。我这边常用的是 Calico 和 Flannel 二选一。Flannel 简单直接适合学习Calico 功能更强支持 NetworkPolicy适合生产。用 kubectl apply 应用对应的 YAML 后等一会儿所有节点应该会变成 Ready。步骤六获取加入命令 初始化成功后kubeadm 会输出一段 join 命令里面包含 token 和证书哈希。把它保存好工作节点加入集群的时候就靠它。步骤七工作节点加入集群 在两个工作节点上分别执行 join 命令。执行完可以去控制面节点上执行kubectl get nodes确认节点状态从 NotReady 变成 Ready 需要一两分钟耐心点。步骤八验证系统 Pod 检查 kube-system 命名空间里的 Pod确认 coredns 等组件正常运行。如果 coredns 一直 CrashLoopBackOff大概率是网络插件没装对或者 Pod 网段冲突了返工检查第五步。步骤九配置命令自动补全和环境变量 kubectl 的自动补全能极大提升操作效率往 shell 配置里加几行 source 命令即可。我见过太多人在长命令里手打拼写错误浪费时间。步骤十给工作节点打标签 给节点添加角色或用途标签比如node-role.kubernetes.io/worker后续用 nodeSelector 调度时能按标签过滤。3.3 基础验证让第一个 Pod 跑起来集群就绪后先用一条命令验证端到端流程。我习惯直接跑一个 nginxkubectl create deployment nginx-test --imagenginx kubectl get pods -w看到 Pod 状态变成 Running 且 READY 显示 1/1说明整个链路已经通了。接着可以暴露成 NodePort 测试外部访问kubectl expose deployment nginx-test --typeNodePort --port80 kubectl get svc然后用任意节点 IP NodePort 端口在浏览器里访问看到 nginx 欢迎页就说明你已经成功跑起来一个完整集群了。这个验证环节值得认真做一遍别图快直接跳过。它能帮你区分集群本身的问题和业务应用的问题后面定位故障时这个判断能力很关键。4. 从能跑到跑稳生产级应用落地要点4.1 资源请求与限制别把集群饿死刚学会用 K8s 的人最容易犯的一个错误是写 Deployment 时完全不写resources字段。这在测试环境暂时不会出问题但到了生产几个内存泄漏的应用就能把整台节点拖死。有两个概念必须分清requests调度时用来做容量估算的K8s 会保证节点剩余资源不低于所有 Pod requests 的和。limits容器可以使用的资源上限超过上限的行为取决于资源类型和运行时配置。我推荐一个相对稳妥的给资源的方法先跑一段时间压测确定平均值再在这个基准上给 requests 留 30% 的余量limits 设为 requests 的 1.5 到 2 倍。不要一上来就把 limits 写满否则节点资源利用率会低得离谱也不要完全不设 limits否则被某个异常应用拖垮的是整台机器上的所有邻居。4.2 健康检查让 K8s 替你照看应用K8s 提供了两类探针作用完全不同别搞混存活探针livenessProbe决定容器要不要被重启。进程还活着不代表应用没卡死所以要有一个能真实反应应用状态的检查接口。就绪探针readinessProbe决定容器要不要接收流量。只有就绪探针通过Service 才会把请求转发到这个 Pod。这两种探针可以配置httpGet、tcpSocket和exec三种方式。对于 HTTP 服务我最常用的是 httpGet指向一个专门用于健康检查的轻量接口比如/healthz。这个接口的业务逻辑要尽量简单避免把数据库连通性这种外部依赖写进存活探针——否则数据库一抖动K8s 就会疯狂重启你的应用等于把一次小故障放大成灾难。心得探针的initialDelaySeconds一定要大于应用真实启动时间。我踩过很多次坑启动慢的应用因为探针设得太激进一直处于被杀死重来的循环中。看日志时会发现应用其实起得来只是来不及完成初始化就被探针误杀了。4.3 优雅滚动更新与回滚Deployment 最值钱的能力之一就是滚动更新。默认策略下K8s 会先起一个新副本等它就绪后再停一个旧副本循环往复。这样发布过程中服务不会中断只是短暂地存在新旧同时运行的情况。这里要留意两个会影响更新体验的参数maxUnavailable更新过程中允许多少个旧副本先不可用。设为 0 可以保证服务完全不中断但发布会稍慢。maxSurge允许超出期望副本数多少个。设为 1 表示会先额外启动一个新副本再逐步替换旧的。实际发布前我习惯先看一遍当前资源状态然后执行kubectl rollout status deployment/应用名 kubectl rollout history deployment/应用名如果发布后发现情况不对立刻回滚到上一个版本kubectl rollout undo deployment/应用名整个过程不需要重新构建镜像也不需要操作容器这份从容感正是声明式 API 带来的最大红利。4.4 存储接入PV、PVC 与 StorageClass无状态应用不需要考虑数据持久化但数据库、消息队列这些有状态的东西在 K8s 上运行就绕不开存储。K8s 的存储抽象分三层PVPersistentVolume是存储资源的抽象PVC 是用户对存储的请求StorageClass 则实现了动态供给——你只需要创建 PVC 并指定 StorageClass系统会自动帮你创建对应的 PV。在本地测试环境里可以用 hostPath 或 local 卷简单体验生产环境则建议根据底层基础设施选择合适的 CSI 驱动。有一点要时刻记住Pod 是有可能被调度到其他节点的如果应用数据存在本地一旦换节点就丢数据了。所以生产上尽量使用共享存储并通过 StatefulSet 的稳定标识管理数据盘和 Pod 的绑定关系。5. 进阶之路调度、伸缩与集群治理5.1 调度策略污点、容忍与亲和性集群里的节点不可能一视同仁。有的节点内存大适合跑大数据任务有的节点有 GPU 适合跑模型训练这时候就需要控制 Pod 的落点。三种常用的调度控制手段nodeSelector最简单通过节点标签精确匹配。节点亲和性nodeAffinity支持硬性要求requiredDuringScheduling和软性偏好preferredDuringScheduling两种。硬性要求不满足就不调度软性偏好是把节点打分择优但是没得选时也可以放到其他节点上。污点Taint与容忍Toleration给节点打上污点除非 Pod 明确容忍这个污点否则不会被调度上去。比如控制面节点默认带有污点普通应用不会被调度上去。我常用的排布策略是污点用来隔离、亲和性用来定向。比如给 GPU 节点打上专门的污点再给模型推理服务加上对应的容忍和亲和性就能保证这类服务只落在 GPU 节点上其他业务彻底隔离。这套组合在混部场景中非常实用。5.2 HPA 自动伸缩的完整链路自动伸缩是很多人冲着 K8s 来的核心诉求。HorizontalPodAutoscalerHPA可以基于 CPU 使用率、内存或者自定义指标自动调整副本数。HPA 的工作链路并不复杂监控指标采集器从各节点上的指标接口拉取数据HPA 控制器定期查询这些数据计算当前副本数离目标水位差多远然后调整 Deployment 的副本数。配置 HPA 前要做好两件事一是确保监控组件已经采集 Pod 的指标二是你的 Pod 必须设置了资源 requests否则 HPA 无法计算使用率。缺少任何一个前置条件HPA 都会静默地不工作而且日志也不太显眼排查起来很费劲。apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: web-app-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: web-app minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60注意HPA 的扩缩容是周期性的不会因为流量一个瞬时尖峰就立刻加机器也不会因为一下子没人访问就立刻缩到最小值。它有默认的冷却逻辑。配置最小副本数时别指望 HPA 帮你省到零实例它做不到我经常建议业务低谷期的场景配合其他方式处理。5.3 多集群与可观测性当业务规模走到一定程度单集群会成为瓶颈和风险点。多集群架构常被用来做故障隔离、区域容灾和开发生产环境分离。多集群的管理和治理目前已经有成熟的开源方案支持它们的控制端可以管理多个集群的 kubeconfig实现统一分发。但别急于多集群先把单集群的资源划分、命名空间隔离做扎实。命名空间配上 ResourceQuota资源配额和 LimitRange范围限制就是最轻量且有效的多租户治理手段。可观测性方面我建议至少做好三件事指标监控以 Prometheus 为核心的监控体系重点盯节点资源、Pod 重启次数、HPA 扩缩容次数。日志采集通过 DaemonSet 方式在每个节点部署日志采集器集中汇聚到统一存储。链路追踪微服务架构建议接入分布式追踪系统否则排查跨服务慢请求非常痛苦。可观测性工具本身也是跑在集群里的应用别把它们搞成靠猜来排障。特别是集群刚搭好时先把核心指标和日志的链路跑通后面所有的排障效率才会有质的提升。6. 从入门到精通的常见问题速查表6.1 Pod 启动异常类问题 1Pod 一直 Pending这是最高频的问题。Pending 通常意味着调度不成功先用kubectl describe pod 名称看 Events 段最常见的三种原因节点资源不足、节点不满足亲和性/污点要求、没有匹配的存储卷。看 Events 比看日志更直接Events 里会写清楚调度器给出的拒绝原因。问题 2Pod 一直 CrashLoopBackOff容器启动后立刻崩溃K8s 在反复重启它。原因可能是探针失败、启动依赖的服务没就绪、或者进程因为某种原因持续异常退出。查看日志用kubectl logs 名称如果容器之前崩溃过想看上一次的输出加--previous参数。问题 3ImagePullBackOff镜像拉不下来。通常是镜像名打错了、私有仓库认证失败或者网络不通。检查时先看 Events 里的具体错误文本逐条确认。6.2 网络与 Service 异常类问题 4Service 有 ClusterIP 但访问不通先确认后端 Pod 是 Running 且就绪探针通过然后检查 Service 的 selector 是否和 Pod 的标签匹配。用kubectl get endpoints看 Service 有没有绑定到后端 Pod如果 Endpoints 列表是空的大概率是标签对不上。问题 5跨节点 Pod 通信失败这种情况基本可以锁定是网络插件的问题。确认网络插件是否已安装、Pod 网段是否与其他网段冲突以及各节点之间的网络安全策略是否放行了所需的端口和协议。问题 6Ingress 配置了但访问返回 404大多数情况是域名或者路径没匹配上。先 curl 的访问结果确认请求到达了哪个 Service再检查 Ingress 里的 host 字段是否和访问域名完全一致以及 Ingress Controller 是否真的在运行。6.3 集群级故障类问题 7kubectl 连不上集群先看 kubeconfig 指向的地址对不对kubectl config current-context和kubectl cluster-info能快速帮你定位。如果是证书过期需要更新集群证书这里有明确的操作流程不建议拖太久。问题 8控制面节点挂了集群还能用吗取决于你搭建的是单控制面还是高可用。单控制面下控制面挂掉之后工作节点上已运行的业务会继续工作但集群的调度、修复能力会中断。这也是为什么生产环境至少需要三个控制面节点组成高可用。问题 9ETCD 数据备份etcd 是集群的记忆体存了所有资源对象的状态。我建议至少做两件事开启定时快照备份定期演练从备份恢复。没做过恢复演练的备份约等于没有备份这句话值得记住。对照这张表排查的时候有个通用方法论先看 Events再看日志最后查网络和存储。从外到内、从下到上一层层收敛比漫无目的地搜日志高效得多。我个人在实际操作中的体会是K8s 这门技术学了一两遍仍然会碰到新问题不要指望看几篇文档就能彻底掌握。最有效的路径就是亲手搭一套集群、亲手把一个应用迁上去、再亲手把它搞挂一次并修好。走完这三步你对 K8s 的理解会远远超过只看书不动手的人。以后团队里讨论架构、做服务迁移的时候你也能真正说得上话而不是在一旁听个热闹。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑