一.系统环境

本文主要基于Kubernetes1.21.9和Linux操作系统CentOS7.4。

服务器版本 docker软件版本 Kubernetes(k8s)集群版本 CPU架构
CentOS Linux release 7.4.1708 (Core) Docker version 20.10.12 v1.21.9 x86_64

Kubernetes集群架构:k8scloude1作为master节点,k8scloude2,k8scloude3作为worker节点。

服务器 操作系统版本 CPU架构 进程 功能描述
k8scloude1/192.168.110.130 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kube-apiserver,etcd,kube-scheduler,kube-controller-manager,kubelet,kube-proxy,coredns,calico k8s master节点
k8scloude2/192.168.110.129 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kubelet,kube-proxy,calico k8s worker节点
k8scloude3/192.168.110.128 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kubelet,kube-proxy,calico k8s worker节点

二.前言

在Kubernetes集群中部署和管理有状态应用是一项复杂的任务。有状态应用(Stateful Applications)通常要求持久化存储、唯一网络标识和有序的启动顺序。为了解决这些需求,Kubernetes提供了一个强大的控制器——StatefulSet。

使用StatefulSet的前提是已经有一套可以正常运行的Kubernetes集群,关于Kubernetes(k8s)集群的安装部署,可以查看博客《Centos7 安装部署Kubernetes(k8s)集群》https://www.cnblogs.com/renshengdezheli/p/16686769.html。

三.StatefulSet简介

StatefulSet是Kubernetes的一个核心控制器,用于管理有状态应用的部署和伸缩。它为每个Pod分配一个唯一的标识符,确保有状态应用的稳定性和可扩展性。StatefulSet还可以自动处理Pod的创建、删除和更新,确保有状态应用的高可用性。

相比于Deployment等控制器,StatefulSet提供了以下特性:

  • 稳定的网络标识符:每个Pod都会被分配一个稳定的主机名和网络标识符,使得有状态应用能够方便地进行服务发现和通信。
  • 有序的部署和扩展:StatefulSet确保有状态应用的Pod按照指定的顺序进行部署和扩展,保证有序启动和关闭。
  • 持久化存储:StatefulSet支持将持久化存储卷(Persistent Volume)与Pod绑定,使得有状态应用能够保留数据。

StatefulSet简写为sts,deployment控制器创建出来的pod都是一样的,更适合用于对外提供服务的pod,但是有一些应用,只适用于公

司内部,并不想对外提供服务,比如一个数据库有1个master节点和3个slave节点,master提供写,slave提供读,master通过日志把数

据拷贝给slave,slave节点有自己的存储,slave1读slave1的存储,slave2读slave2的存储,以此类推,slave节点不共享存储,这种情况

不适合使用deployment来创建pod,这种情况就需要使用statefulset控制器。

StatefulSet常用的属性包括:

  • serviceName:指定与StatefulSet关联的Service的名称。这个Service将用于提供稳定的网络标识符,供其他应用访问StatefulSet中的Pod。
  • volumeClaimTemplates:定义了每个Pod所需的持久化存储卷(Claim)模板。可以在这里指定存储类、存储大小等参数,以满足有状态应用对持久化存储的需求。
  • podManagementPolicy:指定Pod管理策略,可以是OrderedReady或Parallel。OrderedReady表示按顺序启动和终止Pod,保证有序性和稳定性;Parallel表示可以并行启动和终止Pod,适用于无依赖关系的应用场景。

四.有状态应用和无状态应用区别

在Kubernetes中,应用可以分为有状态应用和无状态应用。有状态应用是指需要保留数据或状态的应用,而无状态应用则不需要。

有状态应用与无状态应用之间的主要区别如下:

  • 网络标识符:有状态应用需要稳定的网络标识符,使得其他服务能够准确地访问它们。而无状态应用通常使用Service来提供负载均衡和服务发现,不直接依赖于特定的网络标识符。
  • 数据持久化:有状态应用需要将数据保存在持久化存储中,并且能够在Pod重新启动时恢复数据。无状态应用通常将数据保存在外部数据库或缓存中,不依赖于Pod的生命周期。
  • 有序启动和关闭:有状态应用通常需要按照特定的顺序启动和关闭,以确保数据的一致性。无状态应用则可以并行启动和关闭,不需要保证顺序。

五.StatefulSet

5.1 创建StatefulSet

创建存放yaml文件的目录。

[root@k8scloude1 ~]# mkdir statefulset

[root@k8scloude1 ~]# cd statefulset/

创建命名空间。

[root@k8scloude1 statefulset]# kubectl create ns statefulset
namespace/statefulset created

