跳至主要内容

介绍一下k8s的功能,包括服务注册、服务发现、负载均衡、配置、存储管理、健康检查、自动扩缩容、RBAC、多命名空间隔离。讲解它们的原理、怎么用、有哪些使用场景

Kubernetes 核心功能学习文档

涵盖:服务注册、服务发现、负载均衡、配置管理、存储管理、健康检查、自动扩缩容、RBAC、多命名空间隔离 每节包含:原理讲解、使用方式(含 YAML 示例)、典型场景/案例


0. 全局背景:为什么需要这些机制

Kubernetes(简称 k8s)是一个容器编排平台,核心目标是把一堆"随时可能死掉、随时可能漂移到别的机器上"的容器(Pod),管理成一个稳定、可自愈、可伸缩的分布式系统。

它要解决的根本问题是:Pod 是临时的、IP 会变、数量会变,但访问它的人不希望关心这些细节。下面的每一个功能,几乎都是围绕这个核心矛盾展开的。


1. 服务注册(Service Registration)

原理

在 k8s 里,"服务注册"不是一个独立动作,而是自动完成的:

  1. 每个 Pod 启动后,kubelet 会向 API Server 上报自己的状态(IP、所在节点、是否 Ready)。
  2. API Server 把这些信息写入 etcd(k8s 的分布式键值存储,相当于整个集群的"数据库")。
  3. 当你创建一个 Service 资源,并用 selector(标签选择器)指定它要代理哪些 Pod 时,控制平面里的 Endpoint Controller 会持续监听(watch)匹配该 selector 的 Pod,把它们的 IP:Port 写入一个叫 Endpoints(或新版的 EndpointSlice)的对象里。

也就是说:Pod 通过打标签"自动注册"到 Service 上,不需要像传统微服务架构(如 Eureka、Consul)那样,应用代码里手写"启动时向注册中心汇报"的逻辑。

怎么用

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service   # 关键:Pod 的标签
    spec:
      containers:
      - name: order-service
        image: myrepo/order-service:v1.2
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service       # Service 用同样的标签去"认领" Pod
  ports:
  - port: 80
    targetPort: 8080

一旦这两个资源创建,Service 就会自动"注册"到当前所有匹配的 3 个 Pod,且后续 Pod 扩容/缩容/重启都会自动更新。

使用场景

  • 案例:电商订单服务滚动升级。运维发布新版本时,新 Pod 起来、旧 Pod 被删,Endpoint 列表实时更新,前端调用方完全无感知,不需要手动去注册中心摘除/添加节点。

2. 服务发现(Service Discovery)

原理

"服务注册"解决了"谁在提供服务","服务发现"解决的是"调用方怎么找到它"。k8s 提供两种主流方式:

  1. DNS 方式(最常用):集群内置 CoreDNS,每个 Service 创建后会自动获得一个域名: <service-name>.<namespace>.svc.cluster.local Pod 内部的 DNS 解析会被自动配置指向 CoreDNS,调用方直接用服务名即可访问。
  2. 环境变量方式:Pod 启动时,kubelet 会把同 namespace 下已存在的 Service 信息注入为环境变量(如 ORDER_SERVICE_SERVICE_HOST),但这种方式有先后顺序限制(Service 必须先于 Pod 创建),生产环境很少用,了解即可。

怎么用

# 在同一 namespace 内的 Pod 中,直接用服务名访问
curl http://order-service/api/orders

# 跨 namespace 访问,需要带上 namespace
curl http://order-service.production.svc.cluster.local/api/orders

使用场景

  • 案例:微服务之间调用user-service 要调用 order-service,代码里只需写死一个域名 http://order-service,无论 order-service 扩容到 10 个副本还是缩到 1 个,调用方代码永远不用改。这比传统架构里维护一份"服务地址配置表"要简单得多。

3. 负载均衡(Load Balancing)

原理

k8s 的负载均衡分两个层次:

(1) 集群内负载均衡(Service 层面) - 每个节点上运行 kube-proxy,它监听 API Server 中 Service/Endpoint 的变化,并在本机配置转发规则(早期用 iptables,规则较多时性能下降;新版本可用 IPVS 模式,基于哈希表,性能更好,支持更多负载均衡算法如轮询、最少连接等)。 - 客户端请求 Service 的虚拟 IP(ClusterIP)时,请求包在节点内核层面就被转发到某个真实 Pod,整个过程对客户端透明。

