
这个系列写到第七篇前几篇从容器镜像一路讲到编排工具我不断收到读者私信Kubernetes 概念那么多哪些才是真正的核心说实话很多人学 K8s 半途而废不是不努力而是把顺序搞反了——一上来就扎进 YAML 细节和网络插件源码反而忽视了云原生架构里最本质的几条逻辑。这篇我准备把 K8s 绕不开的核心内容做一次完整梳理适合正在被 Pod、Deployment、Service、Ingress 这些词淹没又不想停留在只会敲 kubectl 层面的朋友。1. 先搞清楚 Kubernetes 到底解决了什么问题想理解 K8s不能只看它有什么组件得先回到问题本身没有它的时候我们是怎么部署应用的1.1 单机 Docker 时代的三个痛点我记得自己最早用 Docker 部署项目时流程很简单把服务做成镜像docker run起容器再用docker-compose把几个容器串起来。这套方案应对开发环境、小网站都没问题但服务规模一上来就露馅了。第一痛没有故障自愈。某个容器突然崩了docker-compose 只能保证重启策略但节点宕机了、磁盘满了这些情况它完全无能为力需要人半夜爬起来手动处理。第二痛没有弹性伸缩。流量涨了我得上服务器手动docker scale或者多起几个容器还得自己改 Nginx 配置做负载均衡流量降了又要手动缩回去整个过程跟手工操作流水线一样。第三痛没有服务发现。A 服务要调 B 服务IP 写死在一个配置文件里B 服务换个节点部署A 服务立刻失联排查半天发现只是地址变了。这三个痛点叠加在微服务架构下会被无限放大。微服务把单体应用拆成几十个甚至上百个小服务每个服务还要多副本部署纯靠人去维护这些容器的启停、注册、发现、网络、存储几乎是不可能完成的任务。1.2 云原生架构对“编排层”的硬性要求云原生架构说白了有三根支柱微服务、容器化、动态编排。微服务解决了应用的拆分问题容器解决了打包和交付问题但真正让这套体系转起来的是动态编排——也就是 Kubernetes 在做的事。K8s 的核心思想是“声明式”你不用告诉它怎么把容器跑起来你只需要描述清楚期望状态比如“我要 3 个副本的 Nginx”“版本是 1.25”剩下的调度、创建、健康检查、故障恢复、滚动更新全由控制器来完成。这个设计很像你请了个物业管家你只需要说“我要三室一厅朝南”管家自己安排保洁、维修、巡查不需要你盯着每个灯泡。它实际接管的能力包括应用生命周期管理创建、更新、回滚、删除、服务发现与负载均衡、弹性伸缩手动和自动、配置管理与密钥管理、存储编排、安全策略RBAC、NetworkPolicy等。这些都是云原生应用的“操作系统级”能力。1.3 什么场景该上 K8s什么场景别硬上我说句得罪人的话不是所有项目都适合上 Kubernetes。它的价值在应用数量多、发布频繁、流量波动大、需要多环境一致交付时体现得最明显。如果你的微服务数量少于 5 个、流量稳定、团队也没有专职运维那我建议先用 Docker Compose 或者干脆单机部署把精力放在业务上。我见过最典型的反面案例有人为了简历上写“精通 K8s”把个人博客塞进了 Kubernetes结果控制平面至少占三台机器资源还要处理证书过期、版本升级、节点维护。折腾两个月后博客流量还没集群本身的维护成本高。判断标准就三条应用规模够不够大发布频率够不够高有没有人愿意长期维护基础设施三个都不满足就别硬上。2. 控制平面与数据平面谁在管理谁在执行K8s 集群从逻辑上分成两半控制平面负责决策工作节点负责干活。理解这条分界线再看任何组件都不会迷路。2.1 控制平面的四个组件各管一摊事控制平面上有四个核心进程每个都很纯粹kube-apiserver是整个集群的唯一入口。所有命令、所有组件要访问集群状态都必须走这一关。它同时负责认证、鉴权和准入控制相当于一个政府办事大厅所有申请材料都从窗口递进去。etcd是集群的“档案室”所有期望状态、实际状态、配置信息都存在这里是分布式 KV 存储apiserver 是唯一能读写 etcd 的组件。kube-scheduler负责给 Pod 找一个合适的节点相当于房屋中介先看哪些房子满足要求再从中挑最优的。kube-controller-manager是一个控制器集合里面装着 Node 控制器、Deployment 控制器、ReplicaSet 控制器等它们的职责是不断“调和”把实际状态往期望状态上拉。我给学员讲这段时常用一个类比apiserver 是前台etcd 是数据库scheduler 是选址顾问controller-manager 是监理团队。四者配合集群才能运转。2.2 工作节点上真正干活的三个组件工作节点是跑业务的地方上面有三个关键进程。kubelet是节点上的“代理人”它负责接收 apiserver 下发的 Pod 定义调用容器运行时真正创建容器还要定期执行健康检查、向控制平面上报节点状态和资源使用情况。kube-proxy负责维护节点上的网络规则主要实现 Service 的虚拟 IP 转发你可以理解为每个节点门口的小型路由器。容器运行时container runtime负责拉镜像、启停容器、管理存储卷K8s 通过 CRIContainer Runtime Interface接口和它对话常用的是 containerd老项目中也能看到 Docker 的身影。2.3 一次 Pod 创建请求背后的完整链路懂原理和不懂原理的人在排查故障时差距特别大。拿最简单的kubectl apply -f deployment.yaml来说背后要经历七个环节kubectl 把 YAML 发给 kube-apiserver经过认证鉴权准入控制器Admission Controller对请求做校验、修改或拦截数据持久化到 etcdDeployment 控制器发现期望副本数大于实际副本数于是创建 ReplicaSetReplicaSet 控制器根据模板创建 Pod 对象kube-scheduler 为 Pod 选节点把结果写进 Pod 的nodeName字段目标节点上的 kubelet 通过 watch 发现这个 Pod调用 CRI 创建容器、CNI 配网络、CSI 挂存储。这条链路我为什么每次都强调因为排障全靠它。Pod 卡在 Pending通常是调度环节出问题卡在 ContainerCreating多半是镜像拉取、存储或者网络插件问题出现 CrashLoopBackOff那就是容器起来之后又退出了。知道问题出在第几个环节你就知道该去看哪个组件的日志。3. 绕不开的六大核心 API 对象K8s 里的 API 对象很多但九成场景你只需要把下面这几个用到熟。3.1 Pod最小调度单元但不是最小部署单元Pod 是 K8s 里最小的调度和资源管理单位一个 Pod 里可以跑一个或多个容器。同一 Pod 内的容器共享网络命名空间、共享存储卷可以通过 localhost 直接互相访问这是设计给“关系紧密的进程组”用的。最经典的场景是 sidecar 模式主容器跑业务逻辑伴生容器做日志采集、流量代理或健康检查。比如用 Istio 做服务网格时每个业务 Pod 旁边都会自动注入一个 envoy 代理容器。但注意最小调度单元不等于最小部署单元。生产环境里没人会直接创建一个孤零零的 Pod因为裸 Pod 没有自愈能力节点挂了它就永久消失了。3.2 Deployment 与 ReplicaSet让应用真正“活”过来ReplicaSet保证指定副本数的 Pod 永远活着Pod 没了它会重建Deployment则在 ReplicaSet 之上加了一层版本管理能力支持滚动更新、一键回滚、声明式扩缩容。二者是上下级关系你在 YAML 里通常只写 Deployment控制器会自动帮你创建 ReplicaSet。我贴一个最常用的 Deployment 示例包含资源限制和探针可以直接当模板apiVersion: apps/v1 kind: Deployment metadata: name: nginx-web spec: replicas: 3 selector: matchLabels: app: nginx-web strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: metadata: labels: app: nginx-web spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi readinessProbe: httpGet: path: /healthz port: 80 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: path: /healthz port: 80 initialDelaySeconds: 15 periodSeconds: 20这里maxSurge表示滚动更新时最多允许超出期望副本数多少个maxUnavailable表示最多允许多少个副本不可用。我自己踩过一个坑默认策略下副本数只有 2 时maxUnavailable默认 25% 会被向上取整成 1结果更新时最多只有 1 个 Pod 可用如果那个 Pod 启动又慢发布期间服务就是半瘫痪状态。所以对小副本数应用我习惯设maxUnavailable: 0宁可多占用一点资源也要保证可用性。3.3 Service 与 Ingress从 ClusterIP 到外部流量入口Pod 的 IP 是随时变化的直接拿它做服务发现肯定不行所以有了Service。Service 是 K8s 内部的负载均衡抽象它提供一个稳定的虚拟 IPClusterIP并把流量转发到后面一组 Pod。Service 有三种常见类型ClusterIP仅集群内部可访问适合服务间调用NodePort在每台节点上开一个端口外部可以通过节点IP:NodePort访问内部服务适合快速验证LoadBalancer通过云厂商负载均衡器暴露服务适合生产环境。流量转发由 kube-proxy 实现底层可以是 iptables 或者性能更好的 IPVS 模式。Ingress则更进一层负责七层路由。它根据域名或路径把外部流量分发到不同的 Service能统一处理 TLS 证书终止、限流、重写规则。如果只靠 NodePort每个服务都要维护一个端口映射关系20 个服务就是 20 个乱七八糟的端口而且没有域名路由能力。所以生产环境的标准做法是外部流量 → 负载均衡器 → Ingress Controller → Service → Pod。3.4 ConfigMap、Secret 与 PVC配置、密钥与存储的标准化ConfigMap和Secret的核心价值是把配置从镜像里剥离出来。镜像从此变成不可变资产——同一个镜像在开发、测试、生产环境用不同的 ConfigMap 注入配置就能跑出不同行为这解决了环境差异的世纪难题。Secret 本质上只是做了 base64 编码不是加密。我看到不少团队把数据库密码直接写进 Secret 文件以为安全了其实拿到集群权限的人 base64 解码就能看到明文。生产环境建议配合外部密钥管理工具或者使用 K8s 的加密机制。PVC和PV的设计更巧妙它把存储的“需求方”和“供给方”解耦。开发者在 PVC 里声明“我要 10Gi 快存储”管理员或云厂商通过 StorageClass 自动创建满足要求的 PVPod 再通过 PVC 挂载。这样开发不需要知道底层是 NFS、Ceph 还是云盘只管用就行。3.5 Namespace 与 RBAC多团队协作的底线Namespace提供逻辑隔离把测试、生产、不同团队放进各自的命名空间互不干扰。RBAC基于角色的权限控制则解决“谁能对什么资源做什么操作”的问题核心对象是 Role、RoleBinding、ClusterRole、ClusterRoleBinding。说实话很多中小企业的集群完全处于“裸奔”状态所有开发都能拿到 cluster-admin 权限集群没做任何审计。这就像把公司大门的钥匙复制给所有员工短期看不出问题一旦出现误删除或者恶意操作你连是谁干的都查不到。我个人的经验是至少做到按角色分权开发人员只给所属 Namespace 的读写权限只有运维组能操作集群级资源。4. 调度、网络与存储背后的设计逻辑这节谈的是 K8s 的底层决策逻辑理解了它你就很容易理解很多“为什么 K8s 会这样做”。4.1 调度器如何给 Pod 挑一台“合适的房子”调度器给 Pod 选节点分两步走过滤和打分。过滤阶段会筛掉不满足硬性条件的节点。比如 Pod 声明需要 2 核 CPU、4Gi 内存那资源不够的节点直接排除如果 Pod 有nodeSelector不匹配标签的节点也直接排除如果节点上有污点而 Pod 没有对应容忍同样不选。打分阶段则对通过过滤的节点按策略打分比如资源均衡使用、拓扑分散、镜像本地已有等得分高的胜出。这里有几个容易混淆的概念nodeSelector是最简单的硬性指定nodeAffinity支持更灵活的表达式匹配podAntiAffinity用来让同一个应用的多个副本尽量分散在不同节点避免单点故障taints和tolerations则是一对“排斥”机制——节点可以打上污点只有具备对应容忍度的 Pod 才允许被调度上来。我在生产里最常用到的是把 GPU 任务调度到 GPU 机器上。做法是给 GPU 节点打标签然后在工作负载 YAML 里加nodeSelector再配合设备的资源声明调度器才会把一个需要 1 张卡的 Pod 放到真正有 GPU 的节点。4.2 CNI 网络模型扁平网络与 Service 负载均衡的真相K8s 对网络有严格要求每个 Pod 都要有独立的 IPPod 之间可以不经过 NAT 直接通信所有节点上的 Pod 都在一个扁平网络里。实现这个网络模型的是 CNI 插件。最常用的两个插件是 Flannel 和 Calico。Flannel 用 VXLAN 或者 host-gw 的方式把不同节点的 Pod 网络打通优点是简单、容易上手但网络策略能力弱。Calico 走 BGP 协议直接交换路由性能更好还支持 NetworkPolicy 做细粒度的网络访问控制。如果只是自己实验Flannel 完全够了如果生产环境有多租户隔离需求或者对网络性能敏感直接上 Calico 更省心。Service 的负载均衡原理也值得理解。ClusterIP 是一个虚拟 IPkube-proxy 监听 Service 和 Endpoint 的变化把访问 ClusterIP 的流量 DNAT 到后端某个 Pod IP。我踩过一个坑某服务 Pod 一直正常但通过 Service 访问时好时坏排查很久才发现是后端 Pod 的标签和 Service 的 selector 不匹配导致 Endpoint 列表为空流量根本没地方转发。遇到这类问题第一时间kubectl get endpoints看后端 IP 列表是否正常。4.3 存储抽象与 Device Plugin让有状态应用也能跑上云K8s 最初是为无状态应用设计的但有状态应用数据库、消息队列也想上容器于是有了StatefulSet和持久化存储体系。StatefulSet给 Pod 提供稳定网络标识比如mysql-0、mysql-1和稳定的持久化存储Pod 重建后标识不变、数据不丢启动和销毁也严格按照顺序执行。PV/PVC/StorageClass这套抽象解决了存储供给问题StorageClass 能根据 PVC 自动创建 PV不用管理员手动预分配磁盘。另外一个很多人没接触过的概念是Device Plugin。K8s 默认只管理 CPU 和内存GPU、FPGA、NPU 这类设备资源要靠 Device Plugin 暴露出来。实现原理不复杂每个节点跑一个 DaemonSet 插件插件向 kubelet 注册自己管理的设备kubelet 把资源上报给 apiserver调度器看到节点上有nvidia.com/gpu: 1这类扩展资源后就可以把需要 GPU 的 Pod 调度到该节点。NVIDIA 官方有个 device plugin 项目部署后 GPU 节点会自动上报资源非常顺滑。5. 生产环境里真正值钱的经验与常见坑概念讲完接下来全是实践里淌出来的教训。5.1 不设置资源 requests/limits 是事故的开始资源限制是生产环境的第一道防线也是我检查任何 Deployment 时第一个看的地方。如果只设requests不设limits调度器会按申请值分配节点但容器实际上可以超过 requests 值吃更多资源多个服务叠加起来可能把节点内存打爆。如果requests和limits都没设那问题更大调度器默认认为这个容器不占资源同一台机器能塞下大量 Pod某个大流量应用一上来其他服务的资源直接被抢走。K8s 根据 requests/limits 的关系把 Pod 分为三个 QoS 等级Guaranteedrequests 和 limits 都设且相等、Burstable设置了部分数值、BestEffort完全没设。资源紧张时BestEffort 的 Pod 第一个被杀。所以生产环境的规则是核心服务尽量做成 Guaranteed非核心服务至少也要有 requests 和 limits绝不让任何容器处于 BestEffort 状态。5.2 探针与优雅终止发布时掉线的真凶探针是 K8s 判断容器是否健康的工具有三种readinessProbe判断容器是否就绪失败就把 Pod 从 Service 后端摘除不接流量livenessProbe判断容器是否存活失败就重启容器startupProbe用于启动缓慢的容器给足初始化时间避免 liveness 在启动过程中误杀。发布时服务掉线最常见的原因是新 Pod 还没就绪就接了流量或者旧 Pod 被立刻终止但老连接还没处理完。解决思路有三个配套手段readinessProbe 设置合理的初始延迟和超时确保新 Pod 真正就绪后再接流量terminationGracePeriodSeconds设置足够的优雅退出时间让进程处理完正在进行的请求如果业务比较特殊还可以加preStop钩子先执行一段冷却时间再真正停掉容器。我自己用maxUnavailable: 0配合预热时间基本能解决绝大多数发布掉线问题。5.3 安全基线未授权访问漏洞是怎么来的网上搜“Kubernetes 未授权访问漏洞”能搜到不少企业中招的案例。这类问题通常不是某个 0day而是安全配置不当导致的最常见的有三种一是 api-server 开启了匿名认证任何人都能通过kubectl直接访问集群不需要任何凭据。二是 Kubernetes Dashboard 绑定了 cluster-admin 权限而且账号密码是弱口令或者默认配置攻击者拿到面板就等于拿到整个集群。三是 kubelet 的 10250 端口暴露到公网这个端口在没有认证配置时可以读取节点上的容器信息和日志甚至能执行命令。防护手段并不复杂关闭匿名认证--anonymous-authfalse把 apiserver 和 kubelet 放在内网并通过防火墙限制访问Dashboard 只授予最小必要的权限绝不给 cluster-admin开启审计日志记录谁在什么时间对集群做了什么操作。安全从来不是某一个配置解决的而是一套机制组合起来但上面这几条是任何生产集群都必须先做到的底线。5.4 Dashboard 操作实战怎么通过界面发布一个全新服务有人问 Kubernetes Dashboard 怎么创建一个新的 Pod 作为新服务发布其实背后逻辑和 kubectl 完全一样只是操作从命令行变成了界面。我以发布一个 Nginx 服务为例说明。在 Dashboard 右上角点“”号选择“从 YAML 创建”粘贴下面这段内容apiVersion: apps/v1 kind: Deployment metadata: name: nginx-new labels: app: nginx-new spec: replicas: 2 selector: matchLabels: app: nginx-new template: metadata: labels: app: nginx-new spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-new-svc spec: type: NodePort selector: app: nginx-new ports: - port: 80 targetPort: 80 nodePort: 30080点击上线按钮后Dashboard 会自动跳转到工作负载页面你能看到 Deployment 创建、ReplicaSet 生成、Pod 进入 Running 状态的全过程。之后再通过“服务”页面就能看到nginx-new-svc的 NodePort 地址。不过我要给个忠告Dashboard 适合学习和快速演示生产环境建议把发布流程沉淀到 CI/CD 或 GitOps 工具链里。原因很简单界面操作不可审计、不可重复点错了只能靠人的记忆补救。当年我用 Dashboard 手动发布结果一次误操作把标签写错服务发布到了错误命名空间排查了半天。5.5 企业项目实战中几类高频故障这几年处理过不少企业集群问题有些非常典型我简单列一下节点状态 NotReady。多半是 kubelet 异常或者容器运行时挂了先看节点上 kubelet 服务状态再看容器运行时日志最后检查磁盘空间是不是被写满了。Pod 报 ImagePullBackOff。先看完整事件区分是镜像不存在、私有仓库认证失败还是镜像仓库限流。如果是自建镜像仓库还要确认 API 地址在集群内是否解析得到。集群内部 DNS 解析失败。大概率是 CoreDNS 副本挂了或者性能被打满检查 CoreDNS Pod 状态和日志同时确认 Pod 的dnsPolicy设置是否合理。发布后服务时断时续。优先看 Service 的 Endpoints 列表如果后端 Pod IP 没有正确更新多半是标签选择器匹配错了。排查这些问题的通用链路就是回到第 2 节讲的创建链路先定位卡在哪一步再看这一步对应的组件日志千万不要毫无头绪地到处翻。我自己的习惯是kubectl describe pod永远比kubectl get pods更有信息量里面藏着大量关键事件。6. 云原生学习路线从敲命令到理解哲学最后聊聊怎么学这条路我自己走了一遍知道哪里最耽误时间。6.1 本地环境与高频命令学习 K8s 最怕的是没有实验环境。现在最省事的方案是用kind或minikube在本地起一个单节点集群几分钟就能搭好不想全装的话也可以用云厂商的托管集群免费额度够玩很久。日常命令没那么复杂先把下面这些用熟就能覆盖绝大多数场景kubectl get nodes # 查看节点状态 kubectl get pods -o wide # 查看 Pod 及所在节点 kubectl describe pod pod-name # 查看 Pod 的详细事件 kubectl logs -f pod-name # 查看容器日志 kubectl exec -it pod-name -- /bin/sh # 进入容器 kubectl apply -f deployment.yaml # 声明式更新资源 kubectl rollout status deployment/name # 查看滚动更新进度 kubectl rollout undo deployment/name # 回滚到上一个版本 kubectl scale deployment/name --replicas5 # 手动扩缩容 kubectl get endpoints # 查看 Service 后端列表但我想强调命令本身不是重点重点是这些命令背后的对象关系。你要能在脑子里画出这样一张图Deployment 管 ReplicaSetReplicaSet 管 PodPod 里是容器Service 指向一组 PodIngress 路由到 Service。这张图画清楚了K8s 就学通了一半。6.2 从“会用”走向“懂原理”的分水岭很多人问我K8s 学到什么程度才算真的入门了我的标准是能回答下面几个问题Pod 里的容器是怎么共享网络命名空间的Service 的 ClusterIP 在 iptables/IPVS 里以什么形态存在调度器在什么情况下会把 Pod 调度到一个资源远不够用的节点Deployment 滚动更新时两个参数对服务可用性的影响是什么如果你能不看资料把这些问题讲清楚说明你已经开始理解设计意图了。如果讲不清楚也不用急回去对照组件日志和实际实验再走一遍这个过程本身就是最好的学习。6.3 学习资料与进阶方向资料方面很多人推荐《Kubernetes 权威指南》目前已经出到第 6 版内容确实全但我建议不要一上来就从头啃到尾那样很容易被细节淹没。更好的方式是把它当工具书遇到问题时按目录查对应章节针对性阅读。手边常备官方文档配合实验环境的动手验证效率其实比死读书高一倍以上。学完基础之后进阶方向大致有这么几条Helm解决复杂应用的打包和版本管理Operator把运维经验代码化Istio做服务网格解决流量治理和可观测性Argo CD做 GitOps实现声明式持续交付Prometheus全家桶解决监控告警。企业项目实战的话建议自己设计几个综合性练手课题交付一套带数据库的微服务、实现滚动发布和分批灰度、完成一次集群升级、尝试把现有部署平滑迁移到新版本。最后再多说一句个人体会学 K8s 真正难的不是某个 YAML 字段怎么配而是建立一套“系统思维”——你能把自己想象成控制平面的一部分用控制器的视角去看待每一次更新、每一次故障。等到你拿到一个陌生集群能通过kubectl get和describe快速定位问题根源时这门技术才算真正长在了你身上。有个小技巧我一直在用每次在实验环境创建一个新资源都顺手跑一遍kubectl get pvc,svc,ep,endpoints -o wide看一遍关联对象的实际状态多观察几次你对整个集群的运转规律会形成很强的直觉。这种直觉比背任何命令都有用。