Kubernetes之StatefulSet詳解


本篇是基於k8s-v1.19.0版本

1.介紹

  • RC、Deployment、DaemonSet都是面向無狀態的服務,它們所管理的Pod的IP、名字,啟停順序等都是隨機的,而StatefulSet是什么?顧名思義,有狀態的集合,管理所有有狀態的服務,比如MySQL、MongoDB集群等。
  • StatefulSet本質上是Deployment的一種變體,在v1.9版本中已成為GA版本,它為了解決有狀態服務的問題,它所管理的Pod擁有固定的Pod名稱,啟停順序,在StatefulSet中,Pod名字稱為網絡標識(hostname),還必須要用到共享存儲。
  • 在Deployment中,與之對應的服務是service,而在StatefulSet中與之對應的headless service,即無頭服務,與service的區別就是它沒有Cluster IP,解析它的名稱時將返回該Headless Service對應的全部Pod的Endpoint列表。
  • 除此之外,StatefulSet在Headless Service的基礎上又為StatefulSet控制的每個Pod副本創建了一個DNS域名,這個域名的格式為: $(podname).(headless server name) FQDN:$(podname).(headless server name).namespace.svc.cluster.local

 

2.特點

Pod一致性:包含次序(啟動、停止次序)、網絡一致性。此一致性與Pod相關,與被調度到哪個node節點無關;

  • 穩定的次序:對於N個副本的StatefulSet,每個Pod都在[0,N)的范圍內分配一個數字序號,且是唯一的;
  • 穩定的網絡:Pod的hostname模式為( StatefulSet名稱 ) − (statefulset名稱)-(statefulset名稱)−(序號);
  • 穩定的存儲:通過VolumeClaimTemplate為每個Pod創建一個PV。刪除、減少副本,不會刪除相關的卷。

 

3.組成部分

  • Headless Service:用來定義Pod網絡標識( DNS domain);
  • volumeClaimTemplates :存儲卷申請模板,創建PVC,指定pvc名稱大小,將自動創建pvc,且pvc必須由存儲類供應;
  • StatefulSet :定義具體應用,名為Nginx,有三個Pod副本,並為每個Pod定義了一個域名部署statefulset。

 

1)為什么需要 headless service 無頭服務?

在用Deployment時,每一個Pod名稱是沒有順序的,是隨機字符串,因此是Pod名稱是無序的,但是在statefulset中要求必須是有序 ,每一個pod不能被隨意取代,pod重建后pod名稱還是一樣的。而pod IP是變化的,
所以是以Pod名稱來識別。pod名稱是pod唯一性的標識符,必須持久穩定有效。這時候要用到無頭服務,它可以給每個Pod一個唯一的名稱 。

 

 

2)為什么需要volumeClaimTemplate?

對於有狀態的副本集都會用到持久存儲,對於分布式系統來講,它的最大特點是數據是不一樣的,所以各個節點不能使用同一存儲卷,每個節點有自已的專用存儲,但是如果在Deployment中的Pod template里定義的存儲卷,
是所有副本集共用一個存儲卷,數據是相同的,因為是基於模板來的 ,而statefulset中每個Pod都要自已的專有存儲卷,所以statefulset的存儲卷就不能再用Pod模板來創建了,於是statefulSet使用volumeClaimTemplate,稱為卷申請模板,
它會為每個Pod生成不同的pvc,並綁定pv,從而實現各pod有專用存儲。這就是為什么要用volumeClaimTemplate的原因。

4.StatefulSet詳解

  • kubectl explain sts.spec :主要字段解釋
  • replicas :副本數
  • selector:那個pod是由自己管理的
  • serviceName:必須關聯到一個無頭服務商
  • template:定義pod模板(其中定義關聯那個存儲卷)
  • volumeClaimTemplates :生成PVC

5.部署一個statefulset服務

本教程假設你的集群被配置為動態的提供 PersistentVolume,動態storeclass參考https://www.cnblogs.com/wuxinchun/p/15266445.html;如果沒有這樣配置,在開始本教程之前,你需要手動准備存儲卷。
如果集群中沒有StorageClass的動態供應PVC的機制,也可以提前手動創建多個PV、PVC,手動創建的PVC名稱必須符合之后創建的StatefulSet命名規則:(volumeClaimTemplates.name)-(pod_name)

5.1statefulset控制器創建nginx

