一、Statefulset控制器:概念、原理解读

StatefulSet是为了管理有状态服务的问题而设计的。

有状态服务

StatefulSet是有状态的集合,管理有状态的服务,它所管理的Pod的名称不能随意变化。数据持久化的目录也是不一样,每一个Pod都有自己独有的数据持久化存储目录。比如MySQL主从、redis集群等。

无状态服务

RC、Deployment、DaemonSet都是管理无状态的服务,它们所管理的Pod的IP、名字,启停顺序等都是随机的。个体对整体无影响,所有pod都是共用一个数据卷的,部署的tomcat就是无状态的服务,tomcat被删除,在启动一个新的tomcat,加入到集群即可,跟tomcat的名字无关。

StatefulSet部分组成

  1. Headless Service:用来定义pod网路标识,生成可解析的DNS记录

  2. volumeClaimTemplates:存储卷申请模板,创建pvc,指定pvc名称大小,自动创建pvc,且pvc由存储类供应。

  3. StatefulSet:管理pod的

Headless service

Headless service不分配clusterIP,headless service可以通过解析service的DNS,返回所有Pod的dns和ip地址 (只有statefulSet部署的Pod才有DNS地址),普通的service,只能通过解析service的DNS返回service的ClusterIP。

为什么要用headless service(没有service ip的service)?

在使用Deployment时,创建的Pod名称是没有顺序的,是随机字符串,在用statefulset管理pod时要求pod名称必须是有序的每一个pod不能被随意取代,pod重建后pod名称还是一样的。因为pod IP是变化的,所以要用Pod名称来识别。pod名称是pod唯一性的标识符,必须持久稳定有效。这时候要用到无头服务,它可以给每个Pod一个唯一的名称。

1、headless service会为service分配一个域名

<service name>.$<namespace name>.svc.cluster.local

K8s中资源的全局FQDN格式:

Service_NAME.NameSpace_NAME.Domain.LTD.Domain.LTD.=svc.cluster.local.  

#这是默认k8s集群的域名。

FQDN 全称 Fully Qualified Domain Name

即全限定域名:同时带有主机名和域名的名称

FQDN = Hostname + DomainName

如:主机名是 duoduo

