复制有状态的Pod
replicaSet通过一个pod模版创建多个pod副本。这些副本除了它们的名字和IP地址不同外,没有别的差异。如果pod模版里描述了一个关联到特定持久卷声明的数据卷,那么ReplicaSet的所有副本都将共享这个持久卷声明,也就是绑定到同一个持久卷声明。
因为是在pod模版里关联持久卷声明的,又会依据pod模版创建多个副本,则不能对每个副本都指定独立的持久卷声明。所以也不能通过一个ReplicaSet来运行一个每个实例都需要独立存储的分布式数据存储服务,至少通过单个ReplicaSet是做不到的。老实说,之前你学习到的所有API对象都不能提供这样的数据存储服务,还需要一个其他的对象--StatefulSet
 
我们先看不使用StatefulSet的情况下有没有方法实现多个副本有自己的持久卷声明。
三种取巧的方法。
第一种方法,不使用ReplicaSet,使用Pod创建多个pod,每个pod都有独立的持久卷声明。需要手动创建它们,当有的pod消失后(节点故障),需要手动创建它们。因此不是一个好方法。
第二种方法,多个replicaSet ,每个rs只有一个pod副本。但这看起来很笨重,而且没办法扩缩容。
第三种方法,使用同一个ReplicaSet,大家也都挂载同一个持久卷声明,应用内部做好互斥,创建多个data数据目录,每一个pod用一个标记为在用,后面应用不能选被标记为在用的目录。这样做很难保证协调的一点没问题,同时大家用同一个持久卷,读写io将成为整个应用的瓶颈。
 
除了上面的存储需求,集群应用也会要求每一个实例拥有生命周期内唯一标识。pod可以随时被删掉,然后被新的pod替代。当一个ReplicaSet中的pod被替换时,尽管新的pod也可能使用被删除pod数据卷中的数据,但它却是拥有全新主机名和IP的崭新pod.在一些应用中,当启动的实例拥有完全新的网络标识,但还使用旧实例的数据时,很可能引起问题,比如etcd存储服务。
当然也可以创建多个service ,每一个replicaset对应一个service,那么一样很笨重,且显得很低级。辛运的是,Kubernetes为我们提供了这类需求的完美解决方案--StatefulSet.
 
了解StatefulSet
可以创建一个StatefulSet资源代替ReplicaSet来运行这类pod.它们是专门定制的一类应用,这类应用中每一个实例都是不可替代的个体,都拥有稳定的名字和状态。
对比StatefulSet 与 ReplicaSet 或 ReplicationController
RS或RC管理的pod副本比较像牛,它们都是无状态的,任何时候它们都可以被一个全新的pod替换。然后有状态的pod需要不同的方法,当一个有状态的pod挂掉后,这个pod实例需要在别的节点上重建,但是新的实例必须与被替换的实例拥有相同的名称、网络标识和状态。这就是StatefulSet如何管理pod的。
StatefulSet 保证了pod在重新调度后保留它们的标识和状态。它让你方便地扩容、缩容。与RS类似,StatefulSet也会指定期望的副本数,它决定了在同一时间内运行的宠物数。也是依据pod模版创建的,与RS不同的是,StatefulSet 创建的pod副本并不是完全一样的。每个pod都可以拥有一组独立的数据卷(持久化状态)。另外pod的名字都是规律的(固定的),而不是每个新pod都随机获取一个名字。
 
提供稳定的网络标识
StatefulSet 创建的pod的名称,按照从零开始的顺序索引,这个会体现在pod的名称和主机名称上,同样还会体现在pod对应的固定存储上。
有状态的pod与普通的pod不一样的是,有状态的pod有时候需要通过其主机名来定位,而无状态的不需要,因为无状态的都一样,随机选一个就行,但对于有状态的来说,每一个pod都不一样,通常希望操作的是特定的一个。基于这个原因,一个StatefulSet要求你创建一个用来记录每个pod网络标记的headless Service。通过这个Service,每个pod将拥有独立的DNS记录,这样集群里它的伙伴或者客户端就可以通过主机名找到它。比如说一个属于default命名空间,名为foo的控制服务,它的一个pod名称为A-0,那么完整域名为:a-0.foo.default.svc.cluster.local。而在ReplicaSet是行不通的。
此外我们可以在容器中通过dig foo.default.svc.cluster.local对应的SRV记录,获取一个StatefulSet中所有pod的名称.
 
StatefulSet扩缩容的特点
扩容,会按照索引进行
缩容,也会按照索引,删除索引值最大的 pod
缩容StatefulSet任何时候只会操作一个pod实例,所以会很慢,不是因为索引要顺序进行,而是为了避免数据丢失。举例来说,一个分布式存储应用副本数为2,如果同时下线两个,一份数据记录就会丢失。
基于以上原因,StatefulSet在有实例不健康的情况下是不允许进行缩容操作的。一个不健康,你又缩容一个这样相当于两个同时下线。
持久卷的创建和删除
扩容Statefulset增加一个副本,会创建两个或更多的API对象(一个pod和一个与之关联的持久卷声明)。但对于缩容来将,只会删除一个pod,而遗留下之前创建的声明。因为当一个声明被删除后,与之绑定的持久卷就会被回收或删除,其上面的数据就会丢失。基于这个原因,你需要释放特定的持久卷时,需要手动删除对应的持久卷声明。
 
StatefulSet的保障机制。
一个有状态的pod总会被一个完全一致的pod替换(两者相同的名称,主机名和存储等)。这个替换发生在kubernetes发现旧pod不存在时(例如手动删除这个pod).
那么当Kubernetes不能确定一个pod的状态呢?如果它创建一个完全一致的pod,那系统中就会有两个完全一致的pod在同时运行。这两个pod会绑定到相同的存储,所以这两个相同标记的进程会同时写相同的文件。
为了保证两个拥有相同标记和绑定相同持久卷声明的有状态的pod实例不会同时运行,statefulset遵循at-most-one语义。也就是说一个StatefulSet必须在准确确认一个pod不再运行后,才会去创建它的替换pod。这对如何处理节点故障有很大帮助。具体实现,内部的,暂不深入。
讲了那么多StatefulSet实现有状态pod的好处,下面看看如何创建。
 
我们假设使用gec创建三个pv

kind: list
apiVersion: v1
item:
- apiVersion: v1
  kind: PersistenVolume
  metadata:
    name: pv-a
  spec:
    capacity:
      storage: 1Mi
    accessModes:
    - ReadWriteOnce
    persistenVolumeReclaimPolicy: Recycle 卷被声明释放后,空间会被回收再利用
    gcePersistentDisk:
      poName: pv-a
      fsType: nfs4
- apiVersion: v1
  kind: PersistenVolume
  metadata:
    name: pv-b
...
准备好pv后,我们接下来创建statefulset
 
如我们之前将到的,在部署一个StatefulSet之前,需要创建一个用于在有状态的pod之间提供网络标识的headless Service

apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  clusterIP: None (StatefulSet的控制Service必须时None即headless模式)
  selector:
    app: kubia
  ports:
  - name: http
    port: 80
创建StatefulSet详单

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: kubia
spec:
  serviceName: kubia
  replicas: 2
  template:
    metadta:
      labels:
        app: kubia
    spec:
      containers:
      - name: kubia
        image: luksa/kubia-pet
        ports:
        - name: kubia
          containerPort: 8080 volumeMounts:         - name: data
             mountPath: /var/data
  volumeClaimTemplates:
  - metadata:
    name: data
    spec:
      resources:
        requests:
          storage: 1Mi
      accessModes:
      - ReadWriteOnce
创建:
kubectl create -f kubia-statefulset.yaml
 
列出pod:
kubectl get pod
Name READY
kubia-0 0/1 ...
看到会一个个进行
kubectl get pod
Name READY
kubia-0 1/1 ...
kubia-1 0/1 ...
 
查看pvc
kubectl get pvc
Name STATUS VOlUME
data-kubia-0 Bound pv - c ...
data-kubia-1 Bound pv - a ...
 
可以看到生成的持久卷声明的名称由 volumeClaimTeplate 字段中定义的名称和每个pod的名称组成。
 
现在你的数据存储集群节点都已经运行,可以开始使用它们了。因为之前创建的Service处于headless模式,所以不能通过service来访问你的pod。需要直接连接每个单独的pod来访问(或者创建一个普通的Service,但是这样还是不允许你访问指定的pod)
 
我们来创建一个普通的service如下:

apiVersion: v1
kind: service
metadata:
  name: kubia-public
spec:
  selector:
    app: kubia
  ports:
  - port: 80
    targetPort: 8080
StatefulSet 已经运行起来了,那么我们看下如何更新它的pod模版,让它使用新的镜像。同时你也会修改副本数为3.通常会使用kubectl edit命令来更新StatefulSet
kubectl edit statefulset kubia
你会看到新的pod实例会使用新的镜像运行,那已经存在的两个副本呢?通过他们的寿命可以看出它们没有更新。这是符合预期的。因为,首先StatefulSet更像ReplicaSet,而不是Deployment,所以在模版被修改后,它们不会重启更新,需要手动删除这些副本,然后StatefulSet会根据新的模版重新调度启动它们。
kubectl delete po kubia-0 kubia-1
注意: 从Kubernetes1.7版本开始,statefulSet支持与Deployment和DaemonSet一样的滚动升级。通过kubectl explain 获取StatefulSet的spec.updateStrategy 相关文档来获取更多信息。
 
前面我们提到StatefulSet的保障机制,那么当一个节点故障了,会出现什么情况。
statefulset在明确知道一个pod不再运行之前,它不能或者不应当创建一个替换pod。只有当集群的管理者告诉它这些信息时候,它才能明确知道。为了做到这一点,管理者需要删除这个pod,或者删除整个节点。
当手动停止一个node的网卡,使用kubectl get node,会显示Status notReady
因为控制台不会再收到该节点发送的状态更新,该节点上吗的所有pod状态都会变为Unknown。
当一个pod状态为Unknown时会发生什么
若该节点过段时间正常连接,并且重新汇报它上面的pod状态,那这个pod就会重新被标记为Runing。但如果这个pod的未知状态持续几分钟后(这个时间是可以配置的),这个pod就会自动从节点上驱逐。这是由主节点(kubernetes的控制组件)处理的。它通过删除pod的资源来把它从节点上驱逐。
当kubelet发现这个pod标记为删除状态后,它开始终止运行该pod。在上面的示例中,kubelet已不能与主节点通信(因为网卡断了),这意味着这个pod会一直运行着。查看
kubectl describe po kubia-0
发现status一直为Terminating,原因是NodeLost,在信息中说明的是节点不回应导致不可达。
这时候你想要手动删除pod
kubectl delete po kubia-0
执行完成后,你的想法是会再次运行一个kubia-0
但是kubectl get po会发现kubia-0 状态为 Unknown 并且还是之前那个旧pod ,因为启动时长没变。
为什么会这样?因为在删除pod之前,这个pod已经被标记为删除。这是因为控制组件已经删除了它(把它从节点驱逐)。这时你用kubectl describe po kubia-0 查看状态依然是Terminating。
这时候只能进行强制删除
kubectl delete po kubia-0 --force --grace-period 0
你需要同时使用--force和 --grace-period 0两个选项。然后kubectl 会对你做的事发出
警告信息。如果你再次列举pod,就可以看到一个新的kubia-0 pod被创建出来。
警告: 除非你确认节点不再运行或者不会再可以访问(永远不会再可以访问),否则不要强制删除有状态的pod.

k8s之StatefulSet介绍(六)的更多相关文章

  1. Kubernetes中StatefulSet介绍

    StatefulSet 是Kubernetes1.9版本中稳定的特性,本文使用的环境为 Kubernetes 1.11.如何搭建环境可以参考kubeadm安装kubernetes V1.11.1 集群 ...

  2. k8s 挂载卷介绍(四)

    kubernetes关于pod挂载卷的知识 首先要知道卷是pod资源的属性,pv,pvc是单独的资源.pod 资源的volumes属性有多种type,其中就包含有挂载pvc的类型.这也帮我理清了之间的 ...

  3. K8s Service原理介绍

    Service的工作方式有三种: 第一种: 是Userspace方式 如下图描述, Client Pod要访问Server Pod时,它先将请求发给本机内核空间中的service规则,由它再将请求, ...

  4. k8s系列---pod介绍

    # yaml格式的pod定义文件完整内容: apiVersion: v1 #必选,版本号,例如v1 kind: Pod #必选,Pod metadata: #必选,元数据 name: string # ...

  5. K8S Kubernetes 简单介绍 转自 http://time-track.cn/kubernetes-trial.html Kubernetes初体验

    这段时间学习了一下 git jenkins docker  最近也在看  Kubernetes  感觉写得很赞  也是对自己对于K8S 有了进一步得理解  感谢 倪 大神得Blog 也希望看到这篇Bl ...

  6. 5.深入k8s:StatefulSet控制器

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 在上一篇中,讲解了容器持久化存储,从中我们知道什么是PV和PVC,这一篇我们讲通过Sta ...

  7. 容器编排系统K8s之StatefulSet控制器

    前文我们聊到了k8s的configmap和secret资源的说明和相关使用示例,回顾请参考:https://www.cnblogs.com/qiuhom-1874/p/14194944.html:今天 ...

  8. k8s调度器介绍(调度框架版本)

    从一个pod的创建开始 由kubectl解析创建pod的yaml,发送创建pod请求到APIServer. APIServer首先做权限认证,然后检查信息并把数据存储到ETCD里,创建deployme ...

  9. 8.2 k8s 基于StatefulSet运行mysql 一主多从 ,数据通过pv/pvc结合NFS服务器持久化

    1.准备mysql和xtrabackup镜像 下载mysql官方镜像并上传到本地harbor docker pull mysql:5.7 docker tag m ysql:5.7 192.168.1 ...

随机推荐

  1. UNIX网络编程卷1 - >环境搭建(ubuntu16.04)

      学习unp网络编程,树上的例子均存在#include“unp.h”,故需要对环境进行配置. 1.到资源页下载www.unpbook.com 2.解压并将unpv13e移动到相应的文件夹下 (因为我 ...

  2. 深入js系列-类型(隐式强制转换)

    隐式强制转换 在其可控的情况下,减少冗余,让代码更简洁,很多地方都进行了隐式转换,比如常见的三目表达式.if().for().while.逻辑运算符 || &&,适当通过语言机制,抽象 ...

  3. Web中线程与IIS线程池自动回收机制

    开发Web项目后,部署到 IIS上 ,运行一直稳定,当Web程序中加入了定时任务,或者线程之类的机制后,第二天发现悲催了,定时任务并没有执行,此时重新登录一下网站,定时任务又重新执行.原来IIS默认有 ...

  4. Salesforce LWC学习(八) Look Up组件实现

    本篇参考https://www.salesforcelwc.in/2019/10/lookup-in-lwc.html,感谢前人种树. 我们做lightning的时候经常会遇到Look up 或者MD ...

  5. Spring boot配置文件application.properties和bootstrap.properties的区别

    spring boot 有两种配置文件 (1)application.properties(application.yml) 系统级别的一些参数配置,这些参数一般是不会变动的 (2)bootstrap ...

  6. Solr7.x学习(8)-使用spring-data-solr

    1.maven配置 <dependency> <groupId>org.springframework.data</groupId> <artifactId& ...

  7. 第十二节:Asp.Net Core 之分布式缓存(SQLServer和Redis)

    一. 整体说明 1. 说明 分布式缓存通常是指在多个应用程序服务器的架构下,作为他们共享的外部服务共享缓存,常用的有SQLServer.Redis.NCache.     特别说明一下:这里的分布式是 ...

  8. js实现对上传图片的路径转成base64编码,并且对图片进行压缩,实现预览功能1

    参考 https://blog.csdn.net/qq_31965515/article/details/82975381 https://www.cnblogs.com/zhangdiIT/p/78 ...

  9. 【数据结构与算法】线性表操作(C语言)

    #include <stdio.h> #include <stdlib.h> #define OK 1 #define NO 0 #define MAXSIZE 20 type ...

  10. FusionInsight大数据开发---Streaming应用开发

    Streaming应用开发 掌握Streaming基本业务开发流 熟悉Streaming常用API接口使用 掌握Streaming业务设计基本原则 了解Streaming应用开发环境 了解CQL开发流 ...