[root@k8s-master statefulset]# pwd
/root/k8s_practice/statefulset
[root@k8s-master statefulset]# cat nginx-statefulset.yaml 
apiVersion: v1
kind: Service
metadata:
  name: nginx-headless
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None   #注意此處的值,None表示無頭服務
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx  #has to match .spec.template.metadata.labels
  serviceName: "nginx-headless"
  replicas: 3  #兩個副本
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: ikubernetes/myapp:v1
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
      annotations:
        volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"   #managed-nfs-storage為我們創建的storage-class名稱,動態storeclass參考https://www.cnblogs.com/wuxinchun/p/15266445.html
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
[root@k8s-master statefulset]# kubectl apply -f nginx-statefulset.yaml 
service/nginx-headless created
statefulset.apps/web created

5.2驗證解析

 1)每個 Pod 都擁有一個基於其順序索引的穩定的主機名

[root@k8s-master statefulset]# kubectl get pods 
NAME                                      READY   STATUS    RESTARTS   AGE
dns-test                                  1/1     Running   0          87m
nfs-client-provisioner-677fc9c97c-9cj92   1/1     Running   2          7h44m
web-0                                     1/1     Running   0          53s
web-1                                     1/1     Running   0          50s
web-2                                     1/1     Running   0          46s
[root@k8s-master statefulset]# kube
kubeadm  kubectl  kubelet  
[root@k8s-master statefulset]# kubectl exec -it web-0 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # hostname 
web-0
/ # exit
[root@k8s-master statefulset]# kubectl exec -it web-1 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # hostname 
web-1
/ # exit
[root@k8s-master statefulset]# kubectl exec -it web-2 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # hostname 
web-2
/ # exit 

 2)使用 kubectl run 運行一個提供 nslookup 命令的容器,該命令來自於 dnsutils 包。通過對 Pod 的主機名執行 nslookup,你可以檢查他們在集群內部的 DNS 地址

[root@k8s-master statefulset]# kubectl run -i --tty --image busybox:1.28.4 dns-test1 --restart=Never --rm  #創建dns-test測試pod
If you don't see a command prompt, try pressing enter.
/ # nslookup web-0
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

nslookup: can't resolve 'web-0'
/ # nslookup web-1
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

nslookup: can't resolve 'web-1'
/ # nslookup web-2
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

nslookup: can't resolve 'web-2'
/ # nslookup nginx-headless
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      nginx-headless   #無頭service服務名,下面是代理的三個pod地址及其DNS域名
Address 1: 10.244.1.32 web-2.nginx-headless.default.svc.cluster.local
Address 2: 10.244.2.29 web-0.nginx-headless.default.svc.cluster.local
Address 3: 10.244.1.31 web-1.nginx-headless.default.svc.cluster.local
/ # 

3)刪除pod,自動拉起的pod ip地址會變,但是主機名和域名是不變的

[root@k8s-master statefulset]# kubectl delete pod web-0 web-1 web-2  #刪除pod
pod "web-0" deleted
pod "web-1" deleted
pod "web-2" deleted
[root@k8s-master statefulset]# kubectl get pod  #pod自動拉起
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-677fc9c97c-9cj92   1/1     Running   2          7h54m
web-0                                     1/1     Running   0          23s
web-1                                     1/1     Running   0          21s
web-2                                     1/1     Running   0          20s
[root@k8s-master statefulset]# kubectl run -i --tty --image busybox:1.28.4 dns-test1 --restart=Never --rm
If you don't see a command prompt, try pressing enter.
/ # nslookup nginx-headless
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      nginx-headless  #如下查看,pod的IP地址變化了,主機名和dns域名無變化(網絡唯一標識)
Address 1: 10.244.2.31 web-0.nginx-headless.default.svc.cluster.local
Address 2: 10.244.1.33 web-1.nginx-headless.default.svc.cluster.local
Address 3: 10.244.2.32 web-2.nginx-headless.default.svc.cluster.local

5.3寫入穩定的存儲

1)將 Pod 的主機名寫入它們的index.html文件並驗證 NGINX web 服務器使用該主機名提供服務

[root@k8s-master statefulset]# kubectl exec -it web-0 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # echo $(hostname) > /usr/share/nginx/html/index.html
/ # exit
[root@k8s-master statefulset]# kubectl exec -it web-1 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # echo $(hostname) > /usr/share/nginx/html/index.html
/ # exit
[root@k8s-master statefulset]# kubectl exec -it web-2 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # echo $(hostname) > /usr/share/nginx/html/index.html
/ # exit

  2)刪除pod,重新調度后,依然掛載原先PV

