k8s 09 有状态应用部署管理StatefuSet
一、Pod(容器组)
Pod(容器组)是 Kubernetes 中最小的可部署单元。一个 Pod(容器组)包含了一个应用程序容器(某些情况下是多个容器)、存储资源、一个唯一的网络 IP 地址、以及一些确定容器该如何运行的选项。Pod 容器组代表了 Kubernetes 中一个独立的应用程序运行实例,该实例可能由单个容器或者几个紧耦合在一起的容器组成。
Docker 是 Kubernetes Pod 中使用最广泛的容器引擎;Kubernetes Pod 同时也支持其他类型的容器引擎。
Kubernetes 集群中的 Pod 存在如下两种使用途径:
-
一个 Pod 中只运行一个容器。"one-container-per-pod" 是 Kubernetes 中最常见的使用方式。此时,您可以认为 Pod 容器组是该容器的 wrapper,Kubernetes 通过 Pod 管理容器,而不是直接管理容器。
-
一个 Pod 中运行多个需要互相协作的容器。您可以将多个紧密耦合、共享资源且始终在一起运行的容器编排在同一个 Pod 中,可能的情况有:
二、控制器
当 Pod 被创建出来,Pod 会被调度到集群中的节点上运行,Pod 会在该节点上一直保持运行状态,直到进程终止、Pod 对象被删除、Pod 因节点资源不足而被驱逐或者节点失效为止。Pod 并不会自愈,当节点失效,或者调度 Pod 的这一操作失败了,Pod 就该被删除。如此,单单用 Pod 来部署应用,是不稳定不安全的。
Kubernetes 使用更高级的资源对象 “控制器” 来实现对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)
组成
-
Headless Service
用于定义网络标志(DNS domain)
Domain Name Server:域名服务
将域名与 ip 绑定映射关系
服务名 => 访问路径(域名) => ip
DNS格式概述:
StatefulSet 中每个 Pod 的 DNS 格式为statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local
serviceName 为 Headless Service 的名字
0..N-1 为 Pod 所在的序号,从 0 开始到 N-1
statefulSetName 为 StatefulSet 的名字
namespace 为服务所在的 namespace,Headless Servic 和 StatefulSet 必须在相同的 namespace
.cluster.local 为 Cluster Domain -
volumeClaimTemplate
用于创建 PersistentVolumes
注意事项
- kubernetes v1.5 版本以上才支持
- 所有Pod的Volume必须使用PersistentVolume或者是管理员事先创建好
- 为了保证数据安全,删除StatefulSet时不会删除Volume
- StatefulSet 需要一个 Headless Service 来定义 DNS domain,需要在 StatefulSet 之前创建好
创建StatefulSet
web.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 # StatefulSet 类型的资源
metadata:
name: web # StatefulSet 对象的名字
spec:
serviceName: "nginx" # 使用哪个 service 来管理 dns
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports: # 容器内部要暴露的端口
- containerPort: 80 # 具体暴露的端口号
name: web # 该端口配置的名字
volumeMounts: # 加载数据卷
- name: www # 指定加载哪个数据卷
mountPath: /usr/share/nginx/html # 加载到容器中的哪个目录
volumeClaimTemplates: # 数据卷模板
- metadata: # 数据卷描述
name: www # 数据卷名称
annotations: # 数据卷的注解
volume.alpha.kubernetes.io/storage-class: anything
spec: # 数据卷的规约
accessModes: ["ReadWriteOnce"] # 访问模式
resources:
requests:
storage: 1Gi # 需要1个G的存储资源
执行命令:
kubectl create -f web.xml
#删除已有的同名deploy, 以免互相影响
kubectl delete deploy nginx-deploy
#查看所有的服务
kubectl get svc
# 创建前先检查是否有重名的
kubectl get statefulset
kubectl get sts
# 创建有状态控制器
kubectl create -f web.xml
#创建结果输出:
service/nginx created
statefulset.apps/web create
创建后的操作实例
[root@VM-0-16-centos statefulset]# kubectl get sts
NAME READY AGE
web 0/2 12m
[root@VM-0-16-centos statefulset]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d19h
nginx ClusterIP None <none> 80/TCP 14m
[root@VM-0-16-centos statefulset]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Pending 13m
[root@VM-0-16-centos statefulset]# kubectl describe pvc www-web-0
Name: www-web-0
Namespace: default
StorageClass:
Status: Pending
Volume:
Labels: app=nginx
Annotations: volume.alpha.kubernetes.io/storage-class: anything
Finalizers: [kubernetes.io/pvc-protection]
Capacity:
Access Modes:
VolumeMode: Filesystem
Used By: web-0
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal FailedBinding 3m35s (x42 over 13m) persistentvolume-controller no persistent volumes available for this claim and no storage class is set
[root@VM-0-16-centos statefulset]#
可见上面的描述信息中是显示持久化存储卷失败了,所以先去掉这些配置:
---
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 # StatefulSet 类型的资源
metadata:
name: web # StatefulSet 对象的名字
spec:
serviceName: "nginx" # 使用哪个 service 来管理 dns
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports: # 容器内部要暴露的端口
- containerPort: 80 # 具体暴露的端口号
name: web # 该端口配置的名字
然后执行:
kubectl replace sts web -f web.yaml
[root@VM-0-16-centos statefulset]# kubectl replace sts web -f web.yaml
service/nginx replaced
The StatefulSet "web" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden
替换失败了
#所以直接干脆删除掉:
kubectl delete sts web
kubectl delete svc nginx
kubectl delete pvc www-web-0
#然后重启
kubectl create -f web.yaml
#然后:
kubectl get sts
kubectl get svc
kubectl get pvc(没有了)
kubectl get po
[root@VM-0-16-centos statefulset]# kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-demo 0/1 ImagePullBackOff 0 2d3h
nginx-po 0/1 ImagePullBackOff 0 28h
web-0 0/1 ImagePullBackOff 0 9m14s
#在当前主机中ping这个web-0, 是ping不通的
[root@VM-0-16-centos statefulset]# ping web-0
# 我们可以进入到另一个容器中,然后在另外的这个k8s管理的这个容器中来ping访问这个web-0
# 进入busybox是linux的工具集合镜像,
kubectl run -it --image busybox dns-test --restart=Never --rm /bin/sh
# 这里的查找名称是基于上面的DNS名称命名规则取的前3个部分: statefulSetName-{0..N-1}.serviceName
/ # nslookup web-0.nginx

