【クラウドネイティブ | Kubernetesをゼロから学ぶ】 23. Kubernetesコントローラー Statefulset

この記事はコラム「ゼロから k8s を学ぶ」に含まれてます

ここに画像の説明を挿入

ステートフルセット コントローラー: 概念と原則の解釈

StatefulSet は、ステートフル サービスの問題を管理するように設計されています。

StatefulSet 内の Pod の場合、各 Pod は独自の独立したストレージをマウントします. Pod が失敗した場合、同じ名前の Pod が別のノードから開始され、元の Pod がマウントされているストレージは、その状態でサービスを提供し続けます.

ステートフル サービス?

StatefulSet は、ステートフル サービスを管理するステートフル コレクションであり、それが管理する Pod の名前は自由に変更できません。データ永続化ディレクトリも異なり、各 Pod には独自のデータ永続化ストレージ ディレクトリがあります。MySQL マスタースレーブ、redis クラスターなど。

  • 各 Pod を独立させる
  • 各 Pod を独立させ、Pod の起動順序と一意性を維持する
  • 一意のネットワーク識別子、永続ストレージ
  • mysql の master-slave などの整然とした

StatefulSet に適したビジネスには、データベース サービスの MySQL と PostgreSQL、クラスター化された管理サービス Zookeeper、etcd およびその他のステートフル サービスが含まれます。

StatefulSet のもう 1 つの典型的なアプリケーション シナリオは、通常のコンテナーよりも仮想マシンをシミュレートするためのより安定した信頼性の高いメカニズムです。従来の仮想マシンはステートフルなペットであり、オペレーターは常にそれを維持する必要があります. コンテナーが普及した当初、仮想マシンの使用をシミュレートするためにコンテナーを使用しました. すべての状態はコンテナーに格納されていました.信頼できない。

StatefulSet を使用すると、Pod は異なるノードにドリフトすることで高可用性を維持でき、ストレージは外部ストレージを介して高い信頼性を提供することもできます.StatefulSet が行うことは、特定の Pod を特定のストレージに関連付けて、状態の継続性を確保することです.

ステートレス サービス?

RC、Deployment、および DaemonSet はすべてステートレス サービスであり、それらが管理する Pod の IP、名前、開始および停止順序はランダムです。個々は全体に影響を与えません. すべてのポッドはデータ ボリュームを共有します. デプロイされた tomcat はステートレス サービスです. tomcat が削除された場合, tomcat の名前に関係なく、新しい tomcat を起動してクラスターに参加します.

  • ポッドはすべて同じだと思います
  • 注文不要
  • アプリケーションが実行されているノードに関係なく
  • 自由にスケーリングおよび拡張する機能

StatefulSet は次の部分で構成されます。

1. ヘッドレス サービス: ポッド ネットワーク識別子を定義し、解決可能な DNS レコードを生成するために使用されます。

2. volumeClaimTemplates: ストレージ ボリューム アプリケーション テンプレート、PVC の作成、PVC の名前とサイズの指定、PVC の自動作成、PVC はストレージ クラスによって提供されます。

3.StatefulSet: ポッドを管理します

ヘッドレスサービスとは

ヘッドレス サービスは clusterIP を割り当てません。ヘッドレス サービスは、サービスの DNS を解析することによって、すべてのポッドの dns と IP アドレスを返すことができます (statefulSet によってデプロイされたポッドには DNS があります)。通常のサービスの場合、サービスの ClusterIP は解析によってのみ返すことができます。サービスの DNS。

ヘッドレス サービス (サービス IP のないサービス) を使用する理由

Deployment を使用する場合、作成された Pod 名は順序付けされませんが、ランダムな文字列です. Statefulset を使用して Pod を管理する場合、Pod 名は順序付けする必要があり、各 Pod を自由に置き換えることはできません. Pod が再構築された後、Pod 名はそのままです.同じです。ポッドの IP は変更されるため、ポッド名を使用してそれらを識別します。ポッド名はポッドの一意の識別子であり、永続的で有効である必要があります。これは、各ポッドに一意の名前を付けることができるヘッドレス サービスが使用される場所です。

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
如 主机名是 paopao 域名是 csdn.com
FQDN= paopao.csdn.com

2.StatefulSet 会为关联的 Pod 保持一个不变的 Pod Name
statefulset 中 Pod 的名字格式为$(StatefulSet name)-$(pod 序号)

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

volumeClaimTemplate を使用する理由