[root@k8s-master statefulset]# kubectl get pod -o wide
NAME                                      READY   STATUS    RESTARTS   AGE     IP            NODE        NOMINATED NODE   READINESS GATES
nfs-client-provisioner-677fc9c97c-9cj92   1/1     Running   2          8h      10.244.1.18   k8s-node1   <none>           <none>
web-0                                     1/1     Running   0          6m59s   10.244.2.31   k8s-node2   <none>           <none>
web-1                                     1/1     Running   0          6m57s   10.244.1.33   k8s-node1   <none>           <none>
web-2                                     1/1     Running   0          6m56s   10.244.2.32   k8s-node2   <none>           <none>
[root@k8s-master statefulset]# curl 10.244.2.31
web-0
[root@k8s-master statefulset]# curl 10.244.1.33
web-1
[root@k8s-master statefulset]# curl 10.244.2.32
web-2
[root@k8s-master statefulset]# kubectl delete pod web-0 web-1 web-2
pod "web-0" deleted
pod "web-1" deleted
pod "web-2" deleted
[root@k8s-master statefulset]# kubectl get pod -o wide
NAME                                      READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
nfs-client-provisioner-677fc9c97c-9cj92   1/1     Running   2          8h    10.244.1.18   k8s-node1   <none>           <none>
web-0                                     1/1     Running   0          14s   10.244.2.33   k8s-node2   <none>           <none>
web-1                                     1/1     Running   0          12s   10.244.1.35   k8s-node1   <none>           <none>
web-2                                     1/1     Running   0          11s   10.244.2.34   k8s-node2   <none>           <none>
[root@k8s-master statefulset]# curl 10.244.2.33
web-0
[root@k8s-master statefulset]# curl 10.244.1.35
web-1
[root@k8s-master statefulset]# curl 10.244.2.34
web-2
[root@k8s-master statefulset]# 

雖然 web-0 、web-1 和web-2被重新調度了,但它們仍然繼續監聽各自的主機名,因為和它們的 PersistentVolumeClaim 相關聯的 PersistentVolume 被重新掛載到了各自的 volumeMount 上。不管 web-0、web-1、web-3 被調度到了哪個節點上,它們的 PersistentVolumes 將會被掛載到合適的掛載點上

5.4擴容/縮容 StatefulSet

 擴容/縮容StatefulSet 指增加或減少它的副本數。這通過更新replicas字段完成。你可以使用kubectl scale 或者kubectl patch來擴容/縮容一個 StatefulSet。

kubectl scale sts web --replicas=4 -n nginx-ss   #擴容
kubectl scale sts web --replicas=2 -n nginx-ss   #縮容

 或者

kubectl patch sts web -p '{"spec":{"replicas":4}}' -n nginx-ss  #擴容
kubectl patch sts web -p '{"spec":{"replicas":2}}' -n nginx-ss  #縮容

6.案例(部署mysql)

 1)yaml文件

[root@k8s-master statefulset]# pwd
/root/k8s_practice/statefulset
[root@k8s-master statefulset]# cat mysql-sts.yaml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql #必須匹配 .spec.template.metadata.labels
  serviceName: "mysql"  #聲明它屬於哪個Headless Service.
  replicas: 3 #副本數
  template:
    metadata:
      labels:
        app: mysql # 必須配置 .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: mysql
        image: mysql:5.7
        ports:
        - containerPort: 3306
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "123456"
        volumeMounts:
        - name: mysql-pvc
          mountPath: /var/lib/mysql

  volumeClaimTemplates:   #可看作pvc的模板
  - metadata:
      name: mysql-pvc
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "managed-nfs-storage"  #存儲類名,改為集群中已存在的
      resources:
        requests:
          storage: 1Gi

2)statefulset創建mysql

[root@k8s-master statefulset]# kubectl apply -f mysql-sts.yaml 
statefulset.apps/mysql created
[root@k8s-master statefulset]# kubectl get pod 
NAME                                      READY   STATUS    RESTARTS   AGE
mysql-0                                   1/1     Running   0          2m1s
mysql-1                                   1/1     Running   0          116s
mysql-2                                   1/1     Running   0          110s

如果創建mysql報錯:chown: changing ownership of ‘/var/lib/mysql/‘: Operation not permitted

解決方案:在NFS做共享時,添加no_root_squash權限后,重啟rpcbind和nfs服務即可

/nfsdata/volumes/ 10.3.104.0/21(rw,sync,no_root_squash)

 3)訪問測試

[root@k8s-master statefulset]# kubectl exec -it mysql-0 sh  #進入mysql-0 pod
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
# mysql -uroot -p123456   #yaml文件定義root密碼為123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.35 MySQL Community Server (GPL)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.01 sec)

