[TOC]
# StatefulSet
(针对有状态服务)`StatefulSet`是用来解决`Deployment`无法满足有状态服务的需求,在有状态服务时,`Deployment`会遇到以下这个问题:
**使用Deployment创建的Pod是无状态、不稳定的,常常会不停的创建和消亡,所以当挂载Volume之后,新创建的Pod是不能重新挂载到这个Volume上,导致新Pod不能正常读取到业务数据。 另一方面,由于用户对Pod的消亡是无感知,所以不易察觉后台Pod异常**
### 特点
StatefulSet 具有以下特点:
* **稳定的持久化存储**,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
* **稳定的网络标志**,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
* **有序部署,有序扩展**,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
* **有序收缩,有序删除**(即从N-1到0) (StatefulSet中的Pod是倒序删除)
### 应用场景构成
一个完整的StatefulSet的应用场景通常由以下几个部分构成:
* **网络无头服务(Headless Service)**:为Pod生成可解析的DNS记录,即没有cluster IP,使用DNS通讯
* **volumeClaimTemplates(卷申请模版)**:提供专有且固定的存储
* **StatefulSet**:用于管理Pod
#### 关于无头服务说明
在StatefulSet场景中,并不需要有头服务那样提供集群负载或者proxy代理,所以使用无头服务
##### 用途
* **有头服务**:虽然service通常对对应多个endpoint,但是dns查询时只会返回service的地址。具体client访问的是哪个Real Server,是由iptables来决定的。
* **无头服务**:dns查询会返回真实的endpoint。
#### 关于volumeClaimTemplate说明
在StatefulSet场景中,每个Pod都是有状态的,而每一个Pod的状态或数据可能会不一样,所以每个节点有自已的专用存储,所以当Pod消亡重建后,能重新用回原来的PV存储,这个就是`volumeClaimTemplate`,用来实现各个Pod使用专用存储
#### Pod的DNS格式
StatefulSet中每个Pod的DNS格式为:`statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local`其中
* serviceName:为无头服务的名字
* 0..N-1:为Pod所在的序号,从0开始到N-1
* statefulSetName:为StatefulSet的名字
* namespace:为服务所在的namespace (Headless Servic和StatefulSet必须在相同的namespace)
* svc.cluster.local为Cluster Domain
### 动态PV申请应用场景示例
#### 本地loca-PV.yaml
~~~
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 3Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /mnt
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- worker-1 # 这里根据官网例子,要设置亲和性,改成节点名
~~~

#### 设置StorageClass
根据官网提示,需要创建一个`Storage Class`,用于延迟什么鬼东西,具体看官网翻译吧
~~~
# 官网有有每种StorageClass的示例,这个示例是创建本地磁盘的示例
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
~~~

#### StatefulSet创建
~~~
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None # 无头 headless Service
selector:
app: nginx
---
apiVersion: apps/v1beta2
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 1 #我只创建了一个local_PV
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "local-storage"
resources:
requests:
storage: 1Gi
~~~

> 集群内部通过DNS的方式进行访问,格式为: $(pod-name).$(service\_name).$(namespace).svc.cluster.local svc.cluster.local为Cluster 为集群默认域名
### 集群外部访问StatefulSet的Pod
因为Pod使用了无头服务的service,即没有Cluster IP,要通过DNS的方式进行访问
**解决方法:**在这种情况下,给 pod 打上 label,然后使用`kubectl expose`的方式,将其以NodePort的方式暴露到集群外部
为RC的nginx创建service,并通过Service的80端口转发至容器的8000端口上。
~~~
#打标签
kubectl label pod web-0 pod=0
#使用标签暴露服务
kubectl expose pod web-0 --port=80 --target-port=80 --name=web-0 --selector=pod=0 --type=NodePort
#不使用标签暴露服务
kubectl expose pod web-0 --port=80 --target-port=8080 --type=NodePort
~~~
*我自己做的测试,貌似不用打label,也能访问,要用的时候看情况吧*
`kubectl expose`还可以使用以资源 pod、service、replication controller(rc)、deployment、replica set(rs)
### 滚动更新
滚动更新由 spec.updateStrategy 字段定义,默认为 RollingUpdate,另一个策略为OnDelete,即删除Pod资源重建以完成更新
##### 更新镜像
更新镜像可以用:
~~~
kubectl set image statefulset myapp myapp=XXXXXX
~~~
##### 暂停更新操作
由 spec.update- Strategy.rollingUpdate.partition 字段的值设置为Pod资源的副本数量(至少保证多少个Pod副本可用)
~~~
# 至少保证3个副本可用
kubectl patch statefulset myapp -p '{"spec":{" updateStrategy":{" rollingUpdate":{" partition": 3}}}}'
~~~
- 一、K8S的安装
- 1.1 安装环境
- 1.2 问题汇总
- 1.3 事前准备
- 1.4 安装配置负载均衡
- 1.5 安装K8S软件
- 1.6 初始化kubeadm
- 1.7 添加控制节点
- 1.8 添加计算节点
- 1.9 安装故障问题处理
- 1.10 安装管理dashboard
- 1.11 编写测试Pod
- 1.12 从外部访问集群中的Pod
- 1.13 部署metrics-server指标采集
- 二、Pod管理
- 2.1 Pod 资源需求和限制
- 2.2 Init 容器
- 2.3 Pod 健康检查(探针)和重启策略
- 2.4 Pod 生命周期(钩子Hook)
- 2.5 静态Pod
- 2.6 初始化容器(init container)
- 2.7 资源限制
- 三、资源控制器
- 3.1 Deployment
- 3.2 StatefulSet
- 3.3 DaemonSet
- 3.4 Job
- 3.5 定时任务
- 3.6 准入控制器
- 3.7 自动伸缩
- 3.8 ReplicaSet
- 四、存储
- 4.1 Secret 管理敏感信息
- 4.2 ConfigMap 存储配置
- 4.3 Volume
- 4.4 PV
- 4.5 PVC
- 4.6 StorageClass
- 4.7 暴露宿主机信息给Pod
- 五、服务Service
- 5.1 Service 资源
- 5.2 服务发现
- 5.3 服务暴露
- 5.4 Ingress 资源
- 5.5 Ingress 专题
- 5.6 traefik 2.X版本使用
- 六、认证、授权、准入控制
- 6.1 服务账户
- 6.2 LimitRange资源与准入控制器
- 6.3 ResourceQuota 资源与准入控制器
- 七、Helm
- 7.1 Helm 安装
- 八、 istio
- 8.1 istio 介绍
- 8.2 iotis 安装
- 九、calico
- 9.1 两种网络模式
- 9.2 全互联模式 与 路由反射模式
- 9.3 BGP跨网段(大型网络)
- 十、Ingress
- 10.1 安装 LoadBalancer
- 10.2 部署 ingress-nginx
- 10.3 Ingress-nginx 的使用
- 10.4 开启TCP和UDP
- 使用中的问题
- CSI Node问题
