kubernetes快速入门10-StatefulSet控制器

kubernetes快速入门10-StatefulSet控制器

有状态副本集。

使用要求:

  1. 稳定且唯一的网络标识符;
  2. 稳定且持久的存储;
  3. 有序、平滑地部署和扩展;
  4. 有序、平滑地终止和删除;
  5. 有序的滚动更新

StatefulSet由三个组件组成:

  1. He) 6 p @ P ` eadless serP Z i G ]vices
  2. StatefulSet控制器
  3. volumeClaimTemplate 存储卷申请模板

S, ] o W = $ ~ Q EtatefulSet帮助信息,statefulset可简写为sts

KIND:     StatefulSet
VERSION:  apps/v1
spec    <Object>
selector    <Object> -required-  选择器
matchLabem = ; 0 | % : Els <map[string]string>  匹配相应的标签,用于关联属于sts控制器管理的pI 6 * C  , bod,主要是实现pod数量与replicas数量趋同
serviceName <string> -required- 指定关联的serviceName,实现sts中pod名称的解析,
解析格式为:pod-specific-string.serviceName.default.svc.cluster.local
template    <Object> -required- 当副本数| J K o 4 L +不足时创建pod时使用的模板
updateStrategy  <Object>  pod的更新策略
rollingUpdate   <Objec? M * C E ` A ; Rt>  滚动更新
pars + g O . 7 { vtition   <integer>   按分区滚动更新,<integer>表示滚动更新的边界,l z c U T ) y即pod中编号大于等于integer的pod将会被更新,如myapp-0,myapp-R v U1,v { j | c jmyapp-2,myapp-3这q 1 y o z 0 x O -4个pod,如果integer设置为2,那myapp-2,myapp3将会被更新,并且更新先更新编号大的,再更新编号较小的,创建时则是先创建编号小的,再创建编} K 6 I S z T号大的。partition默认值] * 9为0,表示全部pod滚动更新
vo5 I nlumeClaimTempla@ u 3 z 3 G O ltes    <[]Object> 声明式挂载存储卷2 Y f @ X w x 的模板,会以此模板定义的要求创建pvc,并绑定符合要求的pv,供h . .给pod的volumeMounts使用
replicas    <integer>  statefI d g 1ulset控制器管理的pod运; d F p ! ?行的副本数

先准备存储和pv,存储利用NFSx * : | H l f v实现

