09--kubernetes持久化存储和StatefulSet控制器

前言:本章是k8s持久化存储的详细笔记,由持久化引申出来的statefulset控制器和无头svc都会在本章有详细记录。

1、K8s持久化存储PV和PVC

前面文章已经使用卷挂载的方式将pod文件持久化保存在宿主机中,但实际工作中pod往往会以多副本形式存在,此时需要每一个宿主机上都有对应的挂载卷,这样往往会造成存储资源极大浪费,所以这里需要引入PV和PVC概念解决这个问题。

1.1、PV和PVC的引入

Volume 提供了有效的数据持久化方案,但在管理上存在不足。以 AWS EBS(亚马逊云存储服务,需要收费,这里使用第四台虚拟机模拟提供此服务) 为例,使用 Volume 时,Pod 必须知道以下信息:当前 Volume 来自 AWS EBS、EBS Volume 已提前创建并且知道 volume-id。这导致应用开发人员和存储系统管理员的职责耦合。如果系统规模较小或在开发环境中尚可接受,但在大规模或生产环境中,效率和安全性的问题变得突出。

Kubernetes 提供了 PersistentVolume (PV) 和 PersistentVolumeClaim (PVC) 来解决这些问题。PV 是管理员创建和维护的外部存储空间,具有持久性,生命周期独立于 PodPVC 是对 PV 的申请,通常由普通用户创建,用于请求存储资源的容量和访问模式等信息。Kubernetes 会根据 PVC 查找并分配满足条件的 PV,使用户无需关心底层细节,由管理员处理 PersistentVolume 的创建和管理。

1.2、通过NFS实现持久化存储

1.2.1、环境准备

使用新虚拟机nfs节点(192.168.188.200)模拟云存储服务

查看磁盘空间,格式化设备,创建将被共享的目录,挂载磁盘,修改权限

[root@localhost ~]# fdisk -l
[root@localhost ~]# mkfs.ext4 /dev/sdb
[root@localhost ~]# mkdir /root/nfs
[root@localhost ~]# mount /dev/sdb /root/nfs
[root@localhost ~]# chmod 777 /root
[root@localhost ~]# chmod 777 /root/nfs

#生产环境中直接在根下创建

为所有节点安装nfs

yum install -y nfs-common nfs-utils

在nfs节点共享目录

[root@localhost ~]# vim /etc/exports
[root@localhost ~]# cat /etc/exports
/root/nfs *(rw,no_root_squash,no_all_squash,sync)

#注释
#    /root/nfs:这是要共享的目录路径。
#    *:表示允许所有客户端访问这个共享目录。可以用具体的 IP 地址或主机名替换 *,以限制访问权限。
#    rw:表示客户端具有读写权限。客户端可以对这个目录进行读和写操作。
#    no_root_squash:允许客户端以 root 用户身份访问共享目录时,拥有 root 权限。在默认设置下,NFS 会将客户端的 root 用户映射为普通用户,以增加安全性。
#    no_all_squash:允许客户端以任何用户身份访问共享目录时,保留客户端的原始身份,而不是映射为匿名用户。
#    sync:表示数据写入磁盘时是同步的,即在响应客户端请求之前,数据会被写入磁盘。这有助于确保数据的一致性,但可能会降低性能。

[root@localhost ~]# systemctl start rpcbind
#    用来告知nfs客户端nfs的服务端口
[root@localhost ~]# systemctl start nfs

此时需要使用客户端(node1,node2)测试nfs挂载是否可用

[root@k8s-node1 ~]# mkdir /test
[root@k8s-node1 ~]# mount -t nfs 192.168.188.200:/root/nfs /test/
[root@k8s-node1 ~]# df -Th | grep "/test"
192.168.188.200:/root/nfs nfs4       99G   60M   94G    1% /test
[root@k8s-node1 ~]# touch /test/123.txt
[root@k8s-node1 ~]# ls /test/
123.txt  lost+found

回到nfs服务端查看

[root@localhost ~]# ls /root/nfs/
123.txt  lost+found

测试完成后回到客户端卸载该目录

[root@k8s-node1 ~]# umount /test/
[root@k8s-node1 ~]# df -Th | grep "/test"

1.2.2、PV和PVC实操

回到master节点

