头秃修炼手册:StorageClass+StatefulSet+ETCD 全套渡劫笔记

头秃修炼手册:StorageClass+StatefulSet+ETCD 全套渡劫笔记

本篇博客涵盖:Kubernetes动态卷供应(Local‑Path / NFS‑Subdir‑External‑Provisioner)、StatefulSet有状态应用管理、ETCD分布式键值存储部署、集群增删节点、备份恢复。适合云原生实验复习、面试梳理。

目录

  1. 动态卷供应 StorageClass & Provisioner
  2. Local‑Path‑Provisioner 本地动态存储
  3. NFS‑Subdir‑External‑Provisioner NFS动态卷
  4. StatefulSet 有状态控制器
  5. StatefulSet 实验:Nginx、ETCD、Redis、Cassandra、MySQL
  6. ETCD分布式存储系统
  7. ETCD单节点部署
  8. ETCD集群部署、节点增删
  9. etcdctl命令、KV管理、用户角色
  10. ETCD 备份与恢复

1. 动态卷供应 StorageClass & Provisioner

什么是静态卷

传统方式,PV需要管理员手动预先创建,用户创建PVC再和PV绑定。缺点:管理员需要提前预估、手动维护大量PV。

动态卷供应

  1. 管理员创建StorageClass存储类(存储套餐模板),指定制备器provisioner;
  2. 用户创建PVC,指定storageClassName;
  3. k8s调用对应provisioner自动创建PV,完成PVC与PV绑定,用户直接使用PVC。

StorageClass 核心字段

字段 说明
provisioner 制备器,造PV的工具;云厂商内置,本地/NFS/Ceph需要第三方
reclaimPolicy 回收策略:Delete删除PVC自动删后端存储;Retain保留数据
volumeBindingMode Immediate:创建PVC立刻生成PV;WaitForFirstConsumer:等Pod消费才创建PV,适合本地存储
allowVolumeExpansion 是否支持PVC扩容
parameters 给provisioner传递自定义参数

部分存储是否内置制备器

存储类型 是否内置provisioner
AWS EBS、AzureDisk ✅内置
NFS、Local、Ceph RBD、CephFS、iSCSI、Glusterfs ❌第三方制备器

volumeBindingMode 两个模式区别

  1. Immediate:PVC创建即创建PV,适合网络存储NFS、云盘;
  2. WaitForFirstConsumer:等待第一个使用PVC的Pod被调度之后,才创建PV;用于local‑path本地存储,PV绑定Pod所在节点。

2. Local‑Path‑Provisioner 本地动态存储

rancher开源本地存储动态制备器,使用节点本地磁盘实现动态PV。 ⚠数据存在节点本地磁盘;节点故障数据丢失,适合测试、缓存,不适合生产高可用业务。

部署

复制代码
wget [https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml](https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml)
kubectl apply -f local-path-storage.yaml

# 查看StorageClass
kubectl get sc local-path
# 查看pod
kubectl get all -n local-path-storage

资源清单包含资源:

  1. Namespace local-path-storage
  2. ServiceAccount + RBAC权限
  3. Deployment运行provisioner主程序
  4. ConfigMap local-path-config:定义本地存储默认路径/opt/local-path-provisioner
  5. StorageClass local‑path,volumeBindingMode: WaitForFirstConsumer

验证实验

创建PVC

复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-path-pvc
spec:
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 5Gi
  storageClassName: local-path

PVC刚创建状态Pending,必须等Pod使用才会变成Bound。

创建Deployment使用PVC

复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: webapp
  name: webapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - image: hub.laoma.cloud/library/nginx
        name: nginx
        volumeMounts:
        - name: webapp-data
          mountPath: /usr/share/nginx/html
      volumes:
      - name: webapp-data
        persistentVolumeClaim:
          claimName: local-path-pvc

现象:

  1. Pod调度到某一台worker节点;provisioner在该节点/opt/local-path-provisioner生成子目录作为PV;
  2. PV自动注入nodeAffinity节点亲和;同一个PVC的所有Pod,只能跑在这一台节点;
  3. 删除PVC,本地目录自动删除(reclaimPolicy:Delete)。

清理

复制代码
kubectl delete deployment webapp
kubectl delete pvc local-path-pvc
kubectl delete -f local-path-storage.yaml

3. NFS‑Subdir‑External‑Provisioner NFS动态卷