(2) 集群外负载均衡(Ingress / LoadBalancer 层面) - Service 类型为 LoadBalancer 时,会向云厂商(如 AWS ELB、阿里云 SLB)申请一个外部负载均衡器。 - Ingress 资源则工作在七层(HTTP/HTTPS),通过 Ingress Controller(如 Nginx Ingress、Traefik)实现基于域名、路径的路由和负载均衡,功能比 Service 的四层转发更丰富(比如可以做灰度发布、路径重写)。

怎么用

# 四层负载均衡:ClusterIP(默认)
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  type: ClusterIP     # 默认类型,集群内负载均衡
  selector:
    app: order-service
  ports:
  - port: 80
    targetPort: 8080
---
# 七层负载均衡:Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop-ingress
spec:
  rules:
  - host: shop.example.com
    http:
      paths:
      - path: /orders
        pathType: Prefix
        backend:
          service:
            name: order-service
            port:
              number: 80
      - path: /users
        pathType: Prefix
        backend:
          service:
            name: user-service
            port:
              number: 80

使用场景

  • 案例:大促流量分发。双十一大促期间,order-service 扩容到 50 个 Pod,通过 Ingress 统一入口按路径 /orders 分流到这些 Pod,同时 kube-proxy 保证请求均匀落到每个副本,避免单点过载。

4. 配置管理(Configuration Management)

原理

k8s 提倡"配置与镜像分离":同一个镜像,通过注入不同配置,就能跑在开发/测试/生产环境。核心对象有两个:

  • ConfigMap:存储非敏感配置(如日志级别、feature flag、URL 地址等),本质是键值对集合。
  • Secret:存储敏感信息(密码、Token、证书),内容以 Base64 编码存储在 etcd 中(注意:Base64 只是编码不是加密,生产环境建议开启 etcd 静态加密或使用外部密钥管理如 Vault)。

两者都可以通过环境变量挂载为文件的方式注入 Pod。挂载为文件的好处是:修改 ConfigMap 后,文件内容会自动热更新(有一定延迟),而环境变量方式修改后必须重启 Pod 才能生效。

怎么用

apiVersion: v1
kind: ConfigMap
metadata:
  name: order-config
data:
  LOG_LEVEL: "info"
  MAX_RETRY: "3"
---
apiVersion: v1
kind: Secret
metadata:
  name: order-db-secret
type: Opaque
data:
  DB_PASSWORD: cGFzc3dvcmQxMjM=   # base64 编码后的值
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  template:
    spec:
      containers:
      - name: order-service
        image: myrepo/order-service:v1.2
        envFrom:
        - configMapRef:
            name: order-config
        env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: order-db-secret
              key: DB_PASSWORD

使用场景

  • 案例:多环境部署。同一份 order-service 镜像,在测试环境挂载 order-config-test(连测试数据库),在生产环境挂载 order-config-prod(连生产数据库),无需重新打镜像。

5. 存储管理(Storage Management)

原理

容器本身是无状态、易失的,重启后本地文件系统数据会丢失。k8s 用三层抽象解决持久化存储问题:

  1. PV(PersistentVolume):由集群管理员(或存储插件自动)创建的一块"实际存储资源",可以是云盘、NFS、Ceph 等。
  2. PVC(PersistentVolumeClaim):应用方"申请"存储的方式,类似"我需要 10GB、ReadWriteOnce 的存储",k8s 会自动找一个匹配的 PV 绑定(Bind)。
  3. StorageClass:定义"动态供应"规则,PVC 提交后,如果没有现成 PV 匹配,StorageClass 会调用对应的存储插件(CSI,Container Storage Interface)自动创建一块新的 PV,免去管理员手动创建的麻烦。

这种"申请-绑定"模式让应用和底层存储解耦:开发者只管写 PVC,不用关心底层是阿里云盘还是 AWS EBS。

怎么用

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: standard   # 指定动态供应的 StorageClass
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        volumes:
        - name: data
          persistentVolumeClaim:
            claimName: mysql-pvc

使用场景

  • 案例:数据库有状态服务迁移。用 StatefulSet + PVC 部署 MySQL,即使 Pod 被重新调度到另一台物理机,只要底层是网络存储(如云盘),数据依然完整挂载回新 Pod,不丢数据。

6. 健康检查(Health Check)

原理

