StatefulSet是为了管理有状态服务的问题而设计的.
扩展:
有状态服务?
StatefulSet是有状态的集合,管理有状态的服务,它所管理的Pod的名称不能随意变化。数据持久化的目录也是不一样,每一个Pod都有自己独有的数据持久化存储目录。比如MySQL主从、redis集群等。
无状态服务?
RC、Deployment、DaemonSet都是管理无状态的服务,它们所管理的Pod的IP、名字,启停顺序等都是随机的。个体对整体无影响,所有pod都是共用一个数据卷的,部署的tomcat就是无状态的服务,tomcat被删除,在启动一个新的tomcat,加入到集群即可,跟tomcat的名字无关。
[root@master ~]# 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> # 定义资源需要使用的api版本
kind <string> # 定义资源类型
metadata <Object> # 定义元数据
spec <Object> # 定义容器相关的信息
[root@master ~]# 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> # 存储卷申请模板
对于template而言,其内部定义的就是pod,pod模板是一个独立的对象。
.spec.template里的内容是声明Pod对象时要定义的各种属性,所以这部分也叫做PodTemplate(Pod模板)
[root@master ~]# kubectl explain statefulset.spec.template
KIND: StatefulSet
VERSION: apps/v1
RESOURCE: template <Object>
DESCRIPTION:
......
FIELDS:
metadata <Object> # 定义多少个Pod副本(默认将仅部署1个Pod)、匹配Pod标签的选择器、创建pod的模板、存储卷申请模板
spec <Object> # Pod里的容器属性等配置
# 在spec.template.spec.selector中定义的标签选择器必须能够匹配到spec.template.metadata.labels里定义的Pod标签,否则Kubernetes将不允许创建statefulset。
[root@master 12]# cat statefulset.yaml
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:
metadata: # 定义pod的模板
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" # 指定从哪个存储类申请pv
resources:
requests:
storage: 1Gi # 需要1G的pvc,会自动跟符合条件的pv绑定
[root@master 12]# kubectl apply -f statefulset.yaml
service/nginx created
statefulset.apps/web created
# 查看statefulset是否创建成功
[root@master 12]# kubectl get statefulset
NAME READY AGE
web 2/2 2m7s
# 查看pod
[root@master 12]# kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2m50s
web-1 1/1 Running 0 2m8s
# 通过上面可以看到创建的pod是有序的
[root@master 12]# kubectl get svc -l app=nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP None <none> 80/TCP 3m50s
[root@master 12]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound pvc-223fc042-1b1b-4516-a1b5-a5572434d3ee 1Gi RWO nfs 4m8s
www-web-1 Bound pvc-b4f026b2-4984-4302-b45d-00c57a74a162 1Gi RWO nfs 3m26s
[root@master 12]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-223fc042-1b1b-4516-a1b5-a5572434d3ee 1Gi RWO Delete Bound default/www-web-0 nfs 4m39s
pvc-b4f026b2-4984-4302-b45d-00c57a74a162 1Gi RWO Delete Bound default/www-web-1 nfs 3m56s
[root@master 12]# for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname';done
web-0
web-1
用来定义pod网路标识,生成可解析的DNS记录
Headless service不分配clusterIP,headless service可以通过解析service的DNS,返回所有Pod的dns和ip地址 (statefulSet部署的Pod才有DNS),普通的service,只能通过解析service的DNS返回service的ClusterIP。
<service name>.$<namespace name>.svc.cluster.local
K8s中资源的全局FQDN格式:
Service_NAME.NameSpace_NAME.Domain.LTD.
Domain.LTD.=svc.cluster.local #这是默认k8s集群的域名。
扩展:FQDN
FQDN 全称 Fully Qualified Domain Name
即全限定域名:同时带有主机名和域名的名称
FQDN = Hostname + DomainName
如 主机名是 xianchao 域名是 baidu.com
FQDN= xianchao.baidu.com
statefulset中Pod的名字格式为$(StatefulSet name)-$(pod序号)
$<Pod Name>.$<service name>.$<namespace name>.svc.cluster.local
为什么要用volumeClaimTemplate?
对于有状态应用都会用到持久化存储,比如mysql主从,由于主从数据库的数据是不能存放在一个目录下的,每个mysql节点都需要有自己独立的存储空间。
而在deployment中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,它们数据是同步的,而statefulset定义中的每一个pod都不能使用同一个存储卷,这就需要使用volumeClainTemplate。
当在使用statefulset创建pod时,volumeClainTemplate会自动生成一个PVC,从而请求绑定一个PV,每一个pod都有自己专用的存储卷。
Pod、PVC和PV对应的关系图如下:
使用kubectl run运行一个提供nslookup命令的容器,这个命令来自于dnsutils包,通过对pod主机名执行nslookup,可以检查它们在集群内部的DNS地址
kubectl run busybox --image docker.io/library/busybox:1.28 --image-pull-policy=IfNotPresent --restart=Never --rm -it busybox -- sh
If you don't see a command prompt, try pressing enter.
/ # nslookup web-0.nginx.default.svc.cluster.local
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-0.nginx.default.svc.cluster.local # 查询service dns,会把对应的pod ip解析出来
Address 1: 10.244.196.144 web-0.nginx.default.svc.cluster.local
如果我们觉得两个副本太少了,想要增加,只需要修改配置文件statefulset.yaml里的replicas的值即可,原来replicas: 2,现在变成replicaset: 3,修改之后,执行如下命令更新:
[root@master 12]# kubectl apply -f statefulset.yaml
service/nginx unchanged
statefulset.apps/web configured
[root@master 12]# kubectl get sts
NAME READY AGE
web 3/3 80m
[root@master 12]# kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 81m
web-1 1/1 Running 0 80m
web-2 1/1 Running 0 28s
也可以直接编辑控制器实现扩容,这个是我们把请求提交给了apiserver,实时修改
[root@master 12]# kubectl edit sts web # 副本改成4
statefulset.apps/web edited
[root@master 12]# kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 82m
web-1 1/1 Running 0 81m
web-2 1/1 Running 0 90s
web-3 1/1 Running 0 7s
kubectl explain sts.spec.updateStrategy
[root@master 12]# kubectl explain sts.spec.updateStrategy
KIND: StatefulSet
VERSION: apps/v1
RESOURCE: updateStrategy <Object>
DESCRIPTION:
updateStrategy indicates the StatefulSetUpdateStrategy that will be
employed to update Pods in the StatefulSet when a revision is made to
Template.
StatefulSetUpdateStrategy indicates the strategy that the StatefulSet
controller will use to perform updates. It includes any additional
parameters necessary to perform the update for the indicated strategy.
FIELDS:
rollingUpdate <Object>
RollingUpdate is used to communicate parameters when Type is
RollingUpdateStatefulSetStrategyType.
type <string>
Type indicates the type of the StatefulSetUpdateStrategy. Default is
RollingUpdate
Possible enum values:
- `"OnDelete"`
- `"RollingUpdate"`
[root@master 12]# 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:
updateStrategy: # 1、添加更新策略
rollingUpdate:
partition: 1 # 序号大于等于1的pod做更新
maxUnavailable: 0 # 滚动更新时允许不可用 Pod 的最大数量
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: ikubernetes/myapp:v2 # 2、修改镜像:容器才会更新
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "nfs"
resources:
requests:
storage: 1Gi
# 在一个终端执行如下命令:kubectl apply -f statefulset.yaml
# 在另一个终端动态查看pod
[root@master ~]# kubectl get pods -l app=nginx -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 102m
web-1 1/1 Running 0 101m
web-2 1/1 Running 0 22m
web-3 1/1 Running 0 20m
web-3 1/1 Terminating 0 20m
web-3 1/1 Terminating 0 20m
web-3 0/1 Terminating 0 20m
web-3 0/1 Terminating 0 20m
web-3 0/1 Terminating 0 20m
web-2 1/1 Terminating 0 22m
web-2 1/1 Terminating 0 22m
web-2 0/1 Terminating 0 22m
web-2 0/1 Terminating 0 22m
web-2 0/1 Terminating 0 22m
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 0/1 ContainerCreating 0 1s
web-2 1/1 Running 0 10s
web-1 1/1 Terminating 0 102m
web-1 1/1 Terminating 0 102m
web-1 0/1 Terminating 0 102m
web-1 0/1 Terminating 0 102m
web-1 0/1 Terminating 0 102m
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 9s
web-2 1/1 Running 0 42s
web-1 1/1 Running 0 52s
# 从上面结果可以看出来,pod在更新的时候,只是更新了web-1、2、3
# partition: 1表示更新的时候会把pod序号大于等于1的进行更新
如果更新策略是OnDelete,那不会自动更新pod,需要手动删除,重新常见的pod才会实现更新
修改策略:
updateStrategy:
type: OnDelete
# rollingUpdate:
# partition: 1
# maxUnavailable: 0
[root@master 12]# kubectl apply -f statefulset.yaml
service/nginx unchanged
statefulset.apps/web configured
[root@master 12]# kubectl get pod -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 121m
web-1 1/1 Running 0 18m
web-2 1/1 Running 0 18m
[root@master 12]# kubectl delete pod web-0
pod "web-0" deleted
# 在另一种终端动态查看
[root@master ~]# kubectl get pod -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 122m
web-1 1/1 Running 0 19m
web-2 1/1 Running 0 19m
web-0 1/1 Terminating 0 122m
web-0 1/1 Terminating 0 122m
web-0 0/1 Terminating 0 122m
web-0 0/1 Terminating 0 122m
web-0 0/1 Terminating 0 122m
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 0/1 ContainerCreating 0 1s
web-0 1/1 Running 0 2s