k8s控制器:StatefulSet

k8s控制器:StatefulSet

一、Statefulset控制器概述

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

1.1、有状态/无状态服务

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

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

1.2、Headless service

1.2.1、什么是Headless service

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

1.2.2、为什么要用headless 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

2)StatefulSet会为关联的Pod保持一个不变的Pod Name:$(StatefulSet name)-$(pod序号)

3)StatefulSet会为关联的Pod分配一个dnsName:$<Pod Name>.$<service name>.$<namespace name>.svc.cluster.local

1.3、volumeClaimTemplate

对于有状态应用都会用到持久化存储,比如mysql主从,由于主从数据库的数据是不能存放在一个目录下的,每个mysql节点都需要有自己独立的存储空间。而在deployment中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,它们数据是同步的,而statefulset定义中的每一个pod都不能使用同一个存储卷,这就需要使用volumeClainTemplate,当在使用statefulset创建pod时,volumeClainTemplate会自动生成一个PVC,从而请求绑定一个PV,每一个pod都有自己专用的存储卷。Pod、PVC和PV对应的关系图如下:

k8s控制器:StatefulSet

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

# 创建存储类
[root@k8s-master1 ~]# cat class-web.yaml 
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-web
provisioner: example.com/nfs

# 更新资源清单文件
[root@k8s-master1 ~]# kubectl apply -f class-web.yaml
[root@k8s-master1 ~]# kubectl get storageclass
NAME      PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs-web   example.com/nfs   Delete          Immediate           false                  30s

# 编写一个Statefulset资源清单文件
[root@k8s-master1 ~]# cat statefulset.yaml 
apiVersion: v1
kind: Service
metadata: 
  name: nginx
  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"
  replicas: 2
  template:
    metadata: 
     labels:
       app: nginx
    spec: 
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "nfs-web"
      resources:
        requests: 
          storage: 1Gi

# 更新资源清单文件
[root@k8s-master1 ~]# kubectl apply -f statefulset.yaml 
service/nginx created
statefulset.apps/web created

# 查看无头服务
[root@k8s-master1 ~]# kubectl describe svc nginx 
Name:              nginx
Namespace:         default
Labels:            app=nginx
Annotations:       <none>
Selector:          app=nginx
Type:              ClusterIP
IP Families:       <none>
IP:                None
IPs:               None
Port:              web  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.36.122:80,10.244.36.123:80
Session Affinity:  None
Events:            <none>

# 查看statefulset是否创建成功
[root@k8s-master1 ~]# kubectl get statefulset
NAME   READY   AGE
web    2/2     13s 

# 查看pod: 可以看到创建的pod是有序的
[root@k8s-master1 ~]# kubectl get pods -l app=nginx
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          35s
web-1   1/1     Running   0          29s

# 查看headless service
[root@k8s-master1 ~]# kubectl get pods -l app=nginx -o wide
NAME    READY   STATUS    RESTARTS   AGE     IP              NODE        NOMINATED NODE   READINESS GATES
web-0   1/1     Running   0          4m22s   10.244.36.122   k8s-node1   <none>           <none>
web-1   1/1     Running   0          4m16s   10.244.36.123   k8s-node1   <none>           <none>

# 查看pvc
[root@k8s-master1 ~]# kubectl get pvc
NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0     Bound    pvc-de5c7090-9d53-4597-b292-233f27534764   1Gi        RWO            nfs-web        92s
www-web-1     Bound    pvc-b3a2c509-f011-448e-9091-afa76726ef40   1Gi        RWO            nfs-web        87s

# 查看pv
[root@k8s-master1 ~]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE
pvc-b3a2c509-f011-448e-9091-afa76726ef40   1Gi        RWO            Delete           Bound    default/www-web-1     nfs-web                 2m2s
pvc-de5c7090-9d53-4597-b292-233f27534764   1Gi        RWO            Delete           Bound    default/www-web-0     nfs-web                 2m8s
                    
# 查看pod主机名
[root@k8s-master1 ~]# for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname';done 
web-0
web-1

# 使用kubectl run运行一个提供nslookup命令的容器的,这个命令来自于dnsutils包,通过对pod主机名执行nslookup,可以检查它们在集群内部的DNS地址:
[root@k8s-master1 ~]# kubectl exec -it web-1 -- /bin/bash
root@web-1:/# apt-get update
root@web-1:/# apt-get install dnsutils -y

root@web-1:/# nslookup web-0.nginx.default.svc.cluster.local  
Server:		10.96.0.10
Address:	10.96.0.10#53
Name:	web-0.nginx.default.svc.cluster.local 

# statefulset创建的pod也是有dns记录的
Address: 10.244.36.122  #解析的是pod的ip地址

root@web-1:/# nslookup nginx.default.svc.cluster.local
Server:		10.96.0.10
Address:	10.96.0.10#53

Name:	nginx.default.svc.cluster.local  #查询service dns,会把对应的pod ip解析出来
Address: 10.244.36.123

Name:	nginx.default.svc.cluster.local
Address: 10.244.36.122

root@web-1:/# dig -t A nginx.default.svc.cluster.local @10.96.0.10
; <<>> DiG 9.11.5-P4-5.1+deb10u5-Debian <<>> -t A nginx.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: 57675
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

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

;; ANSWER SECTION:
nginx.default.svc.cluster.local. 30 IN	A	10.244.36.123
nginx.default.svc.cluster.local. 30 IN	A	10.244.36.122

;; Query time: 1 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sun Jul 11 00:34:04 UTC 2021
;; MSG SIZE  rcvd: 166

三、Statefulset管理pod

3.1、实现Pod的扩缩容

方式一:修改配置文件里的replicas,然后kubectl apply

方式二:kubectl edit sts web修改replicas

3.2、实现Pod的版本更新

方式一:修改配置文件里的image,然后kubectl apply

方式二:kubectl edit sts web修改image

上一篇:k8s-statefulset介绍


下一篇:Kubernetes-Controller(StatefulSet)-部署有状态应用