资讯动态

从Pod到Ingress:Kubernetes对象模型与生产部署实践

发布时间:2026/10/26 3:39:37 来源:尧图企业网站定制
1. 从三台服务器到三百个容器手工运维到底在哪一步崩掉我第一次接触 Kubernetes不是因为觉得它酷而是因为被一堆docker run命令逼到了墙角。事情本身没什么稀奇一个对外提供接口的服务早期只跑在三台机器上装个运行时、拉个镜像、写个 systemd 单元重启就完事。后来访问量上来机器加到十几台服务拆成七八个模块每个模块还要分测试环境和线上环境运维脚本从三十行涨到八百行脚本里开始出现如果这台机器上的容器挂了就去另一台机器上重新拉一个这种逻辑——写到这里其实就已经在手工实现一个调度器了只是实现得很糟糕。真正的崩溃点往往不是某一次大故障而是日常小事故累积起来的疲惫感某台机器上的容器进程莫名其妙退出没人知道某个服务扩容之后忘了改负载均衡的配置流量打到了一台已经不存在的实例上发版的时候需要手动停掉一半实例再启动新版本中间那几分钟的请求全部报错。这些问题单看都能靠脚本补一补但脚本本身又变成了新的维护负担。Kubernetes 出现的意义不是让你多学一套工具而是把我希望集群里始终有 3 个健康实例这句话变成一条可以被机器持续执行的声明。你要做的只是把期望写下来剩下的对齐工作交给集群自己。所以这篇内容适合两类人看一类是刚接手容器化环境的开发者被一堆 Pod、Deployment、Service 的名词砸晕想知道这些对象到底谁管谁另一类是还在用手写脚本管理多台机器的运维或后端同学想判断值不值得迁移过去。我会从对象模型讲到请求在集群内部的完整流转再给出一份可以直接抄的部署清单最后重点说几个新手几乎必踩的坑。原理讲透命令给全不绕弯子。1.1 一个虚构团队的失控过程几乎是所有团队的缩影我参与过一个模拟项目的容器化改造团队规模不大六个后端加一个运维。改造前状态是每台机器上跑三到四个容器靠一份写死的部署清单加人工核对来维持。问题出在一次例行扩容上——业务方要求把某个查询服务从 4 个实例扩到 12 个运维同学照着旧清单改了机器数量但负载均衡那一侧的健康检查路径还是旧的新起的实例因为健康检查失败被摘掉了流量结果扩容之后实际可用的实例反而更少。这个事故排查了将近两个小时因为从监控上看 CPU 和内存都很正常服务也确实在跑就是没有流量进来。这类问题的共同特征是配置分散在多个地方彼此之间没有校验关系。机器清单在运维手里负载均衡规则在另一份配置文件里健康检查路径写在代码里三者靠人的记忆对齐。Kubernetes 的做法是把这些关系收敛到同一套 API 对象里Service 通过标签选择器找到 PodPod 的健康状态由探针决定探针失败就自动从 Endpoints 里摘掉。标签对不上Service 就永远找不到后端这个问题在kubectl describe service里一眼就能看到不需要对着三份文件逐个核对。这里有个容易忽略的细节标签选择器是精确匹配而不是模糊匹配app: demo-web和app: demo-web-v2是完全不同的两组 Pod。写 YAML 时多一个后缀就会导致 Service 后端为空而集群不会报任何错只是流量进不来。1.2 Kubernetes 卖的不是容器是期望状态这四个字很多人把 Kubernetes 理解成容器管理工具这个理解会导致后面一系列困惑。容器运行时负责的是把进程跑起来并隔离资源Kubernetes 负责的是让集群的实际状态不断逼近你声明的期望状态。这两件事的层次完全不同前者是一次性的动作后者是一个永不停止的循环。举个具体例子。你声明了一个 Deploymentreplicas: 3。集群做的事情是创建 ReplicaSetReplicaSet 创建 3 个 Pod然后持续监听——只要有 Pod 因为节点故障、OOM、人为删除等原因消失立刻补一个出来。这个补的动作不需要你写任何脚本是控制器在调谐循环里自动完成的。同理你修改镜像版本控制器会按滚动策略逐步替换你把副本数改成 5它就去调度两个新的 Pod。这个模型带来的直接好处是运维动作从执行步骤变成了描述结果。你不再需要关心先停哪个再起哪个只需要描述最终要有 3 个跑着 1.1.0 版本的实例中间的过渡过程由maxSurge和maxUnavailable这类参数控制。坏处是如果期望状态写错了集群会非常忠实地把错误状态维持下去——写错镜像标签它就会一直尝试拉取一个不存在的镜像并且不断重启直到你发现为止。1.3 有些场景硬上 Kubernetes成本会远大于收益说句实在话不是所有项目都该用 Kubernetes。我在实际工作中见过最典型的错配是一个只有两个服务、日请求量几千的项目为了技术栈先进搭了一套三节点集群结果光是维持控制面、监控、日志、证书轮换这些基础设施就吃掉了团队大量的精力业务代码反而没时间优化。判断标准其实不难我自己会看这么几条第一服务数量和变更频率——如果只有一两个服务一年发版不到十次直接用容器加编排脚本或者更轻量的方案就够第二是否有明确的弹性需求——流量有明显波峰波谷需要按指标自动扩缩容这时候调度和自动伸缩的价值才体现出来第三团队是否有足够的运维能力——Kubernetes 把复杂性从机器转移到了对象和网络它没有消灭复杂性只是换了个地方没人能看懂kubectl describe的输出出了问题照样抓瞎。反过来说只要你的服务超过五六个、有多环境隔离需求、有滚动发布和回滚的硬性要求那么 Kubernetes 提供的这套抽象就是划算的。它把发布这件事变成了一个可以审计、可以回滚、可以自动化的标准动作这个价值在服务数量上去之后会被放大很多倍。2. 先搞清对象模型Pod、Deployment、Service 不是同级概念新手看文档最容易被绕晕的地方是把 Pod、Deployment、Service 当成三个并列的东西去记。实际上它们是有层次、有包含关系的Deployment 管着 ReplicaSetReplicaSet 管着 PodPod 里跑着容器Service 不拥有任何 Pod它只是通过标签选择器指向一组 Pod。理解了这个层次很多为什么我改了 Deployment 但 Service 没反应的困惑就自动消失了——因为它们之间根本没有直接引用关系只有标签匹配关系。我会把这套对象模型拆成四组来讲计算Pod、Deployment、StatefulSet、DaemonSet、Job、网络Service、Ingress、配置ConfigMap、Secret、存储PV、PVC、StorageClass。四组之间的边界很清晰配置类对象不会管调度存储类对象不会管流量混着理解容易乱。2.1 Pod 不是容器它是一起调度的一组容器Pod 是 Kubernetes 里最小的调度单位注意是调度单位不是运行单位。一个 Pod 里可以有一个容器也可以有多个容器它们共享同一个网络命名空间也就是共享 IP 和端口空间和同一组存储卷。最常见的多容器组合是主业务容器 边车容器比如主容器跑业务逻辑边车容器负责日志采集或者代理转发。为什么要有 Pod 这一层而不是直接调度容器核心原因是有些进程必须部署在一起、共享网络和文件。比如日志采集需要读业务容器的日志目录如果用两个独立容器就得走网络传输或者共享宿主机目录前者有性能损耗后者破坏隔离。Pod 把必须在一起的约束表达出来了调度器把它们当成一个整体放到同一个节点上。需要提醒的是同一个 Pod 内的容器共享 IP所以不能监听同一个端口。我见过有人在 Pod 里放两个容器都监听 8080结果第二个容器起不来报端口占用排查半天才发现是这个原因。另外Pod 本身是用完即弃的它没有自愈能力节点挂了 Pod 就没了所以生产环境基本不会直接创建裸 Pod而是通过 Deployment 这类控制器来管理。2.2 Deployment 和 ReplicaSet一条控制链不是一个对象kubectl get deployment和kubectl get replicaset是两个不同的东西但很多人只知道前者。它们的关系是这样的你创建 DeploymentDeployment 控制器会根据 Pod 模板创建一个 ReplicaSetReplicaSet 再根据副本数创建对应数量的 Pod。为什么要多这一层因为 ReplicaSet 记录了某一版 Pod 模板的快照滚动更新时Deployment 会创建一个新的 ReplicaSet然后逐步把旧 ReplicaSet 的副本数降到 0新的升到目标值。旧 ReplicaSet 不会被立刻删除而是保留下来数量由revisionHistoryLimit控制这就是回滚能工作的基础。理解这条链的实际价值在于排查问题。当你发现 Pod 数量不对时正确的排查路径是先看 Deployment 的状态和事件再看它关联的 ReplicaSet 是不是创建成功了最后看 Pod 为什么起不来。如果跳过中间层直接看 Pod很容易漏掉ReplicaSet 因为配额限制根本没创建出来这种情况。不同工作负载的适用场景我整理了一张表选错类型是新手常见问题对象类型适合的场景关键特征Deployment无状态服务、Web 接口、API 网关Pod 可随意替换不保存本地状态StatefulSet数据库、消息队列、需要稳定标识的服务Pod 有固定名称和稳定网络标识按序启停DaemonSet日志采集、节点监控、网络插件每个节点或指定节点跑一个副本Job一次性批处理任务运行到成功结束不重启CronJob定时任务按 cron 表达式周期性创建 Job2.3 Service 和 Ingress一个解决找得到一个解决进得来Pod 的 IP 是会变的重建一次就换一个。如果调用方直接写 Pod IP那这个调用方基本没法维护。Service 解决的就是这个问题它提供一个固定的虚拟 IPClusterIP和 DNS 名称通过标签选择器动态维护后端 Pod 列表请求转发到 Service 的 IP 上由 kube-proxy 或者 eBPF 实现负载均衡到具体 Pod。Service 有几种类型用途差别很大ClusterIP是默认类型只在集群内部可访问NodePort在每个节点上开一个高端口默认 30000-32767转发进来适合临时调试LoadBalancer需要底层基础设施支持会申请一个外部负载均衡器ExternalName比较特殊它不做转发只是返回一个 CNAME 记录。还有一种是clusterIP: None的无头 Service不给虚拟 IPDNS 直接返回所有 Pod 的地址StatefulSet 场景下常用。Ingress 是更上面一层的东西它做的是七层路由根据域名和路径把请求分发到不同的 Service。比如demo.example.internal/api转发到后端服务/static转发到静态资源服务。这里有个必须说清的点Ingress 对象本身只是一份路由规则真正干活的是 Ingress Controller集群里没装 ControllerIngress 写了也不会生效。这个坑我踩过YAML 应用成功、kubectl get ingress也有输出但访问就是 404最后发现是 Controller 没部署。2.4 配置与存储对象ConfigMap、Secret、PVC 各自的边界ConfigMap 用来存非敏感的配置比如应用的环境变量、配置文件内容。Secret 用来存敏感信息比如密码、令牌、证书它的内容在 etcd 里可以做加密存储但要注意默认的 base64 只是编码不是加密任何有权限读 Secret 的人都能解出来。两者的用法类似都可以通过环境变量注入或者挂载成文件区别主要在权限控制和审计上。存储这块稍微复杂一点。PV 是集群级别的存储资源抽象PVC 是用户对存储的申请StorageClass 则定义了动态供给的方式——你创建一个 PVC指定 StorageClass底层插件就会自动创建一个对应的 PV 并绑定。这个设计的好处是应用不需要关心底层是本地盘、网络存储还是对象存储只描述我需要 20Gi 的读写盘。accessModes这几个参数特别容易配错ReadWriteOnce表示只能被单个节点读写挂载注意是节点级不是 Pod 级ReadOnlyMany允许多节点只读ReadWriteMany允许多节点读写但不是所有存储插件都支持。如果应用是多副本部署又需要写共享目录选了 RWO 就会出现第二个 Pod 挂载失败的问题日志里会明确报多节点挂载冲突。3. 一条 kubectl apply 之后请求在集群里走了哪些门把 YAML 交给集群看起来只是一个命令实际背后是一整条流水线。理解这条流水线最大的好处是排查问题时有方向感到底是 API 层被拒绝了还是调度器找不到合适节点还是节点上的容器运行时拉不下镜像。这三种情况的报错位置完全不同处理方法也完全不同。我见过不少人遇到 Pod 一直 Pending 就慌了其实只要看一眼kubectl describe pod最后那段 Events答案基本都在里面Insufficient cpu是资源不够node(s) had untolerated taint是污点没容忍no nodes available to schedule pods是节点都不可用。问题在于很多人不知道去看 Events而是去翻容器日志方向一开始就错了。3.1 控制面四件套apiserver、etcd、scheduler、controller-managerkube-apiserver是整个集群的唯一入口所有组件都通过它读写状态没有例外。它负责认证你是谁、授权你能不能做这件事、准入控制这次请求要不要被修改或拒绝处理完之后把对象持久化到etcd。这里的关键设计是apiserver 是无状态的所有状态都在 etcd 里所以控制面可以做多副本前面挂负载均衡。etcd是键值存储存的是整个集群的期望状态和实际状态。它的重要性怎么强调都不过分——etcd 挂了集群不会立刻崩溃已有的 Pod 还在跑但你无法做任何变更也无法感知新状态。所以生产环境 etcd 一定要做多节点集群、定期备份并且单独放在性能稳定的磁盘上。kube-scheduler负责决定 Pod 放到哪个节点。它监听尚未绑定节点的 Pod执行一轮过滤和打分把结果写回 Pod 的nodeName字段。kube-controller-manager里跑着一堆控制器Deployment 控制器、ReplicaSet 控制器、节点控制器、ServiceAccount 控制器等等每个控制器都在自己的调谐循环里把实际状态推向期望状态。3.2 工作节点三件套kubelet、容器运行时、kube-proxy节点上最关键的是kubelet它负责和 apiserver 通信接收分配到本节点的 Pod然后调用容器运行时把容器真正创建出来。它还会定期上报节点状态心跳执行探针检查管理容器的生命周期。kubelet 不自带容器运行时而是通过 CRI 接口调用外部实现现在主流的是 containerd 这一类。网络这块Pod 之间的互通靠 CNI 插件实现Service 的负载均衡规则维护靠 kube-proxy或者某些方案里用 eBPF 替代。DNS 解析靠 CoreDNS它本身也是以 Pod 形式跑在集群里的。这里有个排查经验如果 Pod 之间网络通但域名解析失败问题大概率在 CoreDNS 或者resolv.conf的配置上而不是网络插件。3.3 调度器怎么挑节点Filter 和 Score 的真实逻辑调度过程分两个阶段。第一阶段是过滤Filter把所有不满足硬性条件的节点剔除。常见的过滤插件包括NodeResourcesFit节点剩余资源是否够 Pod 的 requests、TaintTolerationPod 是否容忍节点污点、NodeAffinity节点标签是否匹配、PodTopologySpread拓扑分布约束。第二阶段是打分Score给剩下的节点排序比如ImageLocality会给已经缓存了目标镜像的节点加分NodeResourcesBalancedAllocation会倾向于让 CPU 和内存利用率更均衡的节点。理解这套机制的实际意义在于调度只看 requests不看实际使用量。一个节点实际 CPU 已经用到 90%但只要 requests 总和没超卖调度器依然会往上放 Pod。所以requests设置得是否合理直接决定了集群会不会出现看起来没满却调度不上去或者调度上去了但节点被压垮的情况。这个点我在第 5 节会详细展开。3.4 调谐循环为什么改 YAML 就能自动修复调谐循环reconcile loop是整个 Kubernetes 最核心的设计模式理解它比记 API 对象重要得多。每个控制器的逻辑都是读取期望状态读取实际状态比较差异执行动作缩小差异然后重复。这个循环不关心上一次是什么状态只看现在应该是什么状态。这个模式带来一个非常实用的特性任何手动操作造成的偏离都会被自动纠正。比如你手动删了一个 PodReplicaSet 控制器发现副本数不足立刻补一个你手动改了 Pod 的标签让它脱离了 Service 的选择器控制器不会管因为 Pod 模板没变但下次 Pod 重建又会恢复原样。反过来说如果你想做临时修改改 Pod 是没用的因为它随时会被替换要改就得改上层控制器。4. 把第一个应用真正跑起来从 YAML 到外部可访问理论讲完动手跑一遍才能把概念串起来。我选择的场景是一个最普通的 Web 服务三个副本、带健康检查、通过 Service 暴露、通过 Ingress 对外提供访问。这个场景覆盖了绝大部分有状态服务之外的需求把这份清单吃透其他变体都是在这个基础上加东西。动手之前先确认三件事这三件事没确认就乱敲命令很容易改到错误的集群上。第一是集群版本不同版本的 API 字段有差异比如 Ingress 在 1.19 之后从extensions/v1beta1换到了networking.k8s.io/v1老 YAML 直接套用会报no matches for kind。第二是当前上下文kubectl config current-context看一眼确认自己连的是测试环境还是生产环境。第三是命名空间不加-n参数默认落在default生产环境通常要单独建命名空间做隔离。4.1 上下文与命名空间的基础确认kubectl version --short kubectl config current-context kubectl get nodes -o wide kubectl create namespace demo-app这几条命令的输出里我会重点看节点状态是不是Ready、节点数量够不够、以及节点的可用资源。如果节点都处于NotReady后面所有操作都会卡在调度阶段。另外-o wide能看到节点的内网地址和操作系统信息排查网络问题时有用。4.2 一份可以直接抄的 Deployment 清单下面这份清单我在多个模拟项目里用过参数是经过权衡的不是随手写的apiVersion: apps/v1 kind: Deployment metadata: name: demo-web namespace: demo-app labels: app: demo-web spec: replicas: 3 revisionHistoryLimit: 5 selector: matchLabels: app: demo-web strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: metadata: labels: app: demo-web spec: containers: - name: web image: registry.example.internal/demo/web:1.0.0 ports: - name: http containerPort: 8080 resources: requests: cpu: 200m memory: 256Mi limits: cpu: 1 memory: 512Mi readinessProbe: httpGet: path: /healthz port: http initialDelaySeconds: 5 periodSeconds: 10 timeoutSeconds: 2 failureThreshold: 3 livenessProbe: httpGet: path: /livez port: http initialDelaySeconds: 20 periodSeconds: 20 timeoutSeconds: 2 failureThreshold: 3 startupProbe: httpGet: path: /healthz port: http periodSeconds: 5 failureThreshold: 12几个参数的选择理由值得说清楚。maxSurge: 1配合maxUnavailable: 0意味着滚动更新时先多起一个 Pod等它就绪后再删一个旧的全程可用副本数不低于 3代价是需要多占一个 Pod 的资源额度。对于对外服务我会优先保证可用性对于内部批处理类服务把maxUnavailable调到 1 可以让发布更快。revisionHistoryLimit: 5保留五个历史版本够回滚用了留太多会占用 etcd 空间。startupProbe是为了应对启动慢的应用——如果没有它livenessProbe会在应用还没启动完成时就开始探测连续失败直接触发重启形成一个永远起不来的死循环。这个坑我在第 5 节会详细讲。requests给 200m CPU 和 256Mi 内存limits给 1 核和 512Mi这个比例是根据实际压测结果定的正常流量下 CPU 使用在 100m 到 300m 之间波动内存稳定在 200Mi 左右留出一定余量。如果一开始没有压测数据宁可先给保守值观察一段时间再调不要一上来就写得很宽裕那样会浪费集群容量。应用之后用这几条命令验证kubectl -n demo-app apply -f deployment.yaml kubectl -n demo-app rollout status deployment/demo-web --timeout120s kubectl -n demo-app get pods -o wide kubectl -n demo-app describe deployment demo-webrollout status会一直阻塞到发布完成或者超时比get pods反复刷好用得多。4.3 用 Service 和 Ingress 把流量接进来apiVersion: v1 kind: Service metadata: name: demo-web namespace: demo-app spec: type: ClusterIP selector: app: demo-web ports: - name: http port: 80 targetPort: http这里的targetPort用了端口名http而不是数字好处是 Pod 模板里改了containerPort之后 Service 不需要跟着改减少一处需要维护的对应关系。selector必须和 Deployment 模板里的标签完全一致这是最容易出错的地方。Ingress 的配置依赖集群里已经装好的 ControllerapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: demo-web namespace: demo-app spec: ingressClassName: nginx rules: - host: demo.example.internal http: paths: - path: / pathType: Prefix backend: service: name: demo-web port: number: 80ingressClassName必须和集群里实际的 IngressClass 名字对上kubectl get ingressclass可以查看。配完之后如果访问返回 404先查 Controller 的日志再看 Ingress 的describe输出里有没有正确解析出后端 Service。4.4 滚动发布与回滚的验证姿势发布新版本时直接改镜像标签再 applykubectl -n demo-app set image deployment/demo-web webregistry.example.internal/demo/web:1.1.0 kubectl -n demo-app rollout status deployment/demo-web发布过程中可以用kubectl -n demo-app get pods -w观察 Pod 的更替过程正常情况下会看到新 Pod 先创建、进入 Ready、旧 Pod 才 Terminating。如果发现新 Pod 一直起不来rollout status会超时这时候要看新 Pod 的事件和日志。回滚kubectl -n demo-app rollout history deployment/demo-web kubectl -n demo-app rollout undo deployment/demo-web --to-revision1回滚之所以能工作就是因为前面保留了旧的 ReplicaSet。回滚不是万能的如果新版本做了数据库结构变更回滚代码不一定能恢复数据这一点必须在发布流程里单独考虑Kubernetes 管不了应用层的数据兼容性。5. 资源、探针、网络新手最容易在这里翻车前面四节讲的是应该怎么做这一节讲为什么照着文档做还是会出问题。Kubernetes 的文档很全但它不会告诉你某些配置组合在特定场景下会引发连锁反应而且报错信息往往指向不了真正的根因。以下四个问题是我在实际环境里遇到频率最高的每一个都值得单独记一笔。5.1 requests 和 limits 写错比不写更危险先说清楚这两个概念的区别。requests是调度依据调度器按它判断节点放不放得下limits是运行时上限超过 CPU 限流超过内存直接 OOMKill。很多人图省事只写limits不写requests结果 Kubernetes 会把requests默认成等于limits导致节点上明明还有余量新 Pod 却调度不上去。更隐蔽的问题是资源超卖。假设一个节点有 4 核你放了 10 个requests: 500m的 Pod从调度器视角看资源刚好占满但实际运行时这 10 个 Pod 可能同时冲高CPU 互相抢占延迟飙升。这种情况在监控上表现为节点 CPU 使用率 100% 但没有任何报警因为所有 Pod 都在 limits 以内。我的经验是requests 按 P50 使用量设置limits 按 P99 的两倍设置中间留出的空间用于应对突发。如果应用对延迟敏感limits的 CPU 值不要设得太紧因为 CPU 限流throttling会直接体现在响应时间上哪怕平均负载并不高。内存的limits要设得比实际峰值高一些OOMKill 的代价是容器重启比 CPU 限流严重得多。5.2 三种探针用反了容器会陷入重启循环三种探针的职责完全不同混淆使用是最常见的配置错误。livenessProbe判断容器还活着吗失败就重启容器readinessProbe判断容器能接流量吗失败就从 Service 后端摘掉但不重启startupProbe判断容器启动完成了吗在它成功之前其他两种探针都不生效。最典型的翻车场景是livenessProbe里检查了一个外部依赖比如数据库连接数据库抖动一下所有 Pod 的 liveness 同时失败全部重启重启之后依赖还没恢复继续失败形成雪崩。正确的做法是 liveness 只检查进程自身是否健康依赖的可用性交给 readiness 去处理——依赖挂了就摘流量等恢复了自动加回来不要动不动就重启。另一个高频错误是initialDelaySeconds设得太短。应用启动需要 30 秒探针 5 秒就开始检查连续失败三次直接杀掉重启永远启动不完成。startupProbe就是为这个场景设计的设置一个足够长的failureThreshold让应用有充分的启动时间启动成功后再交给 liveness 接管。5.3 镜像拉取、DNS 和网络策略的三个高频故障镜像拉取失败通常有三种原因镜像地址写错、私有仓库凭证缺失、节点无法访问仓库。区分方法很直接kubectl describe pod里会给出ErrImagePull或者ImagePullBackOff如果是凭证问题错误信息里会有unauthorized字样。私有仓库的凭证需要通过imagePullSecrets挂到 Pod 上或者在命名空间里配置默认凭证。DNS 解析失败的典型表现是应用日志里报no such host但直接ping外部地址是通的。排查路径是先确认 CoreDNS 的 Pod 正常运行再kubectl exec进业务 Pod 看/etc/resolv.conf的nameserver是不是指向集群 DNS 的 Service IP最后检查 NetworkPolicy 有没有把到 DNS 的 53 端口给挡了。最后这条特别隐蔽因为网络策略默认是全通一旦有人加了默认拒绝规则又忘了放行 DNS整个命名空间的服务都会开始报解析错误。网络策略的生效范围也需要理解清楚。NetworkPolicy 是命名空间级别的一旦某个 Pod 被任何一条策略选中它就进入默认拒绝模式只有被显式允许的流量才能进出。这意味着你只加了一条允许入站 8080 的策略出站的 DNS 查询也会被阻断除非你同时写了出站规则。这个行为让很多第一次用网络策略的人困惑。5.4 节点资源告急时谁先被驱逐节点内存不足时kubelet 会主动驱逐 Pod驱逐顺序由 QoS 类别决定QoS 类别判定条件驱逐优先级BestEffort所有容器都没设 requests 和 limits最先被驱逐Burstable至少一个容器设了 requests但不满足 Guaranteed 条件中间Guaranteed所有容器的 CPU 和内存都设了 requests 且等于 limits最后被驱逐同一个 QoS 类别内部会优先驱逐实际使用量超过 requests幅度更大的 Pod。这个机制的实际意义是关键服务配成 Guaranteed就能在节点资源紧张时活下来而没设资源限制的 Pod会成为第一批牺牲品。我在模拟环境里做过测试一个没设 limits 的调试用 Pod 在节点内存紧张时几乎每次都第一个被清掉这在生产环境里可能是好事但在测试环境里会让人莫名其妙地丢容器。6. 上生产之前我会逐项打勾的几件事从能跑起来到敢放在生产环境中间隔着一整套工程约束。这一节不是泛泛的检查项而是我在多个项目里积累下来的具体动作每一条都对应过真实的问题。6.1 命名空间、标签与资源配额的约定命名空间不是可选项是必须项。按环境测试、预发、生产和按业务域划分是两种常见方式我倾向于两者结合环境一级业务域二级。每个命名空间配一个ResourceQuota限制总的 CPU、内存、Pod 数量防止某个业务无节制地占满集群。再配一个LimitRange设置默认的 requests 和 limits这样即使有人忘了写也有个兜底值不至于出现 BestEffort 的 Pod。标签规范要提前定好至少包含应用名、所属业务域、版本、环境、负责人。这些标签在排查问题时价值巨大——kubectl get pods -l envprod,teamalpha一条命令就能定位到具体范围。我见过标签写得随意的团队出了问题只能靠 Pod 名字猜效率差一个数量级。6.2 可观测性从 kubectl describe 到指标与日志kubectl describe和kubectl logs是排查的第一层工具但它们有三个局限只能看当前状态历史事件会被清除、只能看单个对象、日志在 Pod 重建后就没了。所以生产环境必须补齐三样东西集中式日志采集、指标监控、事件持久化。日志采集通常用 DaemonSet 部署采集器把容器标准输出收集到集中的存储里。指标监控关注几个关键维度Pod 的 CPU 和内存实际使用量用来校正 requests 和 limits、重启次数kube_pod_container_status_restarts_total、Pod 是否处于非 Ready 状态、节点的资源水位。重启次数这个指标特别重要一个安静地每天重启三次的容器往往比一个直接挂掉的服务更危险因为没人注意到它。事件Event默认只保留一小时排查隔夜问题时会发现事件已经被清空。生产环境建议把事件也采集到日志系统里或者部署一个持续记录事件的组件这个投入在排查疑难问题时会体现出价值。6.3 权限与密钥RBAC 和 Secret 的基本体面RBAC 的原则是最小权限。默认的defaultServiceAccount 在很多集群里权限过宽应用如果不需要访问 API就应该显式设置automountServiceAccountToken: false不要在容器里挂一个用不到的令牌。需要访问 API 的场景单独建 ServiceAccount配 Role 和 RoleBinding权限精确到具体的资源和动作。Secret 这块有三个基本要求第一开启 etcd 的静态加密避免磁盘被拿走后密钥明文泄露第二不要用环境变量注入敏感信息环境变量会出现在kubectl describe和进程信息里挂载成文件相对安全第三定期轮换。另外要提醒一句Secret 的 base64 编码不提供任何保密性echo xxx | base64 -d就能还原不要把base64 过了当成安全措施。最后分享一个我自己的习惯每次上线新服务之前先在一个独立的命名空间里用同样的 YAML 跑一遍把资源限制调到最小、故意触发几次故障删 Pod、改错镜像标签、断掉依赖看集群的反应是否符合预期。这个过程花不了多少时间但能提前暴露掉大部分配置问题比在生产环境上用手忙脚乱的方式学习要划算得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