NFS本身k8s没有内置provisioner;使用nfs‑subdir‑external‑provisioner,在同一个NFS共享下自动创建子目录作为各个PV。 NFS是网络存储,所有节点都可以挂载同一个PV,Pod可以分散不同节点。

3.1 部署NFS服务端 master30

复制代码
apt install -y nfs-kernel-server
mkdir -m 777 /shares

cat << EOF > /etc/exports
/shares *(rw,sync,no_root_squash,no_all_squash,insecure)
EOF
systemctl restart nfs-server.service

# 所有worker节点安装nfs客户端
apt install -y nfs-common

3.2 部署nfs‑client‑provisioner

  1. 创建命名空间

    kubectl create ns kube-storage
    kubectl config set-context --current --namespace kube-storage

nfs-rbac.yaml

复制代码
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  namespace: kube-storage
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: kube-storage
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io

nfs-provisioner.yaml

复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  namespace: kube-storage
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      volumes:
      - name: nfs-client-root
        nfs:
          server: 10.1.8.30
          path: /shares
      containers:
      - name: nfs-client-provisioner
        image: hub.laoma.cloud/sig-storage/nfs-subdir-external-provisioner:v4.0.2
        volumeMounts:
        - name: nfs-client-root
          mountPath: /persistentvolumes
        env:
        - name: PROVISIONER_NAME
          value: fuseim.pri/ifs
        - name: NFS_SERVER
          value: 10.1.8.30
        - name: NFS_PATH
          value: /shares

nfs-storageclass.yaml

复制代码
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storage
provisioner: fuseim.pri/ifs
parameters:
  onDelete: retain
  archiveOnDelete: "false"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: Immediate

部署

复制代码
kubectl apply -f nfs-rbac.yaml
kubectl apply -f nfs-provisioner.yaml
kubectl apply -f nfs-storageclass.yaml

kubectl get sc

3.3 验证NFS动态卷

pvc.yaml

复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: webclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: "nfs-storage"

Immediate模式,创建PVC立刻Bound,NFS服务器/shares自动生成子目录。

deployment使用PVC

复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: webapp
  name: webapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - image: hub.laoma.cloud/library/nginx
        name: nginx
        volumeMounts:
        - name: webapp-data
          mountPath: /usr/share/nginx/html
      volumes:
      - name: webapp-data
        persistentVolumeClaim:
          claimName: webclaim

✔NFS特点:同一个PVC,多个Pod可以调度到不同节点同时挂载;底层数据存放在NFS服务器共享目录。

4. StatefulSet 有状态控制器

无状态 vs 有状态应用

维度 Deployment(无状态) StatefulSet(有状态)
实例身份 所有Pod完全等价,随机名称 固定序号:sts‑0、sts‑1、sts‑2,身份永久不变
网络 普通Service,共享ClusterIP Headless Service(clusterIP:None) ,每个Pod拥有固定DNS域名 podname.svcname.ns.svc.cluster.local
扩缩顺序 并发创建删除,无顺序 扩容从0→N依次启动,必须上一个Ready再创建下一个;缩容从最大序号往0删除
存储 PVC手动编写,共享卷 volumeClaimTemplates PVC模板;每个Pod自动生成独立PVC;删除Pod,PVC保留,重建Pod复用旧PVC,数据不丢失
适用场景 web、api微服务 mysql、redis、etcd、zookeeper、kafka分布式有状态组件

StatefulSet核心组件

  1. Headless Service :clusterIP: None,不负载均衡,仅DNS域名解析;StatefulSet必须配置。
  2. volumeClaimTemplates PVC模板:为每一个Pod自动生成独立PVC,Pod删除PVC保留。
  3. serviceName字段:绑定Headless Service名称。

重要:修改StatefulSet的volumeClaimTemplates不会更新已经存在的PVC,需要手动删除PVC才会重建。

5. StatefulSet 实验:Nginx、ETCD、Redis、Cassandra、MySQL

5.1 StatefulSet Nginx示例

1. 创建Headless Service

复制代码
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: statefulset
spec:
  selector:
    app: nginx
  clusterIP: None
  ports:
  - port: 80
    targetPort: 80
    name: nginx-port

2. StatefulSet + PVC模板