切换命名空间。

[root@k8scloude1 statefulset]# kubens statefulset
Context "kubernetes-admin@kubernetes" modified.
Active namespace is "statefulset".

现在还没有pod。

[root@k8scloude1 statefulset]# kubectl get pod
No resources found in statefulset namespace.

查看存储类storageclass,managed-nfs-storage是我们前面章节创建的storageclass,关于存储类storageclass的详细信息,请查看博客《Kubernetes(k8s)存储管理之数据卷volumes(五):动态制备-存储类StorageClass》。

[root@k8scloude1 statefulset]# cd sts/

[root@k8scloude1 sts]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
managed-nfs-storage fuseim.pri/ifs Delete Immediate false 71d

查看managed-nfs-storage 的描述信息。

[root@k8scloude1 sts]# kubectl describe sc managed-nfs-storage
Name: managed-nfs-storage
IsDefaultClass: No
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{},"name":"managed-nfs-storage"},"parameters":{"archiveOnDelete":"false"},"provisioner":"fuseim.pri/ifs"} Provisioner: fuseim.pri/ifs
Parameters: archiveOnDelete=false
AllowVolumeExpansion: <unset>
MountOptions: <none>
ReclaimPolicy: Delete
VolumeBindingMode: Immediate
Events: <none>

StatefulSet的yaml文件如下,功能为:创建一个StatefulSet,其中包含一个Pod运行着Nginx容器,并且有一个挂载了持久化存储卷的文件系统。这个StatefulSet通过Service提供稳定的网络标识符,可以被其他应用访问。

[root@k8scloude1 sts]# vim sts.yaml 

[root@k8scloude1 sts]# cat sts.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
#matchLabels:设置了一个标签选择器,选择具有app: nginx标签的Pod。
matchLabels:
app: nginx
#serviceName: "nginx":指定了与StatefulSet关联的Service的名称为"nginx"。该Service将提供稳定的网络标识符供其他应用访问StatefulSet中的Pod。
serviceName: "nginx"
#replicas: 1:定义了StatefulSet的副本数量为1,即只创建一个Pod。
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
#当需要关闭容器时,立即杀死容器而不等待默认的30秒优雅停机时长。
terminationGracePeriodSeconds: 0
containers:
- name: nginx
image: nginx
#imagePullPolicy: IfNotPresent:表示如果本地已经存在该镜像,则不重新下载;否则从远程 Docker Hub 下载该镜像
imagePullPolicy: IfNotPresent
#把www卷挂载到容器内的路径为"/usr/share/nginx/html"
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
#volumeClaimTemplates:定义了持久化存储卷(Claim)模板,用于创建Pod所需的持久化存储。
volumeClaimTemplates:
- metadata:
#name: www:指定了卷模板的名称为"www"。
name: www
spec:
#accessModes: [ "ReadWriteOnce" ]:设置了卷的访问模式为"ReadWriteOnce",即可以被一个节点以读写方式挂载。
accessModes: [ "ReadWriteOnce" ]
#storageClassName: "managed-nfs-storage":指定了存储类的名称为"managed-nfs-storage",用于创建持久化存储卷。
storageClassName: "managed-nfs-storage"
resources:
requests:
#requests:设置了卷的请求资源,即要求1Gi的存储空间。
storage: 1Gi

现在还没有PVC。关于PV,PVC的详细内容,请查看博客《Kubernetes(k8s)存储管理之数据卷volumes(四):持久卷Persistent Volume》。

[root@k8scloude1 sts]# kubectl get pvc
No resources found in statefulset namespace.

创建statefulset。

[root@k8scloude1 sts]# kubectl apply -f sts.yaml
statefulset.apps/web created

创建statefulset之后,pod和PVC都有了。

[root@k8scloude1 sts]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 8s [root@k8scloude1 sts]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound pvc-3b58a17b-acbd-4eb6-a1e4-a2bd2cf331ac 1Gi RWO managed-nfs-storage 37s

查看statefulset。

[root@k8scloude1 sts]# kubectl get statefulset -o wide
NAME READY AGE CONTAINERS IMAGES
web 1/1 111s nginx nginx [root@k8scloude1 sts]# kubectl get sts -o wide
NAME READY AGE CONTAINERS IMAGES
web 1/1 2m11s nginx nginx

5.2 scale扩展副本数

把statefulset的副本数扩展为2。

[root@k8scloude1 sts]# kubectl scale sts web --replicas=2
statefulset.apps/web scaled

查看statefulset。

