小项目:Headless Service 与 StatefulSet(MySQL 风格行为)

发布: (2026年1月16日 GMT+8 07:23)
6 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的完整文本内容(除代码块和 URL 之外),我将按照要求将其翻译成简体中文并保持原有的 Markdown 格式。

🎯 目标

By the end you will clearly see:

  • 为什么 ClusterIP 隐藏 pod 身份
  • 为什么 Headless Service 暴露 pod 身份
  • 如何 StatefulSet + Headless Service 提供 每个 pod 的稳定 DNS(数据库所需的魔法)

🧠 Mental model (keep this in mind)

设置DNS 结果
Deployment + ClusterIP一个虚拟 IP
Deployment + Headless多个 pod IP
StatefulSet + Headless稳定的 pod DNS 名称(魔法)

🧩 项目结构

headless-demo/
├── mysql-headless.yaml
├── mysql-statefulset.yaml
└── dns-test.yaml

1️⃣ 无头服务(NO ClusterIP)

文件: mysql-headless.yaml

apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
spec:
  clusterIP: None            # 👈 THIS MAKES IT HEADLESS
  selector:
    app: mysql
  ports:
    - port: 3306

重要提示:

  • 不会创建虚拟 IP。
  • DNS 将返回 Pod IP

2️⃣ StatefulSet(稳定的 pod 身份)

文件: mysql-statefulset.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql-headless   # 👈 REQUIRED
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:8.0
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: root
          ports:
            - containerPort: 3306

StatefulSet 的保证

mysql-0
mysql-1
mysql-2
  • 名称 永不改变
  • 身份 稳定

3️⃣ DNS 测试 Pod(观察行为)

文件: dns-test.yaml

apiVersion: v1
kind: Pod
metadata:
  name: dns-test
spec:
  containers:
    - name: dns
      image: busybox:1.28
      command: ["sleep", "3600"]

4️⃣ 应用所有内容(顺序很重要)

kubectl apply -f mysql-headless.yaml
kubectl apply -f mysql-statefulset.yaml
kubectl apply -f dns-test.yaml

等待 Pod 准备就绪:

kubectl get pods

您应该看到:

mysql-0   Running
mysql-1   Running
mysql-2   Running
dns-test  Running

5️⃣ 🔥 最重要的部分 — DNS 行为

进入测试 pod

kubectl exec -it dns-test -- sh

headless 服务进行 DNS 查询

nslookup mysql-headless

结果 – 多个 IP(每个 pod 一个)

Address: 10.244.0.12
Address: 10.244.0.13
Address: 10.244.0.14

这就是 DNS 轮询

单个 pod 进行 DNS 查询(关键)

nslookup mysql-0.mysql-headless
nslookup mysql-1.mysql-headless
nslookup mysql-2.mysql-headless

每个命令都会解析为 特定的 pod IP —— 这是普通的 ClusterIP 服务永远无法做到的。

6️⃣ 为什么数据库 需要 这样

角色DNS 名称
主库mysql-0.mysql-headless
副本 1mysql-1.mysql-headless
副本 2mysql-2.mysql-headless
  • 写入mysql-0.mysql-headless(稳定的主库)
  • 读取 → 副本
  • 复制 → 稳定的目标

不可能 使用 Deployment + ClusterIP,因为该服务隐藏了 Pod 的身份。

7️⃣ 直观视觉(正在发生的事)

Headless Service + StatefulSetClusterIP Service
Headless diagramClusterIP diagram

8️⃣ One‑line interview answer (remember this)

“Headless Service 移除虚拟 IP,并通过 DNS 暴露 Pod 身份。结合 StatefulSets,它提供稳定的每个 Pod 的 DNS 名称,这对于数据库和主从架构是必需的。”

🔥 同一应用,两种服务

🟦 案例 1 — ClusterIP Service(默认行为)

YAML(普通服务)

apiVersion: v1
kind: Service
metadata:
  name: mysql-clusterip
spec:
  selector:
    app: mysql
  ports:
    - port: 3306

Kubernetes 的行为

  • 创建 一个虚拟 IP
  • 隐藏所有 pod IP
  • kube-proxy 进行负载均衡

集群内部的 DNS 行为

nslookup mysql-clusterip
Name:    mysql-clusterip
Address: 10.96.120.15    mysql-0
   +--> mysql-1
   +--> mysql-2

Kubernetes 决定哪个 pod 接收每个请求。

❌ 为什么这会导致数据库问题

如果写入落在任何副本(例如 mysql-2)上,主节点可能看不到,导致一致性和复制出现问题。使用带有稳定 pod DNS 的无头服务可以避免此问题。

❌ 问题:登录失败

- Login request → mysql-0 (no data)
- ❌ login fails

为什么?
写入和读取流量命中 不同的 pods。Pod 身份是隐藏的,因此无法强制流量始终路由到 mysql-0

ClusterIP无状态友好 的,但 有状态敌对 的。

🟨 案例 2 — 无头服务(无 CLUSTER IP)

我们只在 Service 定义中 更改一行

YAML(无头服务)

apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
spec:
  clusterIP: None   # 👈 THIS IS EVERYTHING
  selector:
    app: mysql
  ports:
    - port: 3306

🔍 DNS 行为(关键区别)

在 pod 内部:

nslookup mysql-headless

结果

Address: 10.244.0.10   (mysql-0)
Address: 10.244.0.11   (mysql-1)
Address: 10.244.0.12   (mysql-2)
  • ⚠️ 没有虚拟 IP。
  • ⚠️ DNS 直接返回 pod IP —— DNS 轮询,而非 kube‑proxy 负载均衡。

🧠 单独使用仍不足(重要)

如果你就此止步,仅使用 mysql-headless,你的应用仍可能因为 DNS 响应轮转而访问到不同的 pod。
仅使用 Headless 并不是完整的解决方案。

🟩 进入 StatefulSet(缺失的拼图)

StatefulSet 保证 稳定的 pod 名称

mysql-0
mysql-1
mysql-2

Kubernetes 会为每个 pod 自动创建 DNS 记录:

mysql-0.mysql-headless
mysql-1.mysql-headless
mysql-2.mysql-headless

🔥 恍然大悟的瞬间

写入流量

mysql-0.mysql-headless

读取流量

mysql-1.mysql-headless
mysql-2.mysql-headless

无需猜测。没有随机性。

📊 并排摘要(请记住)

功能ClusterIPHeadless
拥有虚拟IP
隐藏Pod身份
DNS返回1个IP多个Pod IP
Pod特定DNS
适用于Web应用
适用于数据库✅(使用StatefulSet)

🧪 为什么我们使用 dns-test pod

你无法在笔记本电脑上“看到 DNS”。
创建一个 pod 仅用于运行:

nslookup 

这就是 SRE 调试 真实生产问题 的方式。

🎯 ONE‑LINE INTERVIEW ANSWER (IMPORTANT)

“ClusterIP services 隐藏 pod 身份并对流量进行 load‑balance,这不适用于数据库。
Headless services 通过 DNS 暴露 pod IP,结合 StatefulSets 使用时,可提供状态工作负载所需的 stable per‑pod DNS。”

图片

图片 1

图片 2

Back to Blog

相关文章

阅读更多 »

StatefulSet 项目

先决条件:StatefulSet 需要以下组件: - Headless Service —— 为每个 pod 提供稳定的 DNS。 - StatefulSet manifest —— 定义 pod……

Helm是什么

Helm 是什么?类似于 Wordpress 的应用,需要在前端有一个 Wordpress 容器,后端有一个 MySQL 数据库。这些组件需要手动部署……