k8s 通过三种探针(Probe)持续检测容器的健康状态,由 kubelet 定期执行:

  • Liveness Probe(存活探针):判断容器是否"活着"。失败达到阈值 → kubelet 重启该容器。用于解决"进程没崩但已经死锁/卡死"的问题。
  • Readiness Probe(就绪探针):判断容器是否"准备好接收流量"。失败 → 该 Pod 被从 Service 的 Endpoint 列表中摘除,但不会被重启。常用于启动慢、需要预热的服务。
  • Startup Probe(启动探针):用于启动特别慢的应用,在它成功前,Liveness/Readiness 探针都不会生效,避免"还没启动完就被当成死了重启"的死循环。

三种探针的检测方式都支持:httpGet(发 HTTP 请求看状态码)、tcpSocket(探测端口)、exec(执行命令看返回值)。

怎么用

apiVersion: v1
kind: Pod
metadata:
  name: order-service
spec:
  containers:
  - name: order-service
    image: myrepo/order-service:v1.2
    ports:
    - containerPort: 8080
    startupProbe:
      httpGet:
        path: /healthz
        port: 8080
      failureThreshold: 30
      periodSeconds: 2
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      periodSeconds: 5

使用场景

  • 案例:Java 应用启动慢导致的误杀。Spring Boot 应用启动加载需要 60 秒,若只配 Liveness 探针且 initialDelaySeconds 设太短,容器会在没启动完时被反复重启,形成死循环;加上 startupProbe 后,给足启动时间,问题解决。
  • 案例:数据库连接池未就绪。应用进程已经启动(Liveness 通过),但数据库连接还没建立好,此时靠 Readiness 探针让 Service 暂时不转发流量过去,避免用户请求报错。

7. 自动扩缩容(Autoscaling)

原理

k8s 提供三个层面的自动扩缩容,通常配合使用:

  1. HPA(Horizontal Pod Autoscaler,水平扩缩容):根据 CPU/内存使用率或自定义指标(如 QPS、消息队列长度),自动增减 Pod 副本数。原理是 HPA Controller 每隔一段时间(默认 15 秒)向 Metrics Server(或 Prometheus Adapter)查询指标,与目标值比较后调整 Deploymentreplicas 字段。
  2. VPA(Vertical Pod Autoscaler,垂直扩缩容):自动调整单个 Pod 的 CPU/内存 request/limit,而不是增加副本数,适合资源需求波动大但不适合水平扩展的应用(一般需要重启 Pod 生效)。
  3. Cluster Autoscaler(集群扩缩容):当 Pod 因为节点资源不足而 Pending 时,自动向云厂商申请新的节点;反之节点长期空闲会被自动缩减,节省成本。

三者关系:HPA/VPA 解决"Pod 层面"资源不够用的问题,Cluster Autoscaler 解决"节点层面"资源不够用的问题——当 HPA 扩出很多 Pod 但节点装不下时,就会触发 Cluster Autoscaler 加节点。

怎么用

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: order-service-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: order-service
  minReplicas: 3
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70   # CPU 平均利用率超过 70% 就扩容

使用场景

  • 案例:秒杀活动流量突增。平时 order-service 保持 3 个副本,秒杀开始前 CPU 使用率飙升,HPA 在几分钟内自动扩容到 20 个副本;活动结束流量回落后再自动缩回 3 个,全程无需人工干预,也不必长期占用 20 个副本的资源(省钱)。

8. RBAC(基于角色的访问控制)

原理

RBAC 控制"谁(Subject)能对哪些资源(Resource)做什么操作(Verb)",四个核心对象:

  • Role:定义一组权限规则,作用域限定在某个 namespace 内(比如"能读写 default 命名空间下的 Pod")。
  • ClusterRole:和 Role 类似,但作用域是整个集群(可以跨 namespace,也可以用于非命名空间资源如 Node)。
  • RoleBinding:把 Role(或 ClusterRole)绑定给某个用户/组/ServiceAccount,且绑定关系只在某个 namespace 内生效
  • ClusterRoleBinding:把 ClusterRole 绑定给用户,且全集群生效

简单记忆:Role/ClusterRole 定义"能做什么",RoleBinding/ClusterRoleBinding 定义"谁能做,在哪个范围内做"。

怎么用

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]   # 只能查看 Pod,不能删除/修改
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods-binding
  namespace: production
subjects:
- kind: User
  name: alice           # 也可以是 ServiceAccount
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

使用场景

  • 案例:CI/CD 流水线权限最小化。给 Jenkins 的 ServiceAccount 只授予"在 staging 命名空间创建/更新 Deployment"的权限,而无权访问 production 命名空间或删除集群级资源,防止误操作或流水线被攻破后造成大范围破坏(最小权限原则)。
  • 案例:只读监控账号。给运维新人一个只能 get/list 所有资源但不能 create/delete 的 ClusterRole,方便他排查问题又不用担心手滑删除生产资源。