[root@k8scloude1 sts]# kubectl get sts -o wide
NAME READY AGE CONTAINERS IMAGES
web 2/2 3m nginx nginx

pod变为2个了。

[root@k8scloude1 sts]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 3m27s 10.244.251.230 k8scloude3 <none> <none>
web-1 1/1 Running 0 37s 10.244.251.219 k8scloude3 <none> <none>

把statefulset的副本数扩展为3。

[root@k8scloude1 sts]# kubectl scale sts web --replicas=3
statefulset.apps/web scaled

pod变为3个了。

[root@k8scloude1 sts]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 4m43s
web-1 1/1 Running 0 113s
web-2 1/1 Running 0 13s

查看statefulset。

[root@k8scloude1 sts]# kubectl get sts
NAME READY AGE
web 3/3 5m9s

pvc也变为了3个。

[root@k8scloude1 sts]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound pvc-3b58a17b-acbd-4eb6-a1e4-a2bd2cf331ac 1Gi RWO managed-nfs-storage 5m54s
www-web-1 Bound pvc-689e6b63-9ca8-413b-b240-5e2fae467a12 1Gi RWO managed-nfs-storage 3m4s
www-web-2 Bound pvc-565f137f-9e4b-44a1-8953-2b71e393a409 1Gi RWO managed-nfs-storage 84s

在每个pod里写入不同的内容。

[root@k8scloude1 sts]# kubectl exec -it web-0 -- sh -c "echo web-0 > /usr/share/nginx/html/index.html"

[root@k8scloude1 sts]# kubectl exec -it web-1 -- sh -c "echo web-1 > /usr/share/nginx/html/index.html"

[root@k8scloude1 sts]# kubectl exec -it web-2 -- sh -c "echo web-2 > /usr/share/nginx/html/index.html"

把statefulset的副本数扩展为0,所有pod都被删除了。

[root@k8scloude1 sts]# kubectl scale sts web --replicas=0
statefulset.apps/web scaled [root@k8scloude1 sts]# kubectl get pod
No resources found in statefulset namespace.

把statefulset的副本数扩展为3。

[root@k8scloude1 sts]# kubectl scale sts web --replicas=3
statefulset.apps/web scaled

pod变为3个了。

[root@k8scloude1 sts]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 3s
web-1 0/1 ContainerCreating 0 1s [root@k8scloude1 sts]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 8s
web-1 1/1 Running 0 6s
web-2 1/1 Running 0 4s

可以发现,把pod删除之后,又创建3个pod,新的3个pod名字没变,并且web-0的存储还是www-web-0,web-1的存储还是www-web-1,web-2的存储还是www-web-2,pod对应的存储不会错乱,每个pod里面的内容也没有变化。

[root@k8scloude1 sts]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 5m41s 10.244.251.225 k8scloude3 <none> <none>
web-1 1/1 Running 0 5m39s 10.244.251.226 k8scloude3 <none> <none>
web-2 1/1 Running 0 5m37s 10.244.251.218 k8scloude3 <none> <none> [root@k8scloude1 sts]# kubectl exec -it web-0 -- sh -c "cat /usr/share/nginx/html/index.html"
web-0 [root@k8scloude1 sts]# kubectl exec -it web-1 -- sh -c "cat /usr/share/nginx/html/index.html"
web-1 [root@k8scloude1 sts]# kubectl exec -it web-2 -- sh -c "cat /usr/share/nginx/html/index.html"
web-2

手动删除web-1这个pod。

[root@k8scloude1 sts]# kubectl delete pod web-1
pod "web-1" deleted

statefulset自动创建了一个一模一样的web-1。

[root@k8scloude1 sts]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 3h7m 10.244.251.225 k8scloude3 <none> <none>
web-1 1/1 Running 0 3s 10.244.251.221 k8scloude3 <none> <none>
web-2 1/1 Running 0 3h7m 10.244.251.218 k8scloude3 <none> <none>

5.3 创建无头服务headless service

删除其中一个pod,statefulset很快又会生成一个同名的pod,但是IP地址发生变化,不适合直接使用pod的IP地址访问,使用svc访问更

好,但是创建svc有一定问题,svc具有负载均衡的作用,你访问svc,可能给你后端转发到web-0,也可能后端转发到web-1,后端pod又

都有自己专属的存储,所以svc也不合适,这时就需要无头服务(headless service)了。关于svc的详细内容,请查看博客《Kubernetes(k8s)服务service:service的发现和service的发布》。

现在没有svc。

[root@k8scloude1 sts]# kubectl get svc
No resources found in statefulset namespace.

