k8s 09 有状态应用部署管理StatefuSet

一、Pod(容器组)

Pod(容器组)是 Kubernetes 中最小的可部署单元。一个 Pod(容器组)包含了一个应用程序容器(某些情况下是多个容器)、存储资源、一个唯一的网络 IP 地址、以及一些确定容器该如何运行的选项。Pod 容器组代表了 Kubernetes 中一个独立的应用程序运行实例,该实例可能由单个容器或者几个紧耦合在一起的容器组成。

Docker 是 Kubernetes Pod 中使用最广泛的容器引擎;Kubernetes Pod 同时也支持其他类型的容器引擎。

Kubernetes 集群中的 Pod 存在如下两种使用途径:

  1. 一个 Pod 中只运行一个容器。"one-container-per-pod" 是 Kubernetes 中最常见的使用方式。此时,您可以认为 Pod 容器组是该容器的 wrapper,Kubernetes 通过 Pod 管理容器,而不是直接管理容器。

  2. 一个 Pod 中运行多个需要互相协作的容器。您可以将多个紧密耦合、共享资源且始终在一起运行的容器编排在同一个 Pod 中,可能的情况有:

二、控制器

当 Pod 被创建出来,Pod 会被调度到集群中的节点上运行,Pod 会在该节点上一直保持运行状态,直到进程终止、Pod 对象被删除、Pod 因节点资源不足而被驱逐或者节点失效为止。Pod 并不会自愈,当节点失效,或者调度 Pod 的这一操作失败了,Pod 就该被删除。如此,单单用 Pod 来部署应用,是不稳定不安全的。

Kubernetes 使用更高级的资源对象 “控制器” 来实现对Pod的管理。控制器可以为您创建和管理多个 Pod,管理副本和上线,并在集群范围内提供自修复能力。 例如,如果一个节点失败,控制器可以在不同的节点上调度一样的替身来自动替换 Pod。

无状态应用和有状态应用
image

有状态服务部署控制器
image

适用有状态服务的控制器: StatefulSet

主要特点

  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)

组成

  1. 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

  2. volumeClaimTemplate
    用于创建 PersistentVolumes

注意事项

  1. kubernetes v1.5 版本以上才支持
  2. 所有Pod的Volume必须使用PersistentVolume或者是管理员事先创建好
  3. 为了保证数据安全,删除StatefulSet时不会删除Volume
  4. 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

image

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 的顺序倒序更新的

灰度发布(金丝雀发布

  1. 利用滚动更新中的 partition 属性,可以实现简易的灰度发布的效果

  2. 例如我们有 5 个 pod,如果当前 partition 设置为 3,那么此时滚动更新时,只会更新那些 序号 >= 3 的 pod

  3. 利用该机制,我们可以通过控制 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
posted @ 2026-10-06 11:49  超轶绝尘  阅读(4)  评论(0)    收藏  举报