复制代码
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx
  namespace: statefulset
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 3
  minReadySeconds: 10
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: hub.laoma.cloud/library/nginx:latest
        ports:
        - containerPort: 80
          name: nginx
        volumeMounts:
        - name: nginx-data
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: nginx-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "nfs-storage"
      resources:
        requests:
          storage: 10Gi

现象:

  1. Pod顺序启动 nginx‑0 → nginx‑1 → nginx‑2;
  2. 每个Pod自动生成独立PVC:nginx‑data‑nginx‑0;
  3. 删除Pod,StatefulSet重建同名Pod,复用原有PVC,数据保留。

访问Pod域名:nginx‑0.nginx.statefulset.svc.cluster.local
如果需要统一入口访问所有Pod,额外再创建一个普通ClusterIP Service。

5.2 StatefulSet部署ETCD集群

使用Headless Service域名完成etcd节点之间peer通信。 关键点:环境变量利用metadata.name获取pod名称,拼接headless域名完成集群通信。

5.3 StatefulSet部署Redis主从+哨兵

  • redis‑0作为master;redis‑1、redis‑2作为replica;sentinel哨兵实现自动故障切换。
  • Pod重建后PVC保留,持久化数据。

5.4 StatefulSet Cassandra / MySQL

MySQL示例注意:StatefulSet不会自动配置主从复制,复制逻辑需要自己通过脚本/init‑container实现。
⚠生产注意:密码等敏感配置必须使用Secret,禁止yaml明文写密码。

6. ETCD分布式存储系统

etcd:Go语言开发,基于Raft一致性算法,分布式强一致性键值存储;CNCF毕业项目。 k8s全部对象数据存储在etcd。

ETCD特点

  1. 强一致性;
  2. 支持https安全通信;
  3. 提供watch监听机制;
  4. 简单http/gRPC API;
  5. 性能优秀。

典型应用场景

  1. 服务注册与发现
  2. 配置中心发布订阅watch
  3. 分布式协调、选主
  4. 分布式锁
  5. Kubernetes存储集群所有资源对象。

6.1 ETCD单节点部署

复制代码
apt install -y etcd-server etcd-client

配置文件 /etc/default/etcd

复制代码
ETCD_NAME="default"
ETCD_DATA_DIR="/var/lib/etcd/default"
ETCD_LISTEN_CLIENT_URLS="[http://localhost:2379](http://localhost:2379),[http://10.1.8.10:2379](http://10.1.8.10:2379)"
ETCD_ADVERTISE_CLIENT_URLS="[http://localhost:2379](http://localhost:2379),[http://10.1.8.10:2379](http://10.1.8.10:2379)"
systemctl restart etcd
etcdctl member list

简单KV操作

复制代码
etcdctl put /demo/key hello
etcdctl get /demo/key
etcdctl del /demo/key
etcdctl watch /demo/key

6.2 ETCD多节点集群部署

关键参数 /etc/default/etcd

参数 说明
ETCD_NAME 本节点名称,集群唯一
ETCD_DATA_DIR 数据目录
ETCD_LISTEN_PEER_URLS 本节点监听其他节点peer通信地址(2380)
ETCD_INITIAL_ADVERTISE_PEER_URLS 广播给集群其他节点的peer通信地址
ETCD_LISTEN_CLIENT_URLS 本节点监听客户端访问地址(2379)
ETCD_ADVERTISE_CLIENT_URLS 广播给客户端访问本节点client地址
ETCD_INITIAL_CLUSTER 集群全部节点peer地址清单
ETCD_INITIAL_CLUSTER_TOKEN 集群唯一token,所有节点必须一致,区分不同集群
ETCD_INITIAL_CLUSTER_STATE new全新集群全部节点一起初始化;existing加入已经存在的集群

重点实验流程:

  1. 全新集群:全部节点配置ETCD_INITIAL_CLUSTER_STATE="new",同时启动。
  2. 向已经存在集群添加新节点流程:
    1. 在集群任意旧节点执行 etcdctl member add 新节点名 --peer‑urls=http://ip:2380
    2. 输出的环境变量复制给新节点配置;
    3. 新节点配置 ETCD_INITIAL_CLUSTER_STATE="existing";新节点数据目录必须为空;
    4. 启动新节点。
  3. 删除节点:etcdctl member remove <member‑id>;删除后建议修改剩余节点配置,去掉被删除节点,重启服务。

坑:删除member只是集群元数据移除节点;不会清理被删除节点本地磁盘数据目录。