如下是无头服务headless service的yaml文件,功能为:创建一个名为"nginx"的Service,它与具有app: nginx标签的Pod关联。该Service通过暴露在80端口上,并且没有Cluster IP地址。

无头服务headless service和service的区别是:无头服务headless service的 clusterIP为None。

[root@k8scloude1 sts]# cat svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
#设置了一个标签app: nginx,与其他相关资源进行关联。
labels:
app: nginx
spec:
ports:
#port: 80:将Service的端口设置为80。
- port: 80
#name: web:为该端口指定名称为"web"。
name: web
#clusterIP: None:将Service的Cluster IP设置为"None",表示该Service是一个Headless Service,没有集群内部的虚拟IP地址。
clusterIP: None
selector:
#app: nginx:设置了一个标签选择器,选择具有app: nginx标签的Pod。
app: nginx

创建无头服务headless service。

[root@k8scloude1 sts]# kubectl apply -f svc.yaml
service/nginx created

查看无头服务headless service,CLUSTER-IP为None。

[root@k8scloude1 sts]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx ClusterIP None <none> 80/TCP 12s app=nginx

5.4 通过无头服务headless service访问StatefulSet

查看pod的标签。

[root@k8scloude1 sts]# kubectl get pod -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
web-0 1/1 Running 0 3h20m 10.244.251.225 k8scloude3 <none> <none> app=nginx,controller-revision-hash=web-748c7c878b,statefulset.kubernetes.io/pod-name=web-0
web-1 1/1 Running 0 13m 10.244.251.221 k8scloude3 <none> <none> app=nginx,controller-revision-hash=web-748c7c878b,statefulset.kubernetes.io/pod-name=web-1
web-2 1/1 Running 0 3h20m 10.244.251.218 k8scloude3 <none> <none> app=nginx,controller-revision-hash=web-748c7c878b,statefulset.kubernetes.io/pod-name=web-2

因为无头服务headless service的CLUSTER-IP为None,所以访问方式为:pod名.svc名 来访问pod,statefulset创建的pod,pod名字是不变的。

[root@k8scloude1 sts]# curl web-0.nginx
curl: (6) Could not resolve host: web-0.nginx; 未知的名称或服务

容器外无法访问。

[root@k8scloude1 sts]# curl http://web-0.nginx
curl: (6) Could not resolve host: web-0.nginx; 未知的名称或服务

创建一个临时容器podclient作为客户端,来访问pod,--rm表示创建临时容器。

[root@k8scloude1 sts]# kubectl run -it podclient --image=yauritux/busybox-curl:latest --rm --image-pull-policy=IfNotPresent -- sh
If you don't see a command prompt, try pressing enter. #访问成功
/home # curl web-0.nginx
web-0 /home # curl web-1.nginx
web-1 /home # curl web-2.nginx
web-2 /home # exit
Session ended, resume using 'kubectl attach podclient -c podclient -i -t' command when the pod is running
pod "podclient" deleted

可以发现,StatefulSet只适合内部访问,不适合外部访问。

[root@k8scloude1 sts]# kubectl get sts
NAME READY AGE
web 3/3 3h43m

删除StatefulSet。

[root@k8scloude1 sts]# kubectl delete sts web
statefulset.apps "web" deleted [root@k8scloude1 sts]# kubectl get sts
No resources found in statefulset namespace. [root@k8scloude1 sts]# kubectl get pod
No resources found in statefulset namespace.

六.总结

本文介绍了Kubernetes中的一个重要控制器——StatefulSet,用于管理有状态应用的部署和伸缩。通过为每个Pod分配唯一的标识符和稳定的网络标识,StatefulSet确保了有状态应用的稳定性、可扩展性和数据持久性。相比于无状态应用,有状态应用需要更多的关注持久化存储和有序性启动等问题。

使用StatefulSet可以轻松地部署和管理有状态应用,使其能够充分发挥Kubernetes的优势。通过深入理解StatefulSet的概念和特性,我们可以更好地构建和管理复杂的有状态应用。