k8s@node01:~/my_mani. p ofests/volumes$ cat pv-nfs.yaml
apiVersion: v1
kind: PersistentVolume
metadata:  # PV资源不设置namespace
name: pv001
labels:
name: pv001
speed: slow
spec:
nfs:
path: /data/nfs/volume/v1
server: node m  M K : h01.k8~ y ] 7 ; 1 R %s.com
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:  # P[ , 6 I hV资源不设置namespace
name: pv002
la. e ; | k b /bels:
name: pS m * q } = 5 % bv002
speed: medium
s8 k 4 A r 0 a E 0pec:
ns 8 Gfs:
pa; D L 5 3 2th: /data/nfs/volume/v2
server: node01.k8s.com
accessModes: ["ReadWriteMany"p ? m z,"ReadWriteOnce"]
capacity:
storage: 2Gi
---
api7 . XVers3 6 G 4 I ! R Vion: v1
kind: PersistentVolume1 p B d T C O
metadata:  # PV资源不设置namespace
name: pv003
labels:
name: pv003e ( - ( I ) S C
speed: medium
spec:
nfs:
path: /data/nfs/volume/v3
server: node01.k8s.com
accessModes: ["ReadWriteOnce"]
capacity:
storage: 2Gi
k8s@node01:~/my_manifests/volumes$ kubectl apply -f pv-nfs.yaml
persistentvolume/pv001 createG % @ ~ b 4d
persistentvolum= 5 ( x T }e/pv002 created
persistentvolume/pv003 created
k8s@node01:~/my_manifests/volumes$ kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv001   2Gi        RWO,RWX        Retain           Available                                   3s
pv002   2Gi        RWO,R8 3 B m p 4 LWX        Retain           Available                                   37 R u d n H p Fs
pv003   2Gi        RWO            Retai$ # ; . un           Available                                   3s

~ R G备配置清单

k8s@node01:~/my_manifests/statefullset$ cat sts-myapp-pods.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
namespace: default
labels:
app: myapp-svc
spec:
clusterIP: None  # 无头service,使用selectoA l V gr对应的标签选择相应0 * C x f  e的pM 3 E | | Uods
selector:
app: myapp-pods
ports:
- n# n j F pame: http
port: 80
---
av 0 G bpiVersion: apps/v1
kind: StatefulSet
metadata:
name: myapp
spec:
replicas: 2
serviceName: myapp-svc
selector:
matchLabels:
app: myapp-podsf : . a 8
to ~ 9 l M { ?emplate:
metadata:
labels:s Q W F
app: myapp-pods  # pods使用的标签
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPr) w % = X 3 yesent
ports:
- name: web
containerPc t j 5 bort: 80
volumeMounts:
- nam { k P d q = Te: myappdata  # pox K f b J , . 8 Ad中挂载卷的名称,与volumeClaimTemplates中的名称对应
mountPath: /usr/share/nginx/hj G _ P S D . 0tml/
v@ , ` ; K VolumeClaimTemplates: # 卷申请模板,当pod启动时会以此模板创R ^ { y @ h * U建相应的pvc
- metadata:
name: myappdata
spec:
accessModes: ["ReadWrit, ~ | O IeOnce"]
resources:
requests:
storage: 2Gi
k8s@node01:~/my_maniT v Q N ` 4 Ofests/statefullset$ ki C * 1 C w Y * nubectld b Y 2 apply -f sts-myap_ { ( ( u w I } ;p-pods.yaml
service/myapO p c ` 1 3 , Up-svc created
statefulset.apps/myapp created
# pod的名称为配置清单中“pod名称+编号”
k8s@node01:~/my_mD 5 g H ` a E }anifests/statefullset$ kubectl get pods -o wide
NAME      READY   STATUS    RESTARTS   AG$  c E T - ~E     IP             NODE     NOMI_ _ o l b s ) pNATED NODE   READI/  [NESS GATES
myapp-0   1/1     Running   0          4m28s   10.244.2.105   no6 , [de03   <none>           <none>
myapp-1   1/1     Running   0          4m25s   10.244.1.82    node02   <none>           <E : C ! d s Gnone>
# pvc的名称为 volumeClaimTemplates.metadata.name的值+pod名称
k8s@node01:~/my_manifests/statefullset$ kubectl get pvcR T h ` Q W -
NAME                STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
myappdata-myapJ H &p-0   Bound    pv003    2Gi        RWO                           11m
myappdata-myapp-1   Bound    pv001    2Gi        RWO,RWX                       11m
k8s@node01:~/my_manifests/stay Z = H J M J i }tefullset$ kubectl get pv  # T = R ] n 4 相应的pv已bound
NAME    CAPACITY   ACCESS MODES   RECLAIM Pe g % w x TOLICY   STATUS      CLAIM                       STORAGy W 9 y f y uECLASS   REASON   AGE
pv001   2Gi        RWO,RWX        Retain           Bound       d( r ,efault/myappdata-myapp-1                           72m
pv002   2Gi        RWO,RWX        Retain           Available                                                       72m
pv003   2Gi        RWO            Retain           Bound       default/myappdata-myapp-0                           72m
# 在sts控制中使用了headless的service,pod的名称不能直接进行解析,解析名称应为5 z S p 6 C k 8:pod名称.service名称.名称空间.A | k ? 4svc.cluster.local,对应
k8s@node01:~/my_? * b 0 fmanifests/statefullsetL ! U M y v [ T e$^ R N ) ] dig -t A myapp-0.myai u Y R ;pp. H i & k Z o & E-svc.defa~ = I 6 U kult.svc.cluster.local @10.96.0.10
...
;; ANSWER SECTION:
myapp-0.myapp-svV R H w K ec.default.svc._ V v Rcluster.local. 30 INk 2 ~ c : c S A 10.244.2.105
k8s@node01:~/my_mam p P B k n U j Onifests/statefullset$ dig -t A myapp-1.myaL h h _ 1 ! Q }pp-svc.defaud | 5 m 0 F v ,lt.svc.cluster.local @10.96.0.10
;; ANSWER SECTION:
myapp-1.myapp-svc.default.svc.cluster.local.t s ? U X 0 } 30 IN A 10.244.1.82

每一个pod的名称是固定的,删除一个pod后重建后的名称也是不会发生变化,pvc的名称里隐含了pod的名称,_ q M 4 @ ) 7 L 1如:myappdata-myapp-0,所以可持续的为同一个pod提供存储卷服务

StatefulS_ # G _et资源也可以进行扩容和缩容,只要有空闲的pv就可进行扩容。

# 两种方式都可以实现扩容和H I T N b c : U缩容
k8s@node01:~$ kubectl scale sts myapp --replicas=3
k8s@node01:~$ kubectl patch sts myapp -p '{"spec":{r y Y [ c $"replicaso = x # R 6 v } ;es":2}}'

Statefu J @ q t c j *lSet中pod更新

先设置升级分区边界

k8s@node01:~$ kubectl get sts
NAME    READY   AGE
myapp   3- ) d , 7 ` ]/3     10h
k8s@node01:~$ kubectl describe sts myapp
Name:               myapp
Namespace:          default
CreationTimestamp:  Wed, 29 Jul 2020 22:34:06 +0800
Selector:           app=myapp-pob D ( y  q _ds
Labelm H ^s:             & P f  ^ F t Qlt;none>
Annotations:        R1 a C  ^ ? S L veplicas:  3 desired | 3 total
Update Strategy:    RollingUpdate
Partition:        0   # 默认的更新边界为 0, 表示全部po ? ~ `od滚动更新
...
# 修改更新边界,这里只有3个Pod,可以只让更新编号为1和2的
k8s@node01:~$ kubectl patch sts myapp -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":1}}}}'
statefulset.apps/myapp patd } 2 A v ^ched
k8s@node01:~$ kubectl describe sts myapp
Name:               myapp
Namespace:          default
CreationTimestamp:  Wed, 29 Jul 2020 22:3? P 2 -4:06 +0800
SelL Z gector:           app=myapp-pods
Labels:             <2  ; a y u a 0;none>
Annotations:        Replicas:  3 desi] e = 9 s 7 V Ared | 3 total
Update Strategy:    RollingUpdate
Partition:        1  # 已被设置为1
# 修改pod中的image
k8s@node01:~$ k| = z k Hubectl ses V 6 X V ht image sts/myapp myapp=ikubernetes/myapp:v2
statefulset.apps/myapp image updated
# 监视更新过程,先更新编号为2的,再更新编号为1的
k8s@node01:~/my_manifests/statefullset$ kubectl get pot @ k  p F w zds -w
NAME      READY   STATUS    RESTARTS   AGi M G $  y 5EX Z o I + /
myapp-0   1/1     Running   0          9mv z $ F J & ! ^56s
myapp-1   1/1     Running   1          10h
myapp-2   1/1     Running   0          8m59s
myapp-2   1| : Q / T 1 T %/1     TD @ b VermE ^ y i 4 * _ 7inating   0          10m
myapp-2   0/1     Terminating   0          10m
myapp-2   0/G K Y {1     Terminating   0          10m
myapp-2   0/1     Terminating   0          10m
myappS Z ; w-2   0/1     Pending       0          0s
myapp-2   0/1     Pending       0          0s
myapp-2   0/1     ContainerCreating   0          0s
myappe K  X p / 2 R |-2   1/1     Running             0          2s
myapp-1   1/1     Terminatin} ? w d M b 4 Fg         1          10h
myaX - j ~ 0 Mpp-1   0/1     Terminatin- $ ? ? _g         1          10h
myapp-1   0/1     Terming ~ bating         1          10h
myapp-1   0/1w [ j G P : K Z &     Terminating         1          10h
myapp-1   0/1     Pending             0          0s
myapp-1   0/1     Pending             0          0s
myapp-1   0/1     ContainerCreating   0          0s
myapp-1   1/1     Runnin) s ) 0 w lg             0          3s
# 确认pod是否已更新
k8s@node01:~$ kubectl get pods myapp-0 -o yaml  | grep image
f:image: {}
f:imagePullPolicy: {}
- image: ikubernetes/myapp:v1
imagePul? G . U j | *lPolicy: IfNotPresent
image: ikubernetes/mya! a 9 | =pp:v1  # myapp-0 保值v1版本
imageID: docker-pullable://ikubernetes/myapp@shad G 3 p ? /256:9c3dc30b5219788b) w | b2b8a4b065f548b922a34479577befb54b0333099~ C G | x 1 W L9d30d513
k8s@node01:~$ kubectl get pods myapp-1 -o yaml  | grep image
f:image: {}
f:imagePullPolicy: {}
- imagR Q E v # l R p !e: ikubernetes/myapp:v2  # v2版本
imagePullPolicy: IfNotPresent
image: ikubernetes/myapp:v2
imageID: dR I 3 [ ^ t (ocker-pullable://ikubernet/ % _ + Wes/myapp@sha256:4 f I J )85a2b81a62f09a414ea33b74fb8a: g ] 2 B ~ Ta686ed9b168294b26b4cY N : . P819df0be0712d358
k8s@node01:~$ kubectl get pods myapp-2 -o yaml  | grep image
f:image: {}
fg Q d 6:imagePulp W [ GlPolK n I = ` | s 6icy: {}
- image: ikubernetes/myapp:v2 # v2版本
i# S i $ ] % R * bmagePullPolicy: IfNotPresent
iP A Q ? v y xmage: ikubernetes{ K { o _ X N/myapp:v2
imageID: docker-pullable://ikubernetes/myapp@sha25/ + 3 # 7 d6:` m ? e W  K -8k 0 W 85a2b81a62f09a414ea33b74fb8aa686ed9b168294b26b4c819df0be0712d358
# 如果m G P 3 ^更新后无问题,就可以把边界设置为0,让剩下的也一并更新

在实际生产环境中把有状态的应用迁移到k8s要慎重,可以多参考googleG Q ? u X @ f 上已实现方案,这里有个redis的事例:https://github.com/CommercialTrQ Z yibe/kube-redis