域名是 [baidu.com](http://baidu.com/)

FQDN= duoduo.baidu.com.

#eg:
[root@k8s-master deployment]# dig nginx-deploy-svc.default.svc.cluster.local @10.96.0.10

; <<>> DiG 9.11.36-RedHat-9.11.36-14.el8_10 <<>> nginx-deploy-svc.default.svc.cluster.local @10.96.0.10
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19414
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 16379d4770867ed4 (echoed)
;; QUESTION SECTION:
;nginx-deploy-svc.default.svc.cluster.local. IN A

;; ANSWER SECTION:
nginx-deploy-svc.default.svc.cluster.local. 30 IN A 10.97.172.98

;; Query time: 7 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: 一 1月 13 01:25:21 EST 2025
;; MSG SIZE  rcvd: 141

[root@k8s-master deployment]# kubectl get svc
NAME               TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes         ClusterIP   10.96.0.1      <none>        443/TCP        6d4h
nginx-deploy-svc   NodePort    10.97.172.98   <none>        80:30676/TCP   2m20s

2、StatefulSet会为关联的Pod保持一个不变的Pod Name

​    statefulset中Pod的名字格式为   【 $(StatefulSet name)-$(pod序号) 】

3、StatefulSet会为关联的Pod分配一个dnsName

$<Pod Name>.$<service name>.$<namespace name>.svc.cluster.local

二、Statefulset资源清单文件编写技巧

#查看定义Statefulset资源需要的字段

[root@k8s-master01~]# kubectl explain statefulset

FIELDS:

   apiVersion       <string> #定义statefulset资源需要使用的api版本

   kind  <string>          #定义的资源类型

   metadata         <Object>     #元数据

   spec <Object>          #定义容器相关的信息

#查看statefulset.spec字段如何定义

[root@k8s-master01~]# kubectl explain statefulset.spec

FIELDS:

   podManagementPolicy <string> #pod管理策略

   replicas    <integer>  #副本数

   revisionHistoryLimit       <integer> #保留的历史版本

   selector    <Object> -required- #标签选择器,选择它所关联的pod

   serviceName   <string> -required-  #headless service的名字

   template  <Object> -required-     #生成pod的模板

   updateStrategy       <Object>   #更新策略

   volumeClaimTemplates <[]Object> #存储卷申请模板

#查看statefulset的spec.template字段如何定义?

#对于template而言,其内部定义的就是pod,pod模板是一个独立的对象

[root@k8s-master01~]# kubectl explain statefulset.spec.template

FIELDS:

   metadata         <Object>

   spec <Object>  #定义容器属性的

通过上面可以看到,statefulset资源中有两个spec字段。第一个spec声明的是statefulset定义多少个Pod副本(默认将仅部署1个Pod)、匹配Pod标签的选择器、创建pod的模板、存储卷申请模板,第二个spec是spec.template.spec:主要用于Pod里的容器属性等配置。

.spec.template里的内容是声明Pod对象时要定义的各种属性,所以这部分也叫做PodTemplate(Pod模板)。还有一个值得注意的地方是:在.spec.selector中定义的标签选择器必须能够匹配到spec.template.metadata.labels里定义的Pod标签,否则Kubernetes将不允许创建statefulset。

三、Statefulset使用案例:部署web站点

#编写一个Statefulset资源清单文件

[root@k8s-master01~]# cat statefulset.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
     app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: "None"
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx-svc"
  replicas: 2
  template:
    metadata:
     labels:
       app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
          name: web

#更新资源清单文件

[root@k8s-master01~]# kubectl apply -f statefulset.yaml

service/nginx created

statefulset.apps/web created

#查看statefulset是否创建成功

[root@k8s-master01~]# kubectl get statefulset

NAME   READY   AGE

web      2/2      42s

#查看pod

[root@k8s-master01~]# kubectl get pods -l app=nginx

NAME    READY   STATUS    RESTARTS   AGE

web-0   1/1     Running   0          2m17s

web-1   1/1     Running   0          115s

#通过上面可以看到创建的pod是有序的

#查看headless service

[root@k8s-master01~]# kubectl get svc -l app=nginx

NAME    TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE

nginx    ClusterIP      None         <none>        80/TCP    3m19s

#查看pvc

[root@k8s-master01~]# kubectl get pvc

#查看pod主机名

[root@k8s-master01~]# for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname';done

web-0

web-1

#使用k运行一个提供nslookup命令的容器(bubybox:1.28),这个命令来自于dnsutils包,通过对pod主机名执行nslookup,可以检查它们在集群内部的DNS地址:

[root@hd1 ~]# cat pod-second.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-second
  labels:
    app: backend
    tier: db
spec:
    containers:
    - name: busybox
      image: busybox:1.28
      imagePullPolicy: IfNotPresent
      command: ["sh","-c","sleep 3600"]

[root@hd1 ~]# kubectl apply -f pod-second.yaml

[root@hd1 ~]# kubectl exec -it pod-second -- /bin/sh

/ # nslookup  web-0.nginx.default.svc.cluster.local

Server:    10.10.0.10

Address 1: 10.10.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx.default.svc.cluster.local

Address 1: 10.10.156.137 web-0.nginx.default.svc.cluster.local

/ #

#statefulset创建的pod也是有dns记录的

Address: 10.10.209.154  #解析的是pod的ip地址,还可以解析 nginx.default.svc.cluster.local

/ # nslookup  nginx.default.svc.cluster.local

Server:    10.10.0.10

Address 1: 10.10.0.10 kube-dns.kube-system.svc.cluster.local

Name:      nginx.default.svc.cluster.local

Address 1: 10.10.25.245 web-1.nginx.default.svc.cluster.local

Address 2: 10.10.156.137 web-0.nginx.default.svc.cluster.local

/ #

经典面试题

举例说明service 和headless service区别?

#通过deployment创建pod,pod前端创建一个service
[root@k8s-master01~]# cat deploy-service.yaml

apiVersion: v1

kind: Service

metadata:

  name: my-nginx

  labels:

    run: my-nginx

spec:

  type: ClusterIP

  ports:

  - port: 80   #service的端口,暴露给k8s集群内部服务访问

    protocol: TCP

    targetPort: 80    #pod容器中定义的端口

  selector:

    run: my-nginx  #选择拥有run=my-nginx标签的pod

---

apiVersion: apps/v1

kind: Deployment

metadata:

  name: my-nginx

spec:

  selector:

    matchLabels:

      run: my-nginx

  replicas: 2

  template:

    metadata:

      labels:

        run: my-nginx

    spec:

      containers:

   - name: my-nginx

        image: busybox

        imagePullPolicy: IfNotPresent

        ports:

   - containerPort: 80

        command:

   - sleep: 0"

#更新资源清单文件

[root@k8s-master01~]# kubectl apply -f deploy-service.yaml

#查看service

[root@k8s-master01~]# kubectl get svc -l run=my-nginx

NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S) 

my-nginx   ClusterIP     10.100.89.90   <none>        80/TCP  

#查看pod

[root@k8s-master01~]# kubectl get pods -l run=my-nginx

NAME                        READY   STATUS    RESTARTS   AGE

my-nginx-58f74fc5b6-jzbvk   1/1     Running   0          70s

my-nginx-58f74fc5b6-n9lqv   1/1     Running   0          53s

#通过上面可以看到deployment创建的pod是随机生成的

#进入到web-1的pod

[root@k8s-master01~]# kubectl exec -it web-1 -- /bin/bash

root@web-1:/# nslookup my-nginx.default.svc.cluster.local

Server:           10.10.0.10

Address:        10.10.0.10#53

Name:   my-nginx.default.svc.cluster.local

Address: 10.100.89.90   #解析的是service的ip地址

四、Statefulset管理pod:扩容、缩容、更新

Statefulset实现pod的动态扩容

如果我们觉得两个副本太少了,想要增加,只需要修改配置文件statefulset.yaml里的replicas的值即可,原来replicas: 2,现在变成replicaset: 3,修改之后,执行如下命令更新:

[root@k8s-master01~]# kubectl apply -f statefulset.yaml

service/nginx unchanged

statefulset.apps/web configured

[root@k8s-master01~]# kubectl get sts

NAME   READY   AGE

web    3/3     60m

      [root@k8s-master01~]# kubectl get pods -l app=nginx

NAME    READY   STATUS    RESTARTS   AGE

web-0   1/1     Running   0          61m

web-1   1/1     Running   0          60m

web-2   1/1     Running   0          79s

      #也可以直接编辑控制器实现扩容

[root@k8s-master01~]# kubectl edit sts web

#这个是我们把请求提交给了apiserver,实时修改,把上面的spec下的replicas 后面的值改成4,保存退出
[root@k8s-master01~]#  kubectl get pods -l app=nginx

NAME    READY   STATUS    RESTARTS   AGE

web-0   1/1     Running   0          62m

web-1   1/1     Running   0          62m

web-2   1/1     Running   0          3m13s

web-3   1/1     Running   0          26s

Statefulset实现pod的动态缩容

如果我们觉得4个Pod副本太多了,想要减少,只需要修改配置文件statefulset.yaml里的replicas的值即可,把replicaset:4变成replicas: 2,修改之后,执行如下命令更新:

[root@k8s-master01~]# kubectl apply -f statefulset.yaml

service/nginx unchanged

statefulset.apps/web configured

[root@k8s-master01~]#  kubectl get pods -l app=nginx

NAME    READY   STATUS    RESTARTS   AGE

web-0   1/1     Running   0          64m

web-1   1/1     Running   0          64m

Statefulset实现pod的更新

myapp.tar.gz上传到k8s-worker01上,手动解压

root@k8s-worker01 ~]# docker load -i myapp.tar.gz

[root@k8s-master01~]# kubectl edit sts web

#修改镜像nginx变成- image: ikubernetes/myapp:v2,修改之后保存退出

[root@k8s-master01~]# kubectl get pods -o wide -l app=nginx

NAME    READY   STATUS    RESTARTS   AGE   IP               NODE       NOMINATED NODE   READINESS GATES

web-0   1/1     Running   0          18s   10.10.209.156  k8s-worker01  

web-1   1/1     Running   0          36s   10.10.187.115  k8s-worker02

#查看pod详细信息

[root@k8s-master01~]# kubectl describe pods web-0

#可以看到pod已经使用刚才更新的镜像ikubernetes/myapp:v2

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