PVC
PVC是用户用来请求持久化存储 PV的抽象对象。PVC 和 PV 之间的关系类似于Pod 和 Node:PV是集群中的一块存储资源,而PVC是对这些资源的请求。
扩容缩容
# 扩容
$ kubectl scale statefulset web --replicas=5
$ kubectl scale sts web --replicas=5
# 缩容
$ kubectl patch statefulset web -p '{"spec":{"replicas":3}}'
$ kubectl patch sts web -p '{"spec":{"replicas":3}}'
镜像更新
RollingUpdat(滚动更新
StatefulSet 也可以采用滚动更新策略,同样是修改 pod template 属性后会触发更新,但是由于 pod 是有序的,在 StatefulSet 中更新时是基于 pod 的顺序倒序更新的
灰度发布(金丝雀发布
-
利用滚动更新中的 partition 属性,可以实现简易的灰度发布的效果
-
例如我们有 5 个 pod,如果当前 partition 设置为 3,那么此时滚动更新时,只会更新那些 序号 >= 3 的 pod
-
利用该机制,我们可以通过控制 partition 的值,来决定只更新其中一部分 pod,确认没有问题后再主键增大更新的 pod 数量,最终实现全部 pod 更新
OnDelete
只有在 pod 被删除时会进行更新操作
删除
# 删除 StatefulSet 和 Headless Service
# 级联删除:删除 statefulset 时会同时删除 pods
kubectl delete statefulset web
# 非级联删除:删除 statefulset 时不会删除 pods,删除 sts 后,pods 就没人管了,此时再删除 pod 不会重建的
kubectl deelte sts web --cascade=false
# 删除 service
kubectl delete service nginx
删除 pvc
# StatefulSet删除后PVC还会保留着,数据不再使用的话也需要删除
$ kubectl delete pvc www-web-0 www-web-1

浙公网安备 33010602011771号