永続ストレージは、mysql マスター/スレーブなどのステートフル アプリケーションに使用されます. マスター/スレーブ データベースのデータはディレクトリに保存できないため、各 mysql ノードには独自の独立したストレージ スペースが必要です. デプロイで作成されたストレージ ボリュームは共有ストレージ ボリュームであり、複数のポッドが同じストレージ ボリュームを使用し、それらのデータが同期されます。statefulset 定義の各ポッドは同じストレージ ボリュームを使用できません。これには、volumeClainTemplate の使用が必要です。 statefulset を使用して Pod を作成すると、volumeClainTemplate は PVC を自動的に生成して PV のバインドを要求します。各 Pod には専用のストレージ ボリュームがあります。Pod、PVC、および PV に対応する関係図は次のとおりです。

ここに画像の説明を挿入

Statefulset リソース マニフェスト ファイルの記述スキル

#查看定义 Statefulset 资源需要的字段
[root@k8smaster ~]# kubectl explain statefulset
KIND:     StatefulSet
VERSION:  apps/v1

DESCRIPTION:
     StatefulSet represents a set of pods with consistent identities. Identities
     are defined as: - Network: A single stable DNS and hostname. - Storage: As
     many VolumeClaims as requested. The StatefulSet guarantees that a given
     network identity will always map to the same storage identity.
FIELDS:
   apiVersion <string> #定义 statefulset 资源需要使用的 api 版本
   kind <string> #定义的资源类型
   metadata<Object> #元数据
   spec <Object> #定义容器相关的信息
#查看 statefulset.spec 字段如何定义?
[root@k8smaster ~]#  kubectl explain statefulset.spec
KIND:     StatefulSet
VERSION:  apps/v1

RESOURCE: spec <Object>

DESCRIPTION:
     Spec defines the desired identities of pods in this set.

     A StatefulSetSpec is the specification of a StatefulSet.
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@k8smaster ~]# kubectl explain statefulset.spec.template
KIND:     StatefulSet
VERSION:  apps/v1

RESOURCE: template <Object>

DESCRIPTION:
     template is the object that describes the pod that will be created if
     insufficient replicas are detected. Each pod stamped out by the StatefulSet
     will fulfill this Template, but have a unique identity from the rest of the
     StatefulSet.

     PodTemplateSpec describes the data a pod should have when created from a
     template

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

上記のように、statefulset リソースには 2 つの仕様フィールドがあります。

最初の仕様は、ステートフル セットが定義する Pod レプリカの数 (デフォルトでは 1 つの Pod のみがデプロイされます)、Pod ラベルに一致するセレクター、Pod を作成するためのテンプレート、およびストレージ ボリューム リクエスト テンプレートを宣言します。

2 つ目の仕様は spec.template.spec です。主に Pod のコンテナ プロパティなどの設定に使用されます。.spec.template の内容は、Pod オブジェクトを宣言する際に定義する各種プロパティであるため、この部分も PodTemplate (Pod Template) と呼ばれます。

注目に値するもう 1 つの点: .spec.selector で定義されたラベル セレクターは、spec.template.metadata.labels で定義された Pod ラベルと一致できる必要があります。一致しない場合、Kubernetes はステートフル セットの作成を許可しません。

ステートフル セットのユース ケース: Web サイトのデプロイ

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

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

#编写一个 Statefulset 资源清单文件
[root@k8smaster state]# vim 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
        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@k8smaster state]# kubectl apply -f statefulset.yaml 
service/nginx created
statefulset.apps/web created
 #查看 statefulset 是否创建成功
[root@k8smaster state]# kubectl get sts -o wide
NAME   READY   AGE   CONTAINERS   IMAGES
web    2/2     59s   nginx        nginx

#查看 pod
[root@k8smaster state]# kubectl get pods -l app=nginx
NAME                    READY   STATUS    RESTARTS   AGE
web-0                   0/1     Pending   0          76s
web-1                   1/1     Pending   0          76s

#通过上面可以看到创建的 pod 是有序的
#查看 headless service
[root@k8smaster state]# kubectl get svc -l app=nginx
NAME    TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
nginx   ClusterIP  None           <none>        80/TCP   	   3m
#查看 pvc
[root@k8smaster state]# kubectl get pvc
www-web-0 Bound pvc-13a0482f-4927-63ff-9f2e-1b926b299c6f 1Gi RWO,RWX nfs-web 4m45s
www-web-1 Bound pvc-bc23p2a9-5bjd-00df-829f-ccb4d24g01h1 1Gi RWO,RWX nfs-web 4m41s
#查看 pv
[root@k8smaster state]# kubectl get pv
pvc-13a0482f-4927-63ff-9f2e-1b926b299c6f 1Gi RWO,RWX Delete Bound default/www-web-0 nfs-web 5m3s
pvc-bc23p2a9-5bjd-00df-829f-ccb4d24g01h1 1Gi RWO,RWX Delete Bound default/www-web-1 nfs-web 5m59s

#在data nfs_pro目录下划分的pv

#查看 pod 主机名
[root@k8smaster ~]# 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@k8smaster ~]# 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.103.139    #解析到的是 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.103.139
Name: nginx.default.svc.cluster.local
Address: 10.244.121.93