6.3 etcdctl工具使用

默认使用API v3;ETCDCTL_API=3。

member集群成员管理

复制代码
etcdctl member list
etcdctl member add
etcdctl member remove
etcdctl member update

KV操作

复制代码
# 写入
etcdctl put /users/user1/name zhangsan
# 查询
etcdctl get /users/user1/name
# --prefix前缀查询,--keys-only只打印key
etcdctl get /users --prefix --keys-only
# 删除
etcdctl del /users/user1/name
# watch监听key变化
etcdctl watch /users/user1/name

脚本小工具etcdls模拟目录ls查看etcd的key,方便学习。

用户、角色权限管理

  • user:账号
  • role:角色,授予key读写权限;用户绑定角色实现rbac权限控制。

6.4 ETCD备份恢复

两种备份方式

  1. 物理备份:直接拷贝数据目录,停机备份;适合整体全量备份;恢复全部节点替换数据目录。
  2. 逻辑快照备份(snapshot):在线备份,不需要停机。

快照备份单节点:

复制代码
export ETCDCTL_API=3
etcdctl snapshot save snapshot.db

快照恢复:

复制代码
# 停止etcd,清空旧数据目录
systemctl stop etcd
rm -fr /var/lib/etcd/default

etcdctl snapshot restore snapshot.db --data-dir=/var/lib/etcd/default
chown -R etcd:etcd /var/lib/etcd/default
systemctl start etcd

多节点集群快照恢复:所有节点全部使用同一个快照文件恢复。

Kubernetes内部ETCD备份

k8s静态pod部署etcd,需要指定证书、ca、endpoints。

复制代码
export ETCDCTL_API=3
etcdctl \
--key=/etc/kubernetes/pki/etcd/server.key \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--endpoints=[https://127.0.0.1:2379](https://127.0.0.1:2379) \
snapshot save snapshot.db

k8s etcd所有资源存放在/registry前缀下面;etcd v3存储protobuf二进制,直接get拿到是二进制,需要工具解码查看。

知识总结面试速记

  1. StorageClass

    :动态存储模板,provisioner制备器自动创建PV;

    • WaitForFirstConsumer:local‑path本地存储,等Pod调度完成创建PV;PV绑定节点亲和。
    • Immediate:NFS网络存储,PVC创建立刻生成PV。
  2. Local‑Path:本地磁盘,同一个RWO PVC所有Pod只能运行在同一个节点;节点故障数据丢失。

  3. NFS‑subdir‑provisioner:第三方制备器;NFS网络存储,Pod可跨节点挂载同一个PVC。

  4. StatefulSet:用于有状态应用;依赖Headless Service;有序扩缩容;volumeClaimTemplates每个Pod独立PVC,删除Pod保留PVC。

  5. ETCD:Raft分布式强一致性KV;k8s后端存储。

    • new:全新集群初始化;existing加入现存集群。
    • 添加节点必须先etcdctl member add;新节点数据目录必须清空。
    • 备份:snapshot快照在线备份;恢复集群所有节点使用同一个快照。
相关推荐
wuminyu1 小时前
JVM规范学习总结:class执行性能差异
java·linux·c语言·jvm·c++
派大猩猩_1 小时前
选题散在 20 篇笔记里?Nutstore Sync在 Obsidian 里创建选题小工具,创作动线短一半
笔记·obsidian·obsidian插件·obsidian ai
EVEN-09131 小时前
Linux-线程控制
linux·jvm
前进的李工1 小时前
Agent 学习笔记 04:KV Cache 与上下文缓存
笔记·学习·缓存·agent·注意力机制·kv cache·prompt cache
刚入门的大一新生1 小时前
Linux-线程控制
linux·运维·服务器
Decay__1 小时前
03_MySQL 存储引擎与存储管理 —— InnoDB 深入、表空间/ibd、binlog/redo/undo 与双写缓冲
运维·数据库·sql·mysql·架构
艾伦_耶格宇1 小时前
【kubernetes - 1.28版本】-7 K8S命令
云原生·容器·kubernetes
Z21-X1 小时前
云原生运维笔记:打通OpenStack的任督二脉——从Neutron网络踩坑到Swift与Heat硬核实战
运维·笔记·学习·云原生
瀚高PG实验室1 小时前
瀚高数据库提交数据后,是否需要COMMIT
数据库·postgresql·瀚高数据库