Kubernetes 隐藏功能揭秘:你可能不知道的用法

devops进阶10 分钟阅读2026/8/25

(注:您提供的原文已经是简体中文。为了符合您“使用口语化中文”的要求,我对原文进行了润色和重写,去掉了稍微生硬的书面语感,让整体读起来更像是一位资深开发者在跟你面对面“掏心窝子”分享经验,同时严格保留了产品名和技术术语。)

Kubernetes 隐藏玩法揭秘:那些你可能不知道的骚操作

上个月,我们生产环境的一个有状态服务出了个怪毛病。Pod 频繁重启,但一重启数据就没了。排查了大半天,最后发现是存储挂载的配置搞错了——Pod 的生命周期和存储的生命周期没对齐。这事儿让我意识到,虽然我们天天写 Deployment 和 Service,但 K8s 里有些机制,我们顶多算“用过”,根本谈不上“懂”。

今天我想分享几个在实际踩坑中摸索出来的 K8s 玩法。这些不是什么炫酷的新特性,但确实是我一开始没注意、后来却帮了大忙的细节。

Namespace 可不只是用来“分分组”的

刚用 K8s 那会儿,我把 Namespace 纯粹当文件夹用——测试放一个,生产放一个。直到有一次,一个新来的同事在测试环境跑了个有资源泄漏的脚本,差点把整个集群的 CPU 给吃光。

后来我学乖了,用 ResourceQuota 配合 Namespace 做硬隔离:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-quota
  namespace: development
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "10"

加上这个之后,development 这个 Namespace 里最多只能跑 10 个 Pod,CPU 请求总量不能超过 4 核。一旦超限直接报错,绝不会让它在那儿默默把整个集群拖垮。

还有一招更绝的,就是配合 LimitRange,防止有人写 Deployment 的时候忘记配资源限制:

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: development
spec:
  limits:
  - default:
      cpu: 500m
      memory: 512Mi
    defaultRequest:
      cpu: 100m
      memory: 128Mi
    type: Container

这样一来,任何没显式声明资源的容器,都会被强制套上这层默认值。我之前就踩过这么个坑:没设 limits 的容器遇到 OOM 时,K8s 会直接把进程一枪毙了,根本不会优雅退出。加上 LimitRange 后,这种隐形炸弹算是基本排除了。

Secret 的冷门玩法:当环境变量注入器用

大家都知道 Secret 是存密码的,但很多人不知道它其实还能批量注入环境变量。

假设你有个数据库连接配置:

apiVersion: v1
kind: Secret
metadata:
  name: db-config
type: Opaque
stringData:
  DB_HOST: postgres.prod.internal
  DB_PORT: "5432"
  DB_USER: app_reader
  DB_PASSWORD: "xK9$mL2pQ"

在 Pod 里你可以这么用:

envFrom:
- secretRef:
    name: db-config

一行搞定,所有的键值对直接变成环境变量。比一个个去写 valueFrom.secretKeyRef 爽太多了。

不过这里有个我踩过的坑:stringDatadata 的区别。data 要求值必须是 base64 编码的,而 stringData 是明文写进去,K8s 存储的时候会自动帮你编码。我之前手写 base64 的时候搞错了一个字符,导致密码一直验不过,硬是排查了两个小时。从那以后我就只用 stringData 了,编码这活儿还是交给 K8s 自己干吧。

Service 和 Pod 生命周期的解耦

这个概念听起来挺简单,但真正理解透了,能解决一大堆诡异问题。

有一次我碰到个现象:Service 能解析出 IP,但请求就是死活不通。最后才发现是 Pod 被重建了,但客户端的 DNS 里还缓存着旧的 Pod IP。

其实 K8s 的设计逻辑是这样的:Service 拿到一个固定的 IP(也就是 ClusterIP),而 Pod 的 IP 是动态变的。Service 通过标签选择器去找后端的 Pod,就算 Pod 被销毁重建了,只要标签能对上,流量就会自动转到新 Pod 上去。

但如果你代码里头直接去访问 Pod IP(比如有些老代码把 IP 写死了),那就肯定要出问题。正确的做法永远是去访问 Service 的 DNS:

my-service.my-namespace.svc.cluster.local

这个 DNS 记录指向的是 Service 的 ClusterIP,而 Service 会自动把流量转发给健康的 Pod。

StatefulSet 的“隐藏身份”

StatefulSet 有个功能特别容易被忽略:它会给每个 Pod 分配一个稳定的网络标识。

比如你声明了一个叫 redis 的 StatefulSet,副本数设为 3,它会创建出:

  • redis-0
  • redis-1
  • redis-2

这些名字是固定死的。就算 redis-1 挂了被拉起来,新的 Pod 还是叫 redis-1,DNS 记录也不会变。这对数据库集群来说简直是救命的——像 Redis Sentinel 或者 PostgreSQL 的节点识别,全靠这种稳定的主机名。

我之前就犯过傻:用 Deployment 去跑 PostgreSQL 主从,结果主节点挂了重建之后 IP 变了,从节点死活连不上,整个集群直接崩了。后来改成 StatefulSet,配合 Headless Service(把 clusterIP 设为 None),每个 Pod 都有了自己独立的 DNS:

redis-0.redis-headless.default.svc.cluster.local
redis-1.redis-headless.default.svc.cluster.local

这下才算真正稳住了。

PersistentVolume 的一个反直觉行为

PV(PersistentVolume)有个特性一开始让我特别懵:它其实是不绑定到特定节点上的。

我以前以为 PV 就是“把某块硬盘挂到某个节点上”,结果 PV 是个集群级别的资源。不管你的 Pod 被调度到哪个节点,K8s 都有办法把那个 PV “弄”过去——可能是网络存储(比如 NFS、EBS),也可能是本地存储(如果你配了 local PV 的话)。

这里有个血泪教训:我有次用 local PV 跑了个有状态服务,结果节点做维护的时候 Pod 被驱逐了。但因为 local PV 是死死绑在那个节点上的,Pod 调度到其他节点后就一直卡在 Pending 状态。最后只能干等原节点恢复。

所以,如果你的存储不是网络共享的,一定要用 nodeAffinity 把 Pod 钉死在那个节点上:

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - node-1

Ingress 不只是“外部流量的入口”

大家一般都用 Ingress 来暴露 HTTP 服务,但它其实还有个用途:当内部服务的统一网关。

我们有个微服务架构,以前服务间调用都是直接访问 Service DNS。后来上了 gRPC,发现原生 Service 根本不支持 L7 路由。最后的方案是:部署一个内部的 Ingress Controller(不对外暴露),服务间调用全走内部域名:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: internal-grpc
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
spec:
  rules:
  - host: user-service.internal
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: user-service
            port:
              number: 9090

这么搞的话,其他服务只要访问 user-service.internal:80 就能自动路由到 gRPC 后端,而且还能白嫖 Ingress 自带的负载均衡和重试机制。

几条实在的建议

  1. Namespace 隔离要趁早:集群刚建好就把 ResourceQuota 和 LimitRange 配上,别等出了事故再补课。
  2. 管理 Secret 认准 stringData:千万别手写 base64,真的是容易出错。
  3. 有状态服务必须上 StatefulSet:用 Deployment 跑数据库纯纯是给自己挖坑。
  4. 用本地存储一定记得配 nodeAffinity:不然 Pod 漂移过去绝对起不来。
  5. 内部流量也能走 Ingress:需要 L7 路由能力的时候,别硬刚。

K8s 的设计其实非常精妙,很多所谓的“隐藏功能”并不是故意藏起来,纯粹是文档太长咱们没看到而已。我现在养成了个习惯:每用到一种新的资源类型,就把官方文档里关于“行为”的那一节通读一遍,往往能扒出不少以前忽略的细节。

相关 Agent

D

Docker

Docker 是一个容器化平台,允许开发者将应用及其依赖项打包成轻量级、可移植的容器。它简化了跨环境的开发、测试和部署过程。该描述强调它最适合容器化和开发环境,提供免费方案和团队方案,团队方案从每用户每月5美元起。

了解更多 →