[root@k8s-master1 ~]# mkdir pv.yaml.d
[root@k8s-master1 ~]# cd pv.yaml.d/
[root@k8s-master1 pv.yaml.d]# vim nfs-pv1.yaml
[root@k8s-master1 pv.yaml.d]# cat nfs-pv1.yaml
---
apiVersion: v1  # Kubernetes API 版本
kind: PersistentVolume  # 资源类型为持久卷
metadata:
  name: mypv1  # 持久卷的名称
spec:
  capacity:
    storage: 10Gi  # 持久卷的存储容量为 10GB(这里建议最多使用90%空间)
  accessModes:
    - ReadWriteOnce  # 访问模式为单节点读写
  persistentVolumeReclaimPolicy: Retain  # 回收策略为保留
  storageClassName: nfs  # 存储类名称为 nfs
  nfs:
    path: /root/nfs  # NFS 共享目录的路径
    server: 192.168.188.200  # NFS 服务器的 IP 地址

参数详解:

1、accessModes 指定访问模式为 ReadWriteOnce,支持的访问模式有:
ReadWriteOnce – PV 能以 read-write 模式 mount 到单个节点。
ReadOnlyMany – PV 能以 read-only 模式 mount 到多个节点。
ReadWriteMany – PV 能以 read-write 模式 mount 到多个节点