9. 多命名空间隔离(Namespace Isolation)

原理

Namespace 是 k8s 里的逻辑隔离单元,本质上是给资源加了一个"分组前缀"。它提供的隔离体现在几个层面:

  1. 资源命名隔离:不同 namespace 下可以有同名资源(比如 devprod 都能有一个叫 order-service 的 Deployment)。
  2. 资源配额隔离(ResourceQuota):可以限制某个 namespace 总共能用多少 CPU/内存/Pod 数,防止一个团队把整个集群资源耗尽。
  3. 网络隔离(NetworkPolicy):默认情况下所有 Pod 可以互相访问,通过 NetworkPolicy 可以限制"只有同 namespace 或指定标签的 Pod 才能访问我"。
  4. 权限隔离(配合 RBAC):如上一节所说,Role/RoleBinding 天然是 namespace 级别的,可以做到"A 团队看不到 B 团队的资源"。

需要注意:Namespace 不是强安全边界(比如节点、内核层面并不隔离),它更多是一种管理和逻辑上的隔离,真正的强隔离通常还需要多集群方案。

怎么用

apiVersion: v1
kind: Namespace
metadata:
  name: team-a
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "10"
    requests.memory: 20Gi
    pods: "50"
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-from-other-namespaces
  namespace: team-a
spec:
  podSelector: {}
  ingress:
  - from:
    - podSelector: {}   # 只允许来自同 namespace 内 Pod 的流量

使用场景

  • 案例:多团队共享集群。公司有 team-ateam-b 两个业务团队共用一个 k8s 集群,通过 Namespace + ResourceQuota 划分资源边界(team-a 最多用 10 核 CPU),再通过 RBAC 限制彼此看不到对方的资源,实现"逻辑上的多租户",比给每个团队单独建一套集群成本低得多。

10. 综合案例:把这些功能串起来

以一个"电商订单服务"从开发到生产的完整链路为例:

  1. 配置管理:开发用 ConfigMap 挂载测试环境配置,生产切换成 order-config-prod
  2. 服务注册/发现:Deployment 创建 Pod,Service 自动注册并生成 DNS 名 order-service,其他服务通过域名调用它。
  3. 健康检查:配置 Liveness + Readiness 探针,保证只有真正就绪的 Pod 才接收流量。
  4. 存储管理:订单数据落到通过 PVC 挂载的云数据库存储卷(如果自建 MySQL)。
  5. 负载均衡:外部通过 Ingress 按路径把 /orders 流量转发到 order-service,kube-proxy 负责集群内部的均衡分发。
  6. 自动扩缩容:大促期间 HPA 根据 CPU 自动把 Pod 从 3 扩到 20,Cluster Autoscaler 在节点不够时自动加机器。
  7. RBAC:CI/CD 系统的 ServiceAccount 只被授权在 production 命名空间做部署,不能碰其他命名空间。
  8. 多命名空间隔离order-service 部署在 production 命名空间,和 team-b 的服务(在 team-b 命名空间)互不干扰,各自有资源配额限制。

这九个功能不是孤立的,而是共同构成了 k8s "自愈、弹性、多租户、安全"的核心能力。


11. 学习建议

  • 先动手用 minikubekind 搭一个单节点集群,把本文所有 YAML 依次跑一遍,比单纯看文档理解快得多。
  • kubectl explain <资源名> 命令可以直接在命令行看到官方字段说明,比如 kubectl explain deployment.spec
  • 遇到问题优先看 kubectl describe pod <name> 的 Events 部分,90% 的排障线索都在这里。
  • 推荐官方文档作为权威参考:https://kubernetes.io/zh-cn/docs/home/

此博客中的热门博文

Elasticsearch 读写原理指南