Kubernetes(k8s)控制器(五):有状态应用StatefulSet的更多相关文章

  1. Kubernetes(k8s)控制器(四):ReplicaSet

    目录 一.系统环境 二.前言 三.ReplicaSet概览 四.ReplicaSet工作原理 五.ReplicaSet使用场景 六.创建ReplicaSet 七.扩展replicaset副本数 一.系 ...

  2. Kubernetes(k8s)控制器(三):ReplicationController

    目录 一.系统环境 二.前言 三.ReplicationController概览 四.ReplicationController工作机制 五.创建ReplicationController 六.扩展r ...

  3. Kubernetes(k8s)控制器(二):DaemonSet

    目录 一.系统环境 二.前言 三.DaemonSet 概览 四.创建DaemonSet 4.1 创建daemonset 让其在k8s集群所有worker节点运行pod 4.2 创建daemonset让 ...

  4. Kubernetes(k8s)控制器(一):deployment

    目录 一.系统环境 二.前言 三.Kubernetes 控制器 四.Deployment概览 五.创建deployment 六.修改deploy副本数 6.1 kubectl edit deploy ...

  5. k8s控制器资源(五)

    Pod pod在之前说过,pod是kubernetes集群中是最小的调度单元,pod中可以运行多个容器,而node又可以包含多个pod,关系如下图: 在对pod的用法进行说明之前,有必要先对docke ...

  6. Kubernetes K8S之资源控制器RC、RS、Deployment详解

    Kubernetes的资源控制器ReplicationController(RC).ReplicaSet(RS).Deployment(Deploy)详解与示例 主机配置规划 服务器名称(hostna ...

  7. Kubernetes K8S之资源控制器StatefulSets详解

    Kubernetes的资源控制器StatefulSet详解与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS7.7 2 ...

  8. Kubernetes K8S之资源控制器Daemonset详解

    Kubernetes的资源控制器Daemonset详解与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS7.7 2C/ ...

  9. Kubernetes K8S之资源控制器Job和CronJob详解

    Kubernetes的资源控制器Job和CronJob详解与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS7.7 2 ...

  10. Kubernetes(k8s)存储管理之数据卷volumes(五):动态制备-存储类StorageClass

    目录 一.系统环境 二.前言 三.静态制备和动态制备 四.存储类StorageClass 4.1 存储类StorageClass概览 4.2 StorageClass 资源 五.创建存储类Storag ...

随机推荐

  1. DVWA上low级别反射型,存储型,DOM型XSS攻击获取用户cookie

    1.什么是反射型 XSS 攻击? 反射型 XSS 是指应用程序通过 Web 请求获取不可信赖的数据,并在未检验数据是否存在恶意代码的情况下,将其发送给用户. 反射型 XSS 一般可以由攻击者构造带有恶 ...

  2. LeeCode 317周赛复盘

    T1: 可被3整数的偶数的平均值 思路:数组遍历 被3整数的偶数 \(\Leftrightarrow\) 被6整数的数 public int averageValue(int[] nums) { in ...

  3. 解决IDEA创建项目时无法引入依赖问题:Cannot resolve **.**.**(已解决)

    今天在创建SpringBoot整合MyBatis项目时出现报错: Cannot resolve org.springframework:spring-tx:5.3.26 Cannot resolve ...

  4. day02-Redis命令

    Redis命令 1.Redis数据结构介绍 Redis是一个key-value的数据库,key一般是String类型,value的类型多种多样,value常见的八种类型: Redis支持五种基本的数据 ...

  5. Linux进程管理(命令)入门

    进程是一个运行中的程序 进程查看 ps 能够查看当前终端下运行的进程 $ ps PID TTY TIME CMD 26305 pts/0 00:00:00 bash 26312 pts/0 00:00 ...

  6. 程序猿要chatpgpt干掉了?

    如何拥抱被chatpgpt拉开的人工智能大时代 昨天 chatgpt-4 发布了.我看到好多技术圈的人都惶恐着,以后咱们都要失业了/(ㄒoㄒ)/~~ 和之前差不多的是毫无意外地又引动了一大波舆论.虽然 ...

  7. 16-js兼容性处理

    const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); modul ...

  8. Windows防病毒Defender 排除病毒误报

    开发的软件安装后,windows上提示病毒,默默被系统删除了. 一开始以为是自己软件的签名问题,后面发现,将被隔离的文件还原,文件的签名是存在的. 这是微软denfender的误报,为啥会报病毒呢? ...

  9. [C++基础入门] 1、C++初识

    文章目录 1 C++初识 1.1 第一个C++程序 1.1.1 创建项目 1.1.2 创建文件 1.1.3 编写代码 1.1.4 运行程序 1.2 注释 1.3 变量 1.4 常量 1.5 关键字 1 ...

  10. 【机器学习与深度学习理论要点】20. 什么是激活函数,为什么要用激活函数,常见的激活函数和特点,softmax函数

    1)什么是激活函数,为什么要用激活函数? 激活函数,指神经网络中将输入信号的总和转换为输出信号的函数,激活函数将多层感知机输出转换为非线性,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应 ...