2、persistentVolumeReclaimPolicy 指定当 PV 的回收策略为Retain,支持的策略有:
Retain – 需要管理员手工回收。
Recycle – 清除 PV 中的数据,效果相当于执行 rm -rf /nfsdata/*。(已废除,使用不会报错,但无法生效)
Delete – 删除 Storage Provider 上的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。

3、storageClassName 指定 PV 的 class 为 nfs。相当于为 PV 设置了一个分类,PVC 可以指定 class 申请相应 class 的 PV。

创建pv

[root@k8s-master1 pv.yaml.d]# kubectl apply -f nfs-pv1.yaml 
persistentvolume/mypv1 created
[root@k8s-master1 pv.yaml.d]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
mypv1   10Gi       RWO            Retain           Available           nfs                     7s


#    STATUS 为 Available,表示 mypv1 就绪,可以被 PVC 申请。

创建pvc

[root@k8s-master1 pv.yaml.d]# vim nfs-pvc1.yaml
[root@k8s-master1 pv.yaml.d]# cat nfs-pvc1.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc1
spec:
  accessModes:
    - ReadWriteOnce    #权限需要与目标pv保持一致
  resources:
    requests:
      storage: 10Gi    #通过大小可以绑定
  storageClassName: nfs    #通过类型也可以绑定


[root@k8s-master1 pv.yaml.d]# kubectl apply -f nfs-pvc1.yaml 
persistentvolumeclaim/mypvc1 created
[root@k8s-master1 pv.yaml.d]# kubectl get pvc
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc1   Bound    mypv1    10Gi       RWO            nfs            5s

此时再去查看pv状态

[root@k8s-master1 pv.yaml.d]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
mypv1   10Gi       RWO            Retain           Bound    default/mypvc1   nfs                     8m

#从 kubectl get pvc 和 kubectl get pv 的输出可以看到 mypvc1 已经 Bound 到 mypv1,申请成功。

配置pod并引用pvc

[root@k8s-master1 pv.yaml.d]# vim nfs-pod1.yaml
[root@k8s-master1 pv.yaml.d]# cat nfs-pod1.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: mypod1
  labels:
    app: liumuquan
spec:
  containers:
    - name: mypod1
      image: registry.cn-chengdu.aliyuncs.com/liumuquan_app/nginx:1.12.0-alpine
      ports:
      - containerPort: 80
      volumeMounts:
      - mountPath: "/usr/share/nginx/html"
        name: mydata
  volumes:
   - name: mydata
     persistentVolumeClaim:
       claimName: mypvc1

此时在nfs目录下写一个nginx主页文件

nfs服务端操作

[root@localhost ~]# rm -rf /root/nfs/*
[root@localhost ~]# echo "liumuquan-pvc1" > /root/nfs/index.html
[root@localhost ~]# chmod 777 /root/nfs/index.html

回到master创建pod并访问

[root@k8s-master1 pv.yaml.d]# kubectl apply -f nfs-pod1.yaml 
pod/mypod1 created
[root@k8s-master1 daemonset.yaml.d]# kubectl get pod -o wide
NAME     READY   STATUS    RESTARTS   AGE     IP            NODE        NOMINATED NODE   READINESS GATES
mypod1   1/1     Running   0          2m15s   10.244.2.28   k8s-node2   <none>           <none>
[root@k8s-master1 pv.yaml.d]# curl 10.244.2.28
liumuquan-pv1

1.2.3、pv回收

回收流程:svc-pod-pvc-pv

[root@k8s-master1 pv.yaml.d]# kubectl delete pod mypod1
pod "mypod1" deleted
[root@k8s-master1 pv.yaml.d]# kubectl delete pvc mypvc1
persistentvolumeclaim "mypvc1" deleted
[root@k8s-master1 pv.yaml.d]# kubectl delete pv mypv1
persistentvolume "mypv1" deleted

此时查看nfs服务端发现文件仍然存在

[root@localhost nfs]# pwd
/root/nfs
[root@localhost nfs]# ls
index.html
[root@localhost nfs]# cat index.html 
liumuquan-pv1

重复创建流程发现访问结果不变

1.3、PV/PVC实战(Mysql)

操作如下:

在nfs节点准备mysql文件夹,此文件夹可以为空,mysql与nginx不同,运行起来后会在该路径自动生成文件。

[root@localhost nfs]# mkdir /root/nfs/mysql-pv
[root@localhost nfs]# chmod 777 mysql-pv

回到master节点配置yaml文件

root@k8s-master1 pv.yaml.d]# vim mysql-pv-test.yaml
[root@k8s-master1 pv.yaml.d]# cat mysql-pv-test.yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /root/nfs/mysql-pv
    server: 192.168.188.200
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 9Gi
  storageClassName: nfs
---
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
    targetPort: 3306
  selector:
    app: mysql
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: registry.cn-chengdu.aliyuncs.com/liumuquan_app/mysql:5.7
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "Liumuquan@123"
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pvc

创建并检查状态

[root@k8s-master1 pv.yaml.d]# kubectl apply -f mysql-pv-test.yaml 
persistentvolume/mysql-pv created
persistentvolumeclaim/mysql-pvc created
service/mysql created
deployment.apps/mysql created

[root@k8s-master1 pv.yaml.d]# kubectl get pv,pvc,deploy,svc,pod
NAME                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
persistentvolume/mysql-pv   10Gi       RWO            Retain           Bound    default/mysql-pvc   nfs                     73s

NAME                              STATUS   VOLUME     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/mysql-pvc   Bound    mysql-pv   10Gi       RWO            nfs            73s

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mysql   1/1     1            1           73s

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP    17d
service/mysql        ClusterIP   10.102.65.24   <none>        3306/TCP   73s

NAME                         READY   STATUS    RESTARTS   AGE
pod/mysql-5b5d554c48-4b4vl   1/1     Running   0          73s

进入pod内操作(此处也可以使用集群内主机通过svc登录mysql)

[root@k8s-master1 pv.yaml.d]# kubectl exec -it mysql-5b5d554c48-4b4vl /bin/bash
bash-4.2# mysql -uroot -p"Liumuquan@123"
mysql> create database my_id;
Query OK, 1 row affected (0.00 sec)

mysql> use my_id;
Database changed
mysql> create table t1(id int,name varchar(30));
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t1 values(1,"maguocheng");
Query OK, 1 row affected (0.01 sec)

mysql> insert into t1 values(2,"longkeduo");
Query OK, 1 row affected (0.00 sec)

mysql> select * from t1;
+------+------------+
| id   | name       |
+------+------------+
|    1 | maguocheng |
|    2 | longkeduo  |
+------+------------+
2 rows in set (0.00 sec)

退出pod,删除svc,deploy,pvc,pv

[root@k8s-master1 pv.yaml.d]# kubectl delete -f mysql-pv-test.yaml 
persistentvolume "mysql-pv" deleted
persistentvolumeclaim "mysql-pvc" deleted
service "mysql" deleted
deployment.apps "mysql" deleted

重新创建

[root@k8s-master1 pv.yaml.d]# kubectl apply -f mysql-pv-test.yaml 
persistentvolume/mysql-pv created
persistentvolumeclaim/mysql-pvc created
service/mysql created
deployment.apps/mysql created

进入容器查看发现数据并未丢失

[root@k8s-master1 pv.yaml.d]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
mysql-5b5d554c48-zlx7m   1/1     Running   0          43s
[root@k8s-master1 pv.yaml.d]# kubectl exec -it mysql-5b5d554c48-zlx7m /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
bash-4.2# mysql -uroot -p"Liumuquan@123"

mysql> use my_id;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select * from t1;
+------+------------+
| id   | name       |
+------+------------+
|    1 | maguocheng |
|    2 | longkeduo  |
+------+------------+
2 rows in set (0.00 sec)

2、PV/PVC的动态供应

动态存储流程示意图

当k8s业务上来的时候,需要大量的pvc,此时我们人工创建匹配的话,工作量就会非常大了,需要动态的自动挂载相应的存储。

nfs为提供存储的节点

绿色的pod用途为自动创建pv/pvc,使用deploy创建。

黄色的StorageClass,来对接存储,靠他来自动关联pvc,并创建pv。

橙色的StatefulSet,pod控制器,这里用来创建nginx的pod,本章结尾会详细说明。

2.1、PV/PVC动态供应项目实战

1)在nfs节点创建共享文件夹

[root@localhost ~]# mkdir /root/nfs/container_data
[root@localhost ~]# chmod 777 /root/nfs/container_data

2)部署storageclass

[root@k8s-master1 dynamic]# vim storageclass.yaml
[root@k8s-master1 dynamic]# cat storageclass.yaml
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage #指定了存储类的名称
provisioner: fuseim.pri/ifs #指定了用于动态存储卷创建的插件或供应者

3)配置授权

因为storage自动创建pv需要经过kube-apiserver,所以要进行授权

创建1个sa(serviceaccount)

创建1个clusterrole,并赋予应该具有的权限,比如对于一些基本api资源的增删改查;这里也可以使用自带k8s角色。

创建1个clusterrolebinding,将sa和clusterrole绑定到一起;这样sa就有权限了;

然后pod中再使用这个sa,那么pod再创建的时候,会用到sa,sa具有创建pv的权限,便可以自动创建pv;

[root@k8s-master1 dynamic]# vim sa.yaml
[root@k8s-master1 dynamic]# cat sa.yaml
---
apiVersion: v1                        # 定义 ServiceAccount 的 API 版本
kind: ServiceAccount                  # 资源类型为 ServiceAccount
metadata:                             # 元数据部分
  name: nfs-client-provisioner        # ServiceAccount 的名称
  namespace: default                  # ServiceAccount 所在的命名空间
---
kind: ClusterRole                     # 资源类型为 ClusterRole
apiVersion: rbac.authorization.k8s.io/v1 # 定义 ClusterRole 的 API 版本
metadata:                             # 元数据部分
  name: nfs-client-provisioner-runner # ClusterRole 的名称
rules:                                # 权限规则
  - apiGroups: [""]                   # 适用于 core API 组
    resources: ["nodes"]              # 节点资源
    verbs: ["get", "list", "watch"]   # 权限操作
  - apiGroups: [""]                   # 适用于 core API 组
    resources: ["persistentvolumes"]  # 持久卷资源
    verbs: ["get", "list", "watch", "create", "delete"] # 权限操作
  - apiGroups: [""]                   # 适用于 core API 组
    resources: ["persistentvolumeclaims"] # 持久卷声明资源
    verbs: ["get", "list", "watch", "update"] # 权限操作
  - apiGroups: ["storage.k8s.io"]     # 适用于 storage.k8s.io API 组
    resources: ["storageclasses"]     # 存储类资源
    verbs: ["get", "list", "watch"]   # 权限操作
  - apiGroups: [""]                   # 适用于 core API 组
    resources: ["events"]             # 事件资源
    verbs: ["create", "update", "patch"] # 权限操作
---
kind: ClusterRoleBinding              # 资源类型为 ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1 # 定义 ClusterRoleBinding 的 API 版本
metadata:                             # 元数据部分
  name: run-nfs-client-provisioner    # ClusterRoleBinding 的名称
subjects:                             # 关联的主体
  - kind: ServiceAccount              # 主体类型为 ServiceAccount
    name: nfs-client-provisioner      # 关联的 ServiceAccount 名称
    namespace: default                # ServiceAccount 所在的命名空间
roleRef:                              # 角色引用部分
  kind: ClusterRole                   # 角色类型为 ClusterRole
  name: nfs-client-provisioner-runner # 关联的 ClusterRole 名称
  apiGroup: rbac.authorization.k8s.io # 角色的 API 组
---
kind: Role                           # 资源类型为 Role
apiVersion: rbac.authorization.k8s.io/v1 # 定义 Role 的 API 版本
metadata:                             # 元数据部分
  name: leader-locking-nfs-client-provisioner # Role 的名称
  namespace: default                  # Role 所在的命名空间
rules:                                # 权限规则
  - apiGroups: [""]                   # 适用于 core API 组
    resources: ["endpoints"]          # 端点资源
    verbs: ["get", "list", "watch", "create", "update", "patch"] # 权限操作
---
kind: RoleBinding                    # 资源类型为 RoleBinding
apiVersion: rbac.authorization.k8s.io/v1 # 定义 RoleBinding 的 API 版本
metadata:                             # 元数据部分
  name: leader-locking-nfs-client-provisioner # RoleBinding 的名称
  namespace: default                  # RoleBinding 所在的命名空间
subjects:                             # 关联的主体
  - kind: ServiceAccount              # 主体类型为 ServiceAccount
    name: nfs-client-provisioner      # 关联的 ServiceAccount 名称
    namespace: default                # ServiceAccount 所在的命名空间
roleRef:                              # 角色引用部分
  kind: Role                         # 角色类型为 Role
  name: leader-locking-nfs-client-provisioner # 关联的 Role 名称
  apiGroup: rbac.authorization.k8s.io # 角色的 API 组

4)配置deploy

[root@k8s-master1 dynamic]# vim deploy.yaml
[root@k8s-master1 dynamic]# cat deploy.yaml
---
kind: Deployment                    # 部署资源类型
apiVersion: apps/v1                  # API 版本
metadata:                            # 元数据部分
  name: nfs-client-provisioner       # 部署的名称
spec:                                # 规格部分
  selector:                          # 选择器部分,用于确定哪些 Pods 属于这个 Deployment
    matchLabels:                     # 标签匹配规则
      app: nfs-client-provisioner    # 匹配标签 "app" 的值为 "nfs-client-provisioner"
  replicas: 1                        # 副本数,即创建一个 Pod 实例
  strategy:                          # 部署策略部分
    type: Recreate                   # 策略类型为 Recreate,即先删除旧的 Pod 再创建新的 Pod
  template:                          # Pod 模板
    metadata:                        # 模板的元数据部分
      labels:                       # Pod 的标签
        app: nfs-client-provisioner # 标签 "app" 的值为 "nfs-client-provisioner"
    spec:                            # Pod 的规格部分
      serviceAccount: nfs-client-provisioner # 指定使用的 ServiceAccount
      containers:                    # 容器列表
        - name: nfs-client-provisioner # 容器的名称
          image: registry.cn-chengdu.aliyuncs.com/k8s_module_images/lizhenliang_nfs-client-provisioner:v2.0.0 # 容器镜像地址
          volumeMounts:              # 卷挂载配置
            - name: nfs-client-root # 卷的名称
              mountPath: /persistentvolumes # 挂载路径
          env:                       # 环境变量配置
            - name: PROVISIONER_NAME  # 环境变量名称
              value: fuseim.pri/ifs  # 环境变量值,应与 storageclass 中的 provisioner 一致
            - name: NFS_SERVER       # 环境变量名称
              value: 192.168.188.200 # NFS 服务器地址
            - name: NFS_PATH         # 环境变量名称
              value: /root/nfs/container_data # NFS 共享路径
      volumes:                        # 卷列表
        - name: nfs-client-root      # 卷的名称
          nfs:                       # 使用 NFS 卷类型
            server: 192.168.188.200 # NFS 服务器地址
            path: /root/nfs/container_data # NFS 共享路径

5)部署上方配置文件并检查

[root@k8s-master1 dynamic]# kubectl apply -f storageclass.yaml 
storageclass.storage.k8s.io/managed-nfs-storage created
[root@k8s-master1 dynamic]# kubectl apply -f sa.yaml 
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
[root@k8s-master1 dynamic]# kubectl apply -f deploy.yaml 
deployment.apps/nfs-client-provisioner created
[root@k8s-master1 dynamic]# kubectl get sc,sa,deploy,pod
NAME                                              PROVISIONER      RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
storageclass.storage.k8s.io/managed-nfs-storage   fuseim.pri/ifs   Delete          Immediate           false                  64s

NAME                                    SECRETS   AGE
serviceaccount/default                  1         18d
serviceaccount/nfs-client-provisioner   1         58s

NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nfs-client-provisioner   1/1     1            1           50s

NAME                                          READY   STATUS    RESTARTS   AGE
pod/nfs-client-provisioner-65fcd7b4c8-bmcc4   1/1     Running   0          50s

6)配置StatefulSet

这里需要引入无头svc

[root@k8s-master1 dynamic]# vim nginx-StatefulSet.yaml
[root@k8s-master1 dynamic]# cat nginx-StatefulSet.yaml
---
apiVersion: v1                        # API 版本
kind: Service                         # 资源类型为 Service
metadata:                             # 元数据部分
  name: nginx                         # Service 的名称
  labels:                             # 标签
    app: nginx                        # 标签 "app" 的值为 "nginx"
spec:                                 # 规格部分
  ports:                               # 端口配置
  - port: 80                           # Service 公开的端口
    name: web                         # 端口名称
  clusterIP: None                     # 集群内部 IP 地址设为 None,表示这是一个 Headless Service
  selector:                           # 选择器部分
    app: nginx                        # 选择标签为 "app: nginx" 的 Pods
---
apiVersion: apps/v1                   # API 版本
kind: StatefulSet                     # 资源类型为 StatefulSet
metadata:                             # 元数据部分
  name: web                           # StatefulSet 的名称
spec:                                 # 规格部分
  serviceName: "nginx"                # 关联的 Service 名称
  replicas: 2                         # 副本数,即创建两个 Pod 实例
  selector:                           # 选择器部分
   matchLabels:                       # 标签匹配规则
      app: nginx                     # 匹配标签 "app" 的值为 "nginx"
  template:                           # Pod 模板
    metadata:                         # 模板的元数据部分
      labels:                         # Pod 的标签
        app: nginx                    # 标签 "app" 的值为 "nginx"
    spec:                             # Pod 的规格部分
      containers:                     # 容器列表
      - name: nginx                   # 容器的名称
        image: registry.cn-chengdu.aliyuncs.com/liumuquan_app/nginx:1.12.0-alpine # 容器镜像地址
        ports:                        # 端口配置
        - containerPort: 80           # 容器内部端口
          name: web                   # 端口名称
        volumeMounts:                 # 卷挂载配置
        - name: www                   # 卷的名称
          mountPath: /usr/share/nginx/html # 挂载路径
  volumeClaimTemplates:               # 卷声明模板
  - metadata:                         # 模板的元数据部分
      name: www                       # 卷声明的名称
    spec:                             # 卷声明的规格部分
      accessModes: [ "ReadWriteOnce" ] # 存储访问模式
      storageClassName: "managed-nfs-storage" # 存储类名称
      resources:                      # 资源请求配置
        requests:                     # 请求的资源
          storage: 1Gi               # 存储请求量

7)创建nginx

[root@k8s-master1 dynamic]# kubectl apply -f nginx-StatefulSet.yaml 
service/nginx created
statefulset.apps/web created

8)查看pod状态

[root@k8s-master1 dynamic]# kubectl get pod
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-65fcd7b4c8-bmcc4   1/1     Running   0          18m
web-0                                     1/1     Running   0          75s
web-1                                     1/1     Running   0          70s

 这里可以看出StatefulSet是有序命名,逐个创建的(注意运行时间的不同)

 9)查看pv

命名规则:命名空间-模板设置的卷名-pod名字-pvc-随机序列号

[root@k8s-master1 dynamic]# kubectl get pv
NAME                                                         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS          REASON   AGE
default-www-web-0-pvc-88f9b153-9c9f-4e8b-9f7a-6f35d0622582   1Gi        RWO            Delete           Bound    default/www-web-0   managed-nfs-storage            25m
default-www-web-1-pvc-2d1de5c7-1ff0-4765-8760-241be3f2bcd6   1Gi        RWO            Delete           Bound    default/www-web-1   managed-nfs-storage            25m

这里有两个pv分别对应nfs节点目录的两个文件夹

[root@localhost ~]# ls /root/nfs/container_data
default-www-web-0-pvc-88f9b153-9c9f-4e8b-9f7a-6f35d0622582  default-www-web-1-pvc-2d1de5c7-1ff0-4765-8760-241be3f2bcd6

 只在web-0目录创建文件

[root@localhost ~]# echo "statefulset" > /root/nfs/container_data/default-www-web-0-pvc-88f9b153-9c9f-4e8b-9f7a-6f35d0622582/index.html
[root@localhost ~]# chmod 777 /root/nfs/container_data/default-www-web-0-pvc-88f9b153-9c9f-4e8b-9f7a-6f35d0622582/index.html

回到master节点访问

[root@k8s-master1 dynamic]# kubectl get pod -o wide
NAME                                      READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
nfs-client-provisioner-65fcd7b4c8-bmcc4   1/1     Running   0          50m   10.244.2.31   k8s-node2   <none>           <none>
web-0                                     1/1     Running   0          32m   10.244.2.32   k8s-node2   <none>           <none>
web-1                                     1/1     Running   0          32m   10.244.1.46   k8s-node1   <none>           <none>
[root@k8s-master1 dynamic]# curl 10.244.2.32
statefulset
[root@k8s-master1 dynamic]# curl 10.244.1.46
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.12.0</center>
</body>
</html>

备注:

  1. 此时nfs上的/root/nfs/container_data目录,已经被取用nfs的pod(绿色)挂到/persistentvolumes(deploy.yaml内配置)
  2. StatefulSet通过自身的卷模板触发storageclass从绿pod的/persistentvolumes取用空间

10)资源对象回收

回收顺序:StatefulSet-pv/pvc(这里是deloyment控制器)-授权-stc

[root@k8s-master1 dynamic]# ls
deploy.yaml  nginx-StatefulSet.yaml  sa.yaml  storageclass.yaml
[root@k8s-master1 dynamic]# kubectl delete -f nginx-StatefulSet.yaml
service "nginx" deleted
statefulset.apps "web" deleted
[root@k8s-master1 dynamic]# kubectl delete -f deploy.yaml 
deployment.apps "nfs-client-provisioner" deleted
[root@k8s-master1 dynamic]# kubectl delete -f sa.yaml 
serviceaccount "nfs-client-provisioner" deleted
clusterrole.rbac.authorization.k8s.io "nfs-client-provisioner-runner" deleted
clusterrolebinding.rbac.authorization.k8s.io "run-nfs-client-provisioner" deleted
role.rbac.authorization.k8s.io "leader-locking-nfs-client-provisioner" deleted
rolebinding.rbac.authorization.k8s.io "leader-locking-nfs-client-provisioner" deleted
[root@k8s-master1 dynamic]# kubectl delete -f storageclass.yaml 
storageclass.storage.k8s.io "managed-nfs-storage" deleted

此时查看数据仍正常保存

[root@localhost ~]# cat /root/nfs/container_data/default-www-web-0-pvc-88f9b153-9c9f-4e8b-9f7a-6f35d0622582/index.html 
statefulset

3、statefulset控制器

StatefulSet 是用来管理有状态应用的工作负载 API 对象。下面是和deploy的对比。

DeploymentStatefulSet

适合场景

无状态的应用有状态的应用

特点

1.pod之间没有顺序

2.所有pod共享存储

3.pod名字包含随机数字

4.service都有ClusterIP,可以负载均衡

1.部署、扩展、更新、删除都要有顺序


2.每个pod都有自己存储,所以都用volumeClaimTemplates,为每个pod都生成一个自己的存储,保存自己的状态


3.pod名字始终是固定的


4.service没有ClusterIP,是headlessservice,所以无法负载均衡,返回的都是pod名,所以pod名字都必须固定,StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod副本创建了一个DNS域名:$(podname).(headless server name).namespace.svc.cluster.local

StatefulSet 中的 Pod 具有一个独特且持久的身份标识(域名),这个标识基于 StatefulSet 控制器分配给每个 Pod 的唯一顺序索引。Pod 的名称的形式为 <StatefulSetName>-<index>。例如,如果一个 StatefulSet 名为 web 且拥有两个副本,它会创建两个 Pod,分别为 web-0web-1

标识符(域名)解析演示
[root@k8s-master1 dynamic]# kubectl get pod -o wide
NAME                                      READY   STATUS    RESTARTS   AGE     IP            NODE        NOMINATED NODE   READINESS GATES
nfs-client-provisioner-65fcd7b4c8-8qqc5   1/1     Running   0          10m     10.244.2.33   k8s-node2   <none>           <none>
web-0                                     1/1     Running   0          9m56s   10.244.2.34   k8s-node2   <none>           <none>
web-1                                     1/1     Running   0          9m54s   10.244.1.49   k8s-node1   <none>           <none>


[root@k8s-master1 dynamic]# kubectl exec -it web-1 /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # nslookup web-0.nginx.default.svc.cluster.local
Name:      web-0.nginx.default.svc.cluster.local
Address 1: 10.244.2.34 web-0.nginx.default.svc.cluster.local
/ # 

与 Deployment 相似,StatefulSet 也管理一组基于相同容器定义的 Pod。然而,StatefulSet 与 Deployment 的不同之处在于,StatefulSet 为每个 Pod 维护一个固定的 ID。这些 Pod 是基于相同的声明创建的,但它们不可互换,每个 Pod 都有一个永久不变的 ID

使用场景:StatefulSet 对于以下需求的应用程序尤为重要:

  1. 稳定的、唯一的网络标识符:即使 Pod 重新调度,其 PodName 和 HostName 仍保持不变(虽然 IP 地址可能会变)。命名方式为
    PodName.serviceName.namespaceName.svc.cluster.local
  2. 稳定的、持久的存储:即使 Pod 重新调度,仍能访问相同的持久化数据,通过 PVC 实现。
  3. 有序的部署和缩放
  4. 有序的、自动的滚动更新

总之,StatefulSet 提供了稳定性,确保 Pod 调度或重调度的过程是持久的。

如果应用程序不需要稳定的标识符或有序的部署、删除或缩放,可以使用无状态的副本控制器,如 Deployment 或 ReplicaSet,这些更适用于无状态应用的部署。

使用规则:

  1. 存储要求:Pod 的存储必须由 PersistentVolume 驱动,依据所请求的 storage class 提供,或由管理员预先提供。

  2. 存储保护:删除或缩减 StatefulSet 不会删除其关联的存储卷,以确保数据安全,这通常比自动清除所有相关资源更为重要。

  3. 网络标识:StatefulSet 需要一个 headless 服务来处理 Pod 的网络标识。你需要负责创建该服务。

  4. 终止保证:StatefulSet 不保证 Pod 的终止。为了正常终止,可以在删除前将 StatefulSet 缩减到 0。

  5. 滚动更新:在默认的 Pod 管理策略 (OrderedReady) 下,滚动更新可能导致需要人工干预才能修复的损坏状态。

  6. 有序索引:StatefulSet 中的每个 Pod 将被分配一个从 0 到 N-1 的唯一整数序号。

  7. 主机名格式:StatefulSet 中的每个 Pod 的主机名由 StatefulSet 名称和 Pod 的序号组成,格式为 $(StatefulSet 名称)-$(序号)

部署、删除、扩容,缩容规则:

  1. Pod 创建顺序:在 StatefulSet 中,包含 N 个副本的 Pod 按照顺序从 0 到 (N-1) 依次创建

  2. Pod 删除顺序:Pod 被删除时,按照逆序从 (N-1) 到 0 终止。

  3. 缩放前提:在应用缩放操作前,前面的所有 Pod 必须处于 Running 和 Ready 状态。

  4. 缩容保护:如果 web-2 被终止和删除后,但 web-1 尚未被终止,且此时 web-0 发生运行失败,那么系统会等待 web-0 恢复到 Running 和 Ready 状态后才会终止 web-1。这样做是为了确保系统的稳定性和数据一致性,防止在 Pod 失败的情况下过早地终止其他 Pod

  5. 继任者关闭:在终止 Pod 之前,所有后续序号的 Pod必须完全关闭,也就是说,如果 StatefulSet 有 3 个副本,终止序号为 1 的 Pod 前,Pod 2 和 Pod 3 必须完全停止,这样可以确保数据的完整性和正确性。

  6. 终止宽限期pod.Spec.TerminationGracePeriodSeconds 指定 Pod 在终止前的宽限时间。如果将此值设置为 0,Pod 会立即终止,可能导致未完成的请求丢失或数据不一致。为了保证 Pod 有足够时间完成当前操作和清理资源,必须设置一个合理的宽限期。这样可以提高系统的可靠性和安全性。

  • 16
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值