### 1. 什么是 segment,里面装了什么? 在 Lucene(也是 Elasticsearch)里,索引被切分成若干 **segment(段)**,每个 segment 是一个完整的、只读的倒排索引单元。一个 segment 包含: * **倒排词典** —— 用 **FST(Finite‑State Transducer)** 以高度压缩的形式保存每个字段出现的所有 term 以及 term→ord 的映射。对应的磁盘文件是 `*.tim`(新版)或 `*.tis/*.tii`(旧版)。 * **倒排列表(postings)** —— 保存每个 term 出现的文档 ID、频次、位置信息等,文件名通常是 `*.doc`、`*.pos`、`*.pay`。 * **存储字段**(_source、store:true 的字段)—— 以二进制块的形式写入 `*.fdt` / `*.fdx`。 * **doc‑values、norms、向量** 等辅助结构,分别保存在 `*.dv`、`*.norm`、`*.tv` 等文件里。 * **deleted‑docs bitmap**(`*.del`),标记哪些文档已被删除或被更新。 所有这些文件在 segment **写入磁盘后即成为只读**,后续的查询只能读取,永远不会在原文件上进行增删改。 --- ### 2. 原始文档和 FST 为什么都在 segment 里? * **原始文档**:Elasticsearch 默认把完整的 JSON(_source)以及任何 `store:true` 的字段写入 segment 的 `*.fdt/*.fdx` 文件。每个 segment 保存自己的那部分文档,旧的 segment 在合并前仍然保留,直到合并后被删除。 * **FST**:每个字段的词典在每个 segment 中单独维护,采用 FST 进行前缀共享和字节压缩。这样即使同一个 term 在多个 segment 中出现,也会在每个 segment 里拥有独立的映射,查询时只需要在对应 segment 的 FST 中定位即可。 --- ### 3. 查询时到底是怎么遍历 segment 的? 1. **请求入口**      客户端的搜索请求先到达 **协调节点**,协调节点把请求 ...

事务的ACID是什么

 事务的 ACID 是数据库事务必须满足的四个基本性质,用来保证在并发和故障情况下数据的正确性与可靠性: A(Atomicity,原子性) 一个事务中的操作要么 全部成功 ,要么 全部失败回滚 ,不存在“只做了一半”的中间状态。 C(Consistency,一致性) 事务执行前后,数据库都必须处于 一致的合法状态 ,满足约束(如主键、外键、唯一性、业务规则等)。 I(Isolation,隔离性) 并发执行的多个事务之间 相互隔离 ,一个事务未提交的中间结果对其他事务不可见(具体强弱由隔离级别决定)。 D(Durability,持久性) 一旦事务提交成功,其结果会被 永久保存 ,即使系统崩溃也不会丢失(通常依赖 WAL/redo log 等机制)。 一句话记忆: 要么全做完、前后不破坏规则、互不干扰、做完不丢。

LLM缓存详解

 可以把“大模型缓存”理解成: 把已经算过的结果(或中间结果)存下来,下次尽量复用 。但这里面其实分几层,不只是简单的“问题→答案”缓存。 1️⃣ 常见的几种缓存类型 (1)KV Cache(推理内部缓存) Transformer 在生成时,会把前面 token 的 Key/Value 向量 缓存下来。 本质:避免重复计算 attention 作用: 同一请求内部加速 特点: 👉 只对“同一上下文继续生成”有效 👉 不跨用户、不跨请求 这类缓存是你体感“流式输出越来越快”的原因之一。 (2)Prompt Cache(提示词缓存) 缓存的是: 相同(或高度相似)的 prompt → 对应的中间表示 / 输出 典型场景: 系统提示词(system prompt)很长 多轮对话里前文基本不变 👉 这里能省掉 前缀计算成本(prefill) (3)Embedding / 语义缓存(Semantic Cache) 这个才是你问题的关键 👇 不是按“字符串完全一致”,而是: 把问题转成向量 → 找“语义相似”的历史问题 → 直接复用答案 2️⃣ 为什么命中缓存成本低很多? 因为大模型推理成本主要在两块: (1)Prefill(吃 prompt) 复杂度 ~ O(n²) 很贵(尤其长 prompt) (2)Decode(逐 token 生成) 每个 token 都要算一遍模型 而缓存命中后: KV cache:不用重复 attention Prompt cache:不用重新 encode 语义缓存: 直接跳过模型推理 👉 相当于从: 几十~几百毫秒 + GPU算力 变成: 一次向量检索(毫秒级)+ 直接返回 所以成本差一个数量级是正常的。 3️⃣ “每个人问法不同,怎么命中缓存?” 这是核心难点,也是工程重点👇 ❌ 不能靠字符串匹配 比如: “今天天气怎么样” “今天外面热不热” 字符串完全不同 → 必须 miss ✅ 用语义相似度(Embedding) 流程一般是: 把问题转 embedding(向量) 在向量数据库里找 TopK 相似问题 如果相似度 > 阈值(比如 0.9) 直接返回缓存答案 一个简单示意 Q1: 北京天气怎么样 → embedding A Q2: 北京今天热吗 → embedding B cosine(A, B) ≈ 0.95...