[root@k8smaster state]# kubectl describe svc nginx
Name:                     nginx
Namespace:                default
Labels:                   app=nginx
Annotations:              <none>
Type:                     ClusterIP
IP:                       none
Port:                     web  80/TCP
TargetPort:               80/TCP
Endpoints:                10.244.103.139:80,10.244.121.93:80		#这两个pod都有app=ngin的标签,service把这两个都写入endpoints列表了
Session Affinity:         None
Events:                   <none>

root@web-1:/# dig -t A nginx.default.svc.cluster.local @10.96.0.10	
; <<>> DiG 9.11.5-P4-5.1+deb10u3-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: 16869
;; 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: 1cf973a5daa99ac7 (echoed)
;; QUESTION SECTION:
;nginx.default.svc.cluster.local. IN A

;; ANSWER SECTION:
nginx.default.svc.cluster.local. 30 IN A 10.244.103.139
nginx.default.svc.cluster.local. 30 IN A 10.244.121.93

;; Query time: 0 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Thu Jul 14 06:49:51 UTC 2022
;; MSG SIZE rcvd: 166

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

Name:	kubernetes.default.svc.cluster.local
Address: 10.96.0.1

#解析kubernertes,有ip会把ip解析出来,没有会解析后端pod

dig 的使用
dig -t A nginx.default.svc.cluster.local @10.96.0.10

格式如下:
@来指定域名服务器
A 为解析类型 ,A 记录
-t 指定要解析的类型
A 记录:
A 记录是解析域名到 IP

资源清单详细解读:
apiVersion: v1 #定义 api 版本
kind: Service #定义要创建的资源:service
metadata: 
name: nginx #定义 service 的名字
labels:
app: nginx #service 的标签
spec:
ports:
- port: 80
name: web
clusterIP: None #创建一个没有 ip 的 service
selector:
app: nginx #选择拥有 app=nginx 标签的 pod
---
apiVersion: apps/v1
kind: StatefulSet
metadata: 
name: web
spec: 
selector:
matchLabels:
app: nginx
serviceName: "nginx" #headless service 的名字
replicas: 2 #副本数
template: #定义 pod 的模板
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" #指定从哪个存储类申请 pv
resources:
requests: 
storage: 1Gi #需要 1G 的 pvc,会自动跟符合条件的 pv 绑定

扩展:
service 和 headless service 区别:
#deployment 创建的 pod 是随机生成的 解析的是 service 的 ip 地址

Statefulset は Pod を管理します: スケーリング、スケーリング、更新

#Statefulset 实现 pod 的动态扩容
如果我们觉得两个副本太少了,想要增加,只需要修改配置文件 statefulset.yaml 里的 replicas的值即可,原来 replicas: 2,现在变成 replicaset: 3,修改之后,执行如下命令更新:
[root@k8smaster state]# kubectl apply -f statefulset.yaml 
service/nginx unchanged
statefulset.apps/web configured
[root@k8smaster state]# kubectl get sts
web 	3/3 	30m
[root@k8smaster state]# 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@xianchaomaster1 ~]# kubectl edit sts web
#这个是我们把请求提交给了 apiserver,实时修改,把 spec 下的 replicas 后面的值改成 4,保存退出
[root@xianchaomaster1 ~]# 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@k8smaster state]# kubectl apply -f statefulset.yaml
service/nginx unchanged
statefulset.apps/web configured
[root@k8smaster state]# kubectl get pods -l app=nginx
NAME	 READY 		STATUS		 RESTARTS 		AGE
web-0 	 1/1 		Running 	 0				64m
web-1 	 1/1 		Running 	 0 				63m

#Statefulset 实现 pod 的更新
[root@k8smaster state]# kubectl edit sts web
#修改镜像 nginx 变成 image: tomcat,修改之后保存退出 
[root@k8smaster state]# kubectl get pods -o wide -l app=nginx
NAME            READY   STATUS    RESTARTS   AGE   IP             		NODE       
web-0 			1/1 	Running   0 	 	 18s   10.244.201.106 		k8snode 
web-1 			1/1 	Running   0 	 	 36s   10.244.133.176 		k8snode2
#查看 pod 详细信息
[root@k8smaster state]# kubectl describe pods web-0
通过上面可以看到 pod 已经使用刚才更新的镜像 tomcat 了

最後に書く

作成するのは簡単ではありません。コンテンツが役立つと思われる場合は、3 つのリンクをフォローしてサポートしてください。間違いがあればコメントで指摘していただければ修正します!
現在更新中のシリーズ:ゼロからk8sを学ぶ ご覧
いただきありがとうございます 記事には個人的な理解が混在しています 誤りがある場合は、私に連絡して指摘してください〜

おすすめ

転載: blog.csdn.net/qq_45400861/article/details/127035455