什么是StatefulSet?
StatefulSet是为了解决有状态服务的问题(Deployments和ReplicaSets专为无状态服务设计的)而设计,其应用场景包括
1.稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现 2.稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现 3.有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现 4.有序收缩,有序删除(即从N-1到0)
StatefulSet由哪几部分组成?
从上面的应用场景可以发现,StatefulSet由以下几个部分组成
1.通过Headless Service生成可解析的DNS记录 2.通过volumeClaimTemplates创建pvc和对应的pv绑定 3.定义StatefulSet来创建pod
StatefulSet的yaml文件组成?
StatefulSet中每个Pod的DNS格式为statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local,其中
1.serviceName为Headless Service的名字 2.0..N-1为Pod所在的序号,从0开始到N-1 3.statefulSetName为StatefulSet的名字 4.namespace为服务所在的namespace,Headless Servic和StatefulSet必须在相同的namespace 5..cluster.local为Cluster Domain
学习Statefulset之后需要掌握如下
1.如何创建 StatefulSet 2.通过StatefulSet 怎样管理它的 Pod 3.如何删除 StatefulSet 4.如何对 StatefulSet 进行扩容/缩容 5.如何更新一个 StatefulSet 的 Pod
部署一个web应用
作为开始,使用如下示例创建一个StatefulSet。它创建了一个Headless Service nginx 用来发布StatefulSet web中的Pod的IP地址。
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/v1beta1 kind: StatefulSet metadata: name: web spec: 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" ] resources: requests: storage: 1Gi
为什么要使用volumeClaimTemplate?
对于有状态应用都会用到持久化存储,比如mysql主从,由于主从数据库的数据是不能存放在一个目录下的,每个mysql节点都需要有自己独立的存储空间。而在deployment中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,它们数据是同步的,而statefulset定义中的每一个pod都不能使用同一个存储卷,这就需要使用volumeClainTemplate,当在使用statefulset创建pod时,会自动生成一个PVC,从而请求绑定一个PV,每一个pod都有自己专用的存储卷。Pod、PVC和PV对应的关系图如下:
通过statefulset部署pod,并且观察pod创建的过程
你需要使用两个终端窗口。在第一个终端中,使用kubectl get 来查看 StatefulSet的Pods的创建情况。
kubectl get pods -w -l app=nginx
在另一个终端中,使用
kubectl apply -f statefulset.yaml
来创建定义在statefulset.yaml 中的 Headless Service 和 StatefulSet。
上面的命令创建了两个Pod,每个都运行了一个NGINX web 服务器。获取 nginxService和web StatefulSet来验证是否成功的创建了它们。
1.查看service
kubectl get service nginx
显示如下:
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx None 80/TCP 12s
2.查看statefulset
kubectl get statefulset
显示如下:
NAME DESIRED CURRENT AGE
web 2 1 20s
顺序创建Pod
对于一个拥有N个副本的StatefulSet,Pod被部署时是按照{0..N-1}的序号顺序创建的。在第一个终端中使用 kubectl get检查输出。
kubectl get pods -w -l app=nginx
显示如下:
NAME READY STATUS RESTARTS AGE web-0 0/1 Pending 0 0s web-0 0/1 Pending 0 0s web-0 0/1 ContainerCreating 0 0s web-0 1/1 Running 0 19s web-1 0/1 Pending 0 0s web-1 0/1 Pending 0 0s web-1 0/1 ContainerCreating 0 0s web-1 1/1 Running 0 18s |
请注意在 web-0 Pod 处于Running和Ready状态后 web-1 Pod才会被启动。
StatefulSet中的Pod
StatefulSet 中的 Pod 拥有一个唯一的顺序索引和稳定的网络身份标识
检查 Pod 的顺序索引
获取 StatefulSet 的 Pod。
kubectl get pods -l app=nginx
显示如下:
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 1m
web-1 1/1 Running 0 1m
如同StatefulSets概念中所提到的,StatefulSet中的Pod拥有一个具有黏性的、独一无二的身份标志。这个标志基于StatefulSet控制器分配给每个Pod的唯一顺序索引。Pod的名称的形式为-。webStatefulSet 拥有两个副本,所以它创建了两个 Pod:web-0 和 web-1
使用稳定的网络身份标识
每个Pod都拥有一个基于其顺序索引的稳定的主机名(statefulset创建的pod的主机名由statefulset的名称和有序索引组成),使用kubectl exec 在每个Pod中执行hostname
for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; doneweb-0 web-1
使用kubectl run运行一个提供nslookup命令的容器,该命令来自于dnsutils包。通过对Pod的主机名执行nslookup,你可以检查他们在集群内部的DNS地址。
kubectl exec -it web-1 -- /bin/bash apt-get install dns-utils -y nslookup web-0.nginx.default.svc.cluster.local
在一个终端中查看StatefulSet的Pod。
kubectl get pod -w -l app=nginx
在另一个终端中使用 kubectl delete 删除StatefulSet中所有的Pod。
kubectl delete pod -l app=nginx
显示如下:
pod "web-0" deleted
pod "web-1" deleted
等待StatefulSet重启它们,并且两个Pod都变成Running和Ready状态。
kubectl get pod -w -l app=nginx
显示如下:
NAME READY STATUS RESTARTS AGE
web-0 0/1 ContainerCreating 0 0s
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 34s
使用 kubectl exec 和 kubectl run 查看 Pod 的主机名和集群内部的 DNS 表项
for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
显示如下:
web-0
web-1
web-2
kubectl exec -it web-1 -- /bin/bash
apt-get install dnsutils -y #安装nslookup命令的
nslookup web-0.nginx.default.svc.cluster.local
Pod 的序号、主机名、SRV 条目和记录名称没有改变,但和 Pod 相关联的 IP 地址可能发生了改变。这就是为什么不要在其他应用中使用 StatefulSet 中的 Pod 的 IP 地址进行连接,这点很重要。如果你需要查找并连接一个 StatefulSet 的活动成员,你应该查询 Headless Service 的 CNAME。和 CNAME 相关联的 SRV 记录只会包含 StatefulSet 中处于 Running 和 Ready 状态的 Pod。如果你的应用已经实现了用于测试 liveness 和 readiness 的连接逻辑,你可以使用 Pod 的 SRV 记录(web-0.nginx.default.svc.cluster.local, web-1.nginx.default.svc.cluster.local)。因为他们是稳定的,并且当你的 Pod 的状态变为 Running 和 Ready 时,你的应用就能够发现它们的地址。
域名对应的表格:
导入稳定的存储
获取 web-0 和 web-1 的 PersistentVolumeClaims
kubectl get pvc -l app=nginx
显示如下:
StatefulSet控制器创建了两个PersistentVolumeClaims,绑定到两个PersistentVolumes。NGINX web服务器默认会加载位于/usr/share/nginx/html/index.html的index文件。StatefulSets spec中的volumeMounts字段保证了 /usr/share/nginx/html 文件夹由一个 PersistentVolume 支持。将Pod的主机名写入它们的 index.html 文件并验证NGINX web服务器使用该主机名提供服务。
for i in 0 1 2 ; do kubectl exec web-$i -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html'; don
for i in 0 1 2; do kubectl exec -it web-$i -- curl localhost; done
显示如下:
web-0
web-1
请注意,如果你看见上面的 curl 命令返回了403 Forbidden的响应,你需要像修改挂载的目录的权限:
for i in 01 2; do kubectl exec web-$i -- chmod 755 /usr/share/nginx/html; done
修改之后重新尝试上面的 curl 命令。