mysql> 

 4)mysql存儲卷

[root@k8s-master volumes]# pwd
/nfsdata/volumes
[root@k8s-master volumes]# ll
total 24
drwxrwxrwx 5 polkitd root 4096 Sep 14 19:35 default-mysql-pvc-mysql-0-pvc-3586c119-3d58-426a-8bbf-a43c6220b27d
drwxrwxrwx 5 polkitd root 4096 Sep 14 19:35 default-mysql-pvc-mysql-1-pvc-c2b8838a-ab13-413b-8b80-99fd5d4e5840
drwxrwxrwx 5 polkitd root 4096 Sep 14 19:35 default-mysql-pvc-mysql-2-pvc-614539b7-a3e1-450f-a643-ca980cdb73ff
[root@k8s-master volumes]# cd default-mysql-pvc-mysql-0-pvc-3586c119-3d58-426a-8bbf-a43c6220b27d/
[root@k8s-master default-mysql-pvc-mysql-0-pvc-3586c119-3d58-426a-8bbf-a43c6220b27d]# ll
total 188476
-rw-r----- 1 polkitd ssh_keys       56 Sep 14 19:35 auto.cnf
-rw------- 1 polkitd ssh_keys     1680 Sep 14 19:35 ca-key.pem
-rw-r--r-- 1 polkitd ssh_keys     1112 Sep 14 19:35 ca.pem
-rw-r--r-- 1 polkitd ssh_keys     1112 Sep 14 19:35 client-cert.pem
-rw------- 1 polkitd ssh_keys     1680 Sep 14 19:35 client-key.pem
-rw-r----- 1 polkitd ssh_keys     1359 Sep 14 19:35 ib_buffer_pool
-rw-r----- 1 polkitd ssh_keys 79691776 Sep 14 19:35 ibdata1
-rw-r----- 1 polkitd ssh_keys 50331648 Sep 14 19:35 ib_logfile0
-rw-r----- 1 polkitd ssh_keys 50331648 Sep 14 19:35 ib_logfile1
-rw-r----- 1 polkitd ssh_keys 12582912 Sep 14 19:35 ibtmp1
drwxr-x--- 2 polkitd ssh_keys     4096 Sep 14 19:35 mysql
drwxr-x--- 2 polkitd ssh_keys     4096 Sep 14 19:35 performance_schema
-rw------- 1 polkitd ssh_keys     1676 Sep 14 19:35 private_key.pem
-rw-r--r-- 1 polkitd ssh_keys      452 Sep 14 19:35 public_key.pem
-rw-r--r-- 1 polkitd ssh_keys     1112 Sep 14 19:35 server-cert.pem
-rw------- 1 polkitd ssh_keys     1676 Sep 14 19:35 server-key.pem
drwxr-x--- 2 polkitd ssh_keys    12288 Sep 14 19:35 sys

 注:三個mysql的pod數據是相互獨立的

5)創建service使用Nodeport外部訪問

[root@k8s-master statefulset]# pwd
/root/k8s_practice/statefulset
[root@k8s-master statefulset]# cat mysql-svc.yaml 
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  type: NodePort
  ports:
  - port: 3306
    nodePort: 30001
    name: mysql
  selector:
    app: mysql

[root@k8s-master statefulset]# kubectl delete -f mysql-svc.yaml 
service "mysql" deleted
[root@k8s-master statefulset]# vim mysql-svc.yaml 
[root@k8s-master statefulset]# kubectl apply -f mysql-svc.yaml 
service/mysql created
[root@k8s-master statefulset]# kubectl get svc
NAME             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
kubernetes       ClusterIP   10.96.0.1      <none>        443/TCP          7d8h
mysql            NodePort    10.104.90.83   <none>        3306:30001/TCP   3s
nginx-headless   ClusterIP   None           <none>        80/TCP           48m
[root@k8s-master statefulset]# kubectl get ep
NAME              ENDPOINTS                                            AGE
kubernetes        10.3.104.51:6443                                     7d8h
mysql             10.244.1.36:3306,10.244.1.37:3306,10.244.2.35:3306   6s
nginx-headless    10.244.1.35:80,10.244.2.33:80,10.244.2.34:80         48m
wxc-nfs-storage   <none>                                               8h
[root@k8s-master statefulset]# netstat -antpl | grep 30001
tcp        0      0 0.0.0.0:30001           0.0.0.0:*               LISTEN      32270/kube-proxy   

 

 

 注:上述代表通過Nodeport端口訪問mysql成功


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM