复制有状态的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. Redis有哪些数据结构

    String 这应该是应用最广泛的了,简单的 key-value 类型.value 不仅可以是 String,也可以是数字.还可以享受 Redis 的定时持久化(可以选择 RDB 模式或者 AOF 模 ...

  2. 解析node-cors模块

    (function () { 'use strict'; var assign = require('object-assign'); var vary = require('vary'); var ...

  3. Android 10 终于来了!增加了不少新特性

    前言 Android 10 正式发布了,根据官网的介绍,聚焦于隐私可控.手机自定义与使用效率,此版本主要带来了十大新特性:   image 智能回复 使用机器学习来预测你在回复信息时可能会说些什么,这 ...

  4. docker 挂载主机目录 -v 和 --mount区别

    使用-v  时,如果宿主机上没有这个文件,也会自动创建, 但是如果使用--mount时,宿主机中没有这个文件会报错找不到这个文件,并创建失败

  5. K8S之traefik高级特性

    Traefik Traefik是一个用Golang开发的轻量级的Http反向代理和负载均衡器.由于可以自动配置和刷新backend节点,目前可以被绝大部分容器平台支持,例如Kubernetes,Swa ...

  6. mysql 添加注释

    给表添加注释:  alter table m_tb100_disabledsoldier comment '残疾士兵';   给表中的字段添加注释alter table m_tb100_disable ...

  7. [原创]Python免杀ShellCode加载器(Cobaltstrike/Metasploit)

    0x001 原理 采用分离法,即将ShellCode和加载器分离.方法较LOW但免杀. 本文主要将ShellCode转成HEX,再通过加载器执行ShellCode. PS: 何为SC加载器,即专门用于 ...

  8. 031 Spring Data Elasticsearch学习笔记---重点掌握第5节高级查询和第6节聚合部分

    Elasticsearch提供的Java客户端有一些不太方便的地方: 很多地方需要拼接Json字符串,在java中拼接字符串有多恐怖你应该懂的 需要自己把对象序列化为json存储 查询到结果也需要自己 ...

  9. [转帖]String、StringBuilder与StringBuffer

    String.StringBuilder与StringBuffer https://www.jianshu.com/p/37f3799bdb56 1.String String本质 String是不可 ...

  10. 【Appium + Python3】之安卓8.1,使用xpath定位不到元素

    desired_cap = { "deviceName":"vivo", # 真机名称 